Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrspatialreference.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRSpatialReference class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999,  Les Technologies SoftMap Inc.
9
 * Copyright (c) 2008-2018, Even Rouault <even.rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "ogr_spatialref.h"
16
17
#include <cmath>
18
#include <cstddef>
19
#include <cstdio>
20
#include <cstdlib>
21
#include <cstring>
22
#include <limits>
23
#include <string>
24
#include <mutex>
25
#include <set>
26
#include <vector>
27
28
#include "cpl_atomic_ops.h"
29
#include "cpl_conv.h"
30
#include "cpl_csv.h"
31
#include "cpl_error.h"
32
#include "cpl_error_internal.h"
33
#include "cpl_http.h"
34
#include "cpl_json.h"
35
#include "cpl_multiproc.h"
36
#include "cpl_string.h"
37
#include "cpl_vsi.h"
38
#include "ogr_core.h"
39
#include "ogr_p.h"
40
#include "ogr_proj_p.h"
41
#include "ogr_srs_api.h"
42
#include "ogrmitabspatialref.h"
43
44
#include "proj.h"
45
#include "proj_experimental.h"
46
#include "proj_constants.h"
47
48
bool GDALThreadLocalDatasetCacheIsInDestruction();
49
50
// Exists since 8.0.1
51
#ifndef PROJ_AT_LEAST_VERSION
52
#define PROJ_COMPUTE_VERSION(maj, min, patch)                                  \
53
    ((maj) * 10000 + (min) * 100 + (patch))
54
#define PROJ_VERSION_NUMBER                                                    \
55
    PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR,               \
56
                         PROJ_VERSION_PATCH)
57
#define PROJ_AT_LEAST_VERSION(maj, min, patch)                                 \
58
    (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
59
#endif
60
61
0
#define STRINGIFY(s) #s
62
0
#define XSTRINGIFY(s) STRINGIFY(s)
63
64
struct OGRSpatialReference::Private
65
{
66
    struct Listener final : public OGR_SRSNode::Listener
67
    {
68
        OGRSpatialReference::Private *m_poObj = nullptr;
69
70
0
        explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
71
0
        {
72
0
        }
73
74
        Listener(const Listener &) = delete;
75
        Listener &operator=(const Listener &) = delete;
76
77
        void notifyChange(OGR_SRSNode *) override;
78
    };
79
80
    OGRSpatialReference *m_poSelf = nullptr;
81
    PJ *m_pj_crs = nullptr;
82
83
    // Temporary state used for object construction
84
    PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
85
    CPLString m_osPrimeMeridianName{};
86
    CPLString m_osAngularUnits{};
87
    CPLString m_osLinearUnits{};
88
    CPLString m_osAxisName[3]{};
89
90
    std::vector<std::string> m_wktImportWarnings{};
91
    std::vector<std::string> m_wktImportErrors{};
92
    CPLString m_osAreaName{};
93
    CPLString m_celestialBodyName{};
94
95
    bool m_bIsThreadSafe = false;
96
    bool m_bNodesChanged = false;
97
    bool m_bNodesWKT2 = false;
98
    OGR_SRSNode *m_poRoot = nullptr;
99
100
    double dfFromGreenwich = 0.0;
101
    double dfToMeter = 0.0;
102
    double dfToDegrees = 0.0;
103
    double m_dfAngularUnitToRadian = 0.0;
104
105
    int nRefCount = 1;
106
    int bNormInfoSet = FALSE;
107
108
    PJ *m_pj_geod_base_crs_temp = nullptr;
109
    PJ *m_pj_proj_crs_cs_temp = nullptr;
110
111
    bool m_pj_crs_modified_during_demote = false;
112
    PJ *m_pj_bound_crs_target = nullptr;
113
    PJ *m_pj_bound_crs_co = nullptr;
114
    PJ *m_pj_crs_backup = nullptr;
115
    OGR_SRSNode *m_poRootBackup = nullptr;
116
117
    bool m_bMorphToESRI = false;
118
    bool m_bHasCenterLong = false;
119
120
    std::shared_ptr<Listener> m_poListener{};
121
122
    std::recursive_mutex m_mutex{};
123
124
    OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
125
    std::vector<int> m_axisMapping{1, 2, 3};
126
127
    double m_coordinateEpoch = 0;  // as decimal year
128
129
    explicit Private(OGRSpatialReference *poSelf);
130
    ~Private();
131
    Private(const Private &) = delete;
132
    Private &operator=(const Private &) = delete;
133
134
    void SetThreadSafe()
135
0
    {
136
0
        m_bIsThreadSafe = true;
137
0
    }
138
139
    void clear();
140
    void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
141
    void setRoot(OGR_SRSNode *poRoot);
142
    void refreshProjObj();
143
    void nodesChanged();
144
    void refreshRootFromProjObj(bool bForceWKT2);
145
    void invalidateNodes();
146
147
    void setMorphToESRI(bool b);
148
149
    PJ *getGeodBaseCRS();
150
    PJ *getProjCRSCoordSys();
151
152
    const char *getProjCRSName();
153
    OGRErr replaceConversionAndUnref(PJ *conv);
154
155
    void demoteFromBoundCRS();
156
    void undoDemoteFromBoundCRS();
157
158
    PJ_CONTEXT *getPROJContext()
159
0
    {
160
0
        return OSRGetProjTLSContext();
161
0
    }
162
163
    const char *nullifyTargetKeyIfPossible(const char *pszTargetKey);
164
165
    void refreshAxisMapping();
166
167
    // This structures enables locking during calls to OGRSpatialReference
168
    // public methods. Locking is only needed for instances of
169
    // OGRSpatialReference that have been asked to be thread-safe at
170
    // construction.
171
    // The lock is not just for a single call to OGRSpatialReference::Private,
172
    // but for the series of calls done by a OGRSpatialReference method.
173
    // We need a recursive mutex, because some OGRSpatialReference methods
174
    // may call other ones.
175
    struct OptionalLockGuard
176
    {
177
        Private &m_private;
178
179
0
        explicit OptionalLockGuard(Private *p) : m_private(*p)
180
0
        {
181
0
            if (m_private.m_bIsThreadSafe)
182
0
                m_private.m_mutex.lock();
183
0
        }
184
185
        ~OptionalLockGuard()
186
0
        {
187
0
            if (m_private.m_bIsThreadSafe)
188
0
                m_private.m_mutex.unlock();
189
0
        }
190
    };
191
192
    inline OptionalLockGuard GetOptionalLockGuard()
193
0
    {
194
0
        return OptionalLockGuard(this);
195
0
    }
196
};
197
198
void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
199
0
{
200
0
    m_poObj->nodesChanged();
201
0
}
202
203
#define TAKE_OPTIONAL_LOCK()                                                   \
204
0
    auto lock = d->GetOptionalLockGuard();                                     \
205
0
    CPL_IGNORE_RET_VAL(lock)
206
207
static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
208
0
{
209
0
    const char *pszDefaultAMS =
210
0
        CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
211
0
    if (pszDefaultAMS)
212
0
    {
213
0
        if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
214
0
            return OAMS_AUTHORITY_COMPLIANT;
215
0
        else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
216
0
            return OAMS_TRADITIONAL_GIS_ORDER;
217
0
        else
218
0
        {
219
0
            CPLError(CE_Failure, CPLE_AppDefined,
220
0
                     "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
221
0
                     pszDefaultAMS);
222
0
        }
223
0
    }
224
0
    return OAMS_AUTHORITY_COMPLIANT;
225
0
}
226
227
OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
228
0
    : m_poSelf(poSelf), m_poListener(std::make_shared<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
        PROJ_STRING_LIST warnings = nullptr;
374
0
        PROJ_STRING_LIST errors = nullptr;
375
0
        setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
376
0
                                      &warnings, &errors));
377
0
        for (auto iter = warnings; iter && *iter; ++iter)
378
0
        {
379
0
            m_wktImportWarnings.push_back(*iter);
380
0
        }
381
0
        for (auto iter = errors; iter && *iter; ++iter)
382
0
        {
383
0
            m_wktImportErrors.push_back(*iter);
384
0
        }
385
0
        proj_string_list_destroy(warnings);
386
0
        proj_string_list_destroy(errors);
387
388
0
        CPLFree(pszWKT);
389
390
0
        m_poRoot = poRootBackup;
391
0
        m_bNodesChanged = false;
392
0
    }
393
0
}
394
395
void OGRSpatialReference::Private::refreshRootFromProjObj(bool bForceWKT2)
396
0
{
397
0
    CPLAssert(m_poRoot == nullptr);
398
399
0
    if (m_pj_crs)
400
0
    {
401
0
        CPLStringList aosOptions;
402
0
        if (!m_bMorphToESRI)
403
0
        {
404
0
            aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
405
0
            aosOptions.SetNameValue("MULTILINE", "NO");
406
0
        }
407
0
        aosOptions.SetNameValue("STRICT", "NO");
408
409
0
        const char *pszWKT = nullptr;
410
0
        if (!bForceWKT2)
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
 */
936
937
void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
938
0
{
939
0
    delete poSRS;
940
0
}
941
942
/************************************************************************/
943
/*                     OSRDestroySpatialReference()                     */
944
/************************************************************************/
945
946
/**
947
 * \brief OGRSpatialReference destructor.
948
 *
949
 * This function is the same as OGRSpatialReference::~OGRSpatialReference()
950
 * and OGRSpatialReference::DestroySpatialReference()
951
 *
952
 * @param hSRS the object to delete
953
 */
954
void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
955
956
0
{
957
0
    delete ToPointer(hSRS);
958
0
}
959
960
/************************************************************************/
961
/*                               Clear()                                */
962
/************************************************************************/
963
964
/**
965
 * \brief Wipe current definition.
966
 *
967
 * Returns OGRSpatialReference to a state with no definition, as it
968
 * exists when first created.  It does not affect reference counts.
969
 */
970
971
void OGRSpatialReference::Clear()
972
973
0
{
974
0
    d->clear();
975
0
}
976
977
/************************************************************************/
978
/*                             operator=()                              */
979
/************************************************************************/
980
981
/** Assignment operator.
982
 * @param oSource SRS to assign to *this
983
 * @return *this
984
 */
985
OGRSpatialReference &
986
OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
987
988
0
{
989
0
    if (&oSource != this)
990
0
    {
991
0
        Clear();
992
#ifdef CPPCHECK
993
        // Otherwise cppcheck would protest that nRefCount isn't modified
994
        d->nRefCount = (d->nRefCount + 1) - 1;
995
#endif
996
997
0
        oSource.d->refreshProjObj();
998
0
        if (oSource.d->m_pj_crs)
999
0
            d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
1000
0
        if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
1001
0
            SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1002
0
        else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
1003
0
            SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
1004
1005
0
        d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
1006
0
    }
1007
1008
0
    return *this;
1009
0
}
1010
1011
/************************************************************************/
1012
/*                             operator=()                              */
1013
/************************************************************************/
1014
1015
/** Move assignment operator.
1016
 * @param oSource SRS to assign to *this
1017
 * @return *this
1018
 */
1019
OGRSpatialReference &
1020
OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1021
1022
0
{
1023
0
    if (&oSource != this)
1024
0
    {
1025
0
        d = std::move(oSource.d);
1026
0
    }
1027
1028
0
    return *this;
1029
0
}
1030
1031
/************************************************************************/
1032
/*                       AssignAndSetThreadSafe()                       */
1033
/************************************************************************/
1034
1035
/** Assignment method, with thread-safety.
1036
 *
1037
 * Same as an assignment operator, but asking also that the *this instance
1038
 * becomes thread-safe.
1039
 *
1040
 * @param oSource SRS to assign to *this
1041
 * @return *this
1042
 * @since 3.10
1043
 */
1044
1045
OGRSpatialReference &
1046
OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1047
0
{
1048
0
    *this = oSource;
1049
0
    d->SetThreadSafe();
1050
0
    return *this;
1051
0
}
1052
1053
/************************************************************************/
1054
/*                             Reference()                              */
1055
/************************************************************************/
1056
1057
/**
1058
 * \brief Increments the reference count by one.
1059
 *
1060
 * The reference count is used keep track of the number of OGRGeometry objects
1061
 * referencing this SRS.
1062
 *
1063
 * The method does the same thing as the C function OSRReference().
1064
 *
1065
 * @return the updated reference count.
1066
 */
1067
1068
int OGRSpatialReference::Reference()
1069
1070
0
{
1071
0
    return CPLAtomicInc(&d->nRefCount);
1072
0
}
1073
1074
/************************************************************************/
1075
/*                            OSRReference()                            */
1076
/************************************************************************/
1077
1078
/**
1079
 * \brief Increments the reference count by one.
1080
 *
1081
 * This function is the same as OGRSpatialReference::Reference()
1082
 */
1083
int OSRReference(OGRSpatialReferenceH hSRS)
1084
1085
0
{
1086
0
    VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1087
1088
0
    return ToPointer(hSRS)->Reference();
1089
0
}
1090
1091
/************************************************************************/
1092
/*                            Dereference()                             */
1093
/************************************************************************/
1094
1095
/**
1096
 * \brief Decrements the reference count by one.
1097
 *
1098
 * The method does the same thing as the C function OSRDereference().
1099
 *
1100
 * @return the updated reference count.
1101
 */
1102
1103
int OGRSpatialReference::Dereference()
1104
1105
0
{
1106
0
    if (d->nRefCount <= 0)
1107
0
        CPLDebug("OSR",
1108
0
                 "Dereference() called on an object with refcount %d,"
1109
0
                 "likely already destroyed!",
1110
0
                 d->nRefCount);
1111
0
    return CPLAtomicDec(&d->nRefCount);
1112
0
}
1113
1114
/************************************************************************/
1115
/*                           OSRDereference()                           */
1116
/************************************************************************/
1117
1118
/**
1119
 * \brief Decrements the reference count by one.
1120
 *
1121
 * This function is the same as OGRSpatialReference::Dereference()
1122
 */
1123
int OSRDereference(OGRSpatialReferenceH hSRS)
1124
1125
0
{
1126
0
    VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1127
1128
0
    return ToPointer(hSRS)->Dereference();
1129
0
}
1130
1131
/************************************************************************/
1132
/*                         GetReferenceCount()                          */
1133
/************************************************************************/
1134
1135
/**
1136
 * \brief Fetch current reference count.
1137
 *
1138
 * @return the current reference count.
1139
 */
1140
int OGRSpatialReference::GetReferenceCount() const
1141
0
{
1142
0
    return d->nRefCount;
1143
0
}
1144
1145
/************************************************************************/
1146
/*                              Release()                               */
1147
/************************************************************************/
1148
1149
/**
1150
 * \brief Decrements the reference count by one, and destroy if zero.
1151
 *
1152
 * The method does the same thing as the C function OSRRelease().
1153
 */
1154
1155
void OGRSpatialReference::Release()
1156
1157
0
{
1158
0
    if (Dereference() <= 0)
1159
0
        delete this;
1160
0
}
1161
1162
/************************************************************************/
1163
/*                             OSRRelease()                             */
1164
/************************************************************************/
1165
1166
/**
1167
 * \brief Decrements the reference count by one, and destroy if zero.
1168
 *
1169
 * This function is the same as OGRSpatialReference::Release()
1170
 */
1171
void OSRRelease(OGRSpatialReferenceH hSRS)
1172
1173
0
{
1174
0
    VALIDATE_POINTER0(hSRS, "OSRRelease");
1175
1176
0
    ToPointer(hSRS)->Release();
1177
0
}
1178
1179
OGR_SRSNode *OGRSpatialReference::GetRoot()
1180
0
{
1181
0
    TAKE_OPTIONAL_LOCK();
1182
1183
0
    if (!d->m_poRoot)
1184
0
    {
1185
0
        d->refreshRootFromProjObj(false);
1186
0
    }
1187
0
    return d->m_poRoot;
1188
0
}
1189
1190
const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1191
0
{
1192
0
    TAKE_OPTIONAL_LOCK();
1193
1194
0
    if (!d->m_poRoot)
1195
0
    {
1196
0
        d->refreshRootFromProjObj(false);
1197
0
    }
1198
0
    return d->m_poRoot;
1199
0
}
1200
1201
/************************************************************************/
1202
/*                              SetRoot()                               */
1203
/************************************************************************/
1204
1205
/**
1206
 * \brief Set the root SRS node.
1207
 *
1208
 * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1209
 * as part of assigning the new root.  Ownership of the passed OGR_SRSNode is
1210
 * is assumed by the OGRSpatialReference.
1211
 *
1212
 * @param poNewRoot object to assign as root.
1213
 */
1214
1215
void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1216
1217
0
{
1218
0
    if (d->m_poRoot != poNewRoot)
1219
0
    {
1220
0
        delete d->m_poRoot;
1221
0
        d->setRoot(poNewRoot);
1222
0
    }
1223
0
}
1224
1225
/************************************************************************/
1226
/*                            GetAttrNode()                             */
1227
/************************************************************************/
1228
1229
/**
1230
 * \brief Find named node in tree.
1231
 *
1232
 * This method does a pre-order traversal of the node tree searching for
1233
 * a node with this exact value (case insensitive), and returns it.  Leaf
1234
 * nodes are not considered, under the assumption that they are just
1235
 * attribute value nodes.
1236
 *
1237
 * If a node appears more than once in the tree (such as UNIT for instance),
1238
 * the first encountered will be returned.  Use GetNode() on a subtree to be
1239
 * more specific.
1240
 *
1241
 * @param pszNodePath the name of the node to search for.  May contain multiple
1242
 * components such as "GEOGCS|UNIT".
1243
 *
1244
 * @return a pointer to the node found, or NULL if none.
1245
 */
1246
1247
OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1248
1249
0
{
1250
0
    if (strstr(pszNodePath, "CONVERSION") && !d->m_bNodesWKT2)
1251
0
    {
1252
0
        d->invalidateNodes();
1253
0
        d->refreshRootFromProjObj(/* bForceWKT2 = */ true);
1254
0
    }
1255
1256
0
    if (strchr(pszNodePath, '|') == nullptr)
1257
0
    {
1258
        // Fast path
1259
0
        OGR_SRSNode *poNode = GetRoot();
1260
0
        if (poNode)
1261
0
            poNode = poNode->GetNode(pszNodePath);
1262
0
        return poNode;
1263
0
    }
1264
1265
0
    char **papszPathTokens =
1266
0
        CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1267
1268
0
    if (CSLCount(papszPathTokens) < 1)
1269
0
    {
1270
0
        CSLDestroy(papszPathTokens);
1271
0
        return nullptr;
1272
0
    }
1273
1274
0
    OGR_SRSNode *poNode = GetRoot();
1275
0
    for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1276
0
    {
1277
0
        poNode = poNode->GetNode(papszPathTokens[i]);
1278
0
    }
1279
1280
0
    CSLDestroy(papszPathTokens);
1281
1282
0
    return poNode;
1283
0
}
1284
1285
/**
1286
 * \brief Find named node in tree.
1287
 *
1288
 * This method does a pre-order traversal of the node tree searching for
1289
 * a node with this exact value (case insensitive), and returns it.  Leaf
1290
 * nodes are not considered, under the assumption that they are just
1291
 * attribute value nodes.
1292
 *
1293
 * If a node appears more than once in the tree (such as UNIT for instance),
1294
 * the first encountered will be returned.  Use GetNode() on a subtree to be
1295
 * more specific.
1296
 *
1297
 * @param pszNodePath the name of the node to search for.  May contain multiple
1298
 * components such as "GEOGCS|UNIT".
1299
 *
1300
 * @return a pointer to the node found, or NULL if none.
1301
 */
1302
1303
const OGR_SRSNode *
1304
OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1305
1306
0
{
1307
0
    OGR_SRSNode *poNode =
1308
0
        const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1309
1310
0
    return poNode;
1311
0
}
1312
1313
/************************************************************************/
1314
/*                            GetAttrValue()                            */
1315
/************************************************************************/
1316
1317
/**
1318
 * \brief Fetch indicated attribute of named node.
1319
 *
1320
 * This method uses GetAttrNode() to find the named node, and then extracts
1321
 * the value of the indicated child.  Thus a call to GetAttrValue("UNIT",1)
1322
 * would return the second child of the UNIT node, which is normally the
1323
 * length of the linear unit in meters.
1324
 *
1325
 * This method does the same thing as the C function OSRGetAttrValue().
1326
 *
1327
 * @param pszNodeName the tree node to look for (case insensitive).
1328
 * @param iAttr the child of the node to fetch (zero based).
1329
 *
1330
 * @return the requested value, or NULL if it fails for any reason.
1331
 */
1332
1333
const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1334
                                              int iAttr) const
1335
1336
0
{
1337
0
    const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1338
0
    if (poNode == nullptr)
1339
0
    {
1340
0
        if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1341
0
        {
1342
0
            return GetAttrValue("METHOD", iAttr);
1343
0
        }
1344
0
        else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1345
0
        {
1346
0
            return GetAttrValue("PROJCRS|METHOD", iAttr);
1347
0
        }
1348
0
        else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1349
0
        {
1350
0
            return GetAttrValue("PROJCRS", iAttr);
1351
0
        }
1352
0
        return nullptr;
1353
0
    }
1354
1355
0
    if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1356
0
        return nullptr;
1357
1358
0
    return poNode->GetChild(iAttr)->GetValue();
1359
0
}
1360
1361
/************************************************************************/
1362
/*                          OSRGetAttrValue()                           */
1363
/************************************************************************/
1364
1365
/**
1366
 * \brief Fetch indicated attribute of named node.
1367
 *
1368
 * This function is the same as OGRSpatialReference::GetAttrValue()
1369
 */
1370
const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1371
                                        const char *pszKey, int iChild)
1372
1373
0
{
1374
0
    VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1375
1376
0
    return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1377
0
}
1378
1379
/************************************************************************/
1380
/*                              GetName()                               */
1381
/************************************************************************/
1382
1383
/**
1384
 * \brief Return the CRS name.
1385
 *
1386
 * The returned value is only short lived and should not be used after other
1387
 * calls to methods on this object.
1388
 *
1389
 * @since GDAL 3.0
1390
 */
1391
1392
const char *OGRSpatialReference::GetName() const
1393
0
{
1394
0
    TAKE_OPTIONAL_LOCK();
1395
1396
0
    d->refreshProjObj();
1397
0
    if (!d->m_pj_crs)
1398
0
        return nullptr;
1399
0
    const char *pszName = proj_get_name(d->m_pj_crs);
1400
#if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1401
    if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1402
    {
1403
        // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1404
        PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1405
        if (baseCRS)
1406
        {
1407
            pszName = proj_get_name(baseCRS);
1408
            // pszName still remains valid after proj_destroy(), since
1409
            // d->m_pj_crs keeps a reference to the base CRS C++ object.
1410
            proj_destroy(baseCRS);
1411
        }
1412
    }
1413
#endif
1414
0
    return pszName;
1415
0
}
1416
1417
/************************************************************************/
1418
/*                             OSRGetName()                             */
1419
/************************************************************************/
1420
1421
/**
1422
 * \brief Return the CRS name.
1423
 *
1424
 * The returned value is only short lived and should not be used after other
1425
 * calls to methods on this object.
1426
 *
1427
 * @since GDAL 3.0
1428
 */
1429
const char *OSRGetName(OGRSpatialReferenceH hSRS)
1430
1431
0
{
1432
0
    VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1433
1434
0
    return ToPointer(hSRS)->GetName();
1435
0
}
1436
1437
/************************************************************************/
1438
/*                        GetCelestialBodyName()                        */
1439
/************************************************************************/
1440
1441
/**
1442
 * \brief Return the name of the celestial body of this CRS.
1443
 *
1444
 * e.g. "Earth" for an Earth CRS
1445
 *
1446
 * The returned value is only short lived and should not be used after other
1447
 * calls to methods on this object.
1448
 *
1449
 * @since GDAL 3.12 and PROJ 8.1
1450
 */
1451
1452
const char *OGRSpatialReference::GetCelestialBodyName() const
1453
0
{
1454
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
1455
1456
0
    TAKE_OPTIONAL_LOCK();
1457
1458
0
    d->refreshProjObj();
1459
0
    if (!d->m_pj_crs)
1460
0
        return nullptr;
1461
0
    d->demoteFromBoundCRS();
1462
0
    const char *name =
1463
0
        proj_get_celestial_body_name(d->getPROJContext(), d->m_pj_crs);
1464
0
    if (name)
1465
0
    {
1466
0
        d->m_celestialBodyName = name;
1467
0
    }
1468
0
    d->undoDemoteFromBoundCRS();
1469
0
    return d->m_celestialBodyName.c_str();
1470
#else
1471
    if (std::fabs(GetSemiMajor(nullptr) - SRS_WGS84_SEMIMAJOR) <=
1472
        0.05 * SRS_WGS84_SEMIMAJOR)
1473
        return "Earth";
1474
    const char *pszAuthName = GetAuthorityName();
1475
    if (pszAuthName && EQUAL(pszAuthName, "EPSG"))
1476
        return "Earth";
1477
    return nullptr;
1478
#endif
1479
0
}
1480
1481
/************************************************************************/
1482
/*                      OSRGetCelestialBodyName()                       */
1483
/************************************************************************/
1484
1485
/**
1486
 * \brief Return the name of the celestial body of this CRS.
1487
 *
1488
 * e.g. "Earth" for an Earth CRS
1489
 *
1490
 * The returned value is only short lived and should not be used after other
1491
 * calls to methods on this object.
1492
 *
1493
 * @since GDAL 3.12 and PROJ 8.1
1494
 */
1495
1496
const char *OSRGetCelestialBodyName(OGRSpatialReferenceH hSRS)
1497
1498
0
{
1499
0
    VALIDATE_POINTER1(hSRS, "GetCelestialBodyName", nullptr);
1500
1501
0
    return ToPointer(hSRS)->GetCelestialBodyName();
1502
0
}
1503
1504
/************************************************************************/
1505
/*                               Clone()                                */
1506
/************************************************************************/
1507
1508
/**
1509
 * \brief Make a duplicate of this OGRSpatialReference.
1510
 *
1511
 * This method is the same as the C function OSRClone().
1512
 *
1513
 * @return a new SRS, which becomes the responsibility of the caller.
1514
 */
1515
1516
OGRSpatialReference *OGRSpatialReference::Clone() const
1517
1518
0
{
1519
0
    OGRSpatialReference *poNewRef = new OGRSpatialReference();
1520
1521
0
    TAKE_OPTIONAL_LOCK();
1522
1523
0
    d->refreshProjObj();
1524
0
    if (d->m_pj_crs != nullptr)
1525
0
        poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1526
0
    if (d->m_bHasCenterLong && d->m_poRoot)
1527
0
    {
1528
0
        poNewRef->d->setRoot(d->m_poRoot->Clone());
1529
0
    }
1530
0
    poNewRef->d->m_axisMapping = d->m_axisMapping;
1531
0
    poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1532
0
    poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1533
0
    return poNewRef;
1534
0
}
1535
1536
/************************************************************************/
1537
/*                              OSRClone()                              */
1538
/************************************************************************/
1539
1540
/**
1541
 * \brief Make a duplicate of this OGRSpatialReference.
1542
 *
1543
 * This function is the same as OGRSpatialReference::Clone()
1544
 */
1545
OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1546
1547
0
{
1548
0
    VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1549
1550
0
    return ToHandle(ToPointer(hSRS)->Clone());
1551
0
}
1552
1553
/************************************************************************/
1554
/*                            dumpReadable()                            */
1555
/************************************************************************/
1556
1557
/** Dump pretty wkt to stdout, mostly for debugging.
1558
 */
1559
void OGRSpatialReference::dumpReadable()
1560
1561
0
{
1562
0
    char *pszPrettyWkt = nullptr;
1563
1564
0
    const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1565
0
    exportToWkt(&pszPrettyWkt, apszOptions);
1566
0
    printf("%s\n", pszPrettyWkt); /*ok*/
1567
0
    CPLFree(pszPrettyWkt);
1568
0
}
1569
1570
/************************************************************************/
1571
/*                         exportToPrettyWkt()                          */
1572
/************************************************************************/
1573
1574
/**
1575
 * Convert this SRS into a nicely formatted WKT 1 string for display to a
1576
 * person.
1577
 *
1578
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1579
 * Issues</a> page for implementation details of WKT 1 in OGR.
1580
 *
1581
 * Note that the returned WKT string should be freed with
1582
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1583
 *
1584
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1585
 * option. Valid values are the one of the FORMAT option of
1586
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1587
 *
1588
 * This method is the same as the C function OSRExportToPrettyWkt().
1589
 *
1590
 * @param ppszResult the resulting string is returned in this pointer.
1591
 * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1592
 *   stripped off.
1593
 *
1594
 * @return OGRERR_NONE if successful.
1595
 */
1596
1597
OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1598
                                              int bSimplify) const
1599
1600
0
{
1601
0
    CPLStringList aosOptions;
1602
0
    aosOptions.SetNameValue("MULTILINE", "YES");
1603
0
    if (bSimplify)
1604
0
    {
1605
0
        aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1606
0
    }
1607
0
    return exportToWkt(ppszResult, aosOptions.List());
1608
0
}
1609
1610
/************************************************************************/
1611
/*                        OSRExportToPrettyWkt()                        */
1612
/************************************************************************/
1613
1614
/**
1615
 * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1616
 * person.
1617
 *
1618
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1619
 * option. Valid values are the one of the FORMAT option of
1620
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1621
 *
1622
 * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1623
 */
1624
1625
OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1626
                                        char **ppszReturn, int bSimplify)
1627
1628
0
{
1629
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1630
1631
0
    *ppszReturn = nullptr;
1632
1633
0
    return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1634
0
}
1635
1636
/************************************************************************/
1637
/*                            exportToWkt()                             */
1638
/************************************************************************/
1639
1640
/**
1641
 * \brief Convert this SRS into WKT 1 format.
1642
 *
1643
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1644
 * Issues</a> page for implementation details of WKT 1 in OGR.
1645
 *
1646
 * Note that the returned WKT string should be freed with
1647
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1648
 *
1649
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1650
 * option. Valid values are the one of the FORMAT option of
1651
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1652
 *
1653
 * This method is the same as the C function OSRExportToWkt().
1654
 *
1655
 * @param ppszResult the resulting string is returned in this pointer.
1656
 *
1657
 * @return OGRERR_NONE if successful.
1658
 */
1659
1660
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1661
1662
0
{
1663
0
    return exportToWkt(ppszResult, nullptr);
1664
0
}
1665
1666
/************************************************************************/
1667
/*              GDAL_proj_crs_create_bound_crs_to_WGS84()               */
1668
/************************************************************************/
1669
1670
static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1671
                                                   bool onlyIfEPSGCode,
1672
                                                   bool canModifyHorizPart)
1673
0
{
1674
0
    PJ *ret = nullptr;
1675
0
    if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1676
0
    {
1677
0
        auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1678
0
        auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1679
0
        if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1680
0
            vertCRS &&
1681
0
            (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1682
0
        {
1683
0
            auto boundHoriz =
1684
0
                canModifyHorizPart
1685
0
                    ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1686
0
                    : proj_clone(ctx, horizCRS);
1687
0
            auto boundVert =
1688
0
                proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1689
0
            if (boundHoriz && boundVert)
1690
0
            {
1691
0
                ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1692
0
                                               boundHoriz, boundVert);
1693
0
            }
1694
0
            proj_destroy(boundHoriz);
1695
0
            proj_destroy(boundVert);
1696
0
        }
1697
0
        proj_destroy(horizCRS);
1698
0
        proj_destroy(vertCRS);
1699
0
    }
1700
0
    else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1701
0
             (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1702
0
    {
1703
0
        ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1704
0
    }
1705
0
    return ret;
1706
0
}
1707
1708
/************************************************************************/
1709
/*                            exportToWkt()                             */
1710
/************************************************************************/
1711
1712
/**
1713
 * Convert this SRS into a WKT string.
1714
 *
1715
 * Note that the returned WKT string should be freed with
1716
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1717
 *
1718
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1719
 * Issues</a> page for implementation details of WKT 1 in OGR.
1720
 *
1721
 * @param ppszResult the resulting string is returned in this pointer.
1722
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1723
 * supported options are
1724
 * <ul>
1725
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1726
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1727
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1728
 *     node is returned.
1729
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1730
 *     node is returned.
1731
 *     WKT1 is an alias of WKT1_GDAL.
1732
 *     WKT2 will default to the latest revision implemented (currently
1733
 *     WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1734
 * </li>
1735
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1736
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1737
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1738
 * height (for example for use with LAS 1.4 WKT1).
1739
 * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1740
 * </ul>
1741
 *
1742
 * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1743
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1744
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1745
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1746
 * TOWGS84[] node may be added.
1747
 *
1748
 * @return OGRERR_NONE if successful.
1749
 * @since GDAL 3.0
1750
 */
1751
1752
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1753
                                        const char *const *papszOptions) const
1754
0
{
1755
    // In the past calling this method was thread-safe, even if we never
1756
    // guaranteed it. Now proj_as_wkt() will cache the result internally,
1757
    // so this is no longer thread-safe.
1758
0
    std::lock_guard oLock(d->m_mutex);
1759
1760
0
    d->refreshProjObj();
1761
0
    if (!d->m_pj_crs)
1762
0
    {
1763
0
        *ppszResult = CPLStrdup("");
1764
0
        return OGRERR_FAILURE;
1765
0
    }
1766
1767
0
    if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1768
0
    {
1769
0
        return d->m_poRoot->exportToWkt(ppszResult);
1770
0
    }
1771
1772
0
    auto ctxt = d->getPROJContext();
1773
0
    auto wktFormat = PJ_WKT1_GDAL;
1774
0
    const char *pszFormat =
1775
0
        CSLFetchNameValueDef(papszOptions, "FORMAT",
1776
0
                             CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1777
0
    if (EQUAL(pszFormat, "DEFAULT"))
1778
0
        pszFormat = "";
1779
1780
0
    if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1781
0
    {
1782
0
        wktFormat = PJ_WKT1_ESRI;
1783
0
    }
1784
0
    else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1785
0
             EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1786
0
    {
1787
0
        wktFormat = PJ_WKT1_GDAL;
1788
0
    }
1789
0
    else if (EQUAL(pszFormat, "WKT2_2015"))
1790
0
    {
1791
0
        wktFormat = PJ_WKT2_2015;
1792
0
    }
1793
0
    else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1794
0
             EQUAL(pszFormat, "WKT2_2019"))
1795
0
    {
1796
0
        wktFormat = PJ_WKT2_2018;
1797
0
    }
1798
0
    else if (pszFormat[0] == '\0')
1799
0
    {
1800
        // cppcheck-suppress knownConditionTrueFalse
1801
0
        if (IsDerivedGeographic())
1802
0
        {
1803
0
            wktFormat = PJ_WKT2_2018;
1804
0
        }
1805
0
        else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1806
0
                 GetAxesCount() == 3)
1807
0
        {
1808
0
            wktFormat = PJ_WKT2_2018;
1809
0
        }
1810
0
    }
1811
0
    else
1812
0
    {
1813
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1814
0
        *ppszResult = CPLStrdup("");
1815
0
        return OGRERR_FAILURE;
1816
0
    }
1817
1818
0
    CPLStringList aosOptions;
1819
0
    if (wktFormat != PJ_WKT1_ESRI)
1820
0
    {
1821
0
        aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1822
0
    }
1823
0
    aosOptions.SetNameValue(
1824
0
        "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1825
1826
0
    const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1827
0
        papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1828
0
    if (pszAllowEllpsHeightAsVertCS)
1829
0
    {
1830
0
        aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1831
0
                                pszAllowEllpsHeightAsVertCS);
1832
0
    }
1833
1834
0
    PJ *boundCRS = nullptr;
1835
0
    if (wktFormat == PJ_WKT1_GDAL &&
1836
0
        CPLTestBool(CSLFetchNameValueDef(
1837
0
            papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1838
0
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1839
0
    {
1840
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1841
0
            d->getPROJContext(), d->m_pj_crs, true, true);
1842
0
    }
1843
1844
0
    CPLErrorAccumulator oErrorAccumulator;
1845
0
    const char *pszWKT;
1846
0
    {
1847
0
        auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1848
0
        CPL_IGNORE_RET_VAL(oAccumulator);
1849
0
        pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1850
0
                             aosOptions.List());
1851
0
    }
1852
0
    for (const auto &oError : oErrorAccumulator.GetErrors())
1853
0
    {
1854
0
        if (pszFormat[0] == '\0' &&
1855
0
            (oError.msg.find("Unsupported conversion method") !=
1856
0
                 std::string::npos ||
1857
0
             oError.msg.find("can only be exported to WKT2") !=
1858
0
                 std::string::npos ||
1859
0
             oError.msg.find("can only be exported since WKT2:2019") !=
1860
0
                 std::string::npos))
1861
0
        {
1862
0
            CPLErrorReset();
1863
            // If we cannot export in the default mode (WKT1), retry with WKT2
1864
0
            pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1865
0
                                 PJ_WKT2_2018, aosOptions.List());
1866
0
            break;
1867
0
        }
1868
0
        CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1869
0
    }
1870
1871
0
    if (!pszWKT)
1872
0
    {
1873
0
        *ppszResult = CPLStrdup("");
1874
0
        proj_destroy(boundCRS);
1875
0
        return OGRERR_FAILURE;
1876
0
    }
1877
1878
0
    if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1879
0
    {
1880
0
        OGR_SRSNode oRoot;
1881
0
        oRoot.importFromWkt(&pszWKT);
1882
0
        oRoot.StripNodes("AXIS");
1883
0
        if (EQUAL(pszFormat, "SFSQL"))
1884
0
        {
1885
0
            oRoot.StripNodes("TOWGS84");
1886
0
        }
1887
0
        oRoot.StripNodes("AUTHORITY");
1888
0
        oRoot.StripNodes("EXTENSION");
1889
0
        OGRErr eErr;
1890
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1891
0
            eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1892
0
        else
1893
0
            eErr = oRoot.exportToWkt(ppszResult);
1894
0
        proj_destroy(boundCRS);
1895
0
        return eErr;
1896
0
    }
1897
1898
0
    *ppszResult = CPLStrdup(pszWKT);
1899
1900
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1901
    if (wktFormat == PJ_WKT2_2018)
1902
    {
1903
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1904
        // related to a wrong EPSG code assigned to UTM South conversions
1905
        char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1906
        if (pszPtr)
1907
        {
1908
            pszPtr += strlen("CONVERSION[\"UTM zone ");
1909
            const int nZone = atoi(pszPtr);
1910
            while (*pszPtr >= '0' && *pszPtr <= '9')
1911
                ++pszPtr;
1912
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1913
                pszPtr[1] == '"' && pszPtr[2] == ',')
1914
            {
1915
                pszPtr += 3;
1916
                int nLevel = 0;
1917
                bool bInString = false;
1918
                // Find the ID node corresponding to this CONVERSION node
1919
                while (*pszPtr)
1920
                {
1921
                    if (bInString)
1922
                    {
1923
                        if (*pszPtr == '"' && pszPtr[1] == '"')
1924
                        {
1925
                            ++pszPtr;
1926
                        }
1927
                        else if (*pszPtr == '"')
1928
                        {
1929
                            bInString = false;
1930
                        }
1931
                    }
1932
                    else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1933
                    {
1934
                        if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1935
                                                              17000 + nZone)))
1936
                        {
1937
                            CPLAssert(pszPtr[11] == '7');
1938
                            CPLAssert(pszPtr[12] == '0');
1939
                            pszPtr[11] = '6';
1940
                            pszPtr[12] = '1';
1941
                        }
1942
                        break;
1943
                    }
1944
                    else if (*pszPtr == '"')
1945
                    {
1946
                        bInString = true;
1947
                    }
1948
                    else if (*pszPtr == '[')
1949
                    {
1950
                        ++nLevel;
1951
                    }
1952
                    else if (*pszPtr == ']')
1953
                    {
1954
                        --nLevel;
1955
                    }
1956
1957
                    ++pszPtr;
1958
                }
1959
            }
1960
        }
1961
    }
1962
#endif
1963
1964
0
    proj_destroy(boundCRS);
1965
0
    return OGRERR_NONE;
1966
0
}
1967
1968
/************************************************************************/
1969
/*                            exportToWkt()                             */
1970
/************************************************************************/
1971
1972
/**
1973
 * Convert this SRS into a WKT string.
1974
 *
1975
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1976
 * Issues</a> page for implementation details of WKT 1 in OGR.
1977
 *
1978
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1979
 * supported options are
1980
 * <ul>
1981
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1982
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1983
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1984
 *     node is returned.
1985
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1986
 *     node is returned.
1987
 *     WKT1 is an alias of WKT1_GDAL.
1988
 *     WKT2 will default to the latest revision implemented (currently
1989
 *     WKT2_2019)
1990
 * </li>
1991
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1992
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1993
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1994
 * height (for example for use with LAS 1.4 WKT1).
1995
 * Requires PROJ 7.2.1.</li>
1996
 * </ul>
1997
 *
1998
 * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1999
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
2000
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
2001
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
2002
 * TOWGS84[] node may be added.
2003
 *
2004
 * @return a non-empty string if successful.
2005
 * @since GDAL 3.9
2006
 */
2007
2008
std::string
2009
OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
2010
0
{
2011
0
    std::string osWKT;
2012
0
    char *pszWKT = nullptr;
2013
0
    if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
2014
0
        osWKT = pszWKT;
2015
0
    CPLFree(pszWKT);
2016
0
    return osWKT;
2017
0
}
2018
2019
/************************************************************************/
2020
/*                           OSRExportToWkt()                           */
2021
/************************************************************************/
2022
2023
/**
2024
 * \brief Convert this SRS into WKT 1 format.
2025
 *
2026
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2027
 * Issues</a> page for implementation details of WKT in OGR.
2028
 *
2029
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
2030
 * option. Valid values are the one of the FORMAT option of
2031
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
2032
 *
2033
 * This function is the same as OGRSpatialReference::exportToWkt().
2034
 */
2035
2036
OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
2037
2038
0
{
2039
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
2040
2041
0
    *ppszReturn = nullptr;
2042
2043
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn);
2044
0
}
2045
2046
/************************************************************************/
2047
/*                          OSRExportToWktEx()                          */
2048
/************************************************************************/
2049
2050
/**
2051
 * \brief Convert this SRS into WKT format.
2052
 *
2053
 * This function is the same as OGRSpatialReference::exportToWkt(char **
2054
 * ppszResult,const char* const* papszOptions ) const
2055
 *
2056
 * @since GDAL 3.0
2057
 */
2058
2059
OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
2060
                        const char *const *papszOptions)
2061
0
{
2062
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
2063
2064
0
    *ppszReturn = nullptr;
2065
2066
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
2067
0
}
2068
2069
/************************************************************************/
2070
/*                          exportToPROJJSON()                          */
2071
/************************************************************************/
2072
2073
/**
2074
 * Convert this SRS into a PROJJSON string.
2075
 *
2076
 * Note that the returned JSON string should be freed with
2077
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
2078
 *
2079
 * @param ppszResult the resulting string is returned in this pointer.
2080
 * @param papszOptions NULL terminated list of options, or NULL. Currently
2081
 * supported options are
2082
 * <ul>
2083
 * <li>MULTILINE=YES/NO. Defaults to YES</li>
2084
 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2085
 * on).</li>
2086
 * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2087
 * disable it.</li>
2088
 * </ul>
2089
 *
2090
 * @return OGRERR_NONE if successful.
2091
 * @since GDAL 3.1 and PROJ 6.2
2092
 */
2093
2094
OGRErr OGRSpatialReference::exportToPROJJSON(
2095
    char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2096
0
{
2097
0
    TAKE_OPTIONAL_LOCK();
2098
2099
0
    d->refreshProjObj();
2100
0
    if (!d->m_pj_crs)
2101
0
    {
2102
0
        *ppszResult = nullptr;
2103
0
        return OGRERR_FAILURE;
2104
0
    }
2105
2106
0
    const char *pszPROJJSON =
2107
0
        proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2108
2109
0
    if (!pszPROJJSON)
2110
0
    {
2111
0
        *ppszResult = CPLStrdup("");
2112
0
        return OGRERR_FAILURE;
2113
0
    }
2114
2115
0
    *ppszResult = CPLStrdup(pszPROJJSON);
2116
2117
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2118
    {
2119
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2120
        // related to a wrong EPSG code assigned to UTM South conversions
2121
        char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2122
        if (pszPtr)
2123
        {
2124
            pszPtr += strlen("\"name\": \"UTM zone ");
2125
            const int nZone = atoi(pszPtr);
2126
            while (*pszPtr >= '0' && *pszPtr <= '9')
2127
                ++pszPtr;
2128
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2129
            {
2130
                pszPtr += 2;
2131
                int nLevel = 0;
2132
                bool bInString = false;
2133
                // Find the id node corresponding to this conversion node
2134
                while (*pszPtr)
2135
                {
2136
                    if (bInString)
2137
                    {
2138
                        if (*pszPtr == '\\')
2139
                        {
2140
                            ++pszPtr;
2141
                        }
2142
                        else if (*pszPtr == '"')
2143
                        {
2144
                            bInString = false;
2145
                        }
2146
                    }
2147
                    else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2148
                    {
2149
                        const char *pszNextEndCurl = strchr(pszPtr, '}');
2150
                        const char *pszAuthEPSG =
2151
                            strstr(pszPtr, "\"authority\": \"EPSG\"");
2152
                        char *pszCode = strstr(
2153
                            pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2154
                        if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2155
                            pszNextEndCurl - pszAuthEPSG > 0 &&
2156
                            pszNextEndCurl - pszCode > 0)
2157
                        {
2158
                            CPLAssert(pszCode[9] == '7');
2159
                            CPLAssert(pszCode[10] == '0');
2160
                            pszCode[9] = '6';
2161
                            pszCode[10] = '1';
2162
                        }
2163
                        break;
2164
                    }
2165
                    else if (*pszPtr == '"')
2166
                    {
2167
                        bInString = true;
2168
                    }
2169
                    else if (*pszPtr == '{' || *pszPtr == '[')
2170
                    {
2171
                        ++nLevel;
2172
                    }
2173
                    else if (*pszPtr == '}' || *pszPtr == ']')
2174
                    {
2175
                        --nLevel;
2176
                    }
2177
2178
                    ++pszPtr;
2179
                }
2180
            }
2181
        }
2182
    }
2183
#endif
2184
2185
0
    return OGRERR_NONE;
2186
0
}
2187
2188
/************************************************************************/
2189
/*                        OSRExportToPROJJSON()                         */
2190
/************************************************************************/
2191
2192
/**
2193
 * \brief Convert this SRS into PROJJSON format.
2194
 *
2195
 * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2196
 *
2197
 * @since GDAL 3.1 and PROJ 6.2
2198
 */
2199
2200
OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2201
                           const char *const *papszOptions)
2202
0
{
2203
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2204
2205
0
    *ppszReturn = nullptr;
2206
2207
0
    return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2208
0
}
2209
2210
/************************************************************************/
2211
/*                           importFromWkt()                            */
2212
/************************************************************************/
2213
2214
/**
2215
 * \brief Import from WKT string.
2216
 *
2217
 * This method will wipe the existing SRS definition, and
2218
 * reassign it based on the contents of the passed WKT string.  Only as
2219
 * much of the input string as needed to construct this SRS is consumed from
2220
 * the input string, and the input string pointer
2221
 * is then updated to point to the remaining (unused) input.
2222
 *
2223
 * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2224
 * the CRS contained in it will be used to fill the OGRSpatialReference object,
2225
 * and the coordinate epoch potentially present used as the coordinate epoch
2226
 * property of the OGRSpatialReference object.
2227
 *
2228
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2229
 * Issues</a> page for implementation details of WKT in OGR.
2230
 *
2231
 * This method is the same as the C function OSRImportFromWkt().
2232
 *
2233
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2234
 * point to remaining unused input text.
2235
 *
2236
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2237
 * fails for any reason.
2238
 */
2239
2240
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2241
2242
0
{
2243
0
    return importFromWkt(ppszInput, nullptr);
2244
0
}
2245
2246
/************************************************************************/
2247
/*                           importFromWkt()                            */
2248
/************************************************************************/
2249
2250
/*! @cond Doxygen_Suppress */
2251
2252
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2253
                                          CSLConstList papszOptions)
2254
2255
0
{
2256
0
    return importFromWkt(&pszInput, papszOptions);
2257
0
}
2258
2259
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2260
                                          CSLConstList papszOptions)
2261
2262
0
{
2263
0
    TAKE_OPTIONAL_LOCK();
2264
2265
0
    if (!ppszInput || !*ppszInput)
2266
0
        return OGRERR_FAILURE;
2267
2268
0
    if (strlen(*ppszInput) > 100 * 1000 &&
2269
0
        CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2270
0
    {
2271
0
        CPLError(CE_Failure, CPLE_NotSupported,
2272
0
                 "Suspiciously large input for importFromWkt(). Rejecting it. "
2273
0
                 "You can remove this limitation by definition the "
2274
0
                 "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2275
0
        return OGRERR_FAILURE;
2276
0
    }
2277
2278
0
    Clear();
2279
2280
0
    bool canCache = false;
2281
0
    auto tlsCache = OSRGetProjTLSCache();
2282
0
    std::string osWkt;
2283
0
    if (**ppszInput)
2284
0
    {
2285
0
        osWkt = *ppszInput;
2286
0
        auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2287
0
        if (cachedObj)
2288
0
        {
2289
0
            d->setPjCRS(cachedObj);
2290
0
        }
2291
0
        else
2292
0
        {
2293
0
            CPLStringList aosOptions(papszOptions);
2294
0
            if (aosOptions.FetchNameValue("STRICT") == nullptr)
2295
0
                aosOptions.SetNameValue("STRICT", "NO");
2296
0
            PROJ_STRING_LIST warnings = nullptr;
2297
0
            PROJ_STRING_LIST errors = nullptr;
2298
0
            auto ctxt = d->getPROJContext();
2299
0
            auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2300
0
                                           &warnings, &errors);
2301
0
            d->setPjCRS(pj);
2302
2303
0
            for (auto iter = warnings; iter && *iter; ++iter)
2304
0
            {
2305
0
                d->m_wktImportWarnings.push_back(*iter);
2306
0
            }
2307
0
            for (auto iter = errors; iter && *iter; ++iter)
2308
0
            {
2309
0
                d->m_wktImportErrors.push_back(*iter);
2310
0
                if (!d->m_pj_crs)
2311
0
                {
2312
0
                    CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2313
0
                }
2314
0
            }
2315
0
            if (warnings == nullptr && errors == nullptr)
2316
0
            {
2317
0
                canCache = true;
2318
0
            }
2319
0
            proj_string_list_destroy(warnings);
2320
0
            proj_string_list_destroy(errors);
2321
0
        }
2322
0
    }
2323
0
    if (!d->m_pj_crs)
2324
0
        return OGRERR_CORRUPT_DATA;
2325
2326
    // Only accept CRS objects
2327
0
    if (!proj_is_crs(d->m_pj_crs))
2328
0
    {
2329
0
        Clear();
2330
0
        return OGRERR_CORRUPT_DATA;
2331
0
    }
2332
2333
0
    if (canCache)
2334
0
    {
2335
0
        tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2336
0
    }
2337
2338
0
    if (strstr(*ppszInput, "CENTER_LONG"))
2339
0
    {
2340
0
        auto poRoot = new OGR_SRSNode();
2341
0
        d->setRoot(poRoot);
2342
0
        const char *pszTmp = *ppszInput;
2343
0
        poRoot->importFromWkt(&pszTmp);
2344
0
        d->m_bHasCenterLong = true;
2345
0
    }
2346
2347
    // TODO? we don't really update correctly since we assume that the
2348
    // passed string is only WKT.
2349
0
    *ppszInput += strlen(*ppszInput);
2350
0
    return OGRERR_NONE;
2351
2352
#if no_longer_implemented_for_now
2353
    /* -------------------------------------------------------------------- */
2354
    /*      The following seems to try and detect and unconsumed            */
2355
    /*      VERTCS[] coordinate system definition (ESRI style) and to       */
2356
    /*      import and attach it to the existing root.  Likely we will      */
2357
    /*      need to extend this somewhat to bring it into an acceptable     */
2358
    /*      OGRSpatialReference organization at some point.                 */
2359
    /* -------------------------------------------------------------------- */
2360
    if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2361
    {
2362
        if (((*ppszInput)[0]) == ',')
2363
            (*ppszInput)++;
2364
        OGR_SRSNode *poNewChild = new OGR_SRSNode();
2365
        poRoot->AddChild(poNewChild);
2366
        return poNewChild->importFromWkt(ppszInput);
2367
    }
2368
#endif
2369
0
}
2370
2371
/*! @endcond */
2372
2373
/**
2374
 * \brief Import from WKT string.
2375
 *
2376
 * This method will wipe the existing SRS definition, and
2377
 * reassign it based on the contents of the passed WKT string.  Only as
2378
 * much of the input string as needed to construct this SRS is consumed from
2379
 * the input string, and the input string pointer
2380
 * is then updated to point to the remaining (unused) input.
2381
 *
2382
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2383
 * Issues</a> page for implementation details of WKT in OGR.
2384
 *
2385
 * This method is the same as the C function OSRImportFromWkt().
2386
 *
2387
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2388
 * point to remaining unused input text.
2389
 *
2390
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2391
 * fails for any reason.
2392
 * @deprecated Use importFromWkt(const char**) or importFromWkt(const
2393
 * char*)
2394
 */
2395
2396
OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2397
2398
0
{
2399
0
    return importFromWkt(const_cast<const char **>(ppszInput));
2400
0
}
2401
2402
/**
2403
 * \brief Import from WKT string.
2404
 *
2405
 * This method will wipe the existing SRS definition, and
2406
 * reassign it based on the contents of the passed WKT string.  Only as
2407
 * much of the input string as needed to construct this SRS is consumed from
2408
 * the input string, and the input string pointer
2409
 * is then updated to point to the remaining (unused) input.
2410
 *
2411
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2412
 * Issues</a> page for implementation details of WKT in OGR.
2413
 *
2414
 * @param pszInput Input WKT
2415
 *
2416
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2417
 * fails for any reason.
2418
 */
2419
2420
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2421
0
{
2422
0
    return importFromWkt(&pszInput);
2423
0
}
2424
2425
/************************************************************************/
2426
/*                              Validate()                              */
2427
/************************************************************************/
2428
2429
/**
2430
 * \brief Validate CRS imported with importFromWkt() or with modified with
2431
 * direct node manipulations. Otherwise the CRS should be always valid.
2432
 *
2433
 * This method attempts to verify that the spatial reference system is
2434
 * well formed, and consists of known tokens.  The validation is not
2435
 * comprehensive.
2436
 *
2437
 * This method is the same as the C function OSRValidate().
2438
 *
2439
 * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2440
 * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2441
 * but contains non-standard PROJECTION[] values.
2442
 */
2443
2444
OGRErr OGRSpatialReference::Validate() const
2445
2446
0
{
2447
0
    TAKE_OPTIONAL_LOCK();
2448
2449
0
    for (const auto &str : d->m_wktImportErrors)
2450
0
    {
2451
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2452
0
    }
2453
0
    for (const auto &str : d->m_wktImportWarnings)
2454
0
    {
2455
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2456
0
    }
2457
0
    if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2458
0
    {
2459
0
        return OGRERR_CORRUPT_DATA;
2460
0
    }
2461
0
    if (!d->m_wktImportWarnings.empty())
2462
0
    {
2463
0
        return OGRERR_UNSUPPORTED_SRS;
2464
0
    }
2465
0
    return OGRERR_NONE;
2466
0
}
2467
2468
/************************************************************************/
2469
/*                            OSRValidate()                             */
2470
/************************************************************************/
2471
/**
2472
 * \brief Validate SRS tokens.
2473
 *
2474
 * This function is the same as the C++ method OGRSpatialReference::Validate().
2475
 */
2476
OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2477
2478
0
{
2479
0
    VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2480
2481
0
    return OGRSpatialReference::FromHandle(hSRS)->Validate();
2482
0
}
2483
2484
/************************************************************************/
2485
/*                          OSRImportFromWkt()                          */
2486
/************************************************************************/
2487
2488
/**
2489
 * \brief Import from WKT string.
2490
 *
2491
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2492
 * Issues</a> page for implementation details of WKT in OGR.
2493
 *
2494
 * This function is the same as OGRSpatialReference::importFromWkt().
2495
 */
2496
2497
OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2498
2499
0
{
2500
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2501
2502
0
    return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2503
0
}
2504
2505
/************************************************************************/
2506
/*                              SetNode()                               */
2507
/************************************************************************/
2508
2509
/**
2510
 * \brief Set attribute value in spatial reference.
2511
 *
2512
 * Missing intermediate nodes in the path will be created if not already
2513
 * in existence.  If the attribute has no children one will be created and
2514
 * assigned the value otherwise the zeroth child will be assigned the value.
2515
 *
2516
 * This method does the same as the C function OSRSetAttrValue().
2517
 *
2518
 * @param pszNodePath full path to attribute to be set.  For instance
2519
 * "PROJCS|GEOGCS|UNIT".
2520
 *
2521
 * @param pszNewNodeValue value to be assigned to node, such as "meter".
2522
 * This may be NULL if you just want to force creation of the intermediate
2523
 * path.
2524
 *
2525
 * @return OGRERR_NONE on success.
2526
 */
2527
2528
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2529
                                    const char *pszNewNodeValue)
2530
2531
0
{
2532
0
    TAKE_OPTIONAL_LOCK();
2533
2534
0
    char **papszPathTokens =
2535
0
        CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2536
2537
0
    if (CSLCount(papszPathTokens) < 1)
2538
0
    {
2539
0
        CSLDestroy(papszPathTokens);
2540
0
        return OGRERR_FAILURE;
2541
0
    }
2542
2543
0
    if (GetRoot() == nullptr ||
2544
0
        !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2545
0
    {
2546
0
        if (EQUAL(papszPathTokens[0], "PROJCS") &&
2547
0
            CSLCount(papszPathTokens) == 1)
2548
0
        {
2549
0
            CSLDestroy(papszPathTokens);
2550
0
            return SetProjCS(pszNewNodeValue);
2551
0
        }
2552
0
        else
2553
0
        {
2554
0
            SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2555
0
        }
2556
0
    }
2557
2558
0
    OGR_SRSNode *poNode = GetRoot();
2559
0
    for (int i = 1; papszPathTokens[i] != nullptr; i++)
2560
0
    {
2561
0
        int j = 0;  // Used after for.
2562
2563
0
        for (; j < poNode->GetChildCount(); j++)
2564
0
        {
2565
0
            if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2566
0
            {
2567
0
                poNode = poNode->GetChild(j);
2568
0
                j = -1;
2569
0
                break;
2570
0
            }
2571
0
        }
2572
2573
0
        if (j != -1)
2574
0
        {
2575
0
            OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2576
0
            poNode->AddChild(poNewNode);
2577
0
            poNode = poNewNode;
2578
0
        }
2579
0
    }
2580
2581
0
    CSLDestroy(papszPathTokens);
2582
2583
0
    if (pszNewNodeValue != nullptr)
2584
0
    {
2585
0
        if (poNode->GetChildCount() > 0)
2586
0
            poNode->GetChild(0)->SetValue(pszNewNodeValue);
2587
0
        else
2588
0
            poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2589
0
    };
2590
0
    return OGRERR_NONE;
2591
0
}
2592
2593
/************************************************************************/
2594
/*                          OSRSetAttrValue()                           */
2595
/************************************************************************/
2596
2597
/**
2598
 * \brief Set attribute value in spatial reference.
2599
 *
2600
 * This function is the same as OGRSpatialReference::SetNode()
2601
 */
2602
OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2603
                                   const char *pszPath, const char *pszValue)
2604
2605
0
{
2606
0
    VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2607
2608
0
    return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2609
0
}
2610
2611
/************************************************************************/
2612
/*                              SetNode()                               */
2613
/************************************************************************/
2614
2615
/**
2616
 * \brief Set attribute value in spatial reference.
2617
 *
2618
 * Missing intermediate nodes in the path will be created if not already
2619
 * in existence.  If the attribute has no children one will be created and
2620
 * assigned the value otherwise the zeroth child will be assigned the value.
2621
 *
2622
 * This method does the same as the C function OSRSetAttrValue().
2623
 *
2624
 * @param pszNodePath full path to attribute to be set.  For instance
2625
 * "PROJCS|GEOGCS|UNIT".
2626
 *
2627
 * @param dfValue value to be assigned to node.
2628
 *
2629
 * @return OGRERR_NONE on success.
2630
 */
2631
2632
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2633
2634
0
{
2635
0
    char szValue[64] = {'\0'};
2636
2637
0
    if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2638
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2639
0
    else
2640
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2641
2642
0
    return SetNode(pszNodePath, szValue);
2643
0
}
2644
2645
/************************************************************************/
2646
/*                          SetAngularUnits()                           */
2647
/************************************************************************/
2648
2649
/**
2650
 * \brief Set the angular units for the geographic coordinate system.
2651
 *
2652
 * This method creates a UNIT subnode with the specified values as a
2653
 * child of the GEOGCS node.
2654
 *
2655
 * This method does the same as the C function OSRSetAngularUnits().
2656
 *
2657
 * @param pszUnitsName the units name to be used.  Some preferred units
2658
 * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2659
 *
2660
 * @param dfInRadians the value to multiple by an angle in the indicated
2661
 * units to transform to radians.  Some standard conversion factors can
2662
 * be found in ogr_srs_api.h.
2663
 *
2664
 * @return OGRERR_NONE on success.
2665
 */
2666
2667
OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2668
                                            double dfInRadians)
2669
2670
0
{
2671
0
    TAKE_OPTIONAL_LOCK();
2672
2673
0
    d->bNormInfoSet = FALSE;
2674
2675
0
    d->refreshProjObj();
2676
0
    if (!d->m_pj_crs)
2677
0
        return OGRERR_FAILURE;
2678
0
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2679
0
    if (!geodCRS)
2680
0
        return OGRERR_FAILURE;
2681
0
    proj_destroy(geodCRS);
2682
0
    d->demoteFromBoundCRS();
2683
0
    d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2684
0
                                               pszUnitsName, dfInRadians,
2685
0
                                               nullptr, nullptr));
2686
0
    d->undoDemoteFromBoundCRS();
2687
2688
0
    d->m_osAngularUnits = pszUnitsName;
2689
0
    d->m_dfAngularUnitToRadian = dfInRadians;
2690
2691
0
    return OGRERR_NONE;
2692
0
}
2693
2694
/************************************************************************/
2695
/*                         OSRSetAngularUnits()                         */
2696
/************************************************************************/
2697
2698
/**
2699
 * \brief Set the angular units for the geographic coordinate system.
2700
 *
2701
 * This function is the same as OGRSpatialReference::SetAngularUnits()
2702
 */
2703
OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2704
                          double dfInRadians)
2705
2706
0
{
2707
0
    VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2708
2709
0
    return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2710
0
}
2711
2712
/************************************************************************/
2713
/*                          GetAngularUnits()                           */
2714
/************************************************************************/
2715
2716
/**
2717
 * \brief Fetch angular geographic coordinate system units.
2718
 *
2719
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2720
 * will be assumed.  This method only checks directly under the GEOGCS node
2721
 * for units.
2722
 *
2723
 * This method does the same thing as the C function OSRGetAngularUnits().
2724
 *
2725
 * @param ppszName a pointer to be updated with the pointer to the units name.
2726
 * The returned value remains internal to the OGRSpatialReference and should
2727
 * not be freed, or modified.  It may be invalidated on the next
2728
 * OGRSpatialReference call.
2729
 *
2730
 * @return the value to multiply by angular distances to transform them to
2731
 * radians.
2732
 */
2733
2734
double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2735
2736
0
{
2737
0
    TAKE_OPTIONAL_LOCK();
2738
2739
0
    d->refreshProjObj();
2740
2741
0
    if (!d->m_osAngularUnits.empty())
2742
0
    {
2743
0
        if (ppszName != nullptr)
2744
0
            *ppszName = d->m_osAngularUnits.c_str();
2745
0
        return d->m_dfAngularUnitToRadian;
2746
0
    }
2747
2748
0
    do
2749
0
    {
2750
0
        if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2751
0
        {
2752
0
            break;
2753
0
        }
2754
2755
0
        auto geodCRS =
2756
0
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2757
0
        if (!geodCRS)
2758
0
        {
2759
0
            break;
2760
0
        }
2761
0
        auto coordSys =
2762
0
            proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2763
0
        proj_destroy(geodCRS);
2764
0
        if (!coordSys)
2765
0
        {
2766
0
            break;
2767
0
        }
2768
0
        if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2769
0
            PJ_CS_TYPE_ELLIPSOIDAL)
2770
0
        {
2771
0
            proj_destroy(coordSys);
2772
0
            break;
2773
0
        }
2774
2775
0
        double dfConvFactor = 0.0;
2776
0
        const char *pszUnitName = nullptr;
2777
0
        if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2778
0
                                   nullptr, nullptr, &dfConvFactor,
2779
0
                                   &pszUnitName, nullptr, nullptr))
2780
0
        {
2781
0
            proj_destroy(coordSys);
2782
0
            break;
2783
0
        }
2784
2785
0
        d->m_osAngularUnits = pszUnitName;
2786
2787
0
        proj_destroy(coordSys);
2788
0
        d->m_dfAngularUnitToRadian = dfConvFactor;
2789
0
    } while (false);
2790
2791
0
    if (d->m_osAngularUnits.empty())
2792
0
    {
2793
0
        d->m_osAngularUnits = "degree";
2794
0
        d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2795
0
    }
2796
2797
0
    if (ppszName != nullptr)
2798
0
        *ppszName = d->m_osAngularUnits.c_str();
2799
0
    return d->m_dfAngularUnitToRadian;
2800
0
}
2801
2802
/**
2803
 * \brief Fetch angular geographic coordinate system units.
2804
 *
2805
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2806
 * will be assumed.  This method only checks directly under the GEOGCS node
2807
 * for units.
2808
 *
2809
 * This method does the same thing as the C function OSRGetAngularUnits().
2810
 *
2811
 * @param ppszName a pointer to be updated with the pointer to the units name.
2812
 * The returned value remains internal to the OGRSpatialReference and should
2813
 * not be freed, or modified.  It may be invalidated on the next
2814
 * OGRSpatialReference call.
2815
 *
2816
 * @return the value to multiply by angular distances to transform them to
2817
 * radians.
2818
 * @deprecated Use GetAngularUnits(const char**) const.
2819
 */
2820
2821
double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2822
2823
0
{
2824
0
    return GetAngularUnits(const_cast<const char **>(ppszName));
2825
0
}
2826
2827
/************************************************************************/
2828
/*                         OSRGetAngularUnits()                         */
2829
/************************************************************************/
2830
2831
/**
2832
 * \brief Fetch angular geographic coordinate system units.
2833
 *
2834
 * This function is the same as OGRSpatialReference::GetAngularUnits()
2835
 */
2836
double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2837
2838
0
{
2839
0
    VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2840
2841
0
    return ToPointer(hSRS)->GetAngularUnits(
2842
0
        const_cast<const char **>(ppszName));
2843
0
}
2844
2845
/************************************************************************/
2846
/*                 SetLinearUnitsAndUpdateParameters()                  */
2847
/************************************************************************/
2848
2849
/**
2850
 * \brief Set the linear units for the projection.
2851
 *
2852
 * This method creates a UNIT subnode with the specified values as a
2853
 * child of the PROJCS or LOCAL_CS node.   It works the same as the
2854
 * SetLinearUnits() method, but it also updates all existing linear
2855
 * projection parameter values from the old units to the new units.
2856
 *
2857
 * @param pszName the units name to be used.  Some preferred units
2858
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2859
 * and SRS_UL_US_FOOT.
2860
 *
2861
 * @param dfInMeters the value to multiple by a length in the indicated
2862
 * units to transform to meters.  Some standard conversion factors can
2863
 * be found in ogr_srs_api.h.
2864
 *
2865
 * @param pszUnitAuthority Unit authority name. Or nullptr
2866
 *
2867
 * @param pszUnitCode Unit code. Or nullptr
2868
 *
2869
 * @return OGRERR_NONE on success.
2870
 */
2871
2872
OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2873
    const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2874
    const char *pszUnitCode)
2875
2876
0
{
2877
0
    TAKE_OPTIONAL_LOCK();
2878
2879
0
    if (dfInMeters <= 0.0)
2880
0
        return OGRERR_FAILURE;
2881
2882
0
    d->refreshProjObj();
2883
0
    if (!d->m_pj_crs)
2884
0
        return OGRERR_FAILURE;
2885
2886
0
    d->demoteFromBoundCRS();
2887
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2888
0
    {
2889
0
        d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2890
0
            d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2891
0
            pszUnitAuthority, pszUnitCode, true));
2892
0
    }
2893
0
    d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2894
0
                                              pszName, dfInMeters,
2895
0
                                              pszUnitAuthority, pszUnitCode));
2896
0
    d->undoDemoteFromBoundCRS();
2897
2898
0
    d->m_osLinearUnits = pszName;
2899
0
    d->dfToMeter = dfInMeters;
2900
2901
0
    return OGRERR_NONE;
2902
0
}
2903
2904
/************************************************************************/
2905
/*                OSRSetLinearUnitsAndUpdateParameters()                */
2906
/************************************************************************/
2907
2908
/**
2909
 * \brief Set the linear units for the projection.
2910
 *
2911
 * This function is the same as
2912
 *   OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2913
 */
2914
OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2915
                                            const char *pszUnits,
2916
                                            double dfInMeters)
2917
2918
0
{
2919
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2920
0
                      OGRERR_FAILURE);
2921
2922
0
    return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2923
0
                                                              dfInMeters);
2924
0
}
2925
2926
/************************************************************************/
2927
/*                           SetLinearUnits()                           */
2928
/************************************************************************/
2929
2930
/**
2931
 * \brief Set the linear units for the projection.
2932
 *
2933
 * This method creates a UNIT subnode with the specified values as a
2934
 * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2935
 * Geographic 3D CRS the vertical axis units will be set.
2936
 *
2937
 * This method does the same as the C function OSRSetLinearUnits().
2938
 *
2939
 * @param pszUnitsName the units name to be used.  Some preferred units
2940
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2941
 * and SRS_UL_US_FOOT.
2942
 *
2943
 * @param dfInMeters the value to multiple by a length in the indicated
2944
 * units to transform to meters.  Some standard conversion factors can
2945
 * be found in ogr_srs_api.h.
2946
 *
2947
 * @return OGRERR_NONE on success.
2948
 */
2949
2950
OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2951
                                           double dfInMeters)
2952
2953
0
{
2954
0
    return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2955
0
}
2956
2957
/************************************************************************/
2958
/*                         OSRSetLinearUnits()                          */
2959
/************************************************************************/
2960
2961
/**
2962
 * \brief Set the linear units for the projection.
2963
 *
2964
 * This function is the same as OGRSpatialReference::SetLinearUnits()
2965
 */
2966
OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2967
                         double dfInMeters)
2968
2969
0
{
2970
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2971
2972
0
    return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2973
0
}
2974
2975
/************************************************************************/
2976
/*                        SetTargetLinearUnits()                        */
2977
/************************************************************************/
2978
2979
/**
2980
 * \brief Set the linear units for the projection.
2981
 *
2982
 * This method creates a UNIT subnode with the specified values as a
2983
 * child of the target node.
2984
 *
2985
 * This method does the same as the C function OSRSetTargetLinearUnits().
2986
 *
2987
 * @param pszTargetKey the keyword to set the linear units for.
2988
 * i.e. "PROJCS" or "VERT_CS"
2989
 *
2990
 * @param pszUnitsName the units name to be used.  Some preferred units
2991
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2992
 * and SRS_UL_US_FOOT.
2993
 *
2994
 * @param dfInMeters the value to multiple by a length in the indicated
2995
 * units to transform to meters.  Some standard conversion factors can
2996
 * be found in ogr_srs_api.h.
2997
 *
2998
 * @param pszUnitAuthority Unit authority name. Or nullptr
2999
 *
3000
 * @param pszUnitCode Unit code. Or nullptr
3001
 *
3002
 * @return OGRERR_NONE on success.
3003
 *
3004
 */
3005
3006
OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
3007
                                                 const char *pszUnitsName,
3008
                                                 double dfInMeters,
3009
                                                 const char *pszUnitAuthority,
3010
                                                 const char *pszUnitCode)
3011
3012
0
{
3013
0
    TAKE_OPTIONAL_LOCK();
3014
3015
0
    if (dfInMeters <= 0.0)
3016
0
        return OGRERR_FAILURE;
3017
3018
0
    d->refreshProjObj();
3019
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3020
0
    if (pszTargetKey == nullptr)
3021
0
    {
3022
0
        if (!d->m_pj_crs)
3023
0
            return OGRERR_FAILURE;
3024
3025
0
        d->demoteFromBoundCRS();
3026
0
        if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3027
0
        {
3028
0
            d->setPjCRS(proj_crs_alter_parameters_linear_unit(
3029
0
                d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3030
0
                pszUnitAuthority, pszUnitCode, false));
3031
0
        }
3032
0
        d->setPjCRS(proj_crs_alter_cs_linear_unit(
3033
0
            d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3034
0
            pszUnitAuthority, pszUnitCode));
3035
0
        d->undoDemoteFromBoundCRS();
3036
3037
0
        d->m_osLinearUnits = pszUnitsName;
3038
0
        d->dfToMeter = dfInMeters;
3039
3040
0
        return OGRERR_NONE;
3041
0
    }
3042
3043
0
    OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3044
3045
0
    if (poCS == nullptr)
3046
0
        return OGRERR_FAILURE;
3047
3048
0
    char szValue[128] = {'\0'};
3049
0
    if (dfInMeters < std::numeric_limits<int>::max() &&
3050
0
        dfInMeters > std::numeric_limits<int>::min() &&
3051
0
        dfInMeters == static_cast<int>(dfInMeters))
3052
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
3053
0
    else
3054
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
3055
3056
0
    OGR_SRSNode *poUnits = nullptr;
3057
0
    if (poCS->FindChild("UNIT") >= 0)
3058
0
    {
3059
0
        poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
3060
0
        if (poUnits->GetChildCount() < 2)
3061
0
            return OGRERR_FAILURE;
3062
0
        poUnits->GetChild(0)->SetValue(pszUnitsName);
3063
0
        poUnits->GetChild(1)->SetValue(szValue);
3064
0
        if (poUnits->FindChild("AUTHORITY") != -1)
3065
0
            poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
3066
0
    }
3067
0
    else
3068
0
    {
3069
0
        poUnits = new OGR_SRSNode("UNIT");
3070
0
        poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3071
0
        poUnits->AddChild(new OGR_SRSNode(szValue));
3072
3073
0
        poCS->AddChild(poUnits);
3074
0
    }
3075
3076
0
    return OGRERR_NONE;
3077
0
}
3078
3079
/************************************************************************/
3080
/*                         OSRSetLinearUnits()                          */
3081
/************************************************************************/
3082
3083
/**
3084
 * \brief Set the linear units for the target node.
3085
 *
3086
 * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3087
 *
3088
 */
3089
OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3090
                               const char *pszTargetKey, const char *pszUnits,
3091
                               double dfInMeters)
3092
3093
0
{
3094
0
    VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3095
3096
0
    return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3097
0
                                                 dfInMeters);
3098
0
}
3099
3100
/************************************************************************/
3101
/*                           GetLinearUnits()                           */
3102
/************************************************************************/
3103
3104
/**
3105
 * \brief Fetch linear projection units.
3106
 *
3107
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3108
 * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3109
 * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3110
 * axis units will be returned.
3111
 *
3112
 * This method does the same thing as the C function OSRGetLinearUnits()
3113
 *
3114
 * @param ppszName a pointer to be updated with the pointer to the units name.
3115
 * The returned value remains internal to the OGRSpatialReference and should
3116
 * not be freed, or modified.  It may be invalidated on the next
3117
 * OGRSpatialReference call.
3118
 *
3119
 * @return the value to multiply by linear distances to transform them to
3120
 * meters.
3121
 * @deprecated Use GetLinearUnits(const char**) const.
3122
 */
3123
3124
double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3125
3126
0
{
3127
0
    return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3128
0
}
3129
3130
/**
3131
 * \brief Fetch linear projection units.
3132
 *
3133
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3134
 * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3135
 * for units.
3136
 *
3137
 * This method does the same thing as the C function OSRGetLinearUnits()
3138
 *
3139
 * @param ppszName a pointer to be updated with the pointer to the units name.
3140
 * The returned value remains internal to the OGRSpatialReference and should
3141
 * not be freed, or modified.  It may be invalidated on the next
3142
 * OGRSpatialReference call.
3143
 *
3144
 * @return the value to multiply by linear distances to transform them to
3145
 * meters.
3146
 */
3147
3148
double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3149
3150
0
{
3151
0
    return GetTargetLinearUnits(nullptr, ppszName);
3152
0
}
3153
3154
/************************************************************************/
3155
/*                         OSRGetLinearUnits()                          */
3156
/************************************************************************/
3157
3158
/**
3159
 * \brief Fetch linear projection units.
3160
 *
3161
 * This function is the same as OGRSpatialReference::GetLinearUnits()
3162
 */
3163
double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3164
3165
0
{
3166
0
    VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3167
3168
0
    return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3169
0
}
3170
3171
/************************************************************************/
3172
/*                        GetTargetLinearUnits()                        */
3173
/************************************************************************/
3174
3175
/**
3176
 * \brief Fetch linear units for target.
3177
 *
3178
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3179
 *
3180
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3181
 *
3182
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3183
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3184
 * GEOCCS, GEOGCS and VERT_CS are looked up)
3185
 * @param ppszName a pointer to be updated with the pointer to the units name.
3186
 * The returned value remains internal to the OGRSpatialReference and should not
3187
 * be freed, or modified.  It may be invalidated on the next
3188
 * OGRSpatialReference call. ppszName can be set to NULL.
3189
 *
3190
 * @return the value to multiply by linear distances to transform them to
3191
 * meters.
3192
 *
3193
 * @deprecated Use GetTargetLinearUnits(const char*, const char**)
3194
 * const.
3195
 */
3196
3197
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3198
                                                 const char **ppszName) const
3199
3200
0
{
3201
0
    TAKE_OPTIONAL_LOCK();
3202
3203
0
    d->refreshProjObj();
3204
3205
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3206
0
    if (pszTargetKey == nullptr)
3207
0
    {
3208
        // Use cached result if available
3209
0
        if (!d->m_osLinearUnits.empty())
3210
0
        {
3211
0
            if (ppszName)
3212
0
                *ppszName = d->m_osLinearUnits.c_str();
3213
0
            return d->dfToMeter;
3214
0
        }
3215
3216
0
        while (true)
3217
0
        {
3218
0
            if (d->m_pj_crs == nullptr)
3219
0
            {
3220
0
                break;
3221
0
            }
3222
3223
0
            d->demoteFromBoundCRS();
3224
0
            PJ *coordSys = nullptr;
3225
0
            if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3226
0
            {
3227
0
                for (int iComponent = 0; iComponent < 2; iComponent++)
3228
0
                {
3229
0
                    auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3230
0
                                                       d->m_pj_crs, iComponent);
3231
0
                    if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3232
0
                    {
3233
0
                        auto temp =
3234
0
                            proj_get_source_crs(d->getPROJContext(), subCRS);
3235
0
                        proj_destroy(subCRS);
3236
0
                        subCRS = temp;
3237
0
                    }
3238
0
                    if (subCRS &&
3239
0
                        (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3240
0
                         proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3241
0
                         proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3242
0
                    {
3243
0
                        coordSys = proj_crs_get_coordinate_system(
3244
0
                            d->getPROJContext(), subCRS);
3245
0
                        proj_destroy(subCRS);
3246
0
                        break;
3247
0
                    }
3248
0
                    else if (subCRS)
3249
0
                    {
3250
0
                        proj_destroy(subCRS);
3251
0
                    }
3252
0
                }
3253
0
                if (coordSys == nullptr)
3254
0
                {
3255
0
                    d->undoDemoteFromBoundCRS();
3256
0
                    break;
3257
0
                }
3258
0
            }
3259
0
            else
3260
0
            {
3261
0
                coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3262
0
                                                          d->m_pj_crs);
3263
0
            }
3264
3265
0
            d->undoDemoteFromBoundCRS();
3266
0
            if (!coordSys)
3267
0
            {
3268
0
                break;
3269
0
            }
3270
0
            auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3271
3272
0
            if (csType != PJ_CS_TYPE_CARTESIAN &&
3273
0
                csType != PJ_CS_TYPE_VERTICAL &&
3274
0
                csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3275
0
                csType != PJ_CS_TYPE_SPHERICAL)
3276
0
            {
3277
0
                proj_destroy(coordSys);
3278
0
                break;
3279
0
            }
3280
3281
0
            int axis = 0;
3282
3283
0
            if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3284
0
                csType == PJ_CS_TYPE_SPHERICAL)
3285
0
            {
3286
0
                const int axisCount =
3287
0
                    proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3288
3289
0
                if (axisCount == 3)
3290
0
                {
3291
0
                    axis = 2;
3292
0
                }
3293
0
                else
3294
0
                {
3295
0
                    proj_destroy(coordSys);
3296
0
                    break;
3297
0
                }
3298
0
            }
3299
3300
0
            double dfConvFactor = 0.0;
3301
0
            const char *pszUnitName = nullptr;
3302
0
            if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3303
0
                                       nullptr, nullptr, nullptr, &dfConvFactor,
3304
0
                                       &pszUnitName, nullptr, nullptr))
3305
0
            {
3306
0
                proj_destroy(coordSys);
3307
0
                break;
3308
0
            }
3309
3310
0
            d->m_osLinearUnits = pszUnitName;
3311
0
            d->dfToMeter = dfConvFactor;
3312
0
            if (ppszName)
3313
0
                *ppszName = d->m_osLinearUnits.c_str();
3314
3315
0
            proj_destroy(coordSys);
3316
0
            return dfConvFactor;
3317
0
        }
3318
3319
0
        d->m_osLinearUnits = "unknown";
3320
0
        d->dfToMeter = 1.0;
3321
3322
0
        if (ppszName != nullptr)
3323
0
            *ppszName = d->m_osLinearUnits.c_str();
3324
0
        return 1.0;
3325
0
    }
3326
3327
0
    const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3328
3329
0
    if (ppszName != nullptr)
3330
0
        *ppszName = "unknown";
3331
3332
0
    if (poCS == nullptr)
3333
0
        return 1.0;
3334
3335
0
    for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3336
0
    {
3337
0
        const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3338
3339
0
        if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3340
0
        {
3341
0
            if (ppszName != nullptr)
3342
0
                *ppszName = poChild->GetChild(0)->GetValue();
3343
3344
0
            return CPLAtof(poChild->GetChild(1)->GetValue());
3345
0
        }
3346
0
    }
3347
3348
0
    return 1.0;
3349
0
}
3350
3351
/**
3352
 * \brief Fetch linear units for target.
3353
 *
3354
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3355
 *
3356
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3357
 *
3358
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3359
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3360
 * GEOCCS and VERT_CS are looked up)
3361
 * @param ppszName a pointer to be updated with the pointer to the units name.
3362
 * The returned value remains internal to the OGRSpatialReference and should not
3363
 * be freed, or modified.  It may be invalidated on the next
3364
 * OGRSpatialReference call. ppszName can be set to NULL.
3365
 *
3366
 * @return the value to multiply by linear distances to transform them to
3367
 * meters.
3368
 *
3369
 */
3370
3371
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3372
                                                 char **ppszName) const
3373
3374
0
{
3375
0
    return GetTargetLinearUnits(pszTargetKey,
3376
0
                                const_cast<const char **>(ppszName));
3377
0
}
3378
3379
/************************************************************************/
3380
/*                      OSRGetTargetLinearUnits()                       */
3381
/************************************************************************/
3382
3383
/**
3384
 * \brief Fetch linear projection units.
3385
 *
3386
 * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3387
 *
3388
 */
3389
double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3390
                               const char *pszTargetKey, char **ppszName)
3391
3392
0
{
3393
0
    VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3394
3395
0
    return ToPointer(hSRS)->GetTargetLinearUnits(
3396
0
        pszTargetKey, const_cast<const char **>(ppszName));
3397
0
}
3398
3399
/************************************************************************/
3400
/*                          GetPrimeMeridian()                          */
3401
/************************************************************************/
3402
3403
/**
3404
 * \brief Fetch prime meridian info.
3405
 *
3406
 * Returns the offset of the prime meridian from greenwich in degrees,
3407
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3408
 * in the coordinate system definition a value of "Greenwich" and an
3409
 * offset of 0.0 is assumed.
3410
 *
3411
 * If the prime meridian name is returned, the pointer is to an internal
3412
 * copy of the name. It should not be freed, altered or depended on after
3413
 * the next OGR call.
3414
 *
3415
 * This method is the same as the C function OSRGetPrimeMeridian().
3416
 *
3417
 * @param ppszName return location for prime meridian name.  If NULL, name
3418
 * is not returned.
3419
 *
3420
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3421
 * degrees.
3422
 * @deprecated Use GetPrimeMeridian(const char**) const.
3423
 */
3424
3425
double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3426
3427
0
{
3428
0
    TAKE_OPTIONAL_LOCK();
3429
3430
0
    d->refreshProjObj();
3431
3432
0
    if (!d->m_osPrimeMeridianName.empty())
3433
0
    {
3434
0
        if (ppszName != nullptr)
3435
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3436
0
        return d->dfFromGreenwich;
3437
0
    }
3438
3439
0
    while (true)
3440
0
    {
3441
0
        if (!d->m_pj_crs)
3442
0
            break;
3443
3444
0
        auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3445
0
        if (!pm)
3446
0
            break;
3447
3448
0
        d->m_osPrimeMeridianName = proj_get_name(pm);
3449
0
        if (ppszName)
3450
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3451
0
        double dfLongitude = 0.0;
3452
0
        double dfConvFactor = 0.0;
3453
0
        proj_prime_meridian_get_parameters(
3454
0
            d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3455
0
        proj_destroy(pm);
3456
0
        d->dfFromGreenwich =
3457
0
            dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3458
0
        return d->dfFromGreenwich;
3459
0
    }
3460
3461
0
    d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3462
0
    d->dfFromGreenwich = 0.0;
3463
0
    if (ppszName != nullptr)
3464
0
        *ppszName = d->m_osPrimeMeridianName.c_str();
3465
0
    return d->dfFromGreenwich;
3466
0
}
3467
3468
/**
3469
 * \brief Fetch prime meridian info.
3470
 *
3471
 * Returns the offset of the prime meridian from greenwich in degrees,
3472
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3473
 * in the coordinate system definition a value of "Greenwich" and an
3474
 * offset of 0.0 is assumed.
3475
 *
3476
 * If the prime meridian name is returned, the pointer is to an internal
3477
 * copy of the name. It should not be freed, altered or depended on after
3478
 * the next OGR call.
3479
 *
3480
 * This method is the same as the C function OSRGetPrimeMeridian().
3481
 *
3482
 * @param ppszName return location for prime meridian name.  If NULL, name
3483
 * is not returned.
3484
 *
3485
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3486
 * degrees.
3487
 */
3488
3489
double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3490
3491
0
{
3492
0
    return GetPrimeMeridian(const_cast<const char **>(ppszName));
3493
0
}
3494
3495
/************************************************************************/
3496
/*                        OSRGetPrimeMeridian()                         */
3497
/************************************************************************/
3498
3499
/**
3500
 * \brief Fetch prime meridian info.
3501
 *
3502
 * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3503
 */
3504
double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3505
3506
0
{
3507
0
    VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3508
3509
0
    return ToPointer(hSRS)->GetPrimeMeridian(
3510
0
        const_cast<const char **>(ppszName));
3511
0
}
3512
3513
/************************************************************************/
3514
/*                             SetGeogCS()                              */
3515
/************************************************************************/
3516
3517
/**
3518
 * \brief Set geographic coordinate system.
3519
 *
3520
 * This method is used to set the datum, ellipsoid, prime meridian and
3521
 * angular units for a geographic coordinate system.  It can be used on its
3522
 * own to establish a geographic spatial reference, or applied to a
3523
 * projected coordinate system to establish the underlying geographic
3524
 * coordinate system.
3525
 *
3526
 * This method does the same as the C function OSRSetGeogCS().
3527
 *
3528
 * @param pszGeogName user visible name for the geographic coordinate system
3529
 * (not to serve as a key).
3530
 *
3531
 * @param pszDatumName key name for this datum.  The OpenGIS specification
3532
 * lists some known values, and otherwise EPSG datum names with a standard
3533
 * transformation are considered legal keys.
3534
 *
3535
 * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3536
 *
3537
 * @param dfSemiMajor the semi major axis of the spheroid.
3538
 *
3539
 * @param dfInvFlattening the inverse flattening for the spheroid.
3540
 * This can be computed from the semi minor axis as
3541
 * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3542
 *
3543
 * @param pszPMName the name of the prime meridian (not to serve as a key)
3544
 * If this is NULL a default value of "Greenwich" will be used.
3545
 *
3546
 * @param dfPMOffset the longitude of Greenwich relative to this prime
3547
 * meridian. Always in Degrees
3548
 *
3549
 * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3550
 * standard names).  If NULL a value of "degrees" will be assumed.
3551
 *
3552
 * @param dfConvertToRadians value to multiply angular units by to transform
3553
 * them to radians.  A value of SRS_UA_DEGREE_CONV will be used if
3554
 * pszAngularUnits is NULL.
3555
 *
3556
 * @return OGRERR_NONE on success.
3557
 */
3558
3559
OGRErr OGRSpatialReference::SetGeogCS(
3560
    const char *pszGeogName, const char *pszDatumName,
3561
    const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3562
    const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3563
    double dfConvertToRadians)
3564
3565
0
{
3566
0
    TAKE_OPTIONAL_LOCK();
3567
3568
0
    d->bNormInfoSet = FALSE;
3569
0
    d->m_osAngularUnits.clear();
3570
0
    d->m_dfAngularUnitToRadian = 0.0;
3571
0
    d->m_osPrimeMeridianName.clear();
3572
0
    d->dfFromGreenwich = 0.0;
3573
3574
    /* -------------------------------------------------------------------- */
3575
    /*      For a geocentric coordinate system we want to set the datum     */
3576
    /*      and ellipsoid based on the GEOGCS.  Create the GEOGCS in a      */
3577
    /*      temporary srs and use the copy method which has special         */
3578
    /*      handling for GEOCCS.                                            */
3579
    /* -------------------------------------------------------------------- */
3580
0
    if (IsGeocentric())
3581
0
    {
3582
0
        OGRSpatialReference oGCS;
3583
3584
0
        oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3585
0
                       dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3586
0
                       dfConvertToRadians);
3587
0
        return CopyGeogCSFrom(&oGCS);
3588
0
    }
3589
3590
0
    auto cs = proj_create_ellipsoidal_2D_cs(
3591
0
        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3592
0
        dfConvertToRadians);
3593
    // Prime meridian expressed in Degree
3594
0
    auto obj = proj_create_geographic_crs(
3595
0
        d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3596
0
        dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3597
0
    proj_destroy(cs);
3598
3599
0
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3600
0
        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3601
0
    {
3602
0
        d->setPjCRS(obj);
3603
0
    }
3604
0
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3605
0
    {
3606
0
        d->setPjCRS(
3607
0
            proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3608
0
        proj_destroy(obj);
3609
0
    }
3610
0
    else
3611
0
    {
3612
0
        proj_destroy(obj);
3613
0
    }
3614
3615
0
    return OGRERR_NONE;
3616
0
}
3617
3618
/************************************************************************/
3619
/*                            OSRSetGeogCS()                            */
3620
/************************************************************************/
3621
3622
/**
3623
 * \brief Set geographic coordinate system.
3624
 *
3625
 * This function is the same as OGRSpatialReference::SetGeogCS()
3626
 */
3627
OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3628
                    const char *pszDatumName, const char *pszSpheroidName,
3629
                    double dfSemiMajor, double dfInvFlattening,
3630
                    const char *pszPMName, double dfPMOffset,
3631
                    const char *pszAngularUnits, double dfConvertToRadians)
3632
3633
0
{
3634
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3635
3636
0
    return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3637
0
                                      pszSpheroidName, dfSemiMajor,
3638
0
                                      dfInvFlattening, pszPMName, dfPMOffset,
3639
0
                                      pszAngularUnits, dfConvertToRadians);
3640
0
}
3641
3642
/************************************************************************/
3643
/*                         SetWellKnownGeogCS()                         */
3644
/************************************************************************/
3645
3646
/**
3647
 * \brief Set a GeogCS based on well known name.
3648
 *
3649
 * This may be called on an empty OGRSpatialReference to make a geographic
3650
 * coordinate system, or on something with an existing PROJCS node to
3651
 * set the underlying geographic coordinate system of a projected coordinate
3652
 * system.
3653
 *
3654
 * The following well known text values are currently supported,
3655
 * Except for "EPSG:n", the others are without dependency on EPSG data files:
3656
 * <ul>
3657
 * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3658
 * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3659
 * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3660
 * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3661
 * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3662
 * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3663
 * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3664
 * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3665
 * </ul>
3666
 *
3667
 * @param pszName name of well known geographic coordinate system.
3668
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3669
 * recognised, the target object is already initialized, or an EPSG value
3670
 * can't be successfully looked up.
3671
 */
3672
3673
OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3674
3675
0
{
3676
0
    TAKE_OPTIONAL_LOCK();
3677
3678
    /* -------------------------------------------------------------------- */
3679
    /*      Check for EPSG authority numbers.                               */
3680
    /* -------------------------------------------------------------------- */
3681
0
    if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3682
0
    {
3683
0
        OGRSpatialReference oSRS2;
3684
0
        const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3685
0
        if (eErr != OGRERR_NONE)
3686
0
            return eErr;
3687
3688
0
        if (!oSRS2.IsGeographic())
3689
0
            return OGRERR_FAILURE;
3690
3691
0
        return CopyGeogCSFrom(&oSRS2);
3692
0
    }
3693
3694
    /* -------------------------------------------------------------------- */
3695
    /*      Check for simple names.                                         */
3696
    /* -------------------------------------------------------------------- */
3697
0
    const char *pszWKT = nullptr;
3698
3699
0
    if (EQUAL(pszName, "WGS84"))
3700
0
    {
3701
0
        pszWKT = SRS_WKT_WGS84_LAT_LONG;
3702
0
    }
3703
0
    else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3704
0
    {
3705
0
        pszWKT =
3706
0
            "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3707
0
            "ELLIPSOID[\"WGS "
3708
0
            "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3709
0
            "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3710
0
            "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3711
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3712
0
            "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3713
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3714
0
            "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3715
0
            "ID[\"OGC\",\"CRS84\"]]";
3716
0
    }
3717
0
    else if (EQUAL(pszName, "WGS72"))
3718
0
        pszWKT =
3719
0
            "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3720
0
            "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3721
0
            "AUTHORITY[\"EPSG\",\"6322\"]],"
3722
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3723
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3724
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3725
0
            "AUTHORITY[\"EPSG\",\"4322\"]]";
3726
3727
0
    else if (EQUAL(pszName, "NAD27"))
3728
0
        pszWKT =
3729
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3730
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3731
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3732
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3733
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3734
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3735
0
            "AUTHORITY[\"EPSG\",\"4267\"]]";
3736
3737
0
    else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3738
0
        pszWKT =
3739
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3740
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3741
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3742
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3743
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3744
0
            "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3745
3746
0
    else if (EQUAL(pszName, "NAD83"))
3747
0
        pszWKT =
3748
0
            "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3749
0
            "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3750
0
            "AUTHORITY[\"EPSG\",\"7019\"]],"
3751
0
            "AUTHORITY[\"EPSG\",\"6269\"]],"
3752
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3753
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3754
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3755
0
            "\"EPSG\",\"4269\"]]";
3756
3757
0
    else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3758
0
        pszWKT =
3759
0
            "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3760
0
            "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3761
0
            "AUTHORITY[\"EPSG\",\"7019\"]],"
3762
0
            "AUTHORITY[\"EPSG\",\"6269\"]],"
3763
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3764
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3765
0
            "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3766
3767
0
    else
3768
0
        return OGRERR_FAILURE;
3769
3770
    /* -------------------------------------------------------------------- */
3771
    /*      Import the WKT                                                  */
3772
    /* -------------------------------------------------------------------- */
3773
0
    OGRSpatialReference oSRS2;
3774
0
    const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3775
0
    if (eErr != OGRERR_NONE)
3776
0
        return eErr;
3777
3778
    /* -------------------------------------------------------------------- */
3779
    /*      Copy over.                                                      */
3780
    /* -------------------------------------------------------------------- */
3781
0
    return CopyGeogCSFrom(&oSRS2);
3782
0
}
3783
3784
/************************************************************************/
3785
/*                       OSRSetWellKnownGeogCS()                        */
3786
/************************************************************************/
3787
3788
/**
3789
 * \brief Set a GeogCS based on well known name.
3790
 *
3791
 * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3792
 */
3793
OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3794
3795
0
{
3796
0
    VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3797
3798
0
    return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3799
0
}
3800
3801
/************************************************************************/
3802
/*                           CopyGeogCSFrom()                           */
3803
/************************************************************************/
3804
3805
/**
3806
 * \brief Copy GEOGCS from another OGRSpatialReference.
3807
 *
3808
 * The GEOGCS information is copied into this OGRSpatialReference from another.
3809
 * If this object has a PROJCS root already, the GEOGCS is installed within
3810
 * it, otherwise it is installed as the root.
3811
 *
3812
 * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3813
 *
3814
 * @return OGRERR_NONE on success or an error code.
3815
 */
3816
OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3817
3818
0
{
3819
0
    return CopyGeogCSFrom(poSrcSRS, false);
3820
0
}
3821
3822
/**
3823
 * \brief Copy GEOGCS from another OGRSpatialReference.
3824
 *
3825
 * The GEOGCS information is copied into this OGRSpatialReference from another.
3826
 * If this object has a PROJCS root already, the GEOGCS is installed within
3827
 * it, otherwise it is installed as the root.
3828
 *
3829
 * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3830
 * @param bInnerMostGeogCRS Whether the inner-most geographic CRS must be used.
3831
 *                          This setting makes a difference if this CRS is a
3832
 *                          derived geographic CRS. Setting bInnerMostGeogCRS
3833
 *                          to true will then extract its base CRS.
3834
 *
3835
 * @return OGRERR_NONE on success or an error code.
3836
 * @since 3.13.2
3837
 */
3838
OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS,
3839
                                           bool bInnerMostGeogCRS)
3840
3841
0
{
3842
0
    TAKE_OPTIONAL_LOCK();
3843
3844
0
    d->bNormInfoSet = FALSE;
3845
0
    d->m_osAngularUnits.clear();
3846
0
    d->m_dfAngularUnitToRadian = 0.0;
3847
0
    d->m_osPrimeMeridianName.clear();
3848
0
    d->dfFromGreenwich = 0.0;
3849
3850
0
    d->refreshProjObj();
3851
0
    poSrcSRS->d->refreshProjObj();
3852
0
    if (!poSrcSRS->d->m_pj_crs)
3853
0
    {
3854
0
        return OGRERR_FAILURE;
3855
0
    }
3856
0
    auto geodCRS =
3857
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3858
0
    if (!geodCRS)
3859
0
    {
3860
0
        return OGRERR_FAILURE;
3861
0
    }
3862
3863
0
    if (bInnerMostGeogCRS && poSrcSRS->IsDerivedGeographic())
3864
0
    {
3865
0
        auto baseCRS = proj_get_source_crs(d->getPROJContext(), geodCRS);
3866
0
        if (!baseCRS)
3867
0
            return OGRERR_FAILURE;
3868
0
        proj_destroy(geodCRS);
3869
0
        geodCRS = baseCRS;
3870
0
    }
3871
3872
    /* -------------------------------------------------------------------- */
3873
    /*      Handle geocentric coordinate systems specially.  We just        */
3874
    /*      want to copy the DATUM.                                         */
3875
    /* -------------------------------------------------------------------- */
3876
0
    if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3877
0
    {
3878
0
        auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3879
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
3880
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3881
0
        if (datum == nullptr)
3882
0
        {
3883
0
            datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3884
0
        }
3885
0
#endif
3886
0
        if (datum == nullptr)
3887
0
        {
3888
0
            proj_destroy(geodCRS);
3889
0
            return OGRERR_FAILURE;
3890
0
        }
3891
3892
0
        const char *pszUnitName = nullptr;
3893
0
        double unitConvFactor = GetLinearUnits(&pszUnitName);
3894
3895
0
        auto pj_crs = proj_create_geocentric_crs_from_datum(
3896
0
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3897
0
            unitConvFactor);
3898
0
        proj_destroy(datum);
3899
3900
0
        d->setPjCRS(pj_crs);
3901
0
    }
3902
3903
0
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3904
0
    {
3905
0
        auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3906
0
                                                  d->m_pj_crs, geodCRS);
3907
0
        d->setPjCRS(pj_crs);
3908
0
    }
3909
3910
0
    else
3911
0
    {
3912
0
        d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3913
0
    }
3914
3915
    // Apply TOWGS84 of source CRS
3916
0
    if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3917
0
    {
3918
0
        auto target =
3919
0
            proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3920
0
        auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3921
0
                                              poSrcSRS->d->m_pj_crs);
3922
0
        d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3923
0
                                              target, co));
3924
0
        proj_destroy(target);
3925
0
        proj_destroy(co);
3926
0
    }
3927
3928
0
    proj_destroy(geodCRS);
3929
3930
0
    return OGRERR_NONE;
3931
0
}
3932
3933
/************************************************************************/
3934
/*                         OSRCopyGeogCSFrom()                          */
3935
/************************************************************************/
3936
3937
/**
3938
 * \brief Copy GEOGCS from another OGRSpatialReference.
3939
 *
3940
 * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3941
 */
3942
OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3943
                         const OGRSpatialReferenceH hSrcSRS)
3944
3945
0
{
3946
0
    VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3947
0
    VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3948
3949
0
    return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3950
0
}
3951
3952
/************************************************************************/
3953
/*                SET_FROM_USER_INPUT_LIMITATIONS_get()                 */
3954
/************************************************************************/
3955
3956
/** Limitations for OGRSpatialReference::SetFromUserInput().
3957
 *
3958
 * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3959
 */
3960
const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3961
    "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3962
3963
/**
3964
 * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3965
 */
3966
CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3967
0
{
3968
0
    return SET_FROM_USER_INPUT_LIMITATIONS;
3969
0
}
3970
3971
/************************************************************************/
3972
/*                   RemoveIDFromMemberOfEnsembles()                    */
3973
/************************************************************************/
3974
3975
// cppcheck-suppress constParameterReference
3976
static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3977
0
{
3978
    // Remove "id" from members of datum ensembles for compatibility with
3979
    // older PROJ versions
3980
    // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3981
    // and https://github.com/OSGeo/PROJ/pull/3221
3982
0
    if (obj.GetType() == CPLJSONObject::Type::Object)
3983
0
    {
3984
0
        for (auto &subObj : obj.GetChildren())
3985
0
        {
3986
0
            RemoveIDFromMemberOfEnsembles(subObj);
3987
0
        }
3988
0
    }
3989
0
    else if (obj.GetType() == CPLJSONObject::Type::Array &&
3990
0
             obj.GetName() == "members")
3991
0
    {
3992
0
        for (auto &subObj : obj.ToArray())
3993
0
        {
3994
0
            if (subObj.GetType() == CPLJSONObject::Type::Object)
3995
0
            {
3996
0
                subObj.Delete("id");
3997
0
            }
3998
0
        }
3999
0
    }
4000
0
}
4001
4002
/************************************************************************/
4003
/*                          SetFromUserInput()                          */
4004
/************************************************************************/
4005
4006
/**
4007
 * \brief Set spatial reference from various text formats.
4008
 *
4009
 * This method will examine the provided input, and try to deduce the
4010
 * format, and then use it to initialize the spatial reference system.  It
4011
 * may take the following forms:
4012
 *
4013
 * <ol>
4014
 * <li> Well Known Text definition - passed on to importFromWkt().
4015
 * <li> "EPSG:n" - number passed on to importFromEPSG().
4016
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4017
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4018
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4019
 * <li> PROJ.4 definitions - passed on to importFromProj4().
4020
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
4021
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4022
 * WGS84 or WGS72.
4023
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4024
 * <li> PROJJSON (PROJ &gt;= 6.2)
4025
 * </ol>
4026
 *
4027
 * It is expected that this method will be extended in the future to support
4028
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4029
 * State Plane definitions.
4030
 *
4031
 * This method is intended to be flexible, but by its nature it is
4032
 * imprecise as it must guess information about the format intended.  When
4033
 * possible applications should call the specific method appropriate if the
4034
 * input is known to be in a particular format.
4035
 *
4036
 * This method does the same thing as the OSRSetFromUserInput() function.
4037
 *
4038
 * @param pszDefinition text definition to try to deduce SRS from.
4039
 *
4040
 * @return OGRERR_NONE on success, or an error code if the name isn't
4041
 * recognised, the definition is corrupt, or an EPSG value can't be
4042
 * successfully looked up.
4043
 */
4044
4045
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
4046
0
{
4047
0
    return SetFromUserInput(pszDefinition, nullptr);
4048
0
}
4049
4050
/**
4051
 * \brief Set spatial reference from various text formats.
4052
 *
4053
 * This method will examine the provided input, and try to deduce the
4054
 * format, and then use it to initialize the spatial reference system.  It
4055
 * may take the following forms:
4056
 *
4057
 * <ol>
4058
 * <li> Well Known Text definition - passed on to importFromWkt().
4059
 * <li> "EPSG:n" - number passed on to importFromEPSG().
4060
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4061
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4062
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4063
 * <li> PROJ.4 definitions - passed on to importFromProj4().
4064
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
4065
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4066
 * WGS84 or WGS72.
4067
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4068
 * <li> PROJJSON (PROJ &gt;= 6.2)
4069
 * </ol>
4070
 *
4071
 * It is expected that this method will be extended in the future to support
4072
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4073
 * State Plane definitions.
4074
 *
4075
 * This method is intended to be flexible, but by its nature it is
4076
 * imprecise as it must guess information about the format intended.  When
4077
 * possible applications should call the specific method appropriate if the
4078
 * input is known to be in a particular format.
4079
 *
4080
 * This method does the same thing as the OSRSetFromUserInput() and
4081
 * OSRSetFromUserInputEx() functions.
4082
 *
4083
 * @param pszDefinition text definition to try to deduce SRS from.
4084
 *
4085
 * @param papszOptions NULL terminated list of options, or NULL.
4086
 * <ol>
4087
 * <li> ALLOW_NETWORK_ACCESS=YES/NO.
4088
 *      Whether http:// or https:// access is allowed. Defaults to YES.
4089
 * <li> ALLOW_FILE_ACCESS=YES/NO.
4090
 *      Whether reading a file using the Virtual File System layer is allowed
4091
 *      (can also involve network access). Defaults to YES.
4092
 * </ol>
4093
 *
4094
 * @return OGRERR_NONE on success, or an error code if the name isn't
4095
 * recognised, the definition is corrupt, or an EPSG value can't be
4096
 * successfully looked up.
4097
 */
4098
4099
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4100
                                             CSLConstList papszOptions)
4101
0
{
4102
0
    TAKE_OPTIONAL_LOCK();
4103
4104
    // Skip leading white space
4105
0
    while (isspace(static_cast<unsigned char>(*pszDefinition)))
4106
0
        pszDefinition++;
4107
4108
0
    if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4109
0
    {
4110
0
        pszDefinition += 6;
4111
0
    }
4112
4113
    /* -------------------------------------------------------------------- */
4114
    /*      Is it a recognised syntax?                                      */
4115
    /* -------------------------------------------------------------------- */
4116
0
    const char *const wktKeywords[] = {
4117
        // WKT1
4118
0
        "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4119
        // WKT2"
4120
0
        "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4121
0
        "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4122
0
        "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4123
0
    for (const char *keyword : wktKeywords)
4124
0
    {
4125
0
        if (STARTS_WITH_CI(pszDefinition, keyword))
4126
0
        {
4127
0
            return importFromWkt(pszDefinition);
4128
0
        }
4129
0
    }
4130
4131
0
    const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4132
0
    if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4133
0
    {
4134
0
        OGRErr eStatus = OGRERR_NONE;
4135
4136
0
        if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4137
0
        {
4138
            // Use proj_create() as it allows things like EPSG:3157+4617
4139
            // that are not normally supported by the below code that
4140
            // builds manually a compound CRS
4141
0
            PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4142
0
            if (!pj)
4143
0
            {
4144
0
                return OGRERR_FAILURE;
4145
0
            }
4146
0
            Clear();
4147
0
            d->setPjCRS(pj);
4148
0
            return OGRERR_NONE;
4149
0
        }
4150
0
        else
4151
0
        {
4152
0
            eStatus =
4153
0
                importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4154
0
        }
4155
4156
0
        return eStatus;
4157
0
    }
4158
4159
0
    if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4160
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4161
0
        STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4162
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4163
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4164
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4165
0
        return importFromURN(pszDefinition);
4166
4167
0
    if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4168
0
        STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4169
0
        STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4170
0
        STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4171
0
        STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4172
0
        return importFromCRSURL(pszDefinition);
4173
4174
0
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4175
0
        return importFromWMSAUTO(pszDefinition);
4176
4177
    // WMS/WCS OGC codes like OGC:CRS84.
4178
0
    if (EQUAL(pszDefinition, "OGC:CRS84"))
4179
0
        return SetWellKnownGeogCS(pszDefinition + 4);
4180
4181
0
    if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4182
0
        return SetWellKnownGeogCS(pszDefinition);
4183
4184
0
    if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4185
0
    {
4186
0
        char *pszFile = CPLStrdup(pszDefinition + 5);
4187
0
        char *pszCode = strstr(pszFile, ",") + 1;
4188
4189
0
        pszCode[-1] = '\0';
4190
4191
0
        OGRErr err = importFromDict(pszFile, pszCode);
4192
0
        CPLFree(pszFile);
4193
4194
0
        return err;
4195
0
    }
4196
4197
0
    if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4198
0
        EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4199
0
    {
4200
0
        Clear();
4201
0
        return SetWellKnownGeogCS(pszDefinition);
4202
0
    }
4203
4204
    // PROJJSON
4205
0
    if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4206
0
        (strstr(pszDefinition, "GeodeticCRS") ||
4207
0
         strstr(pszDefinition, "GeographicCRS") ||
4208
0
         strstr(pszDefinition, "ProjectedCRS") ||
4209
0
         strstr(pszDefinition, "VerticalCRS") ||
4210
0
         strstr(pszDefinition, "BoundCRS") ||
4211
0
         strstr(pszDefinition, "CompoundCRS") ||
4212
0
         strstr(pszDefinition, "DerivedGeodeticCRS") ||
4213
0
         strstr(pszDefinition, "DerivedGeographicCRS") ||
4214
0
         strstr(pszDefinition, "DerivedProjectedCRS") ||
4215
0
         strstr(pszDefinition, "DerivedVerticalCRS") ||
4216
0
         strstr(pszDefinition, "EngineeringCRS") ||
4217
0
         strstr(pszDefinition, "DerivedEngineeringCRS") ||
4218
0
         strstr(pszDefinition, "ParametricCRS") ||
4219
0
         strstr(pszDefinition, "DerivedParametricCRS") ||
4220
0
         strstr(pszDefinition, "TemporalCRS") ||
4221
0
         strstr(pszDefinition, "DerivedTemporalCRS")))
4222
0
    {
4223
0
        PJ *pj;
4224
0
        if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4225
0
        {
4226
            // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4227
            // a unknown id.
4228
0
            CPLJSONDocument oCRSDoc;
4229
0
            if (!oCRSDoc.LoadMemory(pszDefinition))
4230
0
                return OGRERR_CORRUPT_DATA;
4231
0
            CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4232
0
            RemoveIDFromMemberOfEnsembles(oCRSRoot);
4233
0
            pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4234
0
        }
4235
0
        else
4236
0
        {
4237
0
            pj = proj_create(d->getPROJContext(), pszDefinition);
4238
0
        }
4239
0
        if (!pj)
4240
0
        {
4241
0
            return OGRERR_FAILURE;
4242
0
        }
4243
0
        Clear();
4244
0
        d->setPjCRS(pj);
4245
0
        return OGRERR_NONE;
4246
0
    }
4247
4248
0
    if (strstr(pszDefinition, "+proj") != nullptr ||
4249
0
        strstr(pszDefinition, "+init") != nullptr)
4250
0
        return importFromProj4(pszDefinition);
4251
4252
0
    if (STARTS_WITH_CI(pszDefinition, "http://") ||
4253
0
        STARTS_WITH_CI(pszDefinition, "https://"))
4254
0
    {
4255
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4256
0
                                             "ALLOW_NETWORK_ACCESS", "YES")))
4257
0
            return importFromUrl(pszDefinition);
4258
4259
0
        CPLError(CE_Failure, CPLE_AppDefined,
4260
0
                 "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4261
0
                 pszDefinition);
4262
0
        return OGRERR_FAILURE;
4263
0
    }
4264
4265
0
    if (EQUAL(pszDefinition, "osgb:BNG"))
4266
0
    {
4267
0
        return importFromEPSG(27700);
4268
0
    }
4269
4270
    // Used by German CityGML files
4271
0
    if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4272
0
    {
4273
        // "ETRS89 / UTM Zone 32N + DHHN92 height"
4274
0
        return SetFromUserInput("EPSG:25832+5783");
4275
0
    }
4276
0
    else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4277
0
    {
4278
        // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4279
0
        return SetFromUserInput("EPSG:25832+7837");
4280
0
    }
4281
4282
    // Used by  Japan's Fundamental Geospatial Data (FGD) GML
4283
0
    if (EQUAL(pszDefinition, "fguuid:jgd2001.bl"))
4284
0
        return importFromEPSG(4612);  // JGD2000 (slight difference in years)
4285
0
    else if (EQUAL(pszDefinition, "fguuid:jgd2011.bl"))
4286
0
        return importFromEPSG(6668);  // JGD2011
4287
0
    else if (EQUAL(pszDefinition, "fguuid:jgd2024.bl"))
4288
0
    {
4289
        // FIXME when EPSG attributes a CRS code
4290
0
        return importFromWkt(
4291
0
            "GEOGCRS[\"JGD2024\",\n"
4292
0
            "    DATUM[\"Japanese Geodetic Datum 2024\",\n"
4293
0
            "        ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
4294
0
            "            LENGTHUNIT[\"metre\",1]]],\n"
4295
0
            "    PRIMEM[\"Greenwich\",0,\n"
4296
0
            "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4297
0
            "    CS[ellipsoidal,2],\n"
4298
0
            "        AXIS[\"geodetic latitude (Lat)\",north,\n"
4299
0
            "            ORDER[1],\n"
4300
0
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4301
0
            "        AXIS[\"geodetic longitude (Lon)\",east,\n"
4302
0
            "            ORDER[2],\n"
4303
0
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4304
0
            "    USAGE[\n"
4305
0
            "        SCOPE[\"Horizontal component of 3D system.\"],\n"
4306
0
            "        AREA[\"Japan - onshore and offshore.\"],\n"
4307
0
            "        BBOX[17.09,122.38,46.05,157.65]]]");
4308
0
    }
4309
4310
    // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4311
0
    const char *pszDot = strrchr(pszDefinition, ':');
4312
0
    if (pszDot)
4313
0
    {
4314
0
        CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4315
0
        auto authorities =
4316
0
            proj_get_authorities_from_database(d->getPROJContext());
4317
0
        if (authorities)
4318
0
        {
4319
0
            std::set<std::string> aosCandidateAuthorities;
4320
0
            for (auto iter = authorities; *iter; ++iter)
4321
0
            {
4322
0
                if (*iter == osPrefix)
4323
0
                {
4324
0
                    aosCandidateAuthorities.clear();
4325
0
                    aosCandidateAuthorities.insert(*iter);
4326
0
                    break;
4327
0
                }
4328
                // Deal with "IAU_2015" as authority in the list and input
4329
                // "IAU:code"
4330
0
                else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4331
0
                             0 &&
4332
0
                         (*iter)[osPrefix.size()] == '_')
4333
0
                {
4334
0
                    aosCandidateAuthorities.insert(*iter);
4335
0
                }
4336
                // Deal with "IAU_2015" as authority in the list and input
4337
                // "IAU:2015:code"
4338
0
                else if (osPrefix.find(':') != std::string::npos &&
4339
0
                         osPrefix.size() == strlen(*iter) &&
4340
0
                         CPLString(osPrefix).replaceAll(':', '_') == *iter)
4341
0
                {
4342
0
                    aosCandidateAuthorities.clear();
4343
0
                    aosCandidateAuthorities.insert(*iter);
4344
0
                    break;
4345
0
                }
4346
0
            }
4347
4348
0
            proj_string_list_destroy(authorities);
4349
4350
0
            if (!aosCandidateAuthorities.empty())
4351
0
            {
4352
0
                auto obj = proj_create_from_database(
4353
0
                    d->getPROJContext(),
4354
0
                    aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4355
0
                    PJ_CATEGORY_CRS, false, nullptr);
4356
0
                if (!obj)
4357
0
                {
4358
0
                    return OGRERR_FAILURE;
4359
0
                }
4360
0
                Clear();
4361
0
                d->setPjCRS(obj);
4362
0
                return OGRERR_NONE;
4363
0
            }
4364
0
        }
4365
0
    }
4366
4367
    /* -------------------------------------------------------------------- */
4368
    /*      Try to open it as a file.                                       */
4369
    /* -------------------------------------------------------------------- */
4370
0
    if (!CPLTestBool(
4371
0
            CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4372
0
    {
4373
0
        VSIStatBufL sStat;
4374
0
        if (STARTS_WITH(pszDefinition, "/vsi") ||
4375
0
            VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4376
0
        {
4377
0
            CPLError(CE_Failure, CPLE_AppDefined,
4378
0
                     "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4379
0
                     pszDefinition);
4380
0
            return OGRERR_FAILURE;
4381
0
        }
4382
        // We used to silently return an error without a CE_Failure message
4383
        // Cf https://github.com/Toblerity/Fiona/issues/1063
4384
0
        return OGRERR_CORRUPT_DATA;
4385
0
    }
4386
4387
0
    CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4388
0
    VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4389
0
    if (fp == nullptr)
4390
0
        return OGRERR_CORRUPT_DATA;
4391
4392
0
    const size_t nBufMax = 100000;
4393
0
    char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4394
0
    const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4395
0
    VSIFCloseL(fp);
4396
4397
0
    if (nBytes == nBufMax - 1)
4398
0
    {
4399
0
        CPLDebug("OGR",
4400
0
                 "OGRSpatialReference::SetFromUserInput(%s), opened file "
4401
0
                 "but it is to large for our generous buffer.  Is it really "
4402
0
                 "just a WKT definition?",
4403
0
                 pszDefinition);
4404
0
        CPLFree(pszBuffer);
4405
0
        return OGRERR_FAILURE;
4406
0
    }
4407
4408
0
    pszBuffer[nBytes] = '\0';
4409
4410
0
    char *pszBufPtr = pszBuffer;
4411
0
    while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4412
0
        pszBufPtr++;
4413
4414
0
    OGRErr err = OGRERR_NONE;
4415
0
    if (pszBufPtr[0] == '<')
4416
0
        err = importFromXML(pszBufPtr);
4417
0
    else if ((strstr(pszBuffer, "+proj") != nullptr ||
4418
0
              strstr(pszBuffer, "+init") != nullptr) &&
4419
0
             (strstr(pszBuffer, "EXTENSION") == nullptr &&
4420
0
              strstr(pszBuffer, "extension") == nullptr))
4421
0
        err = importFromProj4(pszBufPtr);
4422
0
    else
4423
0
    {
4424
0
        if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4425
0
        {
4426
0
            pszBufPtr += 6;
4427
0
        }
4428
4429
        // coverity[tainted_data]
4430
0
        err = importFromWkt(pszBufPtr);
4431
0
    }
4432
4433
0
    CPLFree(pszBuffer);
4434
4435
0
    return err;
4436
0
}
4437
4438
/************************************************************************/
4439
/*                        OSRSetFromUserInput()                         */
4440
/************************************************************************/
4441
4442
/**
4443
 * \brief Set spatial reference from various text formats.
4444
 *
4445
 * This function is the same as OGRSpatialReference::SetFromUserInput()
4446
 *
4447
 * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4448
 */
4449
OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4450
                                       const char *pszDef)
4451
4452
0
{
4453
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4454
4455
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef);
4456
0
}
4457
4458
/************************************************************************/
4459
/*                       OSRSetFromUserInputEx()                        */
4460
/************************************************************************/
4461
4462
/**
4463
 * \brief Set spatial reference from various text formats.
4464
 *
4465
 * This function is the same as OGRSpatialReference::SetFromUserInput().
4466
 *
4467
 * @since GDAL 3.9
4468
 */
4469
OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4470
                             CSLConstList papszOptions)
4471
4472
0
{
4473
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4474
4475
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4476
0
}
4477
4478
/************************************************************************/
4479
/*                           ImportFromUrl()                            */
4480
/************************************************************************/
4481
4482
/**
4483
 * \brief Set spatial reference from a URL.
4484
 *
4485
 * This method will download the spatial reference at a given URL and
4486
 * feed it into SetFromUserInput for you.
4487
 *
4488
 * This method does the same thing as the OSRImportFromUrl() function.
4489
 *
4490
 * @param pszUrl text definition to try to deduce SRS from.
4491
 *
4492
 * @return OGRERR_NONE on success, or an error code with the curl
4493
 * error message if it is unable to download data.
4494
 */
4495
4496
OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4497
4498
0
{
4499
0
    TAKE_OPTIONAL_LOCK();
4500
4501
0
    if (!STARTS_WITH_CI(pszUrl, "http://") &&
4502
0
        !STARTS_WITH_CI(pszUrl, "https://"))
4503
0
    {
4504
0
        CPLError(CE_Failure, CPLE_AppDefined,
4505
0
                 "The given string is not recognized as a URL"
4506
0
                 "starting with 'http://' -- %s",
4507
0
                 pszUrl);
4508
0
        return OGRERR_FAILURE;
4509
0
    }
4510
4511
    /* -------------------------------------------------------------------- */
4512
    /*      Fetch the result.                                               */
4513
    /* -------------------------------------------------------------------- */
4514
0
    CPLErrorReset();
4515
4516
0
    std::string osUrl(pszUrl);
4517
    // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4518
    // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4519
    // to query WKT. To allow a static server to be used, rather append a
4520
    // "ogcwkt/" suffix.
4521
0
    for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4522
0
                                  "http://spatialreference.org/ref/"})
4523
0
    {
4524
0
        if (STARTS_WITH(pszUrl, pszPrefix))
4525
0
        {
4526
0
            const CPLStringList aosTokens(
4527
0
                CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4528
0
            if (aosTokens.size() == 2)
4529
0
            {
4530
0
                osUrl = "https://spatialreference.org/ref/";
4531
0
                osUrl += aosTokens[0];  // authority
4532
0
                osUrl += '/';
4533
0
                osUrl += aosTokens[1];  // code
4534
0
                osUrl += "/ogcwkt/";
4535
0
            }
4536
0
            break;
4537
0
        }
4538
0
    }
4539
4540
0
    const char *pszTimeout = "TIMEOUT=10";
4541
0
    char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4542
4543
0
    CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4544
4545
    /* -------------------------------------------------------------------- */
4546
    /*      Try to handle errors.                                           */
4547
    /* -------------------------------------------------------------------- */
4548
4549
0
    if (psResult == nullptr)
4550
0
        return OGRERR_FAILURE;
4551
0
    if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4552
0
        psResult->pabyData == nullptr)
4553
0
    {
4554
0
        if (CPLGetLastErrorNo() == 0)
4555
0
        {
4556
0
            CPLError(CE_Failure, CPLE_AppDefined,
4557
0
                     "No data was returned from the given URL");
4558
0
        }
4559
0
        CPLHTTPDestroyResult(psResult);
4560
0
        return OGRERR_FAILURE;
4561
0
    }
4562
4563
0
    if (psResult->nStatus != 0)
4564
0
    {
4565
0
        CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4566
0
                 psResult->nStatus, psResult->pszErrBuf);
4567
0
        CPLHTTPDestroyResult(psResult);
4568
0
        return OGRERR_FAILURE;
4569
0
    }
4570
4571
0
    const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4572
0
    if (STARTS_WITH_CI(pszData, "http://") ||
4573
0
        STARTS_WITH_CI(pszData, "https://"))
4574
0
    {
4575
0
        CPLError(CE_Failure, CPLE_AppDefined,
4576
0
                 "The data that was downloaded also starts with 'http://' "
4577
0
                 "and cannot be passed into SetFromUserInput.  Is this "
4578
0
                 "really a spatial reference definition? ");
4579
0
        CPLHTTPDestroyResult(psResult);
4580
0
        return OGRERR_FAILURE;
4581
0
    }
4582
0
    if (OGRERR_NONE != SetFromUserInput(pszData))
4583
0
    {
4584
0
        CPLHTTPDestroyResult(psResult);
4585
0
        return OGRERR_FAILURE;
4586
0
    }
4587
4588
0
    CPLHTTPDestroyResult(psResult);
4589
0
    return OGRERR_NONE;
4590
0
}
4591
4592
/************************************************************************/
4593
/*                          OSRimportFromUrl()                          */
4594
/************************************************************************/
4595
4596
/**
4597
 * \brief Set spatial reference from a URL.
4598
 *
4599
 * This function is the same as OGRSpatialReference::importFromUrl()
4600
 */
4601
OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4602
4603
0
{
4604
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4605
4606
0
    return ToPointer(hSRS)->importFromUrl(pszUrl);
4607
0
}
4608
4609
/************************************************************************/
4610
/*                         importFromURNPart()                          */
4611
/************************************************************************/
4612
OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4613
                                              const char *pszCode,
4614
                                              const char *pszURN)
4615
0
{
4616
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4617
0
    (void)this;
4618
0
    (void)pszAuthority;
4619
0
    (void)pszCode;
4620
0
    (void)pszURN;
4621
0
    return OGRERR_FAILURE;
4622
#else
4623
    /* -------------------------------------------------------------------- */
4624
    /*      Is this an EPSG code? Note that we import it with EPSG          */
4625
    /*      preferred axis ordering for geographic coordinate systems.      */
4626
    /* -------------------------------------------------------------------- */
4627
    if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4628
        return importFromEPSGA(atoi(pszCode));
4629
4630
    /* -------------------------------------------------------------------- */
4631
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4632
    /* -------------------------------------------------------------------- */
4633
    if (STARTS_WITH_CI(pszAuthority, "IAU"))
4634
        return importFromDict("IAU2000.wkt", pszCode);
4635
4636
    /* -------------------------------------------------------------------- */
4637
    /*      Is this an OGC code?                                            */
4638
    /* -------------------------------------------------------------------- */
4639
    if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4640
    {
4641
        CPLError(CE_Failure, CPLE_AppDefined,
4642
                 "URN %s has unrecognized authority.", pszURN);
4643
        return OGRERR_FAILURE;
4644
    }
4645
4646
    if (STARTS_WITH_CI(pszCode, "CRS84"))
4647
        return SetWellKnownGeogCS(pszCode);
4648
    else if (STARTS_WITH_CI(pszCode, "CRS83"))
4649
        return SetWellKnownGeogCS(pszCode);
4650
    else if (STARTS_WITH_CI(pszCode, "CRS27"))
4651
        return SetWellKnownGeogCS(pszCode);
4652
    else if (STARTS_WITH_CI(pszCode, "84"))  // urn:ogc:def:crs:OGC:2:84
4653
        return SetWellKnownGeogCS("CRS84");
4654
4655
    /* -------------------------------------------------------------------- */
4656
    /*      Handle auto codes.  We need to convert from format              */
4657
    /*      AUTO42001:99:8888 to format AUTO:42001,99,8888.                 */
4658
    /* -------------------------------------------------------------------- */
4659
    else if (STARTS_WITH_CI(pszCode, "AUTO"))
4660
    {
4661
        char szWMSAuto[100] = {'\0'};
4662
4663
        if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4664
            return OGRERR_FAILURE;
4665
4666
        snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4667
        for (int i = 5; szWMSAuto[i] != '\0'; i++)
4668
        {
4669
            if (szWMSAuto[i] == ':')
4670
                szWMSAuto[i] = ',';
4671
        }
4672
4673
        return importFromWMSAUTO(szWMSAuto);
4674
    }
4675
4676
    /* -------------------------------------------------------------------- */
4677
    /*      Not a recognise OGC item.                                       */
4678
    /* -------------------------------------------------------------------- */
4679
    CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4680
             pszURN);
4681
4682
    return OGRERR_FAILURE;
4683
#endif
4684
0
}
4685
4686
/************************************************************************/
4687
/*                           importFromURN()                            */
4688
/*                                                                      */
4689
/*      See OGC recommendation paper 06-023r1 or later for details.     */
4690
/************************************************************************/
4691
4692
/**
4693
 * \brief Initialize from OGC URN.
4694
 *
4695
 * Initializes this spatial reference from a coordinate system defined
4696
 * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4697
 * paper 06-023r1.  Currently EPSG and OGC authority values are supported,
4698
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4699
 *
4700
 * This method is also support through SetFromUserInput() which can
4701
 * normally be used for URNs.
4702
 *
4703
 * @param pszURN the urn string.
4704
 *
4705
 * @return OGRERR_NONE on success or an error code.
4706
 */
4707
4708
OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4709
4710
0
{
4711
0
    constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4712
0
    if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4713
0
        CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4714
0
            CPL_VALUE_INTEGER)
4715
0
    {
4716
0
        return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4717
0
    }
4718
4719
0
    TAKE_OPTIONAL_LOCK();
4720
4721
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4722
4723
    // PROJ 8.2.0 has support for IAU codes now.
4724
#if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4725
    /* -------------------------------------------------------------------- */
4726
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4727
    /* -------------------------------------------------------------------- */
4728
    const char *pszIAU = strstr(pszURN, "IAU");
4729
    if (pszIAU)
4730
    {
4731
        const char *pszCode = strchr(pszIAU, ':');
4732
        if (pszCode)
4733
        {
4734
            ++pszCode;
4735
            if (*pszCode == ':')
4736
                ++pszCode;
4737
            return importFromDict("IAU2000.wkt", pszCode);
4738
        }
4739
    }
4740
#endif
4741
4742
0
    if (strlen(pszURN) >= 1000)
4743
0
    {
4744
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4745
0
        return OGRERR_CORRUPT_DATA;
4746
0
    }
4747
0
    auto obj = proj_create(d->getPROJContext(), pszURN);
4748
0
    if (!obj)
4749
0
    {
4750
0
        return OGRERR_FAILURE;
4751
0
    }
4752
0
    Clear();
4753
0
    d->setPjCRS(obj);
4754
0
    return OGRERR_NONE;
4755
#else
4756
    const char *pszCur = nullptr;
4757
4758
    if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4759
        pszCur = pszURN + 16;
4760
    else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4761
        pszCur = pszURN + 20;
4762
    else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4763
        pszCur = pszURN + 18;
4764
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4765
        pszCur = pszURN + 16;
4766
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4767
        pszCur = pszURN + 20;
4768
    else
4769
    {
4770
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4771
                 pszURN);
4772
        return OGRERR_FAILURE;
4773
    }
4774
4775
    /* -------------------------------------------------------------------- */
4776
    /*      Clear any existing definition.                                  */
4777
    /* -------------------------------------------------------------------- */
4778
    Clear();
4779
4780
    /* -------------------------------------------------------------------- */
4781
    /*      Find code (ignoring version) out of string like:                */
4782
    /*                                                                      */
4783
    /*      authority:[version]:code                                        */
4784
    /* -------------------------------------------------------------------- */
4785
    const char *pszAuthority = pszCur;
4786
4787
    // skip authority
4788
    while (*pszCur != ':' && *pszCur)
4789
        pszCur++;
4790
    if (*pszCur == ':')
4791
        pszCur++;
4792
4793
    // skip version
4794
    const char *pszBeforeVersion = pszCur;
4795
    while (*pszCur != ':' && *pszCur)
4796
        pszCur++;
4797
    if (*pszCur == ':')
4798
        pszCur++;
4799
    else
4800
        // We come here in the case, the content to parse is authority:code
4801
        // (instead of authority::code) which is probably illegal according to
4802
        // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4803
        // for example in what is returned by GeoServer.
4804
        pszCur = pszBeforeVersion;
4805
4806
    const char *pszCode = pszCur;
4807
4808
    const char *pszComma = strchr(pszCur, ',');
4809
    if (pszComma == nullptr)
4810
        return importFromURNPart(pszAuthority, pszCode, pszURN);
4811
4812
    // There's a second part with the vertical SRS.
4813
    pszCur = pszComma + 1;
4814
    if (!STARTS_WITH(pszCur, "crs:"))
4815
    {
4816
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4817
                 pszURN);
4818
        return OGRERR_FAILURE;
4819
    }
4820
4821
    pszCur += 4;
4822
4823
    char *pszFirstCode = CPLStrdup(pszCode);
4824
    pszFirstCode[pszComma - pszCode] = '\0';
4825
    OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4826
    CPLFree(pszFirstCode);
4827
4828
    // Do we want to turn this into a compound definition
4829
    // with a vertical datum?
4830
    if (eStatus != OGRERR_NONE)
4831
        return eStatus;
4832
4833
    /* -------------------------------------------------------------------- */
4834
    /*      Find code (ignoring version) out of string like:                */
4835
    /*                                                                      */
4836
    /*      authority:[version]:code                                        */
4837
    /* -------------------------------------------------------------------- */
4838
    pszAuthority = pszCur;
4839
4840
    // skip authority
4841
    while (*pszCur != ':' && *pszCur)
4842
        pszCur++;
4843
    if (*pszCur == ':')
4844
        pszCur++;
4845
4846
    // skip version
4847
    pszBeforeVersion = pszCur;
4848
    while (*pszCur != ':' && *pszCur)
4849
        pszCur++;
4850
    if (*pszCur == ':')
4851
        pszCur++;
4852
    else
4853
        pszCur = pszBeforeVersion;
4854
4855
    pszCode = pszCur;
4856
4857
    OGRSpatialReference oVertSRS;
4858
    eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4859
    if (eStatus == OGRERR_NONE)
4860
    {
4861
        OGRSpatialReference oHorizSRS(*this);
4862
4863
        Clear();
4864
4865
        oHorizSRS.d->refreshProjObj();
4866
        oVertSRS.d->refreshProjObj();
4867
        if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4868
            return OGRERR_FAILURE;
4869
4870
        const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4871
        const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4872
4873
        CPLString osName = pszHorizName ? pszHorizName : "";
4874
        osName += " + ";
4875
        osName += pszVertName ? pszVertName : "";
4876
4877
        SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4878
    }
4879
4880
    return eStatus;
4881
#endif
4882
0
}
4883
4884
/************************************************************************/
4885
/*                           importFromCRSURL()                         */
4886
/*                                                                      */
4887
/*      See OGC Best Practice document 11-135 for details.              */
4888
/************************************************************************/
4889
4890
/**
4891
 * \brief Initialize from OGC URL.
4892
 *
4893
 * Initializes this spatial reference from a coordinate system defined
4894
 * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4895
 * paper 11-135.  Currently EPSG and OGC authority values are supported,
4896
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4897
 *
4898
 * This method is also supported through SetFromUserInput() which can
4899
 * normally be used for URLs.
4900
 *
4901
 * @param pszURL the URL string.
4902
 *
4903
 * @return OGRERR_NONE on success or an error code.
4904
 */
4905
4906
OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4907
4908
0
{
4909
0
    TAKE_OPTIONAL_LOCK();
4910
4911
#if !PROJ_AT_LEAST_VERSION(9, 1, 0)
4912
    if (strcmp(pszURL, "http://www.opengis.net/def/crs/OGC/0/CRS84h") == 0)
4913
    {
4914
        PJ *obj = proj_create(
4915
            d->getPROJContext(),
4916
            "GEOGCRS[\"WGS 84 longitude-latitude-height\",\n"
4917
            "    ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n"
4918
            "        MEMBER[\"World Geodetic System 1984 (Transit)\"],\n"
4919
            "        MEMBER[\"World Geodetic System 1984 (G730)\"],\n"
4920
            "        MEMBER[\"World Geodetic System 1984 (G873)\"],\n"
4921
            "        MEMBER[\"World Geodetic System 1984 (G1150)\"],\n"
4922
            "        MEMBER[\"World Geodetic System 1984 (G1674)\"],\n"
4923
            "        MEMBER[\"World Geodetic System 1984 (G1762)\"],\n"
4924
            "        MEMBER[\"World Geodetic System 1984 (G2139)\"],\n"
4925
            "        MEMBER[\"World Geodetic System 1984 (G2296)\"],\n"
4926
            "        ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
4927
            "            LENGTHUNIT[\"metre\",1]],\n"
4928
            "        ENSEMBLEACCURACY[2.0]],\n"
4929
            "    PRIMEM[\"Greenwich\",0,\n"
4930
            "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4931
            "    CS[ellipsoidal,3],\n"
4932
            "        AXIS[\"geodetic longitude (Lon)\",east,\n"
4933
            "            ORDER[1],\n"
4934
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4935
            "        AXIS[\"geodetic latitude (Lat)\",north,\n"
4936
            "            ORDER[2],\n"
4937
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4938
            "        AXIS[\"ellipsoidal height (h)\",up,\n"
4939
            "            ORDER[3],\n"
4940
            "            LENGTHUNIT[\"metre\",1]],\n"
4941
            "    USAGE[\n"
4942
            "        SCOPE[\"3D system frequently used in GIS, Web APIs and "
4943
            "Web applications\"],\n"
4944
            "        AREA[\"World.\"],\n"
4945
            "        BBOX[-90,-180,90,180]],\n"
4946
            "    ID[\"OGC\",\"CRS84h\"]]");
4947
        if (!obj)
4948
        {
4949
            return OGRERR_FAILURE;
4950
        }
4951
        Clear();
4952
        d->setPjCRS(obj);
4953
        return OGRERR_NONE;
4954
    }
4955
#endif
4956
4957
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4958
0
    if (strlen(pszURL) >= 10000)
4959
0
    {
4960
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4961
0
        return OGRERR_CORRUPT_DATA;
4962
0
    }
4963
4964
0
    PJ *obj;
4965
#if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4966
    if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4967
    {
4968
        obj = proj_create(
4969
            d->getPROJContext(),
4970
            CPLSPrintf("IAU:%s",
4971
                       pszURL +
4972
                           strlen("http://www.opengis.net/def/crs/IAU/0/")));
4973
    }
4974
    else
4975
#endif
4976
0
    {
4977
0
        obj = proj_create(d->getPROJContext(), pszURL);
4978
0
    }
4979
0
    if (!obj)
4980
0
    {
4981
0
        return OGRERR_FAILURE;
4982
0
    }
4983
0
    Clear();
4984
0
    d->setPjCRS(obj);
4985
0
    return OGRERR_NONE;
4986
#else
4987
    const char *pszCur = nullptr;
4988
4989
    if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4990
        pszCur = pszURL + 26;
4991
    else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4992
        pszCur = pszURL + 27;
4993
    else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4994
        pszCur = pszURL + 30;
4995
    else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4996
        pszCur = pszURL + 31;
4997
    else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4998
        pszCur = pszURL + 23;
4999
    else
5000
    {
5001
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
5002
                 pszURL);
5003
        return OGRERR_FAILURE;
5004
    }
5005
5006
    if (*pszCur == '\0')
5007
    {
5008
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
5009
        return OGRERR_FAILURE;
5010
    }
5011
5012
    /* -------------------------------------------------------------------- */
5013
    /*      Clear any existing definition.                                  */
5014
    /* -------------------------------------------------------------------- */
5015
    Clear();
5016
5017
    if (STARTS_WITH_CI(pszCur, "-compound?1="))
5018
    {
5019
        /* --------------------------------------------------------------------
5020
         */
5021
        /*      It's a compound CRS, of the form: */
5022
        /*                                                                      */
5023
        /*      http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
5024
        /* --------------------------------------------------------------------
5025
         */
5026
        pszCur += 12;
5027
5028
        // Extract each component CRS URL.
5029
        int iComponentUrl = 2;
5030
5031
        CPLString osName = "";
5032
        Clear();
5033
5034
        while (iComponentUrl != -1)
5035
        {
5036
            char searchStr[15] = {};
5037
            snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
5038
5039
            const char *pszUrlEnd = strstr(pszCur, searchStr);
5040
5041
            // Figure out the next component URL.
5042
            char *pszComponentUrl = nullptr;
5043
5044
            if (pszUrlEnd)
5045
            {
5046
                size_t nLen = pszUrlEnd - pszCur;
5047
                pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
5048
                strncpy(pszComponentUrl, pszCur, nLen);
5049
                pszComponentUrl[nLen] = '\0';
5050
5051
                ++iComponentUrl;
5052
                pszCur += nLen + strlen(searchStr);
5053
            }
5054
            else
5055
            {
5056
                if (iComponentUrl == 2)
5057
                {
5058
                    CPLError(CE_Failure, CPLE_AppDefined,
5059
                             "Compound CRS URLs must have at least two "
5060
                             "component CRSs.");
5061
                    return OGRERR_FAILURE;
5062
                }
5063
                else
5064
                {
5065
                    pszComponentUrl = CPLStrdup(pszCur);
5066
                    // no more components
5067
                    iComponentUrl = -1;
5068
                }
5069
            }
5070
5071
            OGRSpatialReference oComponentSRS;
5072
            OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
5073
5074
            CPLFree(pszComponentUrl);
5075
            pszComponentUrl = nullptr;
5076
5077
            if (eStatus == OGRERR_NONE)
5078
            {
5079
                if (osName.length() != 0)
5080
                {
5081
                    osName += " + ";
5082
                }
5083
                osName += oComponentSRS.GetRoot()->GetValue();
5084
                SetNode("COMPD_CS", osName);
5085
                GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
5086
            }
5087
            else
5088
                return eStatus;
5089
        }
5090
5091
        return OGRERR_NONE;
5092
    }
5093
5094
    /* -------------------------------------------------------------------- */
5095
    /*      It's a normal CRS URL, of the form:                             */
5096
    /*                                                                      */
5097
    /*      http://opengis.net/def/crs/AUTHORITY/VERSION/CODE               */
5098
    /* -------------------------------------------------------------------- */
5099
    ++pszCur;
5100
    const char *pszAuthority = pszCur;
5101
5102
    // skip authority
5103
    while (*pszCur != '/' && *pszCur)
5104
        pszCur++;
5105
    if (*pszCur == '/')
5106
        pszCur++;
5107
5108
    // skip version
5109
    while (*pszCur != '/' && *pszCur)
5110
        pszCur++;
5111
    if (*pszCur == '/')
5112
        pszCur++;
5113
5114
    const char *pszCode = pszCur;
5115
5116
    return importFromURNPart(pszAuthority, pszCode, pszURL);
5117
#endif
5118
0
}
5119
5120
/************************************************************************/
5121
/*                         importFromWMSAUTO()                          */
5122
/************************************************************************/
5123
5124
/**
5125
 * \brief Initialize from WMSAUTO string.
5126
 *
5127
 * Note that the WMS 1.3 specification does not include the
5128
 * units code, while apparently earlier specs do.  We try to
5129
 * guess around this.
5130
 *
5131
 * @param pszDefinition the WMSAUTO string
5132
 *
5133
 * @return OGRERR_NONE on success or an error code.
5134
 */
5135
OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
5136
5137
0
{
5138
0
    TAKE_OPTIONAL_LOCK();
5139
5140
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
5141
0
    if (strlen(pszDefinition) >= 10000)
5142
0
    {
5143
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
5144
0
        return OGRERR_CORRUPT_DATA;
5145
0
    }
5146
5147
0
    auto obj = proj_create(d->getPROJContext(), pszDefinition);
5148
0
    if (!obj)
5149
0
    {
5150
0
        return OGRERR_FAILURE;
5151
0
    }
5152
0
    Clear();
5153
0
    d->setPjCRS(obj);
5154
0
    return OGRERR_NONE;
5155
#else
5156
    int nProjId, nUnitsId;
5157
    double dfRefLong, dfRefLat = 0.0;
5158
5159
    /* -------------------------------------------------------------------- */
5160
    /*      Tokenize                                                        */
5161
    /* -------------------------------------------------------------------- */
5162
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
5163
        pszDefinition += 5;
5164
5165
    char **papszTokens =
5166
        CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5167
5168
    if (CSLCount(papszTokens) == 4)
5169
    {
5170
        nProjId = atoi(papszTokens[0]);
5171
        nUnitsId = atoi(papszTokens[1]);
5172
        dfRefLong = CPLAtof(papszTokens[2]);
5173
        dfRefLat = CPLAtof(papszTokens[3]);
5174
    }
5175
    else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5176
    {
5177
        nProjId = atoi(papszTokens[0]);
5178
        nUnitsId = atoi(papszTokens[1]);
5179
        dfRefLong = CPLAtof(papszTokens[2]);
5180
        dfRefLat = 0.0;
5181
    }
5182
    else if (CSLCount(papszTokens) == 3)
5183
    {
5184
        nProjId = atoi(papszTokens[0]);
5185
        nUnitsId = 9001;
5186
        dfRefLong = CPLAtof(papszTokens[1]);
5187
        dfRefLat = CPLAtof(papszTokens[2]);
5188
    }
5189
    else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5190
    {
5191
        nProjId = atoi(papszTokens[0]);
5192
        nUnitsId = 9001;
5193
        dfRefLong = CPLAtof(papszTokens[1]);
5194
    }
5195
    else
5196
    {
5197
        CSLDestroy(papszTokens);
5198
        CPLError(CE_Failure, CPLE_AppDefined,
5199
                 "AUTO projection has wrong number of arguments, expected\n"
5200
                 "AUTO:proj_id,units_id,ref_long,ref_lat or"
5201
                 "AUTO:proj_id,ref_long,ref_lat");
5202
        return OGRERR_FAILURE;
5203
    }
5204
5205
    CSLDestroy(papszTokens);
5206
    papszTokens = nullptr;
5207
5208
    /* -------------------------------------------------------------------- */
5209
    /*      Build coordsys.                                                 */
5210
    /* -------------------------------------------------------------------- */
5211
    Clear();
5212
5213
    /* -------------------------------------------------------------------- */
5214
    /*      Set WGS84.                                                      */
5215
    /* -------------------------------------------------------------------- */
5216
    SetWellKnownGeogCS("WGS84");
5217
5218
    switch (nProjId)
5219
    {
5220
        case 42001:  // Auto UTM
5221
            SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5222
                   dfRefLat >= 0.0);
5223
            break;
5224
5225
        case 42002:  // Auto TM (strangely very UTM-like).
5226
            SetTM(0, dfRefLong, 0.9996, 500000.0,
5227
                  (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5228
            break;
5229
5230
        case 42003:  // Auto Orthographic.
5231
            SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5232
            break;
5233
5234
        case 42004:  // Auto Equirectangular
5235
            SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5236
            break;
5237
5238
        case 42005:
5239
            SetMollweide(dfRefLong, 0.0, 0.0);
5240
            break;
5241
5242
        default:
5243
            CPLError(CE_Failure, CPLE_AppDefined,
5244
                     "Unsupported projection id in importFromWMSAUTO(): %d",
5245
                     nProjId);
5246
            return OGRERR_FAILURE;
5247
    }
5248
5249
    /* -------------------------------------------------------------------- */
5250
    /*      Set units.                                                      */
5251
    /* -------------------------------------------------------------------- */
5252
5253
    switch (nUnitsId)
5254
    {
5255
        case 9001:
5256
            SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5257
            break;
5258
5259
        case 9002:
5260
            SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5261
            break;
5262
5263
        case 9003:
5264
            SetTargetLinearUnits(nullptr, "US survey foot",
5265
                                 CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5266
            break;
5267
5268
        default:
5269
            CPLError(CE_Failure, CPLE_AppDefined,
5270
                     "Unsupported units code (%d).", nUnitsId);
5271
            return OGRERR_FAILURE;
5272
            break;
5273
    }
5274
5275
    return OGRERR_NONE;
5276
#endif
5277
0
}
5278
5279
/************************************************************************/
5280
/*                            GetSemiMajor()                            */
5281
/************************************************************************/
5282
5283
/**
5284
 * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5285
 *
5286
 * This method does the same thing as the C function OSRGetSemiMajor().
5287
 *
5288
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5289
 * can be found.
5290
 *
5291
 * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5292
 */
5293
5294
double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5295
5296
0
{
5297
0
    TAKE_OPTIONAL_LOCK();
5298
5299
0
    if (pnErr != nullptr)
5300
0
        *pnErr = OGRERR_FAILURE;
5301
5302
0
    d->refreshProjObj();
5303
0
    if (!d->m_pj_crs)
5304
0
        return SRS_WGS84_SEMIMAJOR;
5305
5306
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5307
0
    if (!ellps)
5308
0
        return SRS_WGS84_SEMIMAJOR;
5309
5310
0
    double dfSemiMajor = 0.0;
5311
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5312
0
                                  nullptr, nullptr, nullptr);
5313
0
    proj_destroy(ellps);
5314
5315
0
    if (dfSemiMajor > 0)
5316
0
    {
5317
0
        if (pnErr != nullptr)
5318
0
            *pnErr = OGRERR_NONE;
5319
0
        return dfSemiMajor;
5320
0
    }
5321
5322
0
    return SRS_WGS84_SEMIMAJOR;
5323
0
}
5324
5325
/************************************************************************/
5326
/*                          OSRGetSemiMajor()                           */
5327
/************************************************************************/
5328
5329
/**
5330
 * \brief Get spheroid semi major axis.
5331
 *
5332
 * This function is the same as OGRSpatialReference::GetSemiMajor()
5333
 */
5334
double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5335
5336
0
{
5337
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5338
5339
0
    return ToPointer(hSRS)->GetSemiMajor(pnErr);
5340
0
}
5341
5342
/************************************************************************/
5343
/*                          GetInvFlattening()                          */
5344
/************************************************************************/
5345
5346
/**
5347
 * \brief Get spheroid inverse flattening.
5348
 *
5349
 * This method does the same thing as the C function OSRGetInvFlattening().
5350
 *
5351
 * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5352
 * can be found.
5353
 *
5354
 * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5355
 */
5356
5357
double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5358
5359
0
{
5360
0
    TAKE_OPTIONAL_LOCK();
5361
5362
0
    if (pnErr != nullptr)
5363
0
        *pnErr = OGRERR_FAILURE;
5364
5365
0
    d->refreshProjObj();
5366
0
    if (!d->m_pj_crs)
5367
0
        return SRS_WGS84_INVFLATTENING;
5368
5369
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5370
0
    if (!ellps)
5371
0
        return SRS_WGS84_INVFLATTENING;
5372
5373
0
    double dfInvFlattening = -1.0;
5374
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5375
0
                                  nullptr, &dfInvFlattening);
5376
0
    proj_destroy(ellps);
5377
5378
0
    if (dfInvFlattening >= 0.0)
5379
0
    {
5380
0
        if (pnErr != nullptr)
5381
0
            *pnErr = OGRERR_NONE;
5382
0
        return dfInvFlattening;
5383
0
    }
5384
5385
0
    return SRS_WGS84_INVFLATTENING;
5386
0
}
5387
5388
/************************************************************************/
5389
/*                        OSRGetInvFlattening()                         */
5390
/************************************************************************/
5391
5392
/**
5393
 * \brief Get spheroid inverse flattening.
5394
 *
5395
 * This function is the same as OGRSpatialReference::GetInvFlattening()
5396
 */
5397
double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5398
5399
0
{
5400
0
    VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5401
5402
0
    return ToPointer(hSRS)->GetInvFlattening(pnErr);
5403
0
}
5404
5405
/************************************************************************/
5406
/*                          GetEccentricity()                           */
5407
/************************************************************************/
5408
5409
/**
5410
 * \brief Get spheroid eccentricity
5411
 *
5412
 * @return eccentricity (or -1 in case of error)
5413
 */
5414
5415
double OGRSpatialReference::GetEccentricity() const
5416
5417
0
{
5418
0
    OGRErr eErr = OGRERR_NONE;
5419
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5420
0
    if (eErr != OGRERR_NONE)
5421
0
    {
5422
0
        return -1.0;
5423
0
    }
5424
0
    if (dfInvFlattening == 0.0)
5425
0
        return 0.0;
5426
0
    if (dfInvFlattening < 0.5)
5427
0
        return -1.0;
5428
0
    return sqrt(2.0 / dfInvFlattening -
5429
0
                1.0 / (dfInvFlattening * dfInvFlattening));
5430
0
}
5431
5432
/************************************************************************/
5433
/*                       GetSquaredEccentricity()                       */
5434
/************************************************************************/
5435
5436
/**
5437
 * \brief Get spheroid squared eccentricity
5438
 *
5439
 * @return squared eccentricity (or -1 in case of error)
5440
 */
5441
5442
double OGRSpatialReference::GetSquaredEccentricity() const
5443
5444
0
{
5445
0
    OGRErr eErr = OGRERR_NONE;
5446
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5447
0
    if (eErr != OGRERR_NONE)
5448
0
    {
5449
0
        return -1.0;
5450
0
    }
5451
0
    if (dfInvFlattening == 0.0)
5452
0
        return 0.0;
5453
0
    if (dfInvFlattening < 0.5)
5454
0
        return -1.0;
5455
0
    return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5456
0
}
5457
5458
/************************************************************************/
5459
/*                            GetSemiMinor()                            */
5460
/************************************************************************/
5461
5462
/**
5463
 * \brief Get spheroid semi minor axis.
5464
 *
5465
 * This method does the same thing as the C function OSRGetSemiMinor().
5466
 *
5467
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5468
 * can be found.
5469
 *
5470
 * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5471
 */
5472
5473
double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5474
5475
0
{
5476
0
    const double dfSemiMajor = GetSemiMajor(pnErr);
5477
0
    const double dfInvFlattening = GetInvFlattening(pnErr);
5478
5479
0
    return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5480
0
}
5481
5482
/************************************************************************/
5483
/*                          OSRGetSemiMinor()                           */
5484
/************************************************************************/
5485
5486
/**
5487
 * \brief Get spheroid semi minor axis.
5488
 *
5489
 * This function is the same as OGRSpatialReference::GetSemiMinor()
5490
 */
5491
double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5492
5493
0
{
5494
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5495
5496
0
    return ToPointer(hSRS)->GetSemiMinor(pnErr);
5497
0
}
5498
5499
/************************************************************************/
5500
/*                             SetLocalCS()                             */
5501
/************************************************************************/
5502
5503
/**
5504
 * \brief Set the user visible LOCAL_CS name.
5505
 *
5506
 * This method is the same as the C function OSRSetLocalCS().
5507
 *
5508
 * This method will ensure a LOCAL_CS node is created as the root,
5509
 * and set the provided name on it.  It must be used before SetLinearUnits().
5510
 *
5511
 * @param pszName the user visible name to assign.  Not used as a key.
5512
 *
5513
 * @return OGRERR_NONE on success.
5514
 */
5515
5516
OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5517
5518
0
{
5519
0
    TAKE_OPTIONAL_LOCK();
5520
5521
0
    if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5522
0
        d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5523
0
    {
5524
0
        d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5525
0
    }
5526
0
    else
5527
0
    {
5528
0
        CPLDebug("OGR",
5529
0
                 "OGRSpatialReference::SetLocalCS(%s) failed.  "
5530
0
                 "It appears an incompatible object already exists.",
5531
0
                 pszName);
5532
0
        return OGRERR_FAILURE;
5533
0
    }
5534
5535
0
    return OGRERR_NONE;
5536
0
}
5537
5538
/************************************************************************/
5539
/*                           OSRSetLocalCS()                            */
5540
/************************************************************************/
5541
5542
/**
5543
 * \brief Set the user visible LOCAL_CS name.
5544
 *
5545
 * This function is the same as OGRSpatialReference::SetLocalCS()
5546
 */
5547
OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5548
5549
0
{
5550
0
    VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5551
5552
0
    return ToPointer(hSRS)->SetLocalCS(pszName);
5553
0
}
5554
5555
/************************************************************************/
5556
/*                             SetGeocCS()                              */
5557
/************************************************************************/
5558
5559
/**
5560
 * \brief Set the user visible GEOCCS name.
5561
 *
5562
 * This method is the same as the C function OSRSetGeocCS().
5563
5564
 * This method will ensure a GEOCCS node is created as the root,
5565
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5566
 * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5567
 * the GEOGCS.
5568
 *
5569
 * @param pszName the user visible name to assign.  Not used as a key.
5570
 *
5571
 * @return OGRERR_NONE on success.
5572
 *
5573
 */
5574
5575
OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5576
5577
0
{
5578
0
    TAKE_OPTIONAL_LOCK();
5579
5580
0
    OGRErr eErr = OGRERR_NONE;
5581
0
    d->refreshProjObj();
5582
0
    d->demoteFromBoundCRS();
5583
0
    if (d->m_pjType == PJ_TYPE_UNKNOWN)
5584
0
    {
5585
0
        d->setPjCRS(proj_create_geocentric_crs(
5586
0
            d->getPROJContext(), pszName, "World Geodetic System 1984",
5587
0
            "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5588
0
            SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5589
0
            "Metre", 1.0));
5590
0
    }
5591
0
    else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5592
0
    {
5593
0
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5594
0
    }
5595
0
    else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5596
0
             d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5597
0
    {
5598
0
        auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5599
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
5600
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5601
0
        if (datum == nullptr)
5602
0
        {
5603
0
            datum =
5604
0
                proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5605
0
        }
5606
0
#endif
5607
0
        if (datum == nullptr)
5608
0
        {
5609
0
            d->undoDemoteFromBoundCRS();
5610
0
            return OGRERR_FAILURE;
5611
0
        }
5612
5613
0
        auto pj_crs = proj_create_geocentric_crs_from_datum(
5614
0
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5615
0
            0.0);
5616
0
        d->setPjCRS(pj_crs);
5617
5618
0
        proj_destroy(datum);
5619
0
    }
5620
0
    else
5621
0
    {
5622
0
        CPLDebug("OGR",
5623
0
                 "OGRSpatialReference::SetGeocCS(%s) failed.  "
5624
0
                 "It appears an incompatible object already exists.",
5625
0
                 pszName);
5626
0
        eErr = OGRERR_FAILURE;
5627
0
    }
5628
0
    d->undoDemoteFromBoundCRS();
5629
5630
0
    return eErr;
5631
0
}
5632
5633
/************************************************************************/
5634
/*                            OSRSetGeocCS()                            */
5635
/************************************************************************/
5636
5637
/**
5638
 * \brief Set the user visible PROJCS name.
5639
 *
5640
 * This function is the same as OGRSpatialReference::SetGeocCS()
5641
 *
5642
 */
5643
OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5644
5645
0
{
5646
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5647
5648
0
    return ToPointer(hSRS)->SetGeocCS(pszName);
5649
0
}
5650
5651
/************************************************************************/
5652
/*                             SetVertCS()                              */
5653
/************************************************************************/
5654
5655
/**
5656
 * \brief Set the user visible VERT_CS name.
5657
 *
5658
 * This method is the same as the C function OSRSetVertCS().
5659
5660
 * This method will ensure a VERT_CS node is created if needed.  If the
5661
 * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5662
 * turned into a COMPD_CS.
5663
 *
5664
 * @param pszVertCSName the user visible name of the vertical coordinate
5665
 * system. Not used as a key.
5666
 *
5667
 * @param pszVertDatumName the user visible name of the vertical datum.  It
5668
 * is helpful if this matches the EPSG name.
5669
 *
5670
 * @param nVertDatumType the OGC vertical datum type. Ignored
5671
 *
5672
 * @return OGRERR_NONE on success.
5673
 *
5674
 */
5675
5676
OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5677
                                      const char *pszVertDatumName,
5678
                                      int nVertDatumType)
5679
5680
0
{
5681
0
    TAKE_OPTIONAL_LOCK();
5682
5683
0
    CPL_IGNORE_RET_VAL(nVertDatumType);
5684
5685
0
    d->refreshProjObj();
5686
5687
0
    auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5688
0
                                            pszVertDatumName, nullptr, 0.0);
5689
5690
    /* -------------------------------------------------------------------- */
5691
    /*      Handle the case where we want to make a compound coordinate     */
5692
    /*      system.                                                         */
5693
    /* -------------------------------------------------------------------- */
5694
0
    if (IsProjected() || IsGeographic())
5695
0
    {
5696
0
        auto compoundCRS = proj_create_compound_crs(
5697
0
            d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5698
0
        proj_destroy(vertCRS);
5699
0
        d->setPjCRS(compoundCRS);
5700
0
    }
5701
0
    else
5702
0
    {
5703
0
        d->setPjCRS(vertCRS);
5704
0
    }
5705
0
    return OGRERR_NONE;
5706
0
}
5707
5708
/************************************************************************/
5709
/*                            OSRSetVertCS()                            */
5710
/************************************************************************/
5711
5712
/**
5713
 * \brief Setup the vertical coordinate system.
5714
 *
5715
 * This function is the same as OGRSpatialReference::SetVertCS()
5716
 *
5717
 */
5718
OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5719
                    const char *pszVertDatumName, int nVertDatumType)
5720
5721
0
{
5722
0
    VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5723
5724
0
    return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5725
0
                                      nVertDatumType);
5726
0
}
5727
5728
/************************************************************************/
5729
/*                           SetCompoundCS()                            */
5730
/************************************************************************/
5731
5732
/**
5733
 * \brief Setup a compound coordinate system.
5734
 *
5735
 * This method is the same as the C function OSRSetCompoundCS().
5736
5737
 * This method is replace the current SRS with a COMPD_CS coordinate system
5738
 * consisting of the passed in horizontal and vertical coordinate systems.
5739
 *
5740
 * @param pszName the name of the compound coordinate system.
5741
 *
5742
 * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5743
 *
5744
 * @param poVertSRS the vertical SRS (VERT_CS).
5745
 *
5746
 * @return OGRERR_NONE on success.
5747
 */
5748
5749
OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5750
                                          const OGRSpatialReference *poHorizSRS,
5751
                                          const OGRSpatialReference *poVertSRS)
5752
5753
0
{
5754
0
    TAKE_OPTIONAL_LOCK();
5755
5756
    /* -------------------------------------------------------------------- */
5757
    /*      Verify these are legal horizontal and vertical coordinate       */
5758
    /*      systems.                                                        */
5759
    /* -------------------------------------------------------------------- */
5760
0
    if (!poVertSRS->IsVertical())
5761
0
    {
5762
0
        CPLError(CE_Failure, CPLE_AppDefined,
5763
0
                 "SetCompoundCS() fails, vertical component is not VERT_CS.");
5764
0
        return OGRERR_FAILURE;
5765
0
    }
5766
0
    if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5767
0
    {
5768
0
        CPLError(CE_Failure, CPLE_AppDefined,
5769
0
                 "SetCompoundCS() fails, horizontal component is not PROJCS or "
5770
0
                 "GEOGCS.");
5771
0
        return OGRERR_FAILURE;
5772
0
    }
5773
5774
    /* -------------------------------------------------------------------- */
5775
    /*      Replace with compound srs.                                      */
5776
    /* -------------------------------------------------------------------- */
5777
0
    Clear();
5778
5779
0
    auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5780
0
                                                poHorizSRS->d->m_pj_crs,
5781
0
                                                poVertSRS->d->m_pj_crs);
5782
0
    d->setPjCRS(compoundCRS);
5783
5784
0
    return OGRERR_NONE;
5785
0
}
5786
5787
/************************************************************************/
5788
/*                          OSRSetCompoundCS()                          */
5789
/************************************************************************/
5790
5791
/**
5792
 * \brief Setup a compound coordinate system.
5793
 *
5794
 * This function is the same as OGRSpatialReference::SetCompoundCS()
5795
 */
5796
OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5797
                        OGRSpatialReferenceH hHorizSRS,
5798
                        OGRSpatialReferenceH hVertSRS)
5799
5800
0
{
5801
0
    VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5802
0
    VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5803
0
    VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5804
5805
0
    return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5806
0
                                          ToPointer(hVertSRS));
5807
0
}
5808
5809
/************************************************************************/
5810
/*                        GetCompoundComponent()                        */
5811
/************************************************************************/
5812
5813
/**
5814
 * \brief Get a CRS component from a CompoundCRS
5815
 *
5816
 * @param iComponent Index of the CRS component (typically 0 = horizontal, 1 =
5817
 * vertical)
5818
 * @return new OGRSpatialReference object, or NULL in case of error.
5819
 *
5820
 * @since 3.14
5821
 */
5822
5823
std::unique_ptr<OGRSpatialReference>
5824
OGRSpatialReference::GetCompoundComponent(int iComponent) const
5825
0
{
5826
0
    std::unique_ptr<OGRSpatialReference> poSRS;
5827
0
    d->refreshProjObj();
5828
0
    d->demoteFromBoundCRS();
5829
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
5830
0
    {
5831
0
        auto subCrs =
5832
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, iComponent);
5833
0
        if (subCrs)
5834
0
        {
5835
0
            poSRS = std::make_unique<OGRSpatialReference>();
5836
0
            poSRS->d->setPjCRS(subCrs);
5837
0
        }
5838
0
    }
5839
0
    d->undoDemoteFromBoundCRS();
5840
0
    return poSRS;
5841
0
}
5842
5843
/************************************************************************/
5844
/*                             SetProjCS()                              */
5845
/************************************************************************/
5846
5847
/**
5848
 * \brief Set the user visible PROJCS name.
5849
 *
5850
 * This method is the same as the C function OSRSetProjCS().
5851
 *
5852
 * This method will ensure a PROJCS node is created as the root,
5853
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5854
 * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5855
 *
5856
 * @param pszName the user visible name to assign.  Not used as a key.
5857
 *
5858
 * @return OGRERR_NONE on success.
5859
 */
5860
5861
OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5862
5863
0
{
5864
0
    TAKE_OPTIONAL_LOCK();
5865
5866
0
    d->refreshProjObj();
5867
0
    d->demoteFromBoundCRS();
5868
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5869
0
    {
5870
0
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5871
0
    }
5872
0
    else
5873
0
    {
5874
0
        auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5875
0
                                                nullptr, nullptr, nullptr,
5876
0
                                                nullptr, nullptr, 0, nullptr);
5877
0
        auto cs = proj_create_cartesian_2D_cs(
5878
0
            d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5879
5880
0
        auto projCRS = proj_create_projected_crs(
5881
0
            d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5882
0
        proj_destroy(dummyConv);
5883
0
        proj_destroy(cs);
5884
5885
0
        d->setPjCRS(projCRS);
5886
0
    }
5887
0
    d->undoDemoteFromBoundCRS();
5888
0
    return OGRERR_NONE;
5889
0
}
5890
5891
/************************************************************************/
5892
/*                            OSRSetProjCS()                            */
5893
/************************************************************************/
5894
5895
/**
5896
 * \brief Set the user visible PROJCS name.
5897
 *
5898
 * This function is the same as OGRSpatialReference::SetProjCS()
5899
 */
5900
OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5901
5902
0
{
5903
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5904
5905
0
    return ToPointer(hSRS)->SetProjCS(pszName);
5906
0
}
5907
5908
/************************************************************************/
5909
/*                           SetProjection()                            */
5910
/************************************************************************/
5911
5912
/**
5913
 * \brief Set a projection name.
5914
 *
5915
 * This method is the same as the C function OSRSetProjection().
5916
 *
5917
 * @param pszProjection the projection name, which should be selected from
5918
 * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5919
 *
5920
 * @return OGRERR_NONE on success.
5921
 */
5922
5923
OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5924
5925
0
{
5926
0
    TAKE_OPTIONAL_LOCK();
5927
5928
0
    OGR_SRSNode *poGeogCS = nullptr;
5929
5930
0
    if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5931
0
    {
5932
0
        poGeogCS = d->m_poRoot;
5933
0
        d->m_poRoot = nullptr;
5934
0
    }
5935
5936
0
    if (!GetAttrNode("PROJCS"))
5937
0
    {
5938
0
        SetNode("PROJCS", "unnamed");
5939
0
    }
5940
5941
0
    const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5942
0
    if (eErr != OGRERR_NONE)
5943
0
        return eErr;
5944
5945
0
    if (poGeogCS != nullptr)
5946
0
        d->m_poRoot->InsertChild(poGeogCS, 1);
5947
5948
0
    return OGRERR_NONE;
5949
0
}
5950
5951
/************************************************************************/
5952
/*                          OSRSetProjection()                          */
5953
/************************************************************************/
5954
5955
/**
5956
 * \brief Set a projection name.
5957
 *
5958
 * This function is the same as OGRSpatialReference::SetProjection()
5959
 */
5960
OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5961
5962
0
{
5963
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5964
5965
0
    return ToPointer(hSRS)->SetProjection(pszProjection);
5966
0
}
5967
5968
/************************************************************************/
5969
/*                      GetWKT2ProjectionMethod()                       */
5970
/************************************************************************/
5971
5972
/**
5973
 * \brief Returns info on the projection method, based on WKT2 naming
5974
 * conventions.
5975
 *
5976
 * The returned strings are short lived and should be considered to be
5977
 * invalidated by any further call to the GDAL API.
5978
 *
5979
 * @param[out] ppszMethodName Pointer to a string that will receive the
5980
 * projection method name.
5981
 * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5982
 * receive the name of the authority that defines the projection method.
5983
 * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5984
 * an authority.
5985
 * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5986
 * receive the code that defines the projection method.
5987
 * *ppszMethodCode may be nullptr if the projection method is not linked to
5988
 * an authority.
5989
 *
5990
 * @return OGRERR_NONE on success.
5991
 */
5992
OGRErr
5993
OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5994
                                             const char **ppszMethodAuthName,
5995
                                             const char **ppszMethodCode) const
5996
0
{
5997
0
    TAKE_OPTIONAL_LOCK();
5998
5999
0
    auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
6000
0
    if (!conv)
6001
0
        return OGRERR_FAILURE;
6002
0
    const char *pszTmpMethodName = "";
6003
0
    const char *pszTmpMethodAuthName = "";
6004
0
    const char *pszTmpMethodCode = "";
6005
0
    int ret = proj_coordoperation_get_method_info(
6006
0
        d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
6007
0
        &pszTmpMethodCode);
6008
    // "Internalize" temporary strings returned by PROJ
6009
0
    CPLAssert(pszTmpMethodName);
6010
0
    if (ppszMethodName)
6011
0
        *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
6012
0
    if (ppszMethodAuthName)
6013
0
        *ppszMethodAuthName = pszTmpMethodAuthName
6014
0
                                  ? CPLSPrintf("%s", pszTmpMethodAuthName)
6015
0
                                  : nullptr;
6016
0
    if (ppszMethodCode)
6017
0
        *ppszMethodCode =
6018
0
            pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
6019
0
    proj_destroy(conv);
6020
0
    return ret ? OGRERR_NONE : OGRERR_FAILURE;
6021
0
}
6022
6023
/************************************************************************/
6024
/*                            SetProjParm()                             */
6025
/************************************************************************/
6026
6027
/**
6028
 * \brief Set a projection parameter value.
6029
 *
6030
 * Adds a new PARAMETER under the PROJCS with the indicated name and value.
6031
 *
6032
 * This method is the same as the C function OSRSetProjParm().
6033
 *
6034
 * Please check https://gdal.org/proj_list pages for
6035
 * legal parameter names for specific projections.
6036
 *
6037
 *
6038
 * @param pszParamName the parameter name, which should be selected from
6039
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6040
 *
6041
 * @param dfValue value to assign.
6042
 *
6043
 * @return OGRERR_NONE on success.
6044
 */
6045
6046
OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
6047
                                        double dfValue)
6048
6049
0
{
6050
0
    TAKE_OPTIONAL_LOCK();
6051
6052
0
    OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
6053
6054
0
    if (poPROJCS == nullptr)
6055
0
        return OGRERR_FAILURE;
6056
6057
0
    char szValue[64] = {'\0'};
6058
0
    OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
6059
6060
    /* -------------------------------------------------------------------- */
6061
    /*      Try to find existing parameter with this name.                  */
6062
    /* -------------------------------------------------------------------- */
6063
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6064
0
    {
6065
0
        OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
6066
6067
0
        if (EQUAL(poParam->GetValue(), "PARAMETER") &&
6068
0
            poParam->GetChildCount() == 2 &&
6069
0
            EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
6070
0
        {
6071
0
            poParam->GetChild(1)->SetValue(szValue);
6072
0
            return OGRERR_NONE;
6073
0
        }
6074
0
    }
6075
6076
    /* -------------------------------------------------------------------- */
6077
    /*      Otherwise create a new parameter and append.                    */
6078
    /* -------------------------------------------------------------------- */
6079
0
    OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
6080
0
    poParam->AddChild(new OGR_SRSNode(pszParamName));
6081
0
    poParam->AddChild(new OGR_SRSNode(szValue));
6082
6083
0
    poPROJCS->AddChild(poParam);
6084
6085
0
    return OGRERR_NONE;
6086
0
}
6087
6088
/************************************************************************/
6089
/*                           OSRSetProjParm()                           */
6090
/************************************************************************/
6091
6092
/**
6093
 * \brief Set a projection parameter value.
6094
 *
6095
 * This function is the same as OGRSpatialReference::SetProjParm()
6096
 */
6097
OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6098
                      double dfValue)
6099
6100
0
{
6101
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
6102
6103
0
    return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
6104
0
}
6105
6106
/************************************************************************/
6107
/*                            FindProjParm()                            */
6108
/************************************************************************/
6109
6110
/**
6111
 * \brief Return the child index of the named projection parameter on
6112
 * its parent PROJCS node.
6113
 *
6114
 * @param pszParameter projection parameter to look for
6115
 * @param poPROJCS projection CS node to look in. If NULL is passed,
6116
 *        the PROJCS node of the SpatialReference object will be searched.
6117
 *
6118
 * @return the child index of the named projection parameter. -1 on failure
6119
 */
6120
int OGRSpatialReference::FindProjParm(const char *pszParameter,
6121
                                      const OGR_SRSNode *poPROJCS) const
6122
6123
0
{
6124
0
    TAKE_OPTIONAL_LOCK();
6125
6126
0
    if (poPROJCS == nullptr)
6127
0
        poPROJCS = GetAttrNode("PROJCS");
6128
6129
0
    if (poPROJCS == nullptr)
6130
0
        return -1;
6131
6132
    /* -------------------------------------------------------------------- */
6133
    /*      Search for requested parameter.                                 */
6134
    /* -------------------------------------------------------------------- */
6135
0
    bool bIsWKT2 = false;
6136
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6137
0
    {
6138
0
        const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6139
6140
0
        if (poParameter->GetChildCount() >= 2)
6141
0
        {
6142
0
            const char *pszValue = poParameter->GetValue();
6143
0
            if (EQUAL(pszValue, "PARAMETER") &&
6144
0
                EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
6145
0
                      pszParameter))
6146
0
            {
6147
0
                return iChild;
6148
0
            }
6149
0
            else if (EQUAL(pszValue, "METHOD"))
6150
0
            {
6151
0
                bIsWKT2 = true;
6152
0
            }
6153
0
        }
6154
0
    }
6155
6156
    /* -------------------------------------------------------------------- */
6157
    /*      Try similar names, for selected parameters.                     */
6158
    /* -------------------------------------------------------------------- */
6159
0
    if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
6160
0
    {
6161
0
        if (bIsWKT2)
6162
0
        {
6163
0
            int iChild = FindProjParm(
6164
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6165
0
            if (iChild == -1)
6166
0
                iChild = FindProjParm(
6167
0
                    EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
6168
0
            return iChild;
6169
0
        }
6170
0
        return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
6171
0
    }
6172
6173
0
    if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
6174
0
    {
6175
0
        if (bIsWKT2)
6176
0
        {
6177
0
            int iChild = FindProjParm(
6178
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6179
0
            if (iChild == -1)
6180
0
                iChild = FindProjParm(
6181
0
                    EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
6182
0
            return iChild;
6183
0
        }
6184
0
        int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
6185
0
        if (iChild == -1)
6186
0
            iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
6187
0
        return iChild;
6188
0
    }
6189
6190
0
    return -1;
6191
0
}
6192
6193
/************************************************************************/
6194
/*                            GetProjParm()                             */
6195
/************************************************************************/
6196
6197
/**
6198
 * \brief Fetch a projection parameter value.
6199
 *
6200
 * NOTE: This code should be modified to translate non degree angles into
6201
 * degrees based on the GEOGCS unit.  This has not yet been done.
6202
 *
6203
 * This method is the same as the C function OSRGetProjParm().
6204
 *
6205
 * @param pszName the name of the parameter to fetch, from the set of
6206
 * SRS_PP codes in ogr_srs_api.h.
6207
 *
6208
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6209
 *
6210
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6211
 *
6212
 * @return value of parameter.
6213
 */
6214
6215
double OGRSpatialReference::GetProjParm(const char *pszName,
6216
                                        double dfDefaultValue,
6217
                                        OGRErr *pnErr) const
6218
6219
0
{
6220
0
    TAKE_OPTIONAL_LOCK();
6221
6222
0
    d->refreshProjObj();
6223
0
    GetRoot();  // force update of d->m_bNodesWKT2
6224
6225
0
    if (pnErr != nullptr)
6226
0
        *pnErr = OGRERR_NONE;
6227
6228
    /* -------------------------------------------------------------------- */
6229
    /*      Find the desired parameter.                                     */
6230
    /* -------------------------------------------------------------------- */
6231
0
    const OGR_SRSNode *poPROJCS =
6232
0
        GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6233
0
    if (poPROJCS == nullptr)
6234
0
    {
6235
0
        if (pnErr != nullptr)
6236
0
            *pnErr = OGRERR_FAILURE;
6237
0
        return dfDefaultValue;
6238
0
    }
6239
6240
0
    const int iChild = FindProjParm(pszName, poPROJCS);
6241
0
    if (iChild == -1)
6242
0
    {
6243
0
        if (IsProjected() && GetAxesCount() == 3)
6244
0
        {
6245
0
            OGRSpatialReference *poSRSTmp = Clone();
6246
0
            poSRSTmp->DemoteTo2D(nullptr);
6247
0
            const double dfRet =
6248
0
                poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6249
0
            delete poSRSTmp;
6250
0
            return dfRet;
6251
0
        }
6252
6253
0
        if (pnErr != nullptr)
6254
0
            *pnErr = OGRERR_FAILURE;
6255
0
        return dfDefaultValue;
6256
0
    }
6257
6258
0
    const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6259
0
    return CPLAtof(poParameter->GetChild(1)->GetValue());
6260
0
}
6261
6262
/************************************************************************/
6263
/*                           OSRGetProjParm()                           */
6264
/************************************************************************/
6265
6266
/**
6267
 * \brief Fetch a projection parameter value.
6268
 *
6269
 * This function is the same as OGRSpatialReference::GetProjParm()
6270
 */
6271
double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6272
                      double dfDefaultValue, OGRErr *pnErr)
6273
6274
0
{
6275
0
    VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6276
6277
0
    return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6278
0
}
6279
6280
/************************************************************************/
6281
/*                          GetNormProjParm()                           */
6282
/************************************************************************/
6283
6284
/**
6285
 * \brief Fetch a normalized projection parameter value.
6286
 *
6287
 * This method is the same as GetProjParm() except that the value of
6288
 * the parameter is "normalized" into degrees or meters depending on
6289
 * whether it is linear or angular.
6290
 *
6291
 * This method is the same as the C function OSRGetNormProjParm().
6292
 *
6293
 * @param pszName the name of the parameter to fetch, from the set of
6294
 * SRS_PP codes in ogr_srs_api.h.
6295
 *
6296
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6297
 *
6298
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6299
 *
6300
 * @return value of parameter.
6301
 */
6302
6303
double OGRSpatialReference::GetNormProjParm(const char *pszName,
6304
                                            double dfDefaultValue,
6305
                                            OGRErr *pnErr) const
6306
6307
0
{
6308
0
    TAKE_OPTIONAL_LOCK();
6309
6310
0
    GetNormInfo();
6311
6312
0
    OGRErr nError = OGRERR_NONE;
6313
0
    double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6314
0
    if (pnErr != nullptr)
6315
0
        *pnErr = nError;
6316
6317
    // If we got the default just return it unadjusted.
6318
0
    if (nError != OGRERR_NONE)
6319
0
        return dfRawResult;
6320
6321
0
    if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6322
0
        dfRawResult *= d->dfToDegrees;
6323
6324
0
    if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6325
0
        return dfRawResult * d->dfToMeter;
6326
6327
0
    return dfRawResult;
6328
0
}
6329
6330
/************************************************************************/
6331
/*                         OSRGetNormProjParm()                         */
6332
/************************************************************************/
6333
6334
/**
6335
 * \brief This function is the same as OGRSpatialReference::
6336
 *
6337
 * This function is the same as OGRSpatialReference::GetNormProjParm()
6338
 */
6339
double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6340
                          double dfDefaultValue, OGRErr *pnErr)
6341
6342
0
{
6343
0
    VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6344
6345
0
    return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6346
0
}
6347
6348
/************************************************************************/
6349
/*                          SetNormProjParm()                           */
6350
/************************************************************************/
6351
6352
/**
6353
 * \brief Set a projection parameter with a normalized value.
6354
 *
6355
 * This method is the same as SetProjParm() except that the value of
6356
 * the parameter passed in is assumed to be in "normalized" form (decimal
6357
 * degrees for angular values, meters for linear values.  The values are
6358
 * converted in a form suitable for the GEOGCS and linear units in effect.
6359
 *
6360
 * This method is the same as the C function OSRSetNormProjParm().
6361
 *
6362
 * @param pszName the parameter name, which should be selected from
6363
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6364
 *
6365
 * @param dfValue value to assign.
6366
 *
6367
 * @return OGRERR_NONE on success.
6368
 */
6369
6370
OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6371
6372
0
{
6373
0
    TAKE_OPTIONAL_LOCK();
6374
6375
0
    GetNormInfo();
6376
6377
0
    if (d->dfToDegrees != 0.0 &&
6378
0
        (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6379
0
        IsAngularParameter(pszName))
6380
0
    {
6381
0
        dfValue /= d->dfToDegrees;
6382
0
    }
6383
0
    else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6384
0
             IsLinearParameter(pszName))
6385
0
        dfValue /= d->dfToMeter;
6386
6387
0
    return SetProjParm(pszName, dfValue);
6388
0
}
6389
6390
/************************************************************************/
6391
/*                         OSRSetNormProjParm()                         */
6392
/************************************************************************/
6393
6394
/**
6395
 * \brief Set a projection parameter with a normalized value.
6396
 *
6397
 * This function is the same as OGRSpatialReference::SetNormProjParm()
6398
 */
6399
OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6400
                          double dfValue)
6401
6402
0
{
6403
0
    VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6404
6405
0
    return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6406
0
}
6407
6408
/************************************************************************/
6409
/*                               SetTM()                                */
6410
/************************************************************************/
6411
6412
OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6413
                                  double dfScale, double dfFalseEasting,
6414
                                  double dfFalseNorthing)
6415
6416
0
{
6417
0
    TAKE_OPTIONAL_LOCK();
6418
6419
0
    return d->replaceConversionAndUnref(
6420
0
        proj_create_conversion_transverse_mercator(
6421
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6422
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6423
0
}
6424
6425
/************************************************************************/
6426
/*                              OSRSetTM()                              */
6427
/************************************************************************/
6428
6429
OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6430
                double dfCenterLong, double dfScale, double dfFalseEasting,
6431
                double dfFalseNorthing)
6432
6433
0
{
6434
0
    VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6435
6436
0
    return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6437
0
                                  dfFalseEasting, dfFalseNorthing);
6438
0
}
6439
6440
/************************************************************************/
6441
/*                            SetTMVariant()                            */
6442
/************************************************************************/
6443
6444
OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6445
                                         double dfCenterLat,
6446
                                         double dfCenterLong, double dfScale,
6447
                                         double dfFalseEasting,
6448
                                         double dfFalseNorthing)
6449
6450
0
{
6451
0
    TAKE_OPTIONAL_LOCK();
6452
6453
0
    SetProjection(pszVariantName);
6454
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6455
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6456
0
    SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6457
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6458
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6459
6460
0
    return OGRERR_NONE;
6461
0
}
6462
6463
/************************************************************************/
6464
/*                          OSRSetTMVariant()                           */
6465
/************************************************************************/
6466
6467
OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6468
                       double dfCenterLat, double dfCenterLong, double dfScale,
6469
                       double dfFalseEasting, double dfFalseNorthing)
6470
6471
0
{
6472
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6473
6474
0
    return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6475
0
                                         dfCenterLong, dfScale, dfFalseEasting,
6476
0
                                         dfFalseNorthing);
6477
0
}
6478
6479
/************************************************************************/
6480
/*                              SetTMSO()                               */
6481
/************************************************************************/
6482
6483
OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6484
                                    double dfScale, double dfFalseEasting,
6485
                                    double dfFalseNorthing)
6486
6487
0
{
6488
0
    TAKE_OPTIONAL_LOCK();
6489
6490
0
    auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6491
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6492
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6493
6494
0
    const char *pszName = nullptr;
6495
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6496
0
    CPLString osName = pszName ? pszName : "";
6497
6498
0
    d->refreshProjObj();
6499
6500
0
    d->demoteFromBoundCRS();
6501
6502
0
    auto cs = proj_create_cartesian_2D_cs(
6503
0
        d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6504
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6505
0
    auto projCRS =
6506
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6507
0
                                  d->getGeodBaseCRS(), conv, cs);
6508
0
    proj_destroy(conv);
6509
0
    proj_destroy(cs);
6510
6511
0
    d->setPjCRS(projCRS);
6512
6513
0
    d->undoDemoteFromBoundCRS();
6514
6515
0
    return OGRERR_NONE;
6516
0
}
6517
6518
/************************************************************************/
6519
/*                             OSRSetTMSO()                             */
6520
/************************************************************************/
6521
6522
OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6523
                  double dfCenterLong, double dfScale, double dfFalseEasting,
6524
                  double dfFalseNorthing)
6525
6526
0
{
6527
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6528
6529
0
    return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6530
0
                                    dfFalseEasting, dfFalseNorthing);
6531
0
}
6532
6533
/************************************************************************/
6534
/*                              SetTPED()                               */
6535
/************************************************************************/
6536
6537
OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6538
                                    double dfLat2, double dfLong2,
6539
                                    double dfFalseEasting,
6540
                                    double dfFalseNorthing)
6541
6542
0
{
6543
0
    TAKE_OPTIONAL_LOCK();
6544
6545
0
    return d->replaceConversionAndUnref(
6546
0
        proj_create_conversion_two_point_equidistant(
6547
0
            d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6548
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6549
0
}
6550
6551
/************************************************************************/
6552
/*                             OSRSetTPED()                             */
6553
/************************************************************************/
6554
6555
OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6556
                  double dfLat2, double dfLong2, double dfFalseEasting,
6557
                  double dfFalseNorthing)
6558
6559
0
{
6560
0
    VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6561
6562
0
    return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6563
0
                                    dfFalseEasting, dfFalseNorthing);
6564
0
}
6565
6566
/************************************************************************/
6567
/*                               SetTMG()                               */
6568
/************************************************************************/
6569
6570
OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6571
                                   double dfFalseEasting,
6572
                                   double dfFalseNorthing)
6573
6574
0
{
6575
0
    TAKE_OPTIONAL_LOCK();
6576
6577
0
    return d->replaceConversionAndUnref(
6578
0
        proj_create_conversion_tunisia_mapping_grid(
6579
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6580
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6581
0
}
6582
6583
/************************************************************************/
6584
/*                             OSRSetTMG()                              */
6585
/************************************************************************/
6586
6587
OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6588
                 double dfCenterLong, double dfFalseEasting,
6589
                 double dfFalseNorthing)
6590
6591
0
{
6592
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6593
6594
0
    return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6595
0
                                   dfFalseNorthing);
6596
0
}
6597
6598
/************************************************************************/
6599
/*                              SetACEA()                               */
6600
/************************************************************************/
6601
6602
OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6603
                                    double dfCenterLat, double dfCenterLong,
6604
                                    double dfFalseEasting,
6605
                                    double dfFalseNorthing)
6606
6607
0
{
6608
0
    TAKE_OPTIONAL_LOCK();
6609
6610
    // Note different order of parameters. The one in PROJ is conformant with
6611
    // EPSG
6612
0
    return d->replaceConversionAndUnref(
6613
0
        proj_create_conversion_albers_equal_area(
6614
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6615
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6616
0
}
6617
6618
/************************************************************************/
6619
/*                             OSRSetACEA()                             */
6620
/************************************************************************/
6621
6622
OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6623
                  double dfCenterLat, double dfCenterLong,
6624
                  double dfFalseEasting, double dfFalseNorthing)
6625
6626
0
{
6627
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6628
6629
0
    return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6630
0
                                    dfFalseEasting, dfFalseNorthing);
6631
0
}
6632
6633
/************************************************************************/
6634
/*                               SetAE()                                */
6635
/************************************************************************/
6636
6637
OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6638
                                  double dfFalseEasting, double dfFalseNorthing)
6639
6640
0
{
6641
0
    TAKE_OPTIONAL_LOCK();
6642
6643
0
    return d->replaceConversionAndUnref(
6644
0
        proj_create_conversion_azimuthal_equidistant(
6645
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6646
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6647
0
}
6648
6649
/************************************************************************/
6650
/*                              OSRSetAE()                              */
6651
/************************************************************************/
6652
6653
OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6654
                double dfCenterLong, double dfFalseEasting,
6655
                double dfFalseNorthing)
6656
6657
0
{
6658
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6659
6660
0
    return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6661
0
                                  dfFalseNorthing);
6662
0
}
6663
6664
/************************************************************************/
6665
/*                              SetBonne()                              */
6666
/************************************************************************/
6667
6668
OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6669
                                     double dfFalseEasting,
6670
                                     double dfFalseNorthing)
6671
6672
0
{
6673
0
    TAKE_OPTIONAL_LOCK();
6674
6675
0
    return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6676
0
        d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6677
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6678
0
}
6679
6680
/************************************************************************/
6681
/*                            OSRSetBonne()                             */
6682
/************************************************************************/
6683
6684
OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6685
                   double dfCentralMeridian, double dfFalseEasting,
6686
                   double dfFalseNorthing)
6687
6688
0
{
6689
0
    VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6690
6691
0
    return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6692
0
                                     dfFalseNorthing);
6693
0
}
6694
6695
/************************************************************************/
6696
/*                               SetCEA()                               */
6697
/************************************************************************/
6698
6699
OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6700
                                   double dfFalseEasting,
6701
                                   double dfFalseNorthing)
6702
6703
0
{
6704
0
    TAKE_OPTIONAL_LOCK();
6705
6706
0
    return d->replaceConversionAndUnref(
6707
0
        proj_create_conversion_lambert_cylindrical_equal_area(
6708
0
            d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6709
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6710
0
}
6711
6712
/************************************************************************/
6713
/*                             OSRSetCEA()                              */
6714
/************************************************************************/
6715
6716
OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6717
                 double dfCentralMeridian, double dfFalseEasting,
6718
                 double dfFalseNorthing)
6719
6720
0
{
6721
0
    VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6722
6723
0
    return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6724
0
                                   dfFalseNorthing);
6725
0
}
6726
6727
/************************************************************************/
6728
/*                               SetCS()                                */
6729
/************************************************************************/
6730
6731
OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6732
                                  double dfFalseEasting, double dfFalseNorthing)
6733
6734
0
{
6735
0
    TAKE_OPTIONAL_LOCK();
6736
6737
0
    return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6738
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6739
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6740
0
}
6741
6742
/************************************************************************/
6743
/*                              OSRSetCS()                              */
6744
/************************************************************************/
6745
6746
OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6747
                double dfCenterLong, double dfFalseEasting,
6748
                double dfFalseNorthing)
6749
6750
0
{
6751
0
    VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6752
6753
0
    return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6754
0
                                  dfFalseNorthing);
6755
0
}
6756
6757
/************************************************************************/
6758
/*                               SetEC()                                */
6759
/************************************************************************/
6760
6761
OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6762
                                  double dfCenterLat, double dfCenterLong,
6763
                                  double dfFalseEasting, double dfFalseNorthing)
6764
6765
0
{
6766
0
    TAKE_OPTIONAL_LOCK();
6767
6768
    // Note: different order of arguments
6769
0
    return d->replaceConversionAndUnref(
6770
0
        proj_create_conversion_equidistant_conic(
6771
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6772
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6773
0
}
6774
6775
/************************************************************************/
6776
/*                              OSRSetEC()                              */
6777
/************************************************************************/
6778
6779
OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6780
                double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6781
                double dfFalseNorthing)
6782
6783
0
{
6784
0
    VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6785
6786
0
    return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6787
0
                                  dfFalseEasting, dfFalseNorthing);
6788
0
}
6789
6790
/************************************************************************/
6791
/*                             SetEckert()                              */
6792
/************************************************************************/
6793
6794
OGRErr OGRSpatialReference::SetEckert(int nVariation,  // 1-6.
6795
                                      double dfCentralMeridian,
6796
                                      double dfFalseEasting,
6797
                                      double dfFalseNorthing)
6798
6799
0
{
6800
0
    TAKE_OPTIONAL_LOCK();
6801
6802
0
    PJ *conv;
6803
0
    if (nVariation == 1)
6804
0
    {
6805
0
        conv = proj_create_conversion_eckert_i(
6806
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6807
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6808
0
    }
6809
0
    else if (nVariation == 2)
6810
0
    {
6811
0
        conv = proj_create_conversion_eckert_ii(
6812
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6813
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6814
0
    }
6815
0
    else if (nVariation == 3)
6816
0
    {
6817
0
        conv = proj_create_conversion_eckert_iii(
6818
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6819
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6820
0
    }
6821
0
    else if (nVariation == 4)
6822
0
    {
6823
0
        conv = proj_create_conversion_eckert_iv(
6824
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6825
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6826
0
    }
6827
0
    else if (nVariation == 5)
6828
0
    {
6829
0
        conv = proj_create_conversion_eckert_v(
6830
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6831
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6832
0
    }
6833
0
    else if (nVariation == 6)
6834
0
    {
6835
0
        conv = proj_create_conversion_eckert_vi(
6836
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6837
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6838
0
    }
6839
0
    else
6840
0
    {
6841
0
        CPLError(CE_Failure, CPLE_AppDefined,
6842
0
                 "Unsupported Eckert variation (%d).", nVariation);
6843
0
        return OGRERR_UNSUPPORTED_SRS;
6844
0
    }
6845
6846
0
    return d->replaceConversionAndUnref(conv);
6847
0
}
6848
6849
/************************************************************************/
6850
/*                            OSRSetEckert()                            */
6851
/************************************************************************/
6852
6853
OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6854
                    double dfCentralMeridian, double dfFalseEasting,
6855
                    double dfFalseNorthing)
6856
6857
0
{
6858
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6859
6860
0
    return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6861
0
                                      dfFalseEasting, dfFalseNorthing);
6862
0
}
6863
6864
/************************************************************************/
6865
/*                            SetEckertIV()                             */
6866
/*                                                                      */
6867
/*      Deprecated                                                      */
6868
/************************************************************************/
6869
6870
OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6871
                                        double dfFalseEasting,
6872
                                        double dfFalseNorthing)
6873
6874
0
{
6875
0
    return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6876
0
}
6877
6878
/************************************************************************/
6879
/*                           OSRSetEckertIV()                           */
6880
/************************************************************************/
6881
6882
OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6883
                      double dfFalseEasting, double dfFalseNorthing)
6884
6885
0
{
6886
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6887
6888
0
    return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6889
0
                                        dfFalseNorthing);
6890
0
}
6891
6892
/************************************************************************/
6893
/*                            SetEckertVI()                             */
6894
/*                                                                      */
6895
/*      Deprecated                                                      */
6896
/************************************************************************/
6897
6898
OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6899
                                        double dfFalseEasting,
6900
                                        double dfFalseNorthing)
6901
6902
0
{
6903
0
    return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6904
0
}
6905
6906
/************************************************************************/
6907
/*                           OSRSetEckertVI()                           */
6908
/************************************************************************/
6909
6910
OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6911
                      double dfFalseEasting, double dfFalseNorthing)
6912
6913
0
{
6914
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6915
6916
0
    return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6917
0
                                        dfFalseNorthing);
6918
0
}
6919
6920
/************************************************************************/
6921
/*                         SetEquirectangular()                         */
6922
/************************************************************************/
6923
6924
OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6925
                                               double dfCenterLong,
6926
                                               double dfFalseEasting,
6927
                                               double dfFalseNorthing)
6928
6929
0
{
6930
0
    TAKE_OPTIONAL_LOCK();
6931
6932
0
    if (dfCenterLat == 0.0)
6933
0
    {
6934
0
        return d->replaceConversionAndUnref(
6935
0
            proj_create_conversion_equidistant_cylindrical(
6936
0
                d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6937
0
                dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6938
0
    }
6939
6940
    // Non-standard extension with non-zero latitude of origin
6941
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6942
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6943
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6944
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6945
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6946
6947
0
    return OGRERR_NONE;
6948
0
}
6949
6950
/************************************************************************/
6951
/*                       OSRSetEquirectangular()                        */
6952
/************************************************************************/
6953
6954
OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6955
                             double dfCenterLong, double dfFalseEasting,
6956
                             double dfFalseNorthing)
6957
6958
0
{
6959
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6960
6961
0
    return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6962
0
                                               dfFalseEasting, dfFalseNorthing);
6963
0
}
6964
6965
/************************************************************************/
6966
/*                         SetEquirectangular2()                        */
6967
/* Generalized form                                                     */
6968
/************************************************************************/
6969
6970
OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6971
                                                double dfCenterLong,
6972
                                                double dfStdParallel1,
6973
                                                double dfFalseEasting,
6974
                                                double dfFalseNorthing)
6975
6976
0
{
6977
0
    TAKE_OPTIONAL_LOCK();
6978
6979
0
    if (dfCenterLat == 0.0)
6980
0
    {
6981
0
        return d->replaceConversionAndUnref(
6982
0
            proj_create_conversion_equidistant_cylindrical(
6983
0
                d->getPROJContext(), dfStdParallel1, dfCenterLong,
6984
0
                dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6985
0
    }
6986
6987
    // Non-standard extension with non-zero latitude of origin
6988
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6989
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6990
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6991
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6992
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6993
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6994
6995
0
    return OGRERR_NONE;
6996
0
}
6997
6998
/************************************************************************/
6999
/*                       OSRSetEquirectangular2()                       */
7000
/************************************************************************/
7001
7002
OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
7003
                              double dfCenterLong, double dfStdParallel1,
7004
                              double dfFalseEasting, double dfFalseNorthing)
7005
7006
0
{
7007
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
7008
7009
0
    return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
7010
0
                                                dfStdParallel1, dfFalseEasting,
7011
0
                                                dfFalseNorthing);
7012
0
}
7013
7014
/************************************************************************/
7015
/*                               SetGS()                                */
7016
/************************************************************************/
7017
7018
OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
7019
                                  double dfFalseEasting, double dfFalseNorthing)
7020
7021
0
{
7022
0
    return d->replaceConversionAndUnref(proj_create_conversion_gall(
7023
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7024
0
        nullptr, 0.0, nullptr, 0.0));
7025
0
}
7026
7027
/************************************************************************/
7028
/*                              OSRSetGS()                              */
7029
/************************************************************************/
7030
7031
OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7032
                double dfFalseEasting, double dfFalseNorthing)
7033
7034
0
{
7035
0
    VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
7036
7037
0
    return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
7038
0
                                  dfFalseNorthing);
7039
0
}
7040
7041
/************************************************************************/
7042
/*                               SetGH()                                */
7043
/************************************************************************/
7044
7045
OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
7046
                                  double dfFalseEasting, double dfFalseNorthing)
7047
7048
0
{
7049
0
    TAKE_OPTIONAL_LOCK();
7050
7051
0
    return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
7052
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7053
0
        nullptr, 0.0, nullptr, 0.0));
7054
0
}
7055
7056
/************************************************************************/
7057
/*                              OSRSetGH()                              */
7058
/************************************************************************/
7059
7060
OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7061
                double dfFalseEasting, double dfFalseNorthing)
7062
7063
0
{
7064
0
    VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
7065
7066
0
    return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
7067
0
                                  dfFalseNorthing);
7068
0
}
7069
7070
/************************************************************************/
7071
/*                               SetIGH()                               */
7072
/************************************************************************/
7073
7074
OGRErr OGRSpatialReference::SetIGH()
7075
7076
0
{
7077
0
    TAKE_OPTIONAL_LOCK();
7078
7079
0
    return d->replaceConversionAndUnref(
7080
0
        proj_create_conversion_interrupted_goode_homolosine(
7081
0
            d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
7082
0
}
7083
7084
/************************************************************************/
7085
/*                             OSRSetIGH()                              */
7086
/************************************************************************/
7087
7088
OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
7089
7090
0
{
7091
0
    VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
7092
7093
0
    return ToPointer(hSRS)->SetIGH();
7094
0
}
7095
7096
/************************************************************************/
7097
/*                              SetGEOS()                               */
7098
/************************************************************************/
7099
7100
OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
7101
                                    double dfSatelliteHeight,
7102
                                    double dfFalseEasting,
7103
                                    double dfFalseNorthing)
7104
7105
0
{
7106
0
    TAKE_OPTIONAL_LOCK();
7107
7108
0
    return d->replaceConversionAndUnref(
7109
0
        proj_create_conversion_geostationary_satellite_sweep_y(
7110
0
            d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
7111
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7112
0
}
7113
7114
/************************************************************************/
7115
/*                             OSRSetGEOS()                             */
7116
/************************************************************************/
7117
7118
OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7119
                  double dfSatelliteHeight, double dfFalseEasting,
7120
                  double dfFalseNorthing)
7121
7122
0
{
7123
0
    VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
7124
7125
0
    return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
7126
0
                                    dfFalseEasting, dfFalseNorthing);
7127
0
}
7128
7129
/************************************************************************/
7130
/*                     SetGaussSchreiberTMercator()                     */
7131
/************************************************************************/
7132
7133
OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
7134
                                                       double dfCenterLong,
7135
                                                       double dfScale,
7136
                                                       double dfFalseEasting,
7137
                                                       double dfFalseNorthing)
7138
7139
0
{
7140
0
    TAKE_OPTIONAL_LOCK();
7141
7142
0
    return d->replaceConversionAndUnref(
7143
0
        proj_create_conversion_gauss_schreiber_transverse_mercator(
7144
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7145
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7146
0
}
7147
7148
/************************************************************************/
7149
/*                   OSRSetGaussSchreiberTMercator()                    */
7150
/************************************************************************/
7151
7152
OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
7153
                                     double dfCenterLat, double dfCenterLong,
7154
                                     double dfScale, double dfFalseEasting,
7155
                                     double dfFalseNorthing)
7156
7157
0
{
7158
0
    VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
7159
7160
0
    return ToPointer(hSRS)->SetGaussSchreiberTMercator(
7161
0
        dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
7162
0
}
7163
7164
/************************************************************************/
7165
/*                            SetGnomonic()                             */
7166
/************************************************************************/
7167
7168
OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
7169
                                        double dfFalseEasting,
7170
                                        double dfFalseNorthing)
7171
7172
0
{
7173
0
    TAKE_OPTIONAL_LOCK();
7174
7175
0
    return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
7176
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7177
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7178
0
}
7179
7180
/************************************************************************/
7181
/*                           OSRSetGnomonic()                           */
7182
/************************************************************************/
7183
7184
OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7185
                      double dfCenterLong, double dfFalseEasting,
7186
                      double dfFalseNorthing)
7187
7188
0
{
7189
0
    VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
7190
7191
0
    return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
7192
0
                                        dfFalseEasting, dfFalseNorthing);
7193
0
}
7194
7195
/************************************************************************/
7196
/*                              SetHOMAC()                              */
7197
/************************************************************************/
7198
7199
/**
7200
 * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7201
 * azimuth angle.
7202
 *
7203
 * This projection corresponds to EPSG projection method 9815, also
7204
 * sometimes known as hotine oblique mercator (variant B).
7205
 *
7206
 * This method does the same thing as the C function OSRSetHOMAC().
7207
 *
7208
 * @param dfCenterLat Latitude of the projection origin.
7209
 * @param dfCenterLong Longitude of the projection origin.
7210
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7211
 * centerline.
7212
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7213
 * @param dfScale Scale factor applies to the projection origin.
7214
 * @param dfFalseEasting False easting.
7215
 * @param dfFalseNorthing False northing.
7216
 *
7217
 * @return OGRERR_NONE on success.
7218
 */
7219
7220
OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7221
                                     double dfAzimuth, double dfRectToSkew,
7222
                                     double dfScale, double dfFalseEasting,
7223
                                     double dfFalseNorthing)
7224
7225
0
{
7226
0
    TAKE_OPTIONAL_LOCK();
7227
7228
0
    return d->replaceConversionAndUnref(
7229
0
        proj_create_conversion_hotine_oblique_mercator_variant_b(
7230
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7231
0
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7232
0
            0.0, nullptr, 0.0));
7233
0
}
7234
7235
/************************************************************************/
7236
/*                            OSRSetHOMAC()                             */
7237
/************************************************************************/
7238
7239
/**
7240
 * \brief Set an Oblique Mercator projection using azimuth angle.
7241
 *
7242
 * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7243
 */
7244
OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7245
                   double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7246
                   double dfScale, double dfFalseEasting,
7247
                   double dfFalseNorthing)
7248
7249
0
{
7250
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7251
7252
0
    return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7253
0
                                     dfRectToSkew, dfScale, dfFalseEasting,
7254
0
                                     dfFalseNorthing);
7255
0
}
7256
7257
/************************************************************************/
7258
/*                               SetHOM()                               */
7259
/************************************************************************/
7260
7261
/**
7262
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7263
 *
7264
 * This projection corresponds to EPSG projection method 9812, also
7265
 * sometimes known as hotine oblique mercator (variant A)..
7266
 *
7267
 * This method does the same thing as the C function OSRSetHOM().
7268
 *
7269
 * @param dfCenterLat Latitude of the projection origin.
7270
 * @param dfCenterLong Longitude of the projection origin.
7271
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7272
 * centerline.
7273
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7274
 * @param dfScale Scale factor applies to the projection origin.
7275
 * @param dfFalseEasting False easting.
7276
 * @param dfFalseNorthing False northing.
7277
 *
7278
 * @return OGRERR_NONE on success.
7279
 */
7280
7281
OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7282
                                   double dfAzimuth, double dfRectToSkew,
7283
                                   double dfScale, double dfFalseEasting,
7284
                                   double dfFalseNorthing)
7285
7286
0
{
7287
0
    TAKE_OPTIONAL_LOCK();
7288
7289
0
    return d->replaceConversionAndUnref(
7290
0
        proj_create_conversion_hotine_oblique_mercator_variant_a(
7291
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7292
0
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7293
0
            0.0, nullptr, 0.0));
7294
0
}
7295
7296
/************************************************************************/
7297
/*                             OSRSetHOM()                              */
7298
/************************************************************************/
7299
/**
7300
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7301
 *
7302
 * This is the same as the C++ method OGRSpatialReference::SetHOM()
7303
 */
7304
OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7305
                 double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7306
                 double dfScale, double dfFalseEasting, double dfFalseNorthing)
7307
7308
0
{
7309
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7310
7311
0
    return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7312
0
                                   dfRectToSkew, dfScale, dfFalseEasting,
7313
0
                                   dfFalseNorthing);
7314
0
}
7315
7316
/************************************************************************/
7317
/*                             SetHOM2PNO()                             */
7318
/************************************************************************/
7319
7320
/**
7321
 * \brief Set a Hotine Oblique Mercator projection using two points on
7322
 * projection centerline.
7323
 *
7324
 * This method does the same thing as the C function OSRSetHOM2PNO().
7325
 *
7326
 * @param dfCenterLat Latitude of the projection origin.
7327
 * @param dfLat1 Latitude of the first point on center line.
7328
 * @param dfLong1 Longitude of the first point on center line.
7329
 * @param dfLat2 Latitude of the second point on center line.
7330
 * @param dfLong2 Longitude of the second point on center line.
7331
 * @param dfScale Scale factor applies to the projection origin.
7332
 * @param dfFalseEasting False easting.
7333
 * @param dfFalseNorthing False northing.
7334
 *
7335
 * @return OGRERR_NONE on success.
7336
 */
7337
7338
OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7339
                                       double dfLong1, double dfLat2,
7340
                                       double dfLong2, double dfScale,
7341
                                       double dfFalseEasting,
7342
                                       double dfFalseNorthing)
7343
7344
0
{
7345
0
    TAKE_OPTIONAL_LOCK();
7346
7347
0
    return d->replaceConversionAndUnref(
7348
0
        proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7349
0
            d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7350
0
            dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7351
0
            0.0));
7352
0
}
7353
7354
/************************************************************************/
7355
/*                           OSRSetHOM2PNO()                            */
7356
/************************************************************************/
7357
/**
7358
 * \brief Set a Hotine Oblique Mercator projection using two points on
7359
 *  projection centerline.
7360
 *
7361
 * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7362
 */
7363
OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7364
                     double dfLat1, double dfLong1, double dfLat2,
7365
                     double dfLong2, double dfScale, double dfFalseEasting,
7366
                     double dfFalseNorthing)
7367
7368
0
{
7369
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7370
7371
0
    return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7372
0
                                       dfLong2, dfScale, dfFalseEasting,
7373
0
                                       dfFalseNorthing);
7374
0
}
7375
7376
/************************************************************************/
7377
/*                               SetLOM()                               */
7378
/************************************************************************/
7379
7380
/**
7381
 * \brief Set a Laborde Oblique Mercator projection.
7382
 *
7383
 * @param dfCenterLat Latitude of the projection origin.
7384
 * @param dfCenterLong Longitude of the projection origin.
7385
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7386
 * centerline.
7387
 * @param dfScale Scale factor on the initial line
7388
 * @param dfFalseEasting False easting.
7389
 * @param dfFalseNorthing False northing.
7390
 *
7391
 * @return OGRERR_NONE on success.
7392
 */
7393
7394
OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7395
                                   double dfAzimuth, double dfScale,
7396
                                   double dfFalseEasting,
7397
                                   double dfFalseNorthing)
7398
7399
0
{
7400
0
    TAKE_OPTIONAL_LOCK();
7401
7402
0
    return d->replaceConversionAndUnref(
7403
0
        proj_create_conversion_laborde_oblique_mercator(
7404
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7405
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7406
0
}
7407
7408
/************************************************************************/
7409
/*                          SetIWMPolyconic()                           */
7410
/************************************************************************/
7411
7412
OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7413
                                            double dfCenterLong,
7414
                                            double dfFalseEasting,
7415
                                            double dfFalseNorthing)
7416
7417
0
{
7418
0
    TAKE_OPTIONAL_LOCK();
7419
7420
0
    return d->replaceConversionAndUnref(
7421
0
        proj_create_conversion_international_map_world_polyconic(
7422
0
            d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7423
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7424
0
}
7425
7426
/************************************************************************/
7427
/*                         OSRSetIWMPolyconic()                         */
7428
/************************************************************************/
7429
7430
OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7431
                          double dfLat2, double dfCenterLong,
7432
                          double dfFalseEasting, double dfFalseNorthing)
7433
7434
0
{
7435
0
    VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7436
7437
0
    return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7438
0
                                            dfFalseEasting, dfFalseNorthing);
7439
0
}
7440
7441
/************************************************************************/
7442
/*                             SetKrovak()                              */
7443
/************************************************************************/
7444
7445
/** Krovak east-north projection.
7446
 *
7447
 * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7448
 * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7449
 */
7450
OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7451
                                      double dfAzimuth,
7452
                                      double dfPseudoStdParallel1,
7453
                                      double dfScale, double dfFalseEasting,
7454
                                      double dfFalseNorthing)
7455
7456
0
{
7457
0
    TAKE_OPTIONAL_LOCK();
7458
7459
0
    return d->replaceConversionAndUnref(
7460
0
        proj_create_conversion_krovak_north_oriented(
7461
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7462
0
            dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7463
0
            nullptr, 0.0, nullptr, 0.0));
7464
0
}
7465
7466
/************************************************************************/
7467
/*                            OSRSetKrovak()                            */
7468
/************************************************************************/
7469
7470
OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7471
                    double dfCenterLong, double dfAzimuth,
7472
                    double dfPseudoStdParallel1, double dfScale,
7473
                    double dfFalseEasting, double dfFalseNorthing)
7474
7475
0
{
7476
0
    VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7477
7478
0
    return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7479
0
                                      dfPseudoStdParallel1, dfScale,
7480
0
                                      dfFalseEasting, dfFalseNorthing);
7481
0
}
7482
7483
/************************************************************************/
7484
/*                              SetLAEA()                               */
7485
/************************************************************************/
7486
7487
OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7488
                                    double dfFalseEasting,
7489
                                    double dfFalseNorthing)
7490
7491
0
{
7492
0
    TAKE_OPTIONAL_LOCK();
7493
7494
0
    auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7495
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7496
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7497
7498
0
    const char *pszName = nullptr;
7499
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7500
0
    CPLString osName = pszName ? pszName : "";
7501
7502
0
    d->refreshProjObj();
7503
7504
0
    d->demoteFromBoundCRS();
7505
7506
0
    auto cs = proj_create_cartesian_2D_cs(
7507
0
        d->getPROJContext(),
7508
0
        std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7509
0
            ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7510
0
        : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7511
0
            ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7512
0
            : PJ_CART2D_EASTING_NORTHING,
7513
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7514
0
    auto projCRS =
7515
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7516
0
                                  d->getGeodBaseCRS(), conv, cs);
7517
0
    proj_destroy(conv);
7518
0
    proj_destroy(cs);
7519
7520
0
    d->setPjCRS(projCRS);
7521
7522
0
    d->undoDemoteFromBoundCRS();
7523
7524
0
    return OGRERR_NONE;
7525
0
}
7526
7527
/************************************************************************/
7528
/*                             OSRSetLAEA()                             */
7529
/************************************************************************/
7530
7531
OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7532
                  double dfCenterLong, double dfFalseEasting,
7533
                  double dfFalseNorthing)
7534
7535
0
{
7536
0
    VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7537
7538
0
    return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7539
0
                                    dfFalseNorthing);
7540
0
}
7541
7542
/************************************************************************/
7543
/*                               SetLCC()                               */
7544
/************************************************************************/
7545
7546
OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7547
                                   double dfCenterLat, double dfCenterLong,
7548
                                   double dfFalseEasting,
7549
                                   double dfFalseNorthing)
7550
7551
0
{
7552
0
    TAKE_OPTIONAL_LOCK();
7553
7554
0
    return d->replaceConversionAndUnref(
7555
0
        proj_create_conversion_lambert_conic_conformal_2sp(
7556
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7557
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7558
0
}
7559
7560
/************************************************************************/
7561
/*                             OSRSetLCC()                              */
7562
/************************************************************************/
7563
7564
OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7565
                 double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7566
                 double dfFalseNorthing)
7567
7568
0
{
7569
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7570
7571
0
    return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7572
0
                                   dfFalseEasting, dfFalseNorthing);
7573
0
}
7574
7575
/************************************************************************/
7576
/*                             SetLCC1SP()                              */
7577
/************************************************************************/
7578
7579
OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7580
                                      double dfScale, double dfFalseEasting,
7581
                                      double dfFalseNorthing)
7582
7583
0
{
7584
0
    TAKE_OPTIONAL_LOCK();
7585
7586
0
    return d->replaceConversionAndUnref(
7587
0
        proj_create_conversion_lambert_conic_conformal_1sp(
7588
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7589
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7590
0
}
7591
7592
/************************************************************************/
7593
/*                            OSRSetLCC1SP()                            */
7594
/************************************************************************/
7595
7596
OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7597
                    double dfCenterLong, double dfScale, double dfFalseEasting,
7598
                    double dfFalseNorthing)
7599
7600
0
{
7601
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7602
7603
0
    return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7604
0
                                      dfFalseEasting, dfFalseNorthing);
7605
0
}
7606
7607
/************************************************************************/
7608
/*                              SetLCCB()                               */
7609
/************************************************************************/
7610
7611
OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7612
                                    double dfCenterLat, double dfCenterLong,
7613
                                    double dfFalseEasting,
7614
                                    double dfFalseNorthing)
7615
7616
0
{
7617
0
    TAKE_OPTIONAL_LOCK();
7618
7619
0
    return d->replaceConversionAndUnref(
7620
0
        proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7621
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7622
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7623
0
}
7624
7625
/************************************************************************/
7626
/*                             OSRSetLCCB()                             */
7627
/************************************************************************/
7628
7629
OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7630
                  double dfCenterLat, double dfCenterLong,
7631
                  double dfFalseEasting, double dfFalseNorthing)
7632
7633
0
{
7634
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7635
7636
0
    return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7637
0
                                    dfFalseEasting, dfFalseNorthing);
7638
0
}
7639
7640
/************************************************************************/
7641
/*                               SetMC()                                */
7642
/************************************************************************/
7643
7644
OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7645
                                  double dfFalseEasting, double dfFalseNorthing)
7646
7647
0
{
7648
0
    TAKE_OPTIONAL_LOCK();
7649
7650
0
    (void)dfCenterLat;  // ignored
7651
7652
0
    return d->replaceConversionAndUnref(
7653
0
        proj_create_conversion_miller_cylindrical(
7654
0
            d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7655
0
            nullptr, 0, nullptr, 0));
7656
0
}
7657
7658
/************************************************************************/
7659
/*                              OSRSetMC()                              */
7660
/************************************************************************/
7661
7662
OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7663
                double dfCenterLong, double dfFalseEasting,
7664
                double dfFalseNorthing)
7665
7666
0
{
7667
0
    VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7668
7669
0
    return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7670
0
                                  dfFalseNorthing);
7671
0
}
7672
7673
/************************************************************************/
7674
/*                            SetMercator()                             */
7675
/************************************************************************/
7676
7677
OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7678
                                        double dfScale, double dfFalseEasting,
7679
                                        double dfFalseNorthing)
7680
7681
0
{
7682
0
    TAKE_OPTIONAL_LOCK();
7683
7684
0
    if (dfCenterLat != 0.0 && dfScale == 1.0)
7685
0
    {
7686
        // Not sure this is correct, but this is how it has been used
7687
        // historically
7688
0
        return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7689
0
                              dfFalseNorthing);
7690
0
    }
7691
0
    return d->replaceConversionAndUnref(
7692
0
        proj_create_conversion_mercator_variant_a(
7693
0
            d->getPROJContext(),
7694
0
            dfCenterLat,  // should be zero
7695
0
            dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7696
0
            nullptr, 0));
7697
0
}
7698
7699
/************************************************************************/
7700
/*                           OSRSetMercator()                           */
7701
/************************************************************************/
7702
7703
OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7704
                      double dfCenterLong, double dfScale,
7705
                      double dfFalseEasting, double dfFalseNorthing)
7706
7707
0
{
7708
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7709
7710
0
    return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7711
0
                                        dfFalseEasting, dfFalseNorthing);
7712
0
}
7713
7714
/************************************************************************/
7715
/*                           SetMercator2SP()                           */
7716
/************************************************************************/
7717
7718
OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7719
                                           double dfCenterLong,
7720
                                           double dfFalseEasting,
7721
                                           double dfFalseNorthing)
7722
7723
0
{
7724
0
    if (dfCenterLat == 0.0)
7725
0
    {
7726
0
        return d->replaceConversionAndUnref(
7727
0
            proj_create_conversion_mercator_variant_b(
7728
0
                d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7729
0
                dfFalseNorthing, nullptr, 0, nullptr, 0));
7730
0
    }
7731
7732
0
    TAKE_OPTIONAL_LOCK();
7733
7734
0
    SetProjection(SRS_PT_MERCATOR_2SP);
7735
7736
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7737
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7738
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7739
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7740
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7741
7742
0
    return OGRERR_NONE;
7743
0
}
7744
7745
/************************************************************************/
7746
/*                         OSRSetMercator2SP()                          */
7747
/************************************************************************/
7748
7749
OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7750
                         double dfCenterLat, double dfCenterLong,
7751
                         double dfFalseEasting, double dfFalseNorthing)
7752
7753
0
{
7754
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7755
7756
0
    return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7757
0
                                           dfFalseEasting, dfFalseNorthing);
7758
0
}
7759
7760
/************************************************************************/
7761
/*                            SetMollweide()                            */
7762
/************************************************************************/
7763
7764
OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7765
                                         double dfFalseEasting,
7766
                                         double dfFalseNorthing)
7767
7768
0
{
7769
0
    TAKE_OPTIONAL_LOCK();
7770
7771
0
    return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7772
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7773
0
        nullptr, 0, nullptr, 0));
7774
0
}
7775
7776
/************************************************************************/
7777
/*                          OSRSetMollweide()                           */
7778
/************************************************************************/
7779
7780
OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7781
                       double dfFalseEasting, double dfFalseNorthing)
7782
7783
0
{
7784
0
    VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7785
7786
0
    return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7787
0
                                         dfFalseNorthing);
7788
0
}
7789
7790
/************************************************************************/
7791
/*                              SetNZMG()                               */
7792
/************************************************************************/
7793
7794
OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7795
                                    double dfFalseEasting,
7796
                                    double dfFalseNorthing)
7797
7798
0
{
7799
0
    TAKE_OPTIONAL_LOCK();
7800
7801
0
    return d->replaceConversionAndUnref(
7802
0
        proj_create_conversion_new_zealand_mapping_grid(
7803
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7804
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7805
0
}
7806
7807
/************************************************************************/
7808
/*                             OSRSetNZMG()                             */
7809
/************************************************************************/
7810
7811
OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7812
                  double dfCenterLong, double dfFalseEasting,
7813
                  double dfFalseNorthing)
7814
7815
0
{
7816
0
    VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7817
7818
0
    return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7819
0
                                    dfFalseNorthing);
7820
0
}
7821
7822
/************************************************************************/
7823
/*                               SetOS()                                */
7824
/************************************************************************/
7825
7826
OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7827
                                  double dfScale, double dfFalseEasting,
7828
                                  double dfFalseNorthing)
7829
7830
0
{
7831
0
    TAKE_OPTIONAL_LOCK();
7832
7833
0
    return d->replaceConversionAndUnref(
7834
0
        proj_create_conversion_oblique_stereographic(
7835
0
            d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7836
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7837
0
}
7838
7839
/************************************************************************/
7840
/*                              OSRSetOS()                              */
7841
/************************************************************************/
7842
7843
OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7844
                double dfCMeridian, double dfScale, double dfFalseEasting,
7845
                double dfFalseNorthing)
7846
7847
0
{
7848
0
    VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7849
7850
0
    return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7851
0
                                  dfFalseEasting, dfFalseNorthing);
7852
0
}
7853
7854
/************************************************************************/
7855
/*                          SetOrthographic()                           */
7856
/************************************************************************/
7857
7858
OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7859
                                            double dfCenterLong,
7860
                                            double dfFalseEasting,
7861
                                            double dfFalseNorthing)
7862
7863
0
{
7864
0
    TAKE_OPTIONAL_LOCK();
7865
7866
0
    return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7867
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7868
0
        dfFalseNorthing, nullptr, 0, nullptr, 0));
7869
0
}
7870
7871
/************************************************************************/
7872
/*                         OSRSetOrthographic()                         */
7873
/************************************************************************/
7874
7875
OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7876
                          double dfCenterLong, double dfFalseEasting,
7877
                          double dfFalseNorthing)
7878
7879
0
{
7880
0
    VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7881
7882
0
    return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7883
0
                                            dfFalseEasting, dfFalseNorthing);
7884
0
}
7885
7886
/************************************************************************/
7887
/*                            SetPolyconic()                            */
7888
/************************************************************************/
7889
7890
OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7891
                                         double dfCenterLong,
7892
                                         double dfFalseEasting,
7893
                                         double dfFalseNorthing)
7894
7895
0
{
7896
0
    TAKE_OPTIONAL_LOCK();
7897
7898
    // note: it seems that by some definitions this should include a
7899
    //       scale_factor parameter.
7900
0
    return d->replaceConversionAndUnref(
7901
0
        proj_create_conversion_american_polyconic(
7902
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7903
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7904
0
}
7905
7906
/************************************************************************/
7907
/*                          OSRSetPolyconic()                           */
7908
/************************************************************************/
7909
7910
OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7911
                       double dfCenterLong, double dfFalseEasting,
7912
                       double dfFalseNorthing)
7913
7914
0
{
7915
0
    VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7916
7917
0
    return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7918
0
                                         dfFalseEasting, dfFalseNorthing);
7919
0
}
7920
7921
/************************************************************************/
7922
/*                               SetPS()                                */
7923
/************************************************************************/
7924
7925
/** Sets a Polar Stereographic projection.
7926
 *
7927
 * Two variants are possible:
7928
 * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7929
 *   interpreted as the latitude of origin, combined with the scale factor
7930
 * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7931
 *   is interpreted as the latitude of true scale. In that situation, dfScale
7932
 *   must be set to 1 (it is ignored in the projection parameters)
7933
 */
7934
OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7935
                                  double dfScale, double dfFalseEasting,
7936
                                  double dfFalseNorthing)
7937
7938
0
{
7939
0
    TAKE_OPTIONAL_LOCK();
7940
7941
0
    PJ *conv;
7942
0
    if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7943
0
    {
7944
0
        conv = proj_create_conversion_polar_stereographic_variant_b(
7945
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7946
0
            dfFalseNorthing, nullptr, 0, nullptr, 0);
7947
0
    }
7948
0
    else
7949
0
    {
7950
0
        conv = proj_create_conversion_polar_stereographic_variant_a(
7951
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7952
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7953
0
    }
7954
7955
0
    const char *pszName = nullptr;
7956
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7957
0
    CPLString osName = pszName ? pszName : "";
7958
7959
0
    d->refreshProjObj();
7960
7961
0
    d->demoteFromBoundCRS();
7962
7963
0
    auto cs = proj_create_cartesian_2D_cs(
7964
0
        d->getPROJContext(),
7965
0
        dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7966
0
                        : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7967
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7968
0
    auto projCRS =
7969
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7970
0
                                  d->getGeodBaseCRS(), conv, cs);
7971
0
    proj_destroy(conv);
7972
0
    proj_destroy(cs);
7973
7974
0
    d->setPjCRS(projCRS);
7975
7976
0
    d->undoDemoteFromBoundCRS();
7977
7978
0
    return OGRERR_NONE;
7979
0
}
7980
7981
/************************************************************************/
7982
/*                              OSRSetPS()                              */
7983
/************************************************************************/
7984
7985
OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7986
                double dfCenterLong, double dfScale, double dfFalseEasting,
7987
                double dfFalseNorthing)
7988
7989
0
{
7990
0
    VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7991
7992
0
    return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7993
0
                                  dfFalseEasting, dfFalseNorthing);
7994
0
}
7995
7996
/************************************************************************/
7997
/*                            SetRobinson()                             */
7998
/************************************************************************/
7999
8000
OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
8001
                                        double dfFalseEasting,
8002
                                        double dfFalseNorthing)
8003
8004
0
{
8005
0
    TAKE_OPTIONAL_LOCK();
8006
8007
0
    return d->replaceConversionAndUnref(proj_create_conversion_robinson(
8008
0
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
8009
0
        nullptr, 0, nullptr, 0));
8010
0
}
8011
8012
/************************************************************************/
8013
/*                           OSRSetRobinson()                           */
8014
/************************************************************************/
8015
8016
OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
8017
                      double dfFalseEasting, double dfFalseNorthing)
8018
8019
0
{
8020
0
    VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
8021
8022
0
    return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
8023
0
                                        dfFalseNorthing);
8024
0
}
8025
8026
/************************************************************************/
8027
/*                           SetSinusoidal()                            */
8028
/************************************************************************/
8029
8030
OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
8031
                                          double dfFalseEasting,
8032
                                          double dfFalseNorthing)
8033
8034
0
{
8035
0
    TAKE_OPTIONAL_LOCK();
8036
8037
0
    return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
8038
0
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
8039
0
        nullptr, 0, nullptr, 0));
8040
0
}
8041
8042
/************************************************************************/
8043
/*                          OSRSetSinusoidal()                          */
8044
/************************************************************************/
8045
8046
OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
8047
                        double dfFalseEasting, double dfFalseNorthing)
8048
8049
0
{
8050
0
    VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
8051
8052
0
    return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
8053
0
                                          dfFalseNorthing);
8054
0
}
8055
8056
/************************************************************************/
8057
/*                          SetStereographic()                          */
8058
/************************************************************************/
8059
8060
OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
8061
                                             double dfCMeridian, double dfScale,
8062
                                             double dfFalseEasting,
8063
                                             double dfFalseNorthing)
8064
8065
0
{
8066
0
    TAKE_OPTIONAL_LOCK();
8067
8068
0
    return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
8069
0
        d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
8070
0
        dfFalseNorthing, nullptr, 0, nullptr, 0));
8071
0
}
8072
8073
/************************************************************************/
8074
/*                        OSRSetStereographic()                         */
8075
/************************************************************************/
8076
8077
OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
8078
                           double dfCMeridian, double dfScale,
8079
                           double dfFalseEasting, double dfFalseNorthing)
8080
8081
0
{
8082
0
    VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
8083
8084
0
    return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
8085
0
                                             dfFalseEasting, dfFalseNorthing);
8086
0
}
8087
8088
/************************************************************************/
8089
/*                               SetSOC()                               */
8090
/*                                                                      */
8091
/*      NOTE: This definition isn't really used in practice any more    */
8092
/*      and should be considered deprecated.  It seems that swiss       */
8093
/*      oblique mercator is now define as Hotine_Oblique_Mercator       */
8094
/*      with an azimuth of 90 and a rectified_grid_angle of 90.  See    */
8095
/*      EPSG:2056 and Bug 423.                                          */
8096
/************************************************************************/
8097
8098
OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
8099
                                   double dfCentralMeridian,
8100
                                   double dfFalseEasting,
8101
                                   double dfFalseNorthing)
8102
8103
0
{
8104
0
    TAKE_OPTIONAL_LOCK();
8105
8106
0
    return d->replaceConversionAndUnref(
8107
0
        proj_create_conversion_hotine_oblique_mercator_variant_b(
8108
0
            d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
8109
0
            90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
8110
0
            0.0));
8111
#if 0
8112
    SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
8113
    SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
8114
    SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
8115
    SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
8116
    SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
8117
8118
    return OGRERR_NONE;
8119
#endif
8120
0
}
8121
8122
/************************************************************************/
8123
/*                             OSRSetSOC()                              */
8124
/************************************************************************/
8125
8126
OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
8127
                 double dfCentralMeridian, double dfFalseEasting,
8128
                 double dfFalseNorthing)
8129
8130
0
{
8131
0
    VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
8132
8133
0
    return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
8134
0
                                   dfFalseEasting, dfFalseNorthing);
8135
0
}
8136
8137
/************************************************************************/
8138
/*                               SetVDG()                               */
8139
/************************************************************************/
8140
8141
OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
8142
                                   double dfFalseNorthing)
8143
8144
0
{
8145
0
    TAKE_OPTIONAL_LOCK();
8146
8147
0
    return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
8148
0
        d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
8149
0
        nullptr, 0, nullptr, 0));
8150
0
}
8151
8152
/************************************************************************/
8153
/*                             OSRSetVDG()                              */
8154
/************************************************************************/
8155
8156
OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
8157
                 double dfFalseEasting, double dfFalseNorthing)
8158
8159
0
{
8160
0
    VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
8161
8162
0
    return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
8163
0
                                   dfFalseNorthing);
8164
0
}
8165
8166
/************************************************************************/
8167
/*                               SetUTM()                               */
8168
/************************************************************************/
8169
8170
/**
8171
 * \brief Set UTM projection definition.
8172
 *
8173
 * This will generate a projection definition with the full set of
8174
 * transverse mercator projection parameters for the given UTM zone.
8175
 * If no PROJCS[] description is set yet, one will be set to look
8176
 * like "UTM Zone %d, {Northern, Southern} Hemisphere".
8177
 *
8178
 * This method is the same as the C function OSRSetUTM().
8179
 *
8180
 * @param nZone UTM zone.
8181
 *
8182
 * @param bNorth TRUE for northern hemisphere, or FALSE for southern
8183
 * hemisphere.
8184
 *
8185
 * @return OGRERR_NONE on success.
8186
 */
8187
8188
OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
8189
8190
0
{
8191
0
    TAKE_OPTIONAL_LOCK();
8192
8193
0
    if (nZone < 0 || nZone > 60)
8194
0
    {
8195
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8196
0
        return OGRERR_FAILURE;
8197
0
    }
8198
8199
0
    return d->replaceConversionAndUnref(
8200
0
        proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8201
0
}
8202
8203
/************************************************************************/
8204
/*                             OSRSetUTM()                              */
8205
/************************************************************************/
8206
8207
/**
8208
 * \brief Set UTM projection definition.
8209
 *
8210
 * This is the same as the C++ method OGRSpatialReference::SetUTM()
8211
 */
8212
OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8213
8214
0
{
8215
0
    VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8216
8217
0
    return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8218
0
}
8219
8220
/************************************************************************/
8221
/*                             GetUTMZone()                             */
8222
/*                                                                      */
8223
/*      Returns zero if it isn't UTM.                                   */
8224
/************************************************************************/
8225
8226
/**
8227
 * \brief Get utm zone information.
8228
 *
8229
 * This is the same as the C function OSRGetUTMZone().
8230
 *
8231
 * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8232
 * zone which is negative in the southern hemisphere instead of having the
8233
 * pbNorth flag used in the C and C++ interface.
8234
 *
8235
 * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8236
 * FALSE if southern.
8237
 *
8238
 * @return UTM zone number or zero if this isn't a UTM definition.
8239
 */
8240
8241
int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8242
8243
0
{
8244
0
    TAKE_OPTIONAL_LOCK();
8245
8246
0
    if (IsProjected() && GetAxesCount() == 3)
8247
0
    {
8248
0
        OGRSpatialReference *poSRSTmp = Clone();
8249
0
        poSRSTmp->DemoteTo2D(nullptr);
8250
0
        const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8251
0
        delete poSRSTmp;
8252
0
        return nZone;
8253
0
    }
8254
8255
0
    const char *pszProjection = GetAttrValue("PROJECTION");
8256
8257
0
    if (pszProjection == nullptr ||
8258
0
        !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8259
0
        return 0;
8260
8261
0
    if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8262
0
        return 0;
8263
8264
0
    if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8265
0
        return 0;
8266
8267
0
    if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8268
0
        return 0;
8269
8270
0
    const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8271
8272
0
    if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8273
0
        return 0;
8274
8275
0
    if (pbNorth != nullptr)
8276
0
        *pbNorth = (dfFalseNorthing == 0);
8277
8278
0
    const double dfCentralMeridian =
8279
0
        GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8280
0
    const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8281
8282
0
    if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8283
0
        std::isnan(dfZone) ||
8284
0
        std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8285
0
        return 0;
8286
8287
0
    return static_cast<int>(dfZone);
8288
0
}
8289
8290
/************************************************************************/
8291
/*                           OSRGetUTMZone()                            */
8292
/************************************************************************/
8293
8294
/**
8295
 * \brief Get utm zone information.
8296
 *
8297
 * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8298
 */
8299
int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8300
8301
0
{
8302
0
    VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8303
8304
0
    return ToPointer(hSRS)->GetUTMZone(pbNorth);
8305
0
}
8306
8307
/************************************************************************/
8308
/*                             SetWagner()                              */
8309
/************************************************************************/
8310
8311
OGRErr OGRSpatialReference::SetWagner(int nVariation,  // 1--7.
8312
                                      double dfCenterLat, double dfFalseEasting,
8313
                                      double dfFalseNorthing)
8314
8315
0
{
8316
0
    TAKE_OPTIONAL_LOCK();
8317
8318
0
    PJ *conv;
8319
0
    if (nVariation == 1)
8320
0
    {
8321
0
        conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8322
0
                                               dfFalseEasting, dfFalseNorthing,
8323
0
                                               nullptr, 0.0, nullptr, 0.0);
8324
0
    }
8325
0
    else if (nVariation == 2)
8326
0
    {
8327
0
        conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8328
0
                                                dfFalseEasting, dfFalseNorthing,
8329
0
                                                nullptr, 0.0, nullptr, 0.0);
8330
0
    }
8331
0
    else if (nVariation == 3)
8332
0
    {
8333
0
        conv = proj_create_conversion_wagner_iii(
8334
0
            d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8335
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8336
0
    }
8337
0
    else if (nVariation == 4)
8338
0
    {
8339
0
        conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8340
0
                                                dfFalseEasting, dfFalseNorthing,
8341
0
                                                nullptr, 0.0, nullptr, 0.0);
8342
0
    }
8343
0
    else if (nVariation == 5)
8344
0
    {
8345
0
        conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8346
0
                                               dfFalseEasting, dfFalseNorthing,
8347
0
                                               nullptr, 0.0, nullptr, 0.0);
8348
0
    }
8349
0
    else if (nVariation == 6)
8350
0
    {
8351
0
        conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8352
0
                                                dfFalseEasting, dfFalseNorthing,
8353
0
                                                nullptr, 0.0, nullptr, 0.0);
8354
0
    }
8355
0
    else if (nVariation == 7)
8356
0
    {
8357
0
        conv = proj_create_conversion_wagner_vii(
8358
0
            d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8359
0
            0.0, nullptr, 0.0);
8360
0
    }
8361
0
    else
8362
0
    {
8363
0
        CPLError(CE_Failure, CPLE_AppDefined,
8364
0
                 "Unsupported Wagner variation (%d).", nVariation);
8365
0
        return OGRERR_UNSUPPORTED_SRS;
8366
0
    }
8367
8368
0
    return d->replaceConversionAndUnref(conv);
8369
0
}
8370
8371
/************************************************************************/
8372
/*                            OSRSetWagner()                            */
8373
/************************************************************************/
8374
8375
OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8376
                    double dfCenterLat, double dfFalseEasting,
8377
                    double dfFalseNorthing)
8378
8379
0
{
8380
0
    VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8381
8382
0
    return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8383
0
                                      dfFalseNorthing);
8384
0
}
8385
8386
/************************************************************************/
8387
/*                               SetQSC()                               */
8388
/************************************************************************/
8389
8390
OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8391
0
{
8392
0
    TAKE_OPTIONAL_LOCK();
8393
8394
0
    return d->replaceConversionAndUnref(
8395
0
        proj_create_conversion_quadrilateralized_spherical_cube(
8396
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8397
0
            0, nullptr, 0));
8398
0
}
8399
8400
/************************************************************************/
8401
/*                             OSRSetQSC()                              */
8402
/************************************************************************/
8403
8404
OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8405
                 double dfCenterLong)
8406
8407
0
{
8408
0
    VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8409
8410
0
    return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8411
0
}
8412
8413
/************************************************************************/
8414
/*                               SetSCH()                               */
8415
/************************************************************************/
8416
8417
OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8418
                                   double dfPegHeading, double dfPegHgt)
8419
8420
0
{
8421
0
    TAKE_OPTIONAL_LOCK();
8422
8423
0
    return d->replaceConversionAndUnref(
8424
0
        proj_create_conversion_spherical_cross_track_height(
8425
0
            d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8426
0
            nullptr, 0, nullptr, 0));
8427
0
}
8428
8429
/************************************************************************/
8430
/*                             OSRSetSCH()                              */
8431
/************************************************************************/
8432
8433
OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8434
                 double dfPegHeading, double dfPegHgt)
8435
8436
0
{
8437
0
    VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8438
8439
0
    return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8440
0
}
8441
8442
/************************************************************************/
8443
/*                       SetVerticalPerspective()                       */
8444
/************************************************************************/
8445
8446
OGRErr OGRSpatialReference::SetVerticalPerspective(
8447
    double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8448
    double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8449
0
{
8450
0
    TAKE_OPTIONAL_LOCK();
8451
8452
0
    return d->replaceConversionAndUnref(
8453
0
        proj_create_conversion_vertical_perspective(
8454
0
            d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8455
0
            dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8456
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
8457
0
}
8458
8459
/************************************************************************/
8460
/*                     OSRSetVerticalPerspective()                      */
8461
/************************************************************************/
8462
8463
OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8464
                                 double dfTopoOriginLat, double dfTopoOriginLon,
8465
                                 double dfTopoOriginHeight,
8466
                                 double dfViewPointHeight,
8467
                                 double dfFalseEasting, double dfFalseNorthing)
8468
8469
0
{
8470
0
    VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8471
8472
0
    return ToPointer(hSRS)->SetVerticalPerspective(
8473
0
        dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8474
0
        dfFalseEasting, dfFalseNorthing);
8475
0
}
8476
8477
/************************************************************************/
8478
/*          SetDerivedGeogCRSWithPoleRotationGRIBConvention()           */
8479
/************************************************************************/
8480
8481
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8482
    const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8483
    double dfAxisRotation)
8484
0
{
8485
0
    TAKE_OPTIONAL_LOCK();
8486
8487
0
    d->refreshProjObj();
8488
0
    if (!d->m_pj_crs)
8489
0
        return OGRERR_FAILURE;
8490
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8491
0
        return OGRERR_FAILURE;
8492
0
    auto ctxt = d->getPROJContext();
8493
0
    auto conv = proj_create_conversion_pole_rotation_grib_convention(
8494
0
        ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8495
0
    auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8496
0
    d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8497
0
                                                   d->m_pj_crs, conv, cs));
8498
0
    proj_destroy(conv);
8499
0
    proj_destroy(cs);
8500
0
    return OGRERR_NONE;
8501
0
}
8502
8503
/************************************************************************/
8504
/*        SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention()         */
8505
/************************************************************************/
8506
8507
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8508
    const char *pszCRSName, double dfGridNorthPoleLat,
8509
    double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8510
0
{
8511
0
    TAKE_OPTIONAL_LOCK();
8512
8513
0
#if PROJ_VERSION_MAJOR > 8 ||                                                  \
8514
0
    (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8515
0
    d->refreshProjObj();
8516
0
    if (!d->m_pj_crs)
8517
0
        return OGRERR_FAILURE;
8518
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8519
0
        return OGRERR_FAILURE;
8520
0
    auto ctxt = d->getPROJContext();
8521
0
    auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8522
0
        ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8523
0
        nullptr, 0);
8524
0
    auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8525
0
    d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8526
0
                                                   d->m_pj_crs, conv, cs));
8527
0
    proj_destroy(conv);
8528
0
    proj_destroy(cs);
8529
0
    return OGRERR_NONE;
8530
#else
8531
    (void)pszCRSName;
8532
    SetProjection("Rotated_pole");
8533
    SetExtension(
8534
        "PROJCS", "PROJ4",
8535
        CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8536
                   "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8537
                   "+to_meter=0.0174532925199433 "
8538
                   "+wktext",
8539
                   180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8540
                   dfGridNorthPoleLat, GetSemiMajor(nullptr),
8541
                   GetSemiMinor(nullptr)));
8542
    return OGRERR_NONE;
8543
#endif
8544
0
}
8545
8546
/************************************************************************/
8547
/*                            SetAuthority()                            */
8548
/************************************************************************/
8549
8550
/**
8551
 * \brief Set the authority for a node.
8552
 *
8553
 * This method is the same as the C function OSRSetAuthority().
8554
 *
8555
 * @param pszTargetKey the partial or complete path to the node to
8556
 * set an authority on.  i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8557
 *
8558
 * @param pszAuthority authority name, such as "EPSG".
8559
 *
8560
 * @param nCode code for value with this authority.
8561
 *
8562
 * @return OGRERR_NONE on success.
8563
 */
8564
8565
OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8566
                                         const char *pszAuthority, int nCode)
8567
8568
0
{
8569
0
    TAKE_OPTIONAL_LOCK();
8570
8571
0
    d->refreshProjObj();
8572
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8573
8574
0
    if (pszTargetKey == nullptr)
8575
0
    {
8576
0
        if (!d->m_pj_crs)
8577
0
            return OGRERR_FAILURE;
8578
0
        CPLString osCode;
8579
0
        osCode.Printf("%d", nCode);
8580
0
        d->demoteFromBoundCRS();
8581
0
        d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8582
0
                                  pszAuthority, osCode.c_str()));
8583
0
        d->undoDemoteFromBoundCRS();
8584
0
        return OGRERR_NONE;
8585
0
    }
8586
8587
0
    d->demoteFromBoundCRS();
8588
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8589
0
    {
8590
0
        CPLString osCode;
8591
0
        osCode.Printf("%d", nCode);
8592
0
        auto newGeogCRS =
8593
0
            proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8594
0
                          pszAuthority, osCode.c_str());
8595
8596
0
        auto conv =
8597
0
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8598
8599
0
        auto projCRS = proj_create_projected_crs(
8600
0
            d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8601
0
            d->getProjCRSCoordSys());
8602
8603
        // Preserve existing id on the PROJCRS
8604
0
        const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8605
0
        const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8606
0
        if (pszProjCRSAuthName && pszProjCRSCode)
8607
0
        {
8608
0
            auto projCRSWithId =
8609
0
                proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8610
0
                              pszProjCRSCode);
8611
0
            proj_destroy(projCRS);
8612
0
            projCRS = projCRSWithId;
8613
0
        }
8614
8615
0
        proj_destroy(newGeogCRS);
8616
0
        proj_destroy(conv);
8617
8618
0
        d->setPjCRS(projCRS);
8619
0
        d->undoDemoteFromBoundCRS();
8620
0
        return OGRERR_NONE;
8621
0
    }
8622
0
    d->undoDemoteFromBoundCRS();
8623
8624
    /* -------------------------------------------------------------------- */
8625
    /*      Find the node below which the authority should be put.          */
8626
    /* -------------------------------------------------------------------- */
8627
0
    OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8628
8629
0
    if (poNode == nullptr)
8630
0
        return OGRERR_FAILURE;
8631
8632
    /* -------------------------------------------------------------------- */
8633
    /*      If there is an existing AUTHORITY child blow it away before     */
8634
    /*      trying to set a new one.                                        */
8635
    /* -------------------------------------------------------------------- */
8636
0
    int iOldChild = poNode->FindChild("AUTHORITY");
8637
0
    if (iOldChild != -1)
8638
0
        poNode->DestroyChild(iOldChild);
8639
8640
    /* -------------------------------------------------------------------- */
8641
    /*      Create a new authority node.                                    */
8642
    /* -------------------------------------------------------------------- */
8643
0
    char szCode[32] = {};
8644
8645
0
    snprintf(szCode, sizeof(szCode), "%d", nCode);
8646
8647
0
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8648
0
    poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8649
0
    poAuthNode->AddChild(new OGR_SRSNode(szCode));
8650
8651
0
    poNode->AddChild(poAuthNode);
8652
8653
0
    return OGRERR_NONE;
8654
0
}
8655
8656
/************************************************************************/
8657
/*                          OSRSetAuthority()                           */
8658
/************************************************************************/
8659
8660
/**
8661
 * \brief Set the authority for a node.
8662
 *
8663
 * This function is the same as OGRSpatialReference::SetAuthority().
8664
 */
8665
OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8666
                       const char *pszAuthority, int nCode)
8667
8668
0
{
8669
0
    VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8670
8671
0
    return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8672
0
}
8673
8674
/************************************************************************/
8675
/*                          GetAuthorityCode()                          */
8676
/************************************************************************/
8677
8678
/**
8679
 * \brief Get the authority code for a node.
8680
 *
8681
 * This method is used to query an AUTHORITY[] node from within the
8682
 * WKT tree, and fetch the code value.
8683
 *
8684
 * While in theory values may be non-numeric, for the EPSG authority all
8685
 * code values should be integral.
8686
 *
8687
 * This method is the same as the C function OSRGetAuthorityCode().
8688
 *
8689
 * @param pszTargetKey the partial or complete path to the node to
8690
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8691
 * search for an authority node on the root element.
8692
 *
8693
 * @return value code from authority node, or NULL on failure.  The value
8694
 * returned is internal and should not be freed or modified.
8695
 */
8696
8697
const char *
8698
OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8699
8700
0
{
8701
0
    TAKE_OPTIONAL_LOCK();
8702
8703
0
    d->refreshProjObj();
8704
0
    const char *pszInputTargetKey = pszTargetKey;
8705
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8706
0
    if (pszTargetKey == nullptr)
8707
0
    {
8708
0
        if (!d->m_pj_crs)
8709
0
        {
8710
0
            return nullptr;
8711
0
        }
8712
0
        d->demoteFromBoundCRS();
8713
0
        auto ret = proj_get_id_code(d->m_pj_crs, 0);
8714
0
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8715
0
        {
8716
0
            auto ctxt = d->getPROJContext();
8717
0
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8718
0
            if (cs)
8719
0
            {
8720
0
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8721
0
                proj_destroy(cs);
8722
0
                if (axisCount == 3)
8723
0
                {
8724
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8725
                    // 2002 in which case, using the WKT1 representation will
8726
                    // enable us to recover the EPSG code.
8727
0
                    pszTargetKey = pszInputTargetKey;
8728
0
                }
8729
0
            }
8730
0
        }
8731
0
        d->undoDemoteFromBoundCRS();
8732
0
        if (ret != nullptr || pszTargetKey == nullptr)
8733
0
        {
8734
0
            return ret;
8735
0
        }
8736
0
    }
8737
8738
    // Special key for that context
8739
0
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8740
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8741
0
    {
8742
0
        auto ctxt = d->getPROJContext();
8743
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8744
0
        if (crs)
8745
0
        {
8746
0
            const char *ret = proj_get_id_code(crs, 0);
8747
0
            if (ret)
8748
0
                ret = CPLSPrintf("%s", ret);
8749
0
            proj_destroy(crs);
8750
0
            return ret;
8751
0
        }
8752
0
    }
8753
0
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8754
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8755
0
    {
8756
0
        auto ctxt = d->getPROJContext();
8757
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8758
0
        if (crs)
8759
0
        {
8760
0
            const char *ret = proj_get_id_code(crs, 0);
8761
0
            if (ret)
8762
0
                ret = CPLSPrintf("%s", ret);
8763
0
            proj_destroy(crs);
8764
0
            return ret;
8765
0
        }
8766
0
    }
8767
8768
    /* -------------------------------------------------------------------- */
8769
    /*      Find the node below which the authority should be put.          */
8770
    /* -------------------------------------------------------------------- */
8771
0
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8772
8773
0
    if (poNode == nullptr)
8774
0
        return nullptr;
8775
8776
    /* -------------------------------------------------------------------- */
8777
    /*      Fetch AUTHORITY child if there is one.                          */
8778
    /* -------------------------------------------------------------------- */
8779
0
    if (poNode->FindChild("AUTHORITY") == -1)
8780
0
        return nullptr;
8781
8782
0
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8783
8784
    /* -------------------------------------------------------------------- */
8785
    /*      Create a new authority node.                                    */
8786
    /* -------------------------------------------------------------------- */
8787
0
    if (poNode->GetChildCount() < 2)
8788
0
        return nullptr;
8789
8790
0
    return poNode->GetChild(1)->GetValue();
8791
0
}
8792
8793
/************************************************************************/
8794
/*                        OSRGetAuthorityCode()                         */
8795
/************************************************************************/
8796
8797
/**
8798
 * \brief Get the authority code for a node.
8799
 *
8800
 * This function is the same as OGRSpatialReference::GetAuthorityCode().
8801
 */
8802
const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8803
                                const char *pszTargetKey)
8804
8805
0
{
8806
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8807
8808
0
    return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8809
0
}
8810
8811
/************************************************************************/
8812
/*                          GetAuthorityName()                          */
8813
/************************************************************************/
8814
8815
/**
8816
 * \brief Get the authority name for a node.
8817
 *
8818
 * This method is used to query an AUTHORITY[] node from within the
8819
 * WKT tree, and fetch the authority name value.
8820
 *
8821
 * The most common authority is "EPSG".
8822
 *
8823
 * This method is the same as the C function OSRGetAuthorityName().
8824
 *
8825
 * @param pszTargetKey the partial or complete path to the node to
8826
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8827
 * search for an authority node on the root element.
8828
 *
8829
 * @return value code from authority node, or NULL on failure. The value
8830
 * returned is internal and should not be freed or modified.
8831
 */
8832
8833
const char *
8834
OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8835
8836
0
{
8837
0
    TAKE_OPTIONAL_LOCK();
8838
8839
0
    d->refreshProjObj();
8840
0
    const char *pszInputTargetKey = pszTargetKey;
8841
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8842
0
    if (pszTargetKey == nullptr)
8843
0
    {
8844
0
        if (!d->m_pj_crs)
8845
0
        {
8846
0
            return nullptr;
8847
0
        }
8848
0
        d->demoteFromBoundCRS();
8849
0
        auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8850
0
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8851
0
        {
8852
0
            auto ctxt = d->getPROJContext();
8853
0
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8854
0
            if (cs)
8855
0
            {
8856
0
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8857
0
                proj_destroy(cs);
8858
0
                if (axisCount == 3)
8859
0
                {
8860
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8861
                    // 2002 in which case, using the WKT1 representation will
8862
                    // enable us to recover the EPSG code.
8863
0
                    pszTargetKey = pszInputTargetKey;
8864
0
                }
8865
0
            }
8866
0
        }
8867
0
        d->undoDemoteFromBoundCRS();
8868
0
        if (ret != nullptr || pszTargetKey == nullptr)
8869
0
        {
8870
0
            return ret;
8871
0
        }
8872
0
    }
8873
8874
    // Special key for that context
8875
0
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8876
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8877
0
    {
8878
0
        auto ctxt = d->getPROJContext();
8879
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8880
0
        if (crs)
8881
0
        {
8882
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8883
0
            if (ret)
8884
0
                ret = CPLSPrintf("%s", ret);
8885
0
            proj_destroy(crs);
8886
0
            return ret;
8887
0
        }
8888
0
    }
8889
0
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8890
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8891
0
    {
8892
0
        auto ctxt = d->getPROJContext();
8893
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8894
0
        if (crs)
8895
0
        {
8896
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8897
0
            if (ret)
8898
0
                ret = CPLSPrintf("%s", ret);
8899
0
            proj_destroy(crs);
8900
0
            return ret;
8901
0
        }
8902
0
    }
8903
8904
    /* -------------------------------------------------------------------- */
8905
    /*      Find the node below which the authority should be put.          */
8906
    /* -------------------------------------------------------------------- */
8907
0
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8908
8909
0
    if (poNode == nullptr)
8910
0
        return nullptr;
8911
8912
    /* -------------------------------------------------------------------- */
8913
    /*      Fetch AUTHORITY child if there is one.                          */
8914
    /* -------------------------------------------------------------------- */
8915
0
    if (poNode->FindChild("AUTHORITY") == -1)
8916
0
        return nullptr;
8917
8918
0
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8919
8920
    /* -------------------------------------------------------------------- */
8921
    /*      Create a new authority node.                                    */
8922
    /* -------------------------------------------------------------------- */
8923
0
    if (poNode->GetChildCount() < 2)
8924
0
        return nullptr;
8925
8926
0
    return poNode->GetChild(0)->GetValue();
8927
0
}
8928
8929
/************************************************************************/
8930
/*                        OSRGetAuthorityName()                         */
8931
/************************************************************************/
8932
8933
/**
8934
 * \brief Get the authority name for a node.
8935
 *
8936
 * This function is the same as OGRSpatialReference::GetAuthorityName().
8937
 */
8938
const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8939
                                const char *pszTargetKey)
8940
8941
0
{
8942
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8943
8944
0
    return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8945
0
}
8946
8947
/************************************************************************/
8948
/*                             GetOGCURN()                              */
8949
/************************************************************************/
8950
8951
/**
8952
 * \brief Get a OGC URN string describing the CRS, when possible
8953
 *
8954
 * This method assumes that the CRS has a top-level identifier, or is
8955
 * a compound CRS whose horizontal and vertical parts have a top-level
8956
 * identifier.
8957
 *
8958
 * @return a string to free with CPLFree(), or nullptr when no result can be
8959
 * generated
8960
 *
8961
 * @since GDAL 3.5
8962
 */
8963
8964
char *OGRSpatialReference::GetOGCURN() const
8965
8966
0
{
8967
0
    TAKE_OPTIONAL_LOCK();
8968
8969
0
    const char *pszAuthName = GetAuthorityName();
8970
0
    const char *pszAuthCode = GetAuthorityCode();
8971
0
    if (pszAuthName && pszAuthCode)
8972
0
        return CPLStrdup(
8973
0
            CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8974
0
    if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8975
0
        return nullptr;
8976
0
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8977
0
    auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8978
0
    char *pszRet = nullptr;
8979
0
    if (horizCRS && vertCRS)
8980
0
    {
8981
0
        auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8982
0
        auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8983
0
        auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8984
0
        auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8985
0
        if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8986
0
        {
8987
0
            pszRet = CPLStrdup(CPLSPrintf(
8988
0
                "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8989
0
                horizAuthCode, vertAuthName, vertAuthCode));
8990
0
        }
8991
0
    }
8992
0
    proj_destroy(horizCRS);
8993
0
    proj_destroy(vertCRS);
8994
0
    return pszRet;
8995
0
}
8996
8997
/************************************************************************/
8998
/*                           StripVertical()                            */
8999
/************************************************************************/
9000
9001
/**
9002
 * \brief Convert a compound cs into a horizontal CS.
9003
 *
9004
 * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
9005
 * nodes are stripped resulting and only the horizontal coordinate system
9006
 * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
9007
 *
9008
 * If this is not a compound coordinate system then nothing is changed.
9009
 *
9010
 * This method is the same as the C function OSRStripVertical().
9011
 *
9012
 */
9013
9014
OGRErr OGRSpatialReference::StripVertical()
9015
9016
0
{
9017
0
    TAKE_OPTIONAL_LOCK();
9018
9019
0
    d->refreshProjObj();
9020
0
    d->demoteFromBoundCRS();
9021
0
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
9022
0
    {
9023
0
        d->undoDemoteFromBoundCRS();
9024
0
        return OGRERR_NONE;
9025
0
    }
9026
0
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9027
0
    if (!horizCRS)
9028
0
    {
9029
0
        d->undoDemoteFromBoundCRS();
9030
0
        return OGRERR_FAILURE;
9031
0
    }
9032
9033
0
    bool reuseExistingBoundCRS = false;
9034
0
    if (d->m_pj_bound_crs_target)
9035
0
    {
9036
0
        auto type = proj_get_type(d->m_pj_bound_crs_target);
9037
0
        reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
9038
0
                                type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9039
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9040
0
    }
9041
9042
0
    if (reuseExistingBoundCRS)
9043
0
    {
9044
0
        auto newBoundCRS = proj_crs_create_bound_crs(
9045
0
            d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
9046
0
            d->m_pj_bound_crs_co);
9047
0
        proj_destroy(horizCRS);
9048
0
        d->undoDemoteFromBoundCRS();
9049
0
        d->setPjCRS(newBoundCRS);
9050
0
    }
9051
0
    else
9052
0
    {
9053
0
        d->undoDemoteFromBoundCRS();
9054
0
        d->setPjCRS(horizCRS);
9055
0
    }
9056
9057
0
    return OGRERR_NONE;
9058
0
}
9059
9060
/************************************************************************/
9061
/*                          OSRStripVertical()                          */
9062
/************************************************************************/
9063
/**
9064
 * \brief Convert a compound cs into a horizontal CS.
9065
 *
9066
 * This function is the same as the C++ method
9067
 * OGRSpatialReference::StripVertical().
9068
 */
9069
OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
9070
9071
0
{
9072
0
    VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
9073
9074
0
    return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
9075
0
}
9076
9077
/************************************************************************/
9078
/*                 StripTOWGS84IfKnownDatumAndAllowed()                 */
9079
/************************************************************************/
9080
9081
/**
9082
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9083
 *        and this is allowed by the user.
9084
 *
9085
 * The default behavior is to remove TOWGS84 information if the CRS has a
9086
 * known horizontal datum. This can be disabled by setting the
9087
 * OSR_STRIP_TOWGS84 configuration option to NO.
9088
 *
9089
 * @return true if TOWGS84 has been removed.
9090
 * @since OGR 3.1.0
9091
 */
9092
9093
bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
9094
0
{
9095
0
    if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
9096
0
    {
9097
0
        if (StripTOWGS84IfKnownDatum())
9098
0
        {
9099
0
            CPLDebug("OSR", "TOWGS84 information has been removed. "
9100
0
                            "It can be kept by setting the OSR_STRIP_TOWGS84 "
9101
0
                            "configuration option to NO");
9102
0
            return true;
9103
0
        }
9104
0
    }
9105
0
    return false;
9106
0
}
9107
9108
/************************************************************************/
9109
/*                      StripTOWGS84IfKnownDatum()                      */
9110
/************************************************************************/
9111
9112
/**
9113
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9114
 *
9115
 * @return true if TOWGS84 has been removed.
9116
 * @since OGR 3.1.0
9117
 */
9118
9119
bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
9120
9121
0
{
9122
0
    TAKE_OPTIONAL_LOCK();
9123
9124
0
    d->refreshProjObj();
9125
0
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
9126
0
    {
9127
0
        return false;
9128
0
    }
9129
0
    auto ctxt = d->getPROJContext();
9130
0
    auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
9131
0
    if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
9132
0
    {
9133
0
        proj_destroy(baseCRS);
9134
0
        return false;
9135
0
    }
9136
9137
    // Known base CRS code ? Return base CRS
9138
0
    const char *pszCode = proj_get_id_code(baseCRS, 0);
9139
0
    if (pszCode)
9140
0
    {
9141
0
        d->setPjCRS(baseCRS);
9142
0
        return true;
9143
0
    }
9144
9145
0
    auto datum = proj_crs_get_datum(ctxt, baseCRS);
9146
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9147
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9148
0
    if (datum == nullptr)
9149
0
    {
9150
0
        datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
9151
0
    }
9152
0
#endif
9153
0
    if (!datum)
9154
0
    {
9155
0
        proj_destroy(baseCRS);
9156
0
        return false;
9157
0
    }
9158
9159
    // Known datum code ? Return base CRS
9160
0
    pszCode = proj_get_id_code(datum, 0);
9161
0
    if (pszCode)
9162
0
    {
9163
0
        proj_destroy(datum);
9164
0
        d->setPjCRS(baseCRS);
9165
0
        return true;
9166
0
    }
9167
9168
0
    const char *name = proj_get_name(datum);
9169
0
    if (EQUAL(name, "unknown"))
9170
0
    {
9171
0
        proj_destroy(datum);
9172
0
        proj_destroy(baseCRS);
9173
0
        return false;
9174
0
    }
9175
0
    const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
9176
0
    PJ_OBJ_LIST *list =
9177
0
        proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
9178
9179
0
    bool knownDatumName = false;
9180
0
    if (list)
9181
0
    {
9182
0
        if (proj_list_get_count(list) == 1)
9183
0
        {
9184
0
            knownDatumName = true;
9185
0
        }
9186
0
        proj_list_destroy(list);
9187
0
    }
9188
9189
0
    proj_destroy(datum);
9190
0
    if (knownDatumName)
9191
0
    {
9192
0
        d->setPjCRS(baseCRS);
9193
0
        return true;
9194
0
    }
9195
0
    proj_destroy(baseCRS);
9196
0
    return false;
9197
0
}
9198
9199
/************************************************************************/
9200
/*                             IsCompound()                             */
9201
/************************************************************************/
9202
9203
/**
9204
 * \brief Check if coordinate system is compound.
9205
 *
9206
 * This method is the same as the C function OSRIsCompound().
9207
 *
9208
 * @return TRUE if this is rooted with a COMPD_CS node.
9209
 */
9210
9211
int OGRSpatialReference::IsCompound() const
9212
9213
0
{
9214
0
    TAKE_OPTIONAL_LOCK();
9215
9216
0
    d->refreshProjObj();
9217
0
    d->demoteFromBoundCRS();
9218
0
    bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9219
0
    d->undoDemoteFromBoundCRS();
9220
0
    return isCompound;
9221
0
}
9222
9223
/************************************************************************/
9224
/*                           OSRIsCompound()                            */
9225
/************************************************************************/
9226
9227
/**
9228
 * \brief Check if the coordinate system is compound.
9229
 *
9230
 * This function is the same as OGRSpatialReference::IsCompound().
9231
 */
9232
int OSRIsCompound(OGRSpatialReferenceH hSRS)
9233
9234
0
{
9235
0
    VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9236
9237
0
    return ToPointer(hSRS)->IsCompound();
9238
0
}
9239
9240
/************************************************************************/
9241
/*                            IsProjected()                             */
9242
/************************************************************************/
9243
9244
/**
9245
 * \brief Check if projected coordinate system.
9246
 *
9247
 * This method is the same as the C function OSRIsProjected().
9248
 *
9249
 * @return TRUE if this contains a PROJCS node indicating a it is a
9250
 * projected coordinate system. Also if it is a CompoundCRS made of a
9251
 * ProjectedCRS
9252
 */
9253
9254
int OGRSpatialReference::IsProjected() const
9255
9256
0
{
9257
0
    TAKE_OPTIONAL_LOCK();
9258
9259
0
    d->refreshProjObj();
9260
0
    d->demoteFromBoundCRS();
9261
0
    bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9262
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9263
0
    {
9264
0
        auto horizCRS =
9265
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9266
0
        if (horizCRS)
9267
0
        {
9268
0
            auto horizCRSType = proj_get_type(horizCRS);
9269
0
            isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9270
0
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9271
0
            {
9272
0
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9273
0
                if (base)
9274
0
                {
9275
0
                    isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9276
0
                    proj_destroy(base);
9277
0
                }
9278
0
            }
9279
0
            proj_destroy(horizCRS);
9280
0
        }
9281
0
    }
9282
0
    d->undoDemoteFromBoundCRS();
9283
0
    return isProjected;
9284
0
}
9285
9286
/************************************************************************/
9287
/*                           OSRIsProjected()                           */
9288
/************************************************************************/
9289
/**
9290
 * \brief Check if projected coordinate system.
9291
 *
9292
 * This function is the same as OGRSpatialReference::IsProjected().
9293
 */
9294
int OSRIsProjected(OGRSpatialReferenceH hSRS)
9295
9296
0
{
9297
0
    VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9298
9299
0
    return ToPointer(hSRS)->IsProjected();
9300
0
}
9301
9302
/************************************************************************/
9303
/*                            IsGeocentric()                            */
9304
/************************************************************************/
9305
9306
/**
9307
 * \brief Check if geocentric coordinate system.
9308
 *
9309
 * This method is the same as the C function OSRIsGeocentric().
9310
 *
9311
 * @return TRUE if this contains a GEOCCS node indicating a it is a
9312
 * geocentric coordinate system.
9313
 *
9314
 */
9315
9316
int OGRSpatialReference::IsGeocentric() const
9317
9318
0
{
9319
0
    TAKE_OPTIONAL_LOCK();
9320
9321
0
    d->refreshProjObj();
9322
0
    d->demoteFromBoundCRS();
9323
0
    bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9324
0
    d->undoDemoteFromBoundCRS();
9325
0
    return isGeocentric;
9326
0
}
9327
9328
/************************************************************************/
9329
/*                          OSRIsGeocentric()                           */
9330
/************************************************************************/
9331
/**
9332
 * \brief Check if geocentric coordinate system.
9333
 *
9334
 * This function is the same as OGRSpatialReference::IsGeocentric().
9335
 *
9336
 */
9337
int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9338
9339
0
{
9340
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9341
9342
0
    return ToPointer(hSRS)->IsGeocentric();
9343
0
}
9344
9345
/************************************************************************/
9346
/*                              IsEmpty()                               */
9347
/************************************************************************/
9348
9349
/**
9350
 * \brief Return if the SRS is not set.
9351
 */
9352
9353
bool OGRSpatialReference::IsEmpty() const
9354
0
{
9355
0
    TAKE_OPTIONAL_LOCK();
9356
9357
0
    d->refreshProjObj();
9358
0
    return d->m_pj_crs == nullptr;
9359
0
}
9360
9361
/************************************************************************/
9362
/*                            IsGeographic()                            */
9363
/************************************************************************/
9364
9365
/**
9366
 * \brief Check if geographic coordinate system.
9367
 *
9368
 * This method is the same as the C function OSRIsGeographic().
9369
 *
9370
 * @return TRUE if this spatial reference is geographic ... that is the
9371
 * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9372
 * GeographicCRS
9373
 */
9374
9375
int OGRSpatialReference::IsGeographic() const
9376
9377
0
{
9378
0
    TAKE_OPTIONAL_LOCK();
9379
9380
0
    d->refreshProjObj();
9381
0
    d->demoteFromBoundCRS();
9382
0
    bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9383
0
                  d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9384
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9385
0
    {
9386
0
        auto horizCRS =
9387
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9388
0
        if (horizCRS)
9389
0
        {
9390
0
            auto horizCRSType = proj_get_type(horizCRS);
9391
0
            isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9392
0
                     horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9393
0
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9394
0
            {
9395
0
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9396
0
                if (base)
9397
0
                {
9398
0
                    horizCRSType = proj_get_type(base);
9399
0
                    isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9400
0
                             horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9401
0
                    proj_destroy(base);
9402
0
                }
9403
0
            }
9404
0
            proj_destroy(horizCRS);
9405
0
        }
9406
0
    }
9407
0
    d->undoDemoteFromBoundCRS();
9408
0
    return isGeog;
9409
0
}
9410
9411
/************************************************************************/
9412
/*                          OSRIsGeographic()                           */
9413
/************************************************************************/
9414
/**
9415
 * \brief Check if geographic coordinate system.
9416
 *
9417
 * This function is the same as OGRSpatialReference::IsGeographic().
9418
 */
9419
int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9420
9421
0
{
9422
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9423
9424
0
    return ToPointer(hSRS)->IsGeographic();
9425
0
}
9426
9427
/************************************************************************/
9428
/*                        IsDerivedGeographic()                         */
9429
/************************************************************************/
9430
9431
/**
9432
 * \brief Check if the CRS is a derived geographic coordinate system.
9433
 * (for example a rotated long/lat grid)
9434
 *
9435
 * This method is the same as the C function OSRIsDerivedGeographic().
9436
 *
9437
 * @since GDAL 3.1.0 and PROJ 6.3.0
9438
 */
9439
9440
int OGRSpatialReference::IsDerivedGeographic() const
9441
9442
0
{
9443
0
    TAKE_OPTIONAL_LOCK();
9444
9445
0
    d->refreshProjObj();
9446
0
    d->demoteFromBoundCRS();
9447
0
    const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9448
0
                        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9449
0
    const bool isDerivedGeographic =
9450
0
        isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9451
0
    d->undoDemoteFromBoundCRS();
9452
0
    return isDerivedGeographic ? TRUE : FALSE;
9453
0
}
9454
9455
/************************************************************************/
9456
/*                       OSRIsDerivedGeographic()                       */
9457
/************************************************************************/
9458
/**
9459
 * \brief Check if the CRS is a derived geographic coordinate system.
9460
 * (for example a rotated long/lat grid)
9461
 *
9462
 * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9463
 */
9464
int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9465
9466
0
{
9467
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9468
9469
0
    return ToPointer(hSRS)->IsDerivedGeographic();
9470
0
}
9471
9472
/************************************************************************/
9473
/*                         IsDerivedProjected()                         */
9474
/************************************************************************/
9475
9476
/**
9477
 * \brief Check if the CRS is a derived projected coordinate system.
9478
 *
9479
 * This method is the same as the C function OSRIsDerivedGeographic().
9480
 *
9481
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9482
 */
9483
9484
int OGRSpatialReference::IsDerivedProjected() const
9485
9486
0
{
9487
0
#if PROJ_AT_LEAST_VERSION(9, 2, 0)
9488
0
    TAKE_OPTIONAL_LOCK();
9489
0
    d->refreshProjObj();
9490
0
    d->demoteFromBoundCRS();
9491
0
    const bool isDerivedProjected =
9492
0
        d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9493
0
    d->undoDemoteFromBoundCRS();
9494
0
    return isDerivedProjected ? TRUE : FALSE;
9495
#else
9496
    return FALSE;
9497
#endif
9498
0
}
9499
9500
/************************************************************************/
9501
/*                       OSRIsDerivedProjected()                        */
9502
/************************************************************************/
9503
/**
9504
 * \brief Check if the CRS is a derived projected coordinate system.
9505
 *
9506
 * This function is the same as OGRSpatialReference::IsDerivedProjected().
9507
 *
9508
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9509
 */
9510
int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9511
9512
0
{
9513
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9514
9515
0
    return ToPointer(hSRS)->IsDerivedProjected();
9516
0
}
9517
9518
/************************************************************************/
9519
/*                              IsLocal()                               */
9520
/************************************************************************/
9521
9522
/**
9523
 * \brief Check if local coordinate system.
9524
 *
9525
 * This method is the same as the C function OSRIsLocal().
9526
 *
9527
 * @return TRUE if this spatial reference is local ... that is the
9528
 * root is a LOCAL_CS node.
9529
 */
9530
9531
int OGRSpatialReference::IsLocal() const
9532
9533
0
{
9534
0
    TAKE_OPTIONAL_LOCK();
9535
0
    d->refreshProjObj();
9536
0
    return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9537
0
}
9538
9539
/************************************************************************/
9540
/*                             OSRIsLocal()                             */
9541
/************************************************************************/
9542
/**
9543
 * \brief Check if local coordinate system.
9544
 *
9545
 * This function is the same as OGRSpatialReference::IsLocal().
9546
 */
9547
int OSRIsLocal(OGRSpatialReferenceH hSRS)
9548
9549
0
{
9550
0
    VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9551
9552
0
    return ToPointer(hSRS)->IsLocal();
9553
0
}
9554
9555
/************************************************************************/
9556
/*                             IsVertical()                             */
9557
/************************************************************************/
9558
9559
/**
9560
 * \brief Check if vertical coordinate system.
9561
 *
9562
 * This method is the same as the C function OSRIsVertical().
9563
 *
9564
 * @return TRUE if this contains a VERT_CS node indicating a it is a
9565
 * vertical coordinate system. Also if it is a CompoundCRS made of a
9566
 * VerticalCRS
9567
 *
9568
 */
9569
9570
int OGRSpatialReference::IsVertical() const
9571
9572
0
{
9573
0
    TAKE_OPTIONAL_LOCK();
9574
0
    d->refreshProjObj();
9575
0
    d->demoteFromBoundCRS();
9576
0
    bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9577
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9578
0
    {
9579
0
        auto vertCRS =
9580
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9581
0
        if (vertCRS)
9582
0
        {
9583
0
            const auto vertCRSType = proj_get_type(vertCRS);
9584
0
            isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9585
0
            if (vertCRSType == PJ_TYPE_BOUND_CRS)
9586
0
            {
9587
0
                auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9588
0
                if (base)
9589
0
                {
9590
0
                    isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9591
0
                    proj_destroy(base);
9592
0
                }
9593
0
            }
9594
0
            proj_destroy(vertCRS);
9595
0
        }
9596
0
    }
9597
0
    d->undoDemoteFromBoundCRS();
9598
0
    return isVertical;
9599
0
}
9600
9601
/************************************************************************/
9602
/*                           OSRIsVertical()                            */
9603
/************************************************************************/
9604
/**
9605
 * \brief Check if vertical coordinate system.
9606
 *
9607
 * This function is the same as OGRSpatialReference::IsVertical().
9608
 *
9609
 */
9610
int OSRIsVertical(OGRSpatialReferenceH hSRS)
9611
9612
0
{
9613
0
    VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9614
9615
0
    return ToPointer(hSRS)->IsVertical();
9616
0
}
9617
9618
/************************************************************************/
9619
/*                             IsDynamic()                              */
9620
/************************************************************************/
9621
9622
/**
9623
 * \brief Check if a CRS is a dynamic CRS.
9624
 *
9625
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9626
 * plate-fixed.
9627
 *
9628
 * This method is the same as the C function OSRIsDynamic().
9629
 *
9630
 * @return true if the CRS is dynamic
9631
 *
9632
 * @since OGR 3.4.0
9633
 *
9634
 * @see HasPointMotionOperation()
9635
 */
9636
9637
bool OGRSpatialReference::IsDynamic() const
9638
9639
0
{
9640
0
    TAKE_OPTIONAL_LOCK();
9641
0
    bool isDynamic = false;
9642
0
    d->refreshProjObj();
9643
0
    d->demoteFromBoundCRS();
9644
0
    auto ctxt = d->getPROJContext();
9645
0
    PJ *horiz = nullptr;
9646
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9647
0
    {
9648
0
        horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9649
0
    }
9650
0
    else if (d->m_pj_crs)
9651
0
    {
9652
0
        horiz = proj_clone(ctxt, d->m_pj_crs);
9653
0
    }
9654
0
    if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9655
0
    {
9656
0
        auto baseCRS = proj_get_source_crs(ctxt, horiz);
9657
0
        if (baseCRS)
9658
0
        {
9659
0
            proj_destroy(horiz);
9660
0
            horiz = baseCRS;
9661
0
        }
9662
0
    }
9663
0
    auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9664
0
    if (datum)
9665
0
    {
9666
0
        const auto type = proj_get_type(datum);
9667
0
        isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9668
0
                    type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9669
0
        if (!isDynamic)
9670
0
        {
9671
0
            const char *auth_name = proj_get_id_auth_name(datum, 0);
9672
0
            const char *code = proj_get_id_code(datum, 0);
9673
0
            if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9674
0
                EQUAL(code, "6326"))
9675
0
            {
9676
0
                isDynamic = true;
9677
0
            }
9678
0
        }
9679
0
        proj_destroy(datum);
9680
0
    }
9681
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9682
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9683
0
    else
9684
0
    {
9685
0
        auto ensemble =
9686
0
            horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9687
0
        if (ensemble)
9688
0
        {
9689
0
            auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9690
0
            if (member)
9691
0
            {
9692
0
                const auto type = proj_get_type(member);
9693
0
                isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9694
0
                            type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9695
0
                proj_destroy(member);
9696
0
            }
9697
0
            proj_destroy(ensemble);
9698
0
        }
9699
0
    }
9700
0
#endif
9701
0
    proj_destroy(horiz);
9702
0
    d->undoDemoteFromBoundCRS();
9703
0
    return isDynamic;
9704
0
}
9705
9706
/************************************************************************/
9707
/*                            OSRIsDynamic()                            */
9708
/************************************************************************/
9709
/**
9710
 * \brief Check if a CRS is a dynamic CRS.
9711
 *
9712
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9713
 * plate-fixed.
9714
 *
9715
 * This function is the same as OGRSpatialReference::IsDynamic().
9716
 *
9717
 * @since OGR 3.4.0
9718
 */
9719
int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9720
9721
0
{
9722
0
    VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9723
9724
0
    return ToPointer(hSRS)->IsDynamic();
9725
0
}
9726
9727
/************************************************************************/
9728
/*                      HasPointMotionOperation()                       */
9729
/************************************************************************/
9730
9731
/**
9732
 * \brief Check if a CRS has at least an associated point motion operation.
9733
 *
9734
 * Some CRS are not formally declared as dynamic, but may behave as such
9735
 * in practice due to the presence of point motion operation, to perform
9736
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9737
 *
9738
 * @return true if the CRS has at least an associated point motion operation.
9739
 *
9740
 * @since OGR 3.8.0 and PROJ 9.4.0
9741
 *
9742
 * @see IsDynamic()
9743
 */
9744
9745
bool OGRSpatialReference::HasPointMotionOperation() const
9746
9747
0
{
9748
0
#if PROJ_VERSION_MAJOR > 9 ||                                                  \
9749
0
    (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9750
0
    TAKE_OPTIONAL_LOCK();
9751
0
    d->refreshProjObj();
9752
0
    d->demoteFromBoundCRS();
9753
0
    auto ctxt = d->getPROJContext();
9754
0
    auto res =
9755
0
        CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9756
0
    d->undoDemoteFromBoundCRS();
9757
0
    return res;
9758
#else
9759
    return false;
9760
#endif
9761
0
}
9762
9763
/************************************************************************/
9764
/*                     OSRHasPointMotionOperation()                     */
9765
/************************************************************************/
9766
9767
/**
9768
 * \brief Check if a CRS has at least an associated point motion operation.
9769
 *
9770
 * Some CRS are not formally declared as dynamic, but may behave as such
9771
 * in practice due to the presence of point motion operation, to perform
9772
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9773
 *
9774
 * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9775
 *
9776
 * @since OGR 3.8.0 and PROJ 9.4.0
9777
 */
9778
int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9779
9780
0
{
9781
0
    VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9782
9783
0
    return ToPointer(hSRS)->HasPointMotionOperation();
9784
0
}
9785
9786
/************************************************************************/
9787
/*                            CloneGeogCS()                             */
9788
/************************************************************************/
9789
9790
/**
9791
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9792
 * object.
9793
 *
9794
 * @return a new SRS, which becomes the responsibility of the caller.
9795
 */
9796
OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9797
9798
0
{
9799
0
    TAKE_OPTIONAL_LOCK();
9800
0
    d->refreshProjObj();
9801
0
    if (d->m_pj_crs)
9802
0
    {
9803
0
        if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9804
0
            return nullptr;
9805
9806
0
        auto geodCRS =
9807
0
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9808
0
        if (geodCRS)
9809
0
        {
9810
0
            OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9811
0
            if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9812
0
            {
9813
0
                PJ *hub_crs =
9814
0
                    proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9815
0
                PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9816
0
                                                     d->m_pj_crs);
9817
0
                auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9818
0
                                                      geodCRS, hub_crs, co);
9819
0
                proj_destroy(geodCRS);
9820
0
                geodCRS = temp;
9821
0
                proj_destroy(hub_crs);
9822
0
                proj_destroy(co);
9823
0
            }
9824
9825
            /* --------------------------------------------------------------------
9826
             */
9827
            /*      We have to reconstruct the GEOGCS node for geocentric */
9828
            /*      coordinate systems. */
9829
            /* --------------------------------------------------------------------
9830
             */
9831
0
            if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9832
0
            {
9833
0
                auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9834
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9835
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9836
0
                if (datum == nullptr)
9837
0
                {
9838
0
                    datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9839
0
                                                        geodCRS);
9840
0
                }
9841
0
#endif
9842
0
                if (datum)
9843
0
                {
9844
0
                    auto cs = proj_create_ellipsoidal_2D_cs(
9845
0
                        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9846
0
                        nullptr, 0);
9847
0
                    auto temp = proj_create_geographic_crs_from_datum(
9848
0
                        d->getPROJContext(), "unnamed", datum, cs);
9849
0
                    proj_destroy(datum);
9850
0
                    proj_destroy(cs);
9851
0
                    proj_destroy(geodCRS);
9852
0
                    geodCRS = temp;
9853
0
                }
9854
0
            }
9855
9856
0
            poNewSRS->d->setPjCRS(geodCRS);
9857
0
            if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9858
0
                poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9859
0
            return poNewSRS;
9860
0
        }
9861
0
    }
9862
0
    return nullptr;
9863
0
}
9864
9865
/************************************************************************/
9866
/*                           OSRCloneGeogCS()                           */
9867
/************************************************************************/
9868
/**
9869
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9870
 * object.
9871
 *
9872
 * This function is the same as OGRSpatialReference::CloneGeogCS().
9873
 */
9874
OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9875
9876
0
{
9877
0
    VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9878
9879
0
    return ToHandle(ToPointer(hSource)->CloneGeogCS());
9880
0
}
9881
9882
/************************************************************************/
9883
/*                            IsSameGeogCS()                            */
9884
/************************************************************************/
9885
9886
/**
9887
 * \brief Do the GeogCS'es match?
9888
 *
9889
 * This method is the same as the C function OSRIsSameGeogCS().
9890
 *
9891
 * @param poOther the SRS being compared against.
9892
 *
9893
 * @return TRUE if they are the same or FALSE otherwise.
9894
 */
9895
9896
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9897
9898
0
{
9899
0
    return IsSameGeogCS(poOther, nullptr);
9900
0
}
9901
9902
/**
9903
 * \brief Do the GeogCS'es match?
9904
 *
9905
 * This method is the same as the C function OSRIsSameGeogCS().
9906
 *
9907
 * @param poOther the SRS being compared against.
9908
 * @param papszOptions options. ignored
9909
 *
9910
 * @return TRUE if they are the same or FALSE otherwise.
9911
 */
9912
9913
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9914
                                      const char *const *papszOptions) const
9915
9916
0
{
9917
0
    TAKE_OPTIONAL_LOCK();
9918
9919
0
    CPL_IGNORE_RET_VAL(papszOptions);
9920
9921
0
    d->refreshProjObj();
9922
0
    poOther->d->refreshProjObj();
9923
0
    if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9924
0
        return FALSE;
9925
0
    if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9926
0
        d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9927
0
        poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9928
0
        poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9929
0
    {
9930
0
        return FALSE;
9931
0
    }
9932
9933
0
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9934
0
    auto otherGeodCRS =
9935
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9936
0
    if (!geodCRS || !otherGeodCRS)
9937
0
    {
9938
0
        proj_destroy(geodCRS);
9939
0
        proj_destroy(otherGeodCRS);
9940
0
        return FALSE;
9941
0
    }
9942
9943
0
    int ret = proj_is_equivalent_to(
9944
0
        geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9945
9946
0
    proj_destroy(geodCRS);
9947
0
    proj_destroy(otherGeodCRS);
9948
0
    return ret;
9949
0
}
9950
9951
/************************************************************************/
9952
/*                          OSRIsSameGeogCS()                           */
9953
/************************************************************************/
9954
9955
/**
9956
 * \brief Do the GeogCS'es match?
9957
 *
9958
 * This function is the same as OGRSpatialReference::IsSameGeogCS().
9959
 */
9960
int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9961
9962
0
{
9963
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9964
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9965
9966
0
    return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9967
0
}
9968
9969
/************************************************************************/
9970
/*                            IsSameVertCS()                            */
9971
/************************************************************************/
9972
9973
/**
9974
 * \brief Do the VertCS'es match?
9975
 *
9976
 * This method is the same as the C function OSRIsSameVertCS().
9977
 *
9978
 * @param poOther the SRS being compared against.
9979
 *
9980
 * @return TRUE if they are the same or FALSE otherwise.
9981
 */
9982
9983
int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9984
9985
0
{
9986
0
    TAKE_OPTIONAL_LOCK();
9987
9988
    /* -------------------------------------------------------------------- */
9989
    /*      Does the datum name match?                                      */
9990
    /* -------------------------------------------------------------------- */
9991
0
    const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9992
0
    const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9993
9994
0
    if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9995
0
        !EQUAL(pszThisValue, pszOtherValue))
9996
0
        return FALSE;
9997
9998
    /* -------------------------------------------------------------------- */
9999
    /*      Do the units match?                                             */
10000
    /* -------------------------------------------------------------------- */
10001
0
    pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
10002
0
    if (pszThisValue == nullptr)
10003
0
        pszThisValue = "1.0";
10004
10005
0
    pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
10006
0
    if (pszOtherValue == nullptr)
10007
0
        pszOtherValue = "1.0";
10008
10009
0
    if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
10010
0
        return FALSE;
10011
10012
0
    return TRUE;
10013
0
}
10014
10015
/************************************************************************/
10016
/*                          OSRIsSameVertCS()                           */
10017
/************************************************************************/
10018
10019
/**
10020
 * \brief Do the VertCS'es match?
10021
 *
10022
 * This function is the same as OGRSpatialReference::IsSameVertCS().
10023
 */
10024
int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10025
10026
0
{
10027
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
10028
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
10029
10030
0
    return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
10031
0
}
10032
10033
/************************************************************************/
10034
/*                               IsSame()                               */
10035
/************************************************************************/
10036
10037
/**
10038
 * \brief Do these two spatial references describe the same system ?
10039
 *
10040
 * @param poOtherSRS the SRS being compared to.
10041
 *
10042
 * @return TRUE if equivalent or FALSE otherwise.
10043
 */
10044
10045
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
10046
10047
0
{
10048
0
    return IsSame(poOtherSRS, nullptr);
10049
0
}
10050
10051
/**
10052
 * \brief Do these two spatial references describe the same system ?
10053
 *
10054
 * This also takes into account the data axis to CRS axis mapping by default
10055
 *
10056
 * @param poOtherSRS the SRS being compared to.
10057
 * @param papszOptions options. NULL or NULL terminated list of options.
10058
 * Currently supported options are:
10059
 * <ul>
10060
 * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
10061
 * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
10062
 *     Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
10063
 * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
10064
 * </ul>
10065
 *
10066
 * @return TRUE if equivalent or FALSE otherwise.
10067
 */
10068
10069
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
10070
                                const char *const *papszOptions) const
10071
10072
0
{
10073
0
    TAKE_OPTIONAL_LOCK();
10074
10075
0
    d->refreshProjObj();
10076
0
    poOtherSRS->d->refreshProjObj();
10077
0
    if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
10078
0
        return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
10079
0
    if (!CPLTestBool(CSLFetchNameValueDef(
10080
0
            papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
10081
0
    {
10082
0
        if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
10083
0
            return false;
10084
0
    }
10085
10086
0
    if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
10087
0
                                          "IGNORE_COORDINATE_EPOCH", "NO")))
10088
0
    {
10089
0
        if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
10090
0
            return false;
10091
0
    }
10092
10093
0
    bool reboundSelf = false;
10094
0
    bool reboundOther = false;
10095
0
    if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
10096
0
        poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
10097
0
    {
10098
0
        d->demoteFromBoundCRS();
10099
0
        reboundSelf = true;
10100
0
    }
10101
0
    else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
10102
0
             poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
10103
0
    {
10104
0
        poOtherSRS->d->demoteFromBoundCRS();
10105
0
        reboundOther = true;
10106
0
    }
10107
10108
0
    PJ_COMPARISON_CRITERION criterion =
10109
0
        PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
10110
0
    const char *pszCriterion = CSLFetchNameValueDef(
10111
0
        papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
10112
0
    if (EQUAL(pszCriterion, "STRICT"))
10113
0
        criterion = PJ_COMP_STRICT;
10114
0
    else if (EQUAL(pszCriterion, "EQUIVALENT"))
10115
0
        criterion = PJ_COMP_EQUIVALENT;
10116
0
    else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
10117
0
    {
10118
0
        CPLError(CE_Warning, CPLE_NotSupported,
10119
0
                 "Unsupported value for CRITERION: %s", pszCriterion);
10120
0
    }
10121
0
    int ret =
10122
0
        proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
10123
0
    if (reboundSelf)
10124
0
        d->undoDemoteFromBoundCRS();
10125
0
    if (reboundOther)
10126
0
        poOtherSRS->d->undoDemoteFromBoundCRS();
10127
10128
0
    return ret;
10129
0
}
10130
10131
/************************************************************************/
10132
/*                             OSRIsSame()                              */
10133
/************************************************************************/
10134
10135
/**
10136
 * \brief Do these two spatial references describe the same system ?
10137
 *
10138
 * This function is the same as OGRSpatialReference::IsSame().
10139
 */
10140
int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10141
10142
0
{
10143
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10144
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10145
10146
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
10147
0
}
10148
10149
/************************************************************************/
10150
/*                            OSRIsSameEx()                             */
10151
/************************************************************************/
10152
10153
/**
10154
 * \brief Do these two spatial references describe the same system ?
10155
 *
10156
 * This function is the same as OGRSpatialReference::IsSame().
10157
 */
10158
int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
10159
                const char *const *papszOptions)
10160
0
{
10161
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10162
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10163
10164
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
10165
0
}
10166
10167
/************************************************************************/
10168
/*                      convertToOtherProjection()                      */
10169
/************************************************************************/
10170
10171
/**
10172
 * \brief Convert to another equivalent projection
10173
 *
10174
 * Currently implemented:
10175
 * <ul>
10176
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10177
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10178
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10179
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10180
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10181
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10182
 * </ul>
10183
 *
10184
 * @param pszTargetProjection target projection.
10185
 * @param papszOptions lists of options. None supported currently.
10186
 * @return a new SRS, or NULL in case of error.
10187
 *
10188
 */
10189
OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10190
    const char *pszTargetProjection,
10191
    CPL_UNUSED const char *const *papszOptions) const
10192
0
{
10193
0
    TAKE_OPTIONAL_LOCK();
10194
10195
0
    if (pszTargetProjection == nullptr)
10196
0
        return nullptr;
10197
0
    int new_code;
10198
0
    if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10199
0
    {
10200
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10201
0
    }
10202
0
    else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10203
0
    {
10204
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10205
0
    }
10206
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10207
0
    {
10208
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10209
0
    }
10210
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10211
0
    {
10212
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10213
0
    }
10214
0
    else
10215
0
    {
10216
0
        return nullptr;
10217
0
    }
10218
10219
0
    d->refreshProjObj();
10220
0
    d->demoteFromBoundCRS();
10221
0
    OGRSpatialReference *poNewSRS = nullptr;
10222
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10223
0
    {
10224
0
        auto conv =
10225
0
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10226
0
        auto new_conv = proj_convert_conversion_to_other_method(
10227
0
            d->getPROJContext(), conv, new_code, nullptr);
10228
0
        proj_destroy(conv);
10229
0
        if (new_conv)
10230
0
        {
10231
0
            auto geodCRS =
10232
0
                proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10233
0
            auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10234
0
                                                     d->m_pj_crs);
10235
0
            if (geodCRS && cs)
10236
0
            {
10237
0
                auto new_proj_crs = proj_create_projected_crs(
10238
0
                    d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10239
0
                    new_conv, cs);
10240
0
                proj_destroy(new_conv);
10241
0
                if (new_proj_crs)
10242
0
                {
10243
0
                    poNewSRS = new OGRSpatialReference();
10244
10245
0
                    if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10246
0
                    {
10247
0
                        auto boundCRS = proj_crs_create_bound_crs(
10248
0
                            d->getPROJContext(), new_proj_crs,
10249
0
                            d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10250
0
                        if (boundCRS)
10251
0
                        {
10252
0
                            proj_destroy(new_proj_crs);
10253
0
                            new_proj_crs = boundCRS;
10254
0
                        }
10255
0
                    }
10256
10257
0
                    poNewSRS->d->setPjCRS(new_proj_crs);
10258
0
                }
10259
0
            }
10260
0
            proj_destroy(geodCRS);
10261
0
            proj_destroy(cs);
10262
0
        }
10263
0
    }
10264
0
    d->undoDemoteFromBoundCRS();
10265
0
    return poNewSRS;
10266
0
}
10267
10268
/************************************************************************/
10269
/*                    OSRConvertToOtherProjection()                     */
10270
/************************************************************************/
10271
10272
/**
10273
 * \brief Convert to another equivalent projection
10274
 *
10275
 * Currently implemented:
10276
 * <ul>
10277
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10278
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10279
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10280
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10281
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10282
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10283
 * </ul>
10284
 *
10285
 * @param hSRS source SRS
10286
 * @param pszTargetProjection target projection.
10287
 * @param papszOptions lists of options. None supported currently.
10288
 * @return a new SRS, or NULL in case of error.
10289
 *
10290
 */
10291
OGRSpatialReferenceH
10292
OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10293
                            const char *pszTargetProjection,
10294
                            const char *const *papszOptions)
10295
0
{
10296
0
    VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10297
0
    return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10298
0
        pszTargetProjection, papszOptions));
10299
0
}
10300
10301
/************************************************************************/
10302
/*                           OSRFindMatches()                           */
10303
/************************************************************************/
10304
10305
/**
10306
 * \brief Try to identify a match between the passed SRS and a related SRS
10307
 * in a catalog.
10308
 *
10309
 * Matching may be partial, or may fail.
10310
 * Returned entries will be sorted by decreasing match confidence (first
10311
 * entry has the highest match confidence).
10312
 *
10313
 * The exact way matching is done may change in future versions. Starting with
10314
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
10315
 *
10316
 * This function is the same as OGRSpatialReference::FindMatches().
10317
 *
10318
 * @param hSRS SRS to match
10319
 * @param papszOptions NULL terminated list of options or NULL
10320
 * @param pnEntries Output parameter. Number of values in the returned array.
10321
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10322
 * will be allocated to an array of *pnEntries whose values between 0 and 100
10323
 * indicate the confidence in the match. 100 is the highest confidence level.
10324
 * The array must be freed with CPLFree().
10325
 *
10326
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10327
 * with OSRFreeSRSArray()
10328
 *
10329
 */
10330
OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10331
                                     CSLConstList papszOptions, int *pnEntries,
10332
                                     int **ppanMatchConfidence)
10333
0
{
10334
0
    if (pnEntries)
10335
0
        *pnEntries = 0;
10336
0
    if (ppanMatchConfidence)
10337
0
        *ppanMatchConfidence = nullptr;
10338
0
    VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10339
10340
0
    OGRSpatialReference *poSRS = ToPointer(hSRS);
10341
0
    return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10342
0
}
10343
10344
/************************************************************************/
10345
/*                          OSRFreeSRSArray()                           */
10346
/************************************************************************/
10347
10348
/**
10349
 * \brief Free return of OSRIdentifyMatches()
10350
 *
10351
 * @param pahSRS array of SRS (must be NULL terminated)
10352
 */
10353
void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10354
0
{
10355
0
    if (pahSRS != nullptr)
10356
0
    {
10357
0
        for (int i = 0; pahSRS[i] != nullptr; ++i)
10358
0
        {
10359
0
            OSRRelease(pahSRS[i]);
10360
0
        }
10361
0
        CPLFree(pahSRS);
10362
0
    }
10363
0
}
10364
10365
/************************************************************************/
10366
/*                           FindBestMatch()                            */
10367
/************************************************************************/
10368
10369
/**
10370
 * \brief Try to identify the best match between the passed SRS and a related
10371
 * SRS in a catalog.
10372
 *
10373
 * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10374
 * of filtering its output.
10375
 * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10376
 * will be considered. If there is a single match, it is returned.
10377
 * If there are several matches, only return the one under the
10378
 * pszPreferredAuthority, if there is a single one under that authority.
10379
 *
10380
 * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10381
 * 100). If set to 0, 90 is used.
10382
 * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10383
 * "EPSG" is used.
10384
 * @param papszOptions NULL terminated list of options or NULL. No option is
10385
 * defined at time of writing.
10386
 *
10387
 * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10388
 *
10389
 * @since GDAL 3.6
10390
 * @see OGRSpatialReference::FindMatches()
10391
 */
10392
OGRSpatialReference *
10393
OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10394
                                   const char *pszPreferredAuthority,
10395
                                   CSLConstList papszOptions) const
10396
0
{
10397
0
    TAKE_OPTIONAL_LOCK();
10398
10399
0
    CPL_IGNORE_RET_VAL(papszOptions);  // ignored for now.
10400
10401
0
    if (nMinimumMatchConfidence == 0)
10402
0
        nMinimumMatchConfidence = 90;
10403
0
    if (pszPreferredAuthority == nullptr)
10404
0
        pszPreferredAuthority = "EPSG";
10405
10406
    // Try to identify the CRS with the database
10407
0
    int nEntries = 0;
10408
0
    int *panConfidence = nullptr;
10409
0
    OGRSpatialReferenceH *pahSRS =
10410
0
        FindMatches(nullptr, &nEntries, &panConfidence);
10411
0
    if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10412
0
    {
10413
0
        std::vector<double> adfTOWGS84(7);
10414
0
        if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10415
0
        {
10416
0
            adfTOWGS84.clear();
10417
0
        }
10418
10419
0
        auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10420
10421
0
        auto poBaseGeogCRS =
10422
0
            std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10423
0
        if (poBaseGeogCRS)
10424
0
        {
10425
            // If the base geographic SRS of the SRS is EPSG:4326
10426
            // with TOWGS84[0,0,0,0,0,0], then just use the official
10427
            // SRS code
10428
            // Same with EPSG:4258 (ETRS89), since it's the only known
10429
            // TOWGS84[] style transformation to WGS 84, and given the
10430
            // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10431
            // chance that a non-NULL TOWGS84[] will emerge.
10432
0
            const char *pszAuthorityName = nullptr;
10433
0
            const char *pszAuthorityCode = nullptr;
10434
0
            const char *pszBaseAuthorityName = nullptr;
10435
0
            const char *pszBaseAuthorityCode = nullptr;
10436
0
            const char *pszBaseName = poBaseGeogCRS->GetName();
10437
0
            if (adfTOWGS84 == std::vector<double>(7) &&
10438
0
                (pszAuthorityName = poSRS->GetAuthorityName()) != nullptr &&
10439
0
                EQUAL(pszAuthorityName, "EPSG") &&
10440
0
                (pszAuthorityCode = poSRS->GetAuthorityCode()) != nullptr &&
10441
0
                (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName()) !=
10442
0
                    nullptr &&
10443
0
                EQUAL(pszBaseAuthorityName, "EPSG") &&
10444
0
                (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode()) !=
10445
0
                    nullptr &&
10446
0
                (EQUAL(pszBaseAuthorityCode, "4326") ||
10447
0
                 EQUAL(pszBaseAuthorityCode, "4258") ||
10448
                 // For ETRS89-XXX [...] new CRS added in EPSG 12.033+
10449
0
                 (pszBaseName && STARTS_WITH(pszBaseName, "ETRS89"))))
10450
0
            {
10451
0
                poSRS->importFromEPSG(atoi(pszAuthorityCode));
10452
0
            }
10453
0
        }
10454
10455
0
        CPLFree(pahSRS);
10456
0
        CPLFree(panConfidence);
10457
10458
0
        return poSRS;
10459
0
    }
10460
0
    else
10461
0
    {
10462
        // If there are several matches >= nMinimumMatchConfidence, take the
10463
        // only one that is under pszPreferredAuthority
10464
0
        int iBestEntry = -1;
10465
0
        for (int i = 0; i < nEntries; i++)
10466
0
        {
10467
0
            if (panConfidence[i] >= nMinimumMatchConfidence)
10468
0
            {
10469
0
                const char *pszAuthName =
10470
0
                    OGRSpatialReference::FromHandle(pahSRS[i])
10471
0
                        ->GetAuthorityName();
10472
0
                if (pszAuthName != nullptr &&
10473
0
                    EQUAL(pszAuthName, pszPreferredAuthority))
10474
0
                {
10475
0
                    if (iBestEntry < 0)
10476
0
                        iBestEntry = i;
10477
0
                    else
10478
0
                    {
10479
0
                        iBestEntry = -1;
10480
0
                        break;
10481
0
                    }
10482
0
                }
10483
0
            }
10484
0
        }
10485
0
        if (iBestEntry >= 0)
10486
0
        {
10487
0
            auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10488
0
            OSRFreeSRSArray(pahSRS);
10489
0
            CPLFree(panConfidence);
10490
0
            return poRet;
10491
0
        }
10492
0
    }
10493
0
    OSRFreeSRSArray(pahSRS);
10494
0
    CPLFree(panConfidence);
10495
0
    return nullptr;
10496
0
}
10497
10498
/************************************************************************/
10499
/*                             SetTOWGS84()                             */
10500
/************************************************************************/
10501
10502
/**
10503
 * \brief Set the Bursa-Wolf conversion to WGS84.
10504
 *
10505
 * This will create the TOWGS84 node as a child of the DATUM.  It will fail
10506
 * if there is no existing DATUM node. It will replace
10507
 * an existing TOWGS84 node if there is one.
10508
 *
10509
 * The parameters have the same meaning as EPSG transformation 9606
10510
 * (Position Vector 7-param. transformation).
10511
 *
10512
 * This method is the same as the C function OSRSetTOWGS84().
10513
 *
10514
 * @param dfDX X child in meters.
10515
 * @param dfDY Y child in meters.
10516
 * @param dfDZ Z child in meters.
10517
 * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10518
 * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10519
 * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10520
 * @param dfPPM scaling factor (parts per million).
10521
 *
10522
 * @return OGRERR_NONE on success.
10523
 */
10524
10525
OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10526
                                       double dfEX, double dfEY, double dfEZ,
10527
                                       double dfPPM)
10528
10529
0
{
10530
0
    TAKE_OPTIONAL_LOCK();
10531
10532
0
    d->refreshProjObj();
10533
0
    if (d->m_pj_crs == nullptr)
10534
0
    {
10535
0
        return OGRERR_FAILURE;
10536
0
    }
10537
10538
    // Remove existing BoundCRS
10539
0
    if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10540
0
    {
10541
0
        auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10542
0
        if (!baseCRS)
10543
0
            return OGRERR_FAILURE;
10544
0
        d->setPjCRS(baseCRS);
10545
0
    }
10546
10547
0
    PJ_PARAM_DESCRIPTION params[7];
10548
10549
0
    params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10550
0
    params[0].auth_name = "EPSG";
10551
0
    params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10552
0
    params[0].value = dfDX;
10553
0
    params[0].unit_name = "metre";
10554
0
    params[0].unit_conv_factor = 1.0;
10555
0
    params[0].unit_type = PJ_UT_LINEAR;
10556
10557
0
    params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10558
0
    params[1].auth_name = "EPSG";
10559
0
    params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10560
0
    params[1].value = dfDY;
10561
0
    params[1].unit_name = "metre";
10562
0
    params[1].unit_conv_factor = 1.0;
10563
0
    params[1].unit_type = PJ_UT_LINEAR;
10564
10565
0
    params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10566
0
    params[2].auth_name = "EPSG";
10567
0
    params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10568
0
    params[2].value = dfDZ;
10569
0
    params[2].unit_name = "metre";
10570
0
    params[2].unit_conv_factor = 1.0;
10571
0
    params[2].unit_type = PJ_UT_LINEAR;
10572
10573
0
    params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10574
0
    params[3].auth_name = "EPSG";
10575
0
    params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10576
0
    params[3].value = dfEX;
10577
0
    params[3].unit_name = "arc-second";
10578
0
    params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10579
0
    params[3].unit_type = PJ_UT_ANGULAR;
10580
10581
0
    params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10582
0
    params[4].auth_name = "EPSG";
10583
0
    params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10584
0
    params[4].value = dfEY;
10585
0
    params[4].unit_name = "arc-second";
10586
0
    params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10587
0
    params[4].unit_type = PJ_UT_ANGULAR;
10588
10589
0
    params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10590
0
    params[5].auth_name = "EPSG";
10591
0
    params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10592
0
    params[5].value = dfEZ;
10593
0
    params[5].unit_name = "arc-second";
10594
0
    params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10595
0
    params[5].unit_type = PJ_UT_ANGULAR;
10596
10597
0
    params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10598
0
    params[6].auth_name = "EPSG";
10599
0
    params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10600
0
    params[6].value = dfPPM;
10601
0
    params[6].unit_name = "parts per million";
10602
0
    params[6].unit_conv_factor = 1e-6;
10603
0
    params[6].unit_type = PJ_UT_SCALE;
10604
10605
0
    auto sourceCRS =
10606
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10607
0
    if (!sourceCRS)
10608
0
    {
10609
0
        return OGRERR_FAILURE;
10610
0
    }
10611
10612
0
    const auto sourceType = proj_get_type(sourceCRS);
10613
10614
0
    auto targetCRS = proj_create_from_database(
10615
0
        d->getPROJContext(), "EPSG",
10616
0
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS   ? "4326"
10617
0
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10618
0
                                                  : "4978",
10619
0
        PJ_CATEGORY_CRS, false, nullptr);
10620
0
    if (!targetCRS)
10621
0
    {
10622
0
        proj_destroy(sourceCRS);
10623
0
        return OGRERR_FAILURE;
10624
0
    }
10625
10626
0
    CPLString osMethodCode;
10627
0
    osMethodCode.Printf("%d",
10628
0
                        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10629
0
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10630
0
                        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10631
0
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10632
0
                            : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10633
10634
0
    auto transf = proj_create_transformation(
10635
0
        d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10636
0
        sourceCRS, targetCRS, nullptr,
10637
0
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10638
0
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10639
0
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10640
0
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10641
0
            : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10642
0
        "EPSG", osMethodCode.c_str(), 7, params, -1);
10643
0
    proj_destroy(sourceCRS);
10644
0
    if (!transf)
10645
0
    {
10646
0
        proj_destroy(targetCRS);
10647
0
        return OGRERR_FAILURE;
10648
0
    }
10649
10650
0
    auto newBoundCRS = proj_crs_create_bound_crs(
10651
0
        d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10652
0
    proj_destroy(transf);
10653
0
    proj_destroy(targetCRS);
10654
0
    if (!newBoundCRS)
10655
0
    {
10656
0
        return OGRERR_FAILURE;
10657
0
    }
10658
10659
0
    d->setPjCRS(newBoundCRS);
10660
0
    return OGRERR_NONE;
10661
0
}
10662
10663
/************************************************************************/
10664
/*                           OSRSetTOWGS84()                            */
10665
/************************************************************************/
10666
10667
/**
10668
 * \brief Set the Bursa-Wolf conversion to WGS84.
10669
 *
10670
 * This function is the same as OGRSpatialReference::SetTOWGS84().
10671
 */
10672
OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10673
                     double dfDZ, double dfEX, double dfEY, double dfEZ,
10674
                     double dfPPM)
10675
10676
0
{
10677
0
    VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10678
10679
0
    return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10680
0
                                       dfPPM);
10681
0
}
10682
10683
/************************************************************************/
10684
/*                             GetTOWGS84()                             */
10685
/************************************************************************/
10686
10687
/**
10688
 * \brief Fetch TOWGS84 parameters, if available.
10689
 *
10690
 * The parameters have the same meaning as EPSG transformation 9606
10691
 * (Position Vector 7-param. transformation).
10692
 *
10693
 * @param padfCoeff array into which up to 7 coefficients are placed.
10694
 * @param nCoeffCount size of padfCoeff - defaults to 7.
10695
 *
10696
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10697
 * TOWGS84 node available.
10698
 */
10699
10700
OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10701
10702
0
{
10703
0
    TAKE_OPTIONAL_LOCK();
10704
10705
0
    d->refreshProjObj();
10706
0
    if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10707
0
        return OGRERR_FAILURE;
10708
10709
0
    memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10710
10711
0
    auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10712
0
    int success = proj_coordoperation_get_towgs84_values(
10713
0
        d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10714
0
    proj_destroy(transf);
10715
10716
0
    return success ? OGRERR_NONE : OGRERR_FAILURE;
10717
0
}
10718
10719
/************************************************************************/
10720
/*                           OSRGetTOWGS84()                            */
10721
/************************************************************************/
10722
10723
/**
10724
 * \brief Fetch TOWGS84 parameters, if available.
10725
 *
10726
 * This function is the same as OGRSpatialReference::GetTOWGS84().
10727
 */
10728
OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10729
                     int nCoeffCount)
10730
10731
0
{
10732
0
    VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10733
10734
0
    return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10735
0
}
10736
10737
/************************************************************************/
10738
/*                         IsAngularParameter()                         */
10739
/************************************************************************/
10740
10741
/** Is the passed projection parameter an angular one?
10742
 *
10743
 * @return TRUE or FALSE
10744
 */
10745
10746
/* static */
10747
int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10748
10749
0
{
10750
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10751
0
        STARTS_WITH_CI(pszParameterName, "lati") ||
10752
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10753
0
        STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10754
0
        EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10755
0
        EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10756
0
        return TRUE;
10757
10758
0
    return FALSE;
10759
0
}
10760
10761
/************************************************************************/
10762
/*                        IsLongitudeParameter()                        */
10763
/************************************************************************/
10764
10765
/** Is the passed projection parameter an angular longitude
10766
 * (relative to a prime meridian)?
10767
 *
10768
 * @return TRUE or FALSE
10769
 */
10770
10771
/* static */
10772
int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10773
10774
0
{
10775
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10776
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10777
0
        return TRUE;
10778
10779
0
    return FALSE;
10780
0
}
10781
10782
/************************************************************************/
10783
/*                         IsLinearParameter()                          */
10784
/************************************************************************/
10785
10786
/** Is the passed projection parameter an linear one measured in meters or
10787
 * some similar linear measure.
10788
 *
10789
 * @return TRUE or FALSE
10790
 */
10791
10792
/* static */
10793
int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10794
10795
0
{
10796
0
    if (STARTS_WITH_CI(pszParameterName, "false_") ||
10797
0
        EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10798
0
        return TRUE;
10799
10800
0
    return FALSE;
10801
0
}
10802
10803
/************************************************************************/
10804
/*                            GetNormInfo()                             */
10805
/************************************************************************/
10806
10807
/**
10808
 * \brief Set the internal information for normalizing linear, and angular
10809
 * values.
10810
 */
10811
void OGRSpatialReference::GetNormInfo() const
10812
10813
0
{
10814
0
    TAKE_OPTIONAL_LOCK();
10815
10816
0
    if (d->bNormInfoSet)
10817
0
        return;
10818
10819
    /* -------------------------------------------------------------------- */
10820
    /*      Initialize values.                                              */
10821
    /* -------------------------------------------------------------------- */
10822
0
    d->bNormInfoSet = TRUE;
10823
10824
0
    d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10825
0
    d->dfToMeter = GetLinearUnits(nullptr);
10826
0
    d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10827
0
    if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10828
0
        d->dfToDegrees = 1.0;
10829
0
}
10830
10831
/************************************************************************/
10832
/*                            GetExtension()                            */
10833
/************************************************************************/
10834
10835
/**
10836
 * \brief Fetch extension value.
10837
 *
10838
 * Fetch the value of the named EXTENSION item for the identified
10839
 * target node.
10840
 *
10841
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10842
 * @param pszName the name of the extension being fetched.
10843
 * @param pszDefault the value to return if the extension is not found.
10844
 *
10845
 * @return node value if successful or pszDefault on failure.
10846
 */
10847
10848
const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10849
                                              const char *pszName,
10850
                                              const char *pszDefault) const
10851
10852
0
{
10853
0
    TAKE_OPTIONAL_LOCK();
10854
10855
    /* -------------------------------------------------------------------- */
10856
    /*      Find the target node.                                           */
10857
    /* -------------------------------------------------------------------- */
10858
0
    const OGR_SRSNode *poNode =
10859
0
        pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10860
10861
0
    if (poNode == nullptr)
10862
0
        return nullptr;
10863
10864
    /* -------------------------------------------------------------------- */
10865
    /*      Fetch matching EXTENSION if there is one.                       */
10866
    /* -------------------------------------------------------------------- */
10867
0
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10868
0
    {
10869
0
        const OGR_SRSNode *poChild = poNode->GetChild(i);
10870
10871
0
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10872
0
            poChild->GetChildCount() >= 2)
10873
0
        {
10874
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10875
0
                return poChild->GetChild(1)->GetValue();
10876
0
        }
10877
0
    }
10878
10879
0
    return pszDefault;
10880
0
}
10881
10882
/************************************************************************/
10883
/*                            SetExtension()                            */
10884
/************************************************************************/
10885
/**
10886
 * \brief Set extension value.
10887
 *
10888
 * Set the value of the named EXTENSION item for the identified
10889
 * target node.
10890
 *
10891
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10892
 * @param pszName the name of the extension being fetched.
10893
 * @param pszValue the value to set
10894
 *
10895
 * @return OGRERR_NONE on success
10896
 */
10897
10898
OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10899
                                         const char *pszName,
10900
                                         const char *pszValue)
10901
10902
0
{
10903
0
    TAKE_OPTIONAL_LOCK();
10904
10905
    /* -------------------------------------------------------------------- */
10906
    /*      Find the target node.                                           */
10907
    /* -------------------------------------------------------------------- */
10908
0
    OGR_SRSNode *poNode = nullptr;
10909
10910
0
    if (pszTargetKey == nullptr)
10911
0
        poNode = GetRoot();
10912
0
    else
10913
0
        poNode = GetAttrNode(pszTargetKey);
10914
10915
0
    if (poNode == nullptr)
10916
0
        return OGRERR_FAILURE;
10917
10918
    /* -------------------------------------------------------------------- */
10919
    /*      Fetch matching EXTENSION if there is one.                       */
10920
    /* -------------------------------------------------------------------- */
10921
0
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10922
0
    {
10923
0
        OGR_SRSNode *poChild = poNode->GetChild(i);
10924
10925
0
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10926
0
            poChild->GetChildCount() >= 2)
10927
0
        {
10928
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10929
0
            {
10930
0
                poChild->GetChild(1)->SetValue(pszValue);
10931
0
                return OGRERR_NONE;
10932
0
            }
10933
0
        }
10934
0
    }
10935
10936
    /* -------------------------------------------------------------------- */
10937
    /*      Create a new EXTENSION node.                                    */
10938
    /* -------------------------------------------------------------------- */
10939
0
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10940
0
    poAuthNode->AddChild(new OGR_SRSNode(pszName));
10941
0
    poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10942
10943
0
    poNode->AddChild(poAuthNode);
10944
10945
0
    return OGRERR_NONE;
10946
0
}
10947
10948
/************************************************************************/
10949
/*                             OSRCleanup()                             */
10950
/************************************************************************/
10951
10952
static void CleanupSRSWGS84Mutex();
10953
10954
/**
10955
 * \brief Cleanup cached SRS related memory.
10956
 *
10957
 * This function will attempt to cleanup any cache spatial reference
10958
 * related information, such as cached tables of coordinate systems.
10959
 *
10960
 * This function should not be called concurrently with any other GDAL/OGR
10961
 * function. It is meant at being called once before process termination
10962
 * (typically from the main thread). CPLCleanupTLS() might be used to clean
10963
 * thread-specific resources before thread termination.
10964
 */
10965
void OSRCleanup(void)
10966
10967
0
{
10968
0
    OGRCTDumpStatistics();
10969
0
    CSVDeaccess(nullptr);
10970
0
    CleanupSRSWGS84Mutex();
10971
0
    OSRCTCleanCache();
10972
0
    OSRCleanupTLSContext();
10973
0
}
10974
10975
/************************************************************************/
10976
/*                            GetAxesCount()                            */
10977
/************************************************************************/
10978
10979
/**
10980
 * \brief Return the number of axis of the coordinate system of the CRS.
10981
 *
10982
 * @since GDAL 3.0
10983
 */
10984
int OGRSpatialReference::GetAxesCount() const
10985
0
{
10986
0
    TAKE_OPTIONAL_LOCK();
10987
10988
0
    int axisCount = 0;
10989
0
    d->refreshProjObj();
10990
0
    if (d->m_pj_crs == nullptr)
10991
0
    {
10992
0
        return 0;
10993
0
    }
10994
0
    d->demoteFromBoundCRS();
10995
0
    auto ctxt = d->getPROJContext();
10996
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10997
0
    {
10998
0
        for (int i = 0;; i++)
10999
0
        {
11000
0
            auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
11001
0
            if (!subCRS)
11002
0
                break;
11003
0
            if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
11004
0
            {
11005
0
                auto baseCRS = proj_get_source_crs(ctxt, subCRS);
11006
0
                if (baseCRS)
11007
0
                {
11008
0
                    proj_destroy(subCRS);
11009
0
                    subCRS = baseCRS;
11010
0
                }
11011
0
            }
11012
0
            auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
11013
0
            if (cs)
11014
0
            {
11015
0
                axisCount += proj_cs_get_axis_count(ctxt, cs);
11016
0
                proj_destroy(cs);
11017
0
            }
11018
0
            proj_destroy(subCRS);
11019
0
        }
11020
0
    }
11021
0
    else
11022
0
    {
11023
0
        auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11024
0
        if (cs)
11025
0
        {
11026
0
            axisCount = proj_cs_get_axis_count(ctxt, cs);
11027
0
            proj_destroy(cs);
11028
0
        }
11029
0
    }
11030
0
    d->undoDemoteFromBoundCRS();
11031
0
    return axisCount;
11032
0
}
11033
11034
/************************************************************************/
11035
/*                          OSRGetAxesCount()                           */
11036
/************************************************************************/
11037
11038
/**
11039
 * \brief Return the number of axis of the coordinate system of the CRS.
11040
 *
11041
 * This method is the equivalent of the C++ method
11042
 * OGRSpatialReference::GetAxesCount()
11043
 *
11044
 * @since GDAL 3.1
11045
 */
11046
int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
11047
11048
0
{
11049
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
11050
11051
0
    return ToPointer(hSRS)->GetAxesCount();
11052
0
}
11053
11054
/************************************************************************/
11055
/*                              GetAxis()                               */
11056
/************************************************************************/
11057
11058
/**
11059
 * \brief Fetch the orientation of one axis.
11060
 *
11061
 * Fetches the request axis (iAxis - zero based) from the
11062
 * indicated portion of the coordinate system (pszTargetKey) which
11063
 * should be either "GEOGCS" or "PROJCS".
11064
 *
11065
 * No CPLError is issued on routine failures (such as not finding the AXIS).
11066
 *
11067
 * This method is equivalent to the C function OSRGetAxis().
11068
 *
11069
 * @param pszTargetKey the coordinate system part to query ("PROJCS" or
11070
 * "GEOGCS").
11071
 * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
11072
 * @param peOrientation location into which to place the fetch orientation, may
11073
 * be NULL.
11074
 * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
11075
 * factor. May be NULL. Only set if pszTargetKey == NULL
11076
 *
11077
 * @return the name of the axis or NULL on failure.
11078
 */
11079
11080
const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
11081
                                         OGRAxisOrientation *peOrientation,
11082
                                         double *pdfConvUnit) const
11083
11084
0
{
11085
0
    TAKE_OPTIONAL_LOCK();
11086
11087
0
    if (peOrientation != nullptr)
11088
0
        *peOrientation = OAO_Other;
11089
0
    if (pdfConvUnit != nullptr)
11090
0
        *pdfConvUnit = 0;
11091
11092
0
    d->refreshProjObj();
11093
0
    if (d->m_pj_crs == nullptr)
11094
0
    {
11095
0
        return nullptr;
11096
0
    }
11097
11098
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
11099
0
    if (pszTargetKey == nullptr && iAxis <= 2)
11100
0
    {
11101
0
        auto ctxt = d->getPROJContext();
11102
11103
0
        int iAxisModified = iAxis;
11104
11105
0
        d->demoteFromBoundCRS();
11106
11107
0
        PJ *cs = nullptr;
11108
0
        if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11109
0
        {
11110
0
            auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
11111
0
            if (horizCRS)
11112
0
            {
11113
0
                if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
11114
0
                {
11115
0
                    auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
11116
0
                    if (baseCRS)
11117
0
                    {
11118
0
                        proj_destroy(horizCRS);
11119
0
                        horizCRS = baseCRS;
11120
0
                    }
11121
0
                }
11122
0
                cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
11123
0
                proj_destroy(horizCRS);
11124
0
                if (cs)
11125
0
                {
11126
0
                    if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
11127
0
                    {
11128
0
                        iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
11129
0
                        proj_destroy(cs);
11130
0
                        cs = nullptr;
11131
0
                    }
11132
0
                }
11133
0
            }
11134
11135
0
            if (cs == nullptr)
11136
0
            {
11137
0
                auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11138
0
                if (vertCRS)
11139
0
                {
11140
0
                    if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
11141
0
                    {
11142
0
                        auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
11143
0
                        if (baseCRS)
11144
0
                        {
11145
0
                            proj_destroy(vertCRS);
11146
0
                            vertCRS = baseCRS;
11147
0
                        }
11148
0
                    }
11149
11150
0
                    cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
11151
0
                    proj_destroy(vertCRS);
11152
0
                }
11153
0
            }
11154
0
        }
11155
0
        else
11156
0
        {
11157
0
            cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11158
0
        }
11159
11160
0
        if (cs)
11161
0
        {
11162
0
            const char *pszName = nullptr;
11163
0
            const char *pszOrientation = nullptr;
11164
0
            double dfConvFactor = 0.0;
11165
0
            proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
11166
0
                                  &pszOrientation, &dfConvFactor, nullptr,
11167
0
                                  nullptr, nullptr);
11168
11169
0
            if (pdfConvUnit != nullptr)
11170
0
            {
11171
0
                *pdfConvUnit = dfConvFactor;
11172
0
            }
11173
11174
0
            if (pszName && pszOrientation)
11175
0
            {
11176
0
                d->m_osAxisName[iAxis] = pszName;
11177
0
                if (peOrientation)
11178
0
                {
11179
0
                    if (EQUAL(pszOrientation, "NORTH"))
11180
0
                        *peOrientation = OAO_North;
11181
0
                    else if (EQUAL(pszOrientation, "EAST"))
11182
0
                        *peOrientation = OAO_East;
11183
0
                    else if (EQUAL(pszOrientation, "SOUTH"))
11184
0
                        *peOrientation = OAO_South;
11185
0
                    else if (EQUAL(pszOrientation, "WEST"))
11186
0
                        *peOrientation = OAO_West;
11187
0
                    else if (EQUAL(pszOrientation, "UP"))
11188
0
                        *peOrientation = OAO_Up;
11189
0
                    else if (EQUAL(pszOrientation, "DOWN"))
11190
0
                        *peOrientation = OAO_Down;
11191
0
                }
11192
0
                proj_destroy(cs);
11193
0
                d->undoDemoteFromBoundCRS();
11194
0
                return d->m_osAxisName[iAxis].c_str();
11195
0
            }
11196
0
            proj_destroy(cs);
11197
0
        }
11198
0
        d->undoDemoteFromBoundCRS();
11199
0
    }
11200
11201
    /* -------------------------------------------------------------------- */
11202
    /*      Find the target node.                                           */
11203
    /* -------------------------------------------------------------------- */
11204
0
    const OGR_SRSNode *poNode = nullptr;
11205
11206
0
    if (pszTargetKey == nullptr)
11207
0
        poNode = GetRoot();
11208
0
    else
11209
0
        poNode = GetAttrNode(pszTargetKey);
11210
11211
0
    if (poNode == nullptr)
11212
0
        return nullptr;
11213
11214
    /* -------------------------------------------------------------------- */
11215
    /*      Find desired child AXIS.                                        */
11216
    /* -------------------------------------------------------------------- */
11217
0
    const OGR_SRSNode *poAxis = nullptr;
11218
0
    const int nChildCount = poNode->GetChildCount();
11219
11220
0
    for (int iChild = 0; iChild < nChildCount; iChild++)
11221
0
    {
11222
0
        const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11223
11224
0
        if (!EQUAL(poChild->GetValue(), "AXIS"))
11225
0
            continue;
11226
11227
0
        if (iAxis == 0)
11228
0
        {
11229
0
            poAxis = poChild;
11230
0
            break;
11231
0
        }
11232
0
        iAxis--;
11233
0
    }
11234
11235
0
    if (poAxis == nullptr)
11236
0
        return nullptr;
11237
11238
0
    if (poAxis->GetChildCount() < 2)
11239
0
        return nullptr;
11240
11241
    /* -------------------------------------------------------------------- */
11242
    /*      Extract name and orientation if possible.                       */
11243
    /* -------------------------------------------------------------------- */
11244
0
    if (peOrientation != nullptr)
11245
0
    {
11246
0
        const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11247
11248
0
        if (EQUAL(pszOrientation, "NORTH"))
11249
0
            *peOrientation = OAO_North;
11250
0
        else if (EQUAL(pszOrientation, "EAST"))
11251
0
            *peOrientation = OAO_East;
11252
0
        else if (EQUAL(pszOrientation, "SOUTH"))
11253
0
            *peOrientation = OAO_South;
11254
0
        else if (EQUAL(pszOrientation, "WEST"))
11255
0
            *peOrientation = OAO_West;
11256
0
        else if (EQUAL(pszOrientation, "UP"))
11257
0
            *peOrientation = OAO_Up;
11258
0
        else if (EQUAL(pszOrientation, "DOWN"))
11259
0
            *peOrientation = OAO_Down;
11260
0
        else if (EQUAL(pszOrientation, "OTHER"))
11261
0
            *peOrientation = OAO_Other;
11262
0
        else
11263
0
        {
11264
0
            CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11265
0
                     pszOrientation);
11266
0
        }
11267
0
    }
11268
11269
0
    return poAxis->GetChild(0)->GetValue();
11270
0
}
11271
11272
/************************************************************************/
11273
/*                             OSRGetAxis()                             */
11274
/************************************************************************/
11275
11276
/**
11277
 * \brief Fetch the orientation of one axis.
11278
 *
11279
 * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11280
 */
11281
const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11282
                       int iAxis, OGRAxisOrientation *peOrientation)
11283
11284
0
{
11285
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11286
11287
0
    return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11288
0
}
11289
11290
/************************************************************************/
11291
/*                         OSRAxisEnumToName()                          */
11292
/************************************************************************/
11293
11294
/**
11295
 * \brief Return the string representation for the OGRAxisOrientation
11296
 * enumeration.
11297
 *
11298
 * For example "NORTH" for OAO_North.
11299
 *
11300
 * @return an internal string
11301
 */
11302
const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11303
11304
0
{
11305
0
    if (eOrientation == OAO_North)
11306
0
        return "NORTH";
11307
0
    if (eOrientation == OAO_East)
11308
0
        return "EAST";
11309
0
    if (eOrientation == OAO_South)
11310
0
        return "SOUTH";
11311
0
    if (eOrientation == OAO_West)
11312
0
        return "WEST";
11313
0
    if (eOrientation == OAO_Up)
11314
0
        return "UP";
11315
0
    if (eOrientation == OAO_Down)
11316
0
        return "DOWN";
11317
0
    if (eOrientation == OAO_Other)
11318
0
        return "OTHER";
11319
11320
0
    return "UNKNOWN";
11321
0
}
11322
11323
/************************************************************************/
11324
/*                              SetAxes()                               */
11325
/************************************************************************/
11326
11327
/**
11328
 * \brief Set the axes for a coordinate system.
11329
 *
11330
 * Set the names, and orientations of the axes for either a projected
11331
 * (PROJCS) or geographic (GEOGCS) coordinate system.
11332
 *
11333
 * This method is equivalent to the C function OSRSetAxes().
11334
 *
11335
 * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11336
 * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11337
 * @param eXAxisOrientation normally OAO_East.
11338
 * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11339
 * @param eYAxisOrientation normally OAO_North.
11340
 *
11341
 * @return OGRERR_NONE on success or an error code.
11342
 */
11343
11344
OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11345
                                    const char *pszXAxisName,
11346
                                    OGRAxisOrientation eXAxisOrientation,
11347
                                    const char *pszYAxisName,
11348
                                    OGRAxisOrientation eYAxisOrientation)
11349
11350
0
{
11351
0
    TAKE_OPTIONAL_LOCK();
11352
11353
    /* -------------------------------------------------------------------- */
11354
    /*      Find the target node.                                           */
11355
    /* -------------------------------------------------------------------- */
11356
0
    OGR_SRSNode *poNode = nullptr;
11357
11358
0
    if (pszTargetKey == nullptr)
11359
0
        poNode = GetRoot();
11360
0
    else
11361
0
        poNode = GetAttrNode(pszTargetKey);
11362
11363
0
    if (poNode == nullptr)
11364
0
        return OGRERR_FAILURE;
11365
11366
    /* -------------------------------------------------------------------- */
11367
    /*      Strip any existing AXIS children.                               */
11368
    /* -------------------------------------------------------------------- */
11369
0
    while (poNode->FindChild("AXIS") >= 0)
11370
0
        poNode->DestroyChild(poNode->FindChild("AXIS"));
11371
11372
    /* -------------------------------------------------------------------- */
11373
    /*      Insert desired axes                                             */
11374
    /* -------------------------------------------------------------------- */
11375
0
    OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11376
11377
0
    poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11378
0
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11379
11380
0
    poNode->AddChild(poAxis);
11381
11382
0
    poAxis = new OGR_SRSNode("AXIS");
11383
11384
0
    poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11385
0
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11386
11387
0
    poNode->AddChild(poAxis);
11388
11389
0
    return OGRERR_NONE;
11390
0
}
11391
11392
/************************************************************************/
11393
/*                             OSRSetAxes()                             */
11394
/************************************************************************/
11395
/**
11396
 * \brief Set the axes for a coordinate system.
11397
 *
11398
 * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11399
 */
11400
OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11401
                  const char *pszXAxisName,
11402
                  OGRAxisOrientation eXAxisOrientation,
11403
                  const char *pszYAxisName,
11404
                  OGRAxisOrientation eYAxisOrientation)
11405
0
{
11406
0
    VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11407
11408
0
    return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11409
0
                                    eXAxisOrientation, pszYAxisName,
11410
0
                                    eYAxisOrientation);
11411
0
}
11412
11413
/************************************************************************/
11414
/*                       OSRExportToMICoordSys()                        */
11415
/************************************************************************/
11416
/**
11417
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11418
 *
11419
 * This method is the equivalent of the C++ method
11420
 * OGRSpatialReference::exportToMICoordSys
11421
 */
11422
OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11423
11424
0
{
11425
0
    VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11426
11427
0
    *ppszReturn = nullptr;
11428
11429
0
    return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11430
0
}
11431
11432
/************************************************************************/
11433
/*                         exportToMICoordSys()                         */
11434
/************************************************************************/
11435
11436
/**
11437
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11438
 *
11439
 * Note that the returned WKT string should be freed with
11440
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
11441
 *
11442
 * This method is the same as the C function OSRExportToMICoordSys().
11443
 *
11444
 * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11445
 * definition will be assigned.
11446
 *
11447
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11448
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11449
 */
11450
11451
OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11452
11453
0
{
11454
0
    *ppszResult = MITABSpatialRef2CoordSys(this);
11455
0
    if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11456
0
        return OGRERR_NONE;
11457
11458
0
    return OGRERR_FAILURE;
11459
0
}
11460
11461
/************************************************************************/
11462
/*                      OSRImportFromMICoordSys()                       */
11463
/************************************************************************/
11464
/**
11465
 * \brief Import Mapinfo style CoordSys definition.
11466
 *
11467
 * This method is the equivalent of the C++ method
11468
 * OGRSpatialReference::importFromMICoordSys
11469
 */
11470
11471
OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11472
                               const char *pszCoordSys)
11473
11474
0
{
11475
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11476
11477
0
    return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11478
0
}
11479
11480
/************************************************************************/
11481
/*                        importFromMICoordSys()                        */
11482
/************************************************************************/
11483
11484
/**
11485
 * \brief Import Mapinfo style CoordSys definition.
11486
 *
11487
 * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11488
 * definition string.
11489
 *
11490
 * This method is the equivalent of the C function OSRImportFromMICoordSys().
11491
 *
11492
 * @param pszCoordSys Mapinfo style CoordSys definition string.
11493
 *
11494
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11495
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11496
 */
11497
11498
OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11499
11500
0
{
11501
0
    OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11502
11503
0
    if (poResult == nullptr)
11504
0
        return OGRERR_FAILURE;
11505
11506
0
    *this = *poResult;
11507
0
    delete poResult;
11508
11509
0
    return OGRERR_NONE;
11510
0
}
11511
11512
/************************************************************************/
11513
/*                        OSRCalcInvFlattening()                        */
11514
/************************************************************************/
11515
11516
/**
11517
 * \brief Compute inverse flattening from semi-major and semi-minor axis
11518
 *
11519
 * @param dfSemiMajor Semi-major axis length.
11520
 * @param dfSemiMinor Semi-minor axis length.
11521
 *
11522
 * @return inverse flattening, or 0 if both axis are equal.
11523
 */
11524
11525
double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11526
0
{
11527
0
    if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11528
0
        return 0;
11529
0
    if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11530
0
    {
11531
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11532
0
                 "OSRCalcInvFlattening(): Wrong input values");
11533
0
        return 0;
11534
0
    }
11535
11536
0
    return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11537
0
}
11538
11539
/************************************************************************/
11540
/*                        OSRCalcInvFlattening()                        */
11541
/************************************************************************/
11542
11543
/**
11544
 * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11545
 *
11546
 * @param dfSemiMajor Semi-major axis length.
11547
 * @param dfInvFlattening Inverse flattening or 0 for sphere.
11548
 *
11549
 * @return semi-minor axis
11550
 */
11551
11552
double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11553
                                         double dfInvFlattening)
11554
0
{
11555
0
    if (fabs(dfInvFlattening) < 0.000000000001)
11556
0
        return dfSemiMajor;
11557
0
    if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11558
0
    {
11559
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11560
0
                 "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11561
0
        return dfSemiMajor;
11562
0
    }
11563
11564
0
    return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11565
0
}
11566
11567
/************************************************************************/
11568
/*                            GetWGS84SRS()                             */
11569
/************************************************************************/
11570
11571
static OGRSpatialReference *poSRSWGS84 = nullptr;
11572
static CPLMutex *hMutex = nullptr;
11573
11574
/**
11575
 * \brief Returns an instance of a SRS object with WGS84 WKT.
11576
 *
11577
 * Note: the instance will have
11578
 * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11579
 *
11580
 * The reference counter of the returned object is not increased by this
11581
 * operation.
11582
 *
11583
 * @return instance.
11584
 */
11585
11586
OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11587
0
{
11588
0
    CPLMutexHolderD(&hMutex);
11589
0
    if (poSRSWGS84 == nullptr)
11590
0
    {
11591
0
        poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11592
0
        poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11593
0
    }
11594
0
    return poSRSWGS84;
11595
0
}
11596
11597
/************************************************************************/
11598
/*                        CleanupSRSWGS84Mutex()                        */
11599
/************************************************************************/
11600
11601
static void CleanupSRSWGS84Mutex()
11602
0
{
11603
0
    if (hMutex != nullptr)
11604
0
    {
11605
0
        poSRSWGS84->Release();
11606
0
        poSRSWGS84 = nullptr;
11607
0
        CPLDestroyMutex(hMutex);
11608
0
        hMutex = nullptr;
11609
0
    }
11610
0
}
11611
11612
/************************************************************************/
11613
/*                         OSRImportFromProj4()                         */
11614
/************************************************************************/
11615
/**
11616
 * \brief Import PROJ coordinate string.
11617
 *
11618
 * This function is the same as OGRSpatialReference::importFromProj4().
11619
 */
11620
OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11621
11622
0
{
11623
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11624
11625
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11626
0
}
11627
11628
/************************************************************************/
11629
/*                          importFromProj4()                           */
11630
/************************************************************************/
11631
11632
/**
11633
 * \brief Import PROJ coordinate string.
11634
 *
11635
 * The OGRSpatialReference is initialized from the passed PROJs style
11636
 * coordinate system string.
11637
 *
11638
 * Example:
11639
 *   pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11640
 *
11641
 * It is also possible to import "+init=epsg:n" style definitions. Those are
11642
 * a legacy syntax that should be avoided in the future. In particular they will
11643
 * result in CRS objects whose axis order might not correspond to the official
11644
 * EPSG axis order.
11645
 *
11646
 * This method is the equivalent of the C function OSRImportFromProj4().
11647
 *
11648
 * @param pszProj4 the PROJ style string.
11649
 *
11650
 * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11651
 */
11652
11653
OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11654
11655
0
{
11656
0
    TAKE_OPTIONAL_LOCK();
11657
11658
0
    if (strlen(pszProj4) >= 10000)
11659
0
    {
11660
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11661
0
        return OGRERR_CORRUPT_DATA;
11662
0
    }
11663
11664
    /* -------------------------------------------------------------------- */
11665
    /*      Clear any existing definition.                                  */
11666
    /* -------------------------------------------------------------------- */
11667
0
    Clear();
11668
11669
0
    CPLString osProj4(pszProj4);
11670
0
    if (osProj4.find("type=crs") == std::string::npos)
11671
0
    {
11672
0
        osProj4 += " +type=crs";
11673
0
    }
11674
11675
0
    if (osProj4.find("+init=epsg:") != std::string::npos &&
11676
0
        getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11677
0
    {
11678
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11679
0
                     "+init=epsg:XXXX syntax is deprecated. It might return "
11680
0
                     "a CRS with a non-EPSG compliant axis order.");
11681
0
    }
11682
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11683
0
    d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11684
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11685
0
    return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11686
0
}
11687
11688
/************************************************************************/
11689
/*                          OSRExportToProj4()                          */
11690
/************************************************************************/
11691
/**
11692
 * \brief Export coordinate system in PROJ.4 legacy format.
11693
 *
11694
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11695
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11696
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11697
 * will be missing most of the time. PROJ strings to encode CRS should be
11698
 * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11699
 * is the recommended way.
11700
 *
11701
 * This function is the same as OGRSpatialReference::exportToProj4().
11702
 */
11703
OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11704
                                    char **ppszReturn)
11705
11706
0
{
11707
0
    VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11708
11709
0
    *ppszReturn = nullptr;
11710
11711
0
    return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11712
0
}
11713
11714
/************************************************************************/
11715
/*                           exportToProj4()                            */
11716
/************************************************************************/
11717
11718
/**
11719
 * \brief Export coordinate system in PROJ.4 legacy format.
11720
 *
11721
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11722
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11723
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11724
 * will be missing most of the time. PROJ strings to encode CRS should be
11725
 * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11726
 * representation is the recommended way.
11727
 *
11728
 * Converts the loaded coordinate reference system into PROJ format
11729
 * to the extent possible.  The string returned in ppszProj4 should be
11730
 * deallocated by the caller with CPLFree() when no longer needed.
11731
 *
11732
 * LOCAL_CS coordinate systems are not translatable.  An empty string
11733
 * will be returned along with OGRERR_NONE.
11734
 *
11735
 * Special processing for Transverse Mercator:
11736
 * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11737
 * set to YES, the PROJ definition built from the SRS will use the +approx flag
11738
 * for the tmerc and utm projection methods, rather than the more accurate
11739
 * method.
11740
 *
11741
 * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11742
 * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11743
 * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11744
 * added. This automatic addition may be disabled by setting the
11745
 * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11746
 *
11747
 * This method is the equivalent of the C function OSRExportToProj4().
11748
 *
11749
 * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11750
 * will be assigned.
11751
 *
11752
 * @return OGRERR_NONE on success or an error code on failure.
11753
 */
11754
11755
OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11756
11757
0
{
11758
    // In the past calling this method was thread-safe, even if we never
11759
    // guaranteed it. Now proj_as_proj_string() will cache the result
11760
    // internally, so this is no longer thread-safe.
11761
0
    std::lock_guard oLock(d->m_mutex);
11762
11763
0
    d->refreshProjObj();
11764
0
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11765
0
    {
11766
0
        *ppszProj4 = CPLStrdup("");
11767
0
        return OGRERR_FAILURE;
11768
0
    }
11769
11770
    // OSR_USE_ETMERC is here just for legacy
11771
0
    bool bForceApproxTMerc = false;
11772
0
    const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11773
0
    if (pszUseETMERC && pszUseETMERC[0])
11774
0
    {
11775
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11776
0
                     "OSR_USE_ETMERC is a legacy configuration option, which "
11777
0
                     "now has only effect when set to NO (YES is the default). "
11778
0
                     "Use OSR_USE_APPROX_TMERC=YES instead");
11779
0
        bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11780
0
    }
11781
0
    else
11782
0
    {
11783
0
        const char *pszUseApproxTMERC =
11784
0
            CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11785
0
        if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11786
0
        {
11787
0
            bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11788
0
        }
11789
0
    }
11790
0
    const char *options[] = {
11791
0
        bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11792
11793
0
    const char *projString = proj_as_proj_string(
11794
0
        d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11795
11796
0
    PJ *boundCRS = nullptr;
11797
0
    if (projString &&
11798
0
        (strstr(projString, "+datum=") == nullptr ||
11799
0
         d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11800
0
        CPLTestBool(
11801
0
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11802
0
    {
11803
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11804
0
            d->getPROJContext(), d->m_pj_crs, true,
11805
0
            strstr(projString, "+datum=") == nullptr);
11806
0
        if (boundCRS)
11807
0
        {
11808
0
            projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11809
0
                                             PJ_PROJ_4, options);
11810
0
        }
11811
0
    }
11812
11813
0
    if (projString == nullptr)
11814
0
    {
11815
0
        *ppszProj4 = CPLStrdup("");
11816
0
        proj_destroy(boundCRS);
11817
0
        return OGRERR_FAILURE;
11818
0
    }
11819
0
    *ppszProj4 = CPLStrdup(projString);
11820
0
    proj_destroy(boundCRS);
11821
0
    char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11822
0
    if (pszTypeCrs)
11823
0
        *pszTypeCrs = '\0';
11824
0
    return OGRERR_NONE;
11825
0
}
11826
11827
/************************************************************************/
11828
/*                            morphToESRI()                             */
11829
/************************************************************************/
11830
/**
11831
 * \brief Convert in place to ESRI WKT format.
11832
 *
11833
 * The value nodes of this coordinate system are modified in various manners
11834
 * more closely map onto the ESRI concept of WKT format.  This includes
11835
 * renaming a variety of projections and arguments, and stripping out
11836
 * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11837
 *
11838
 * \note Since GDAL 3.0, this function has only user-visible effects at
11839
 * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11840
 * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11841
 *
11842
 * This does the same as the C function OSRMorphToESRI().
11843
 *
11844
 * @return OGRERR_NONE unless something goes badly wrong.
11845
 * @deprecated
11846
 */
11847
11848
OGRErr OGRSpatialReference::morphToESRI()
11849
11850
0
{
11851
0
    TAKE_OPTIONAL_LOCK();
11852
11853
0
    d->refreshProjObj();
11854
0
    d->setMorphToESRI(true);
11855
11856
0
    return OGRERR_NONE;
11857
0
}
11858
11859
/************************************************************************/
11860
/*                           OSRMorphToESRI()                           */
11861
/************************************************************************/
11862
11863
/**
11864
 * \brief Convert in place to ESRI WKT format.
11865
 *
11866
 * This function is the same as the C++ method
11867
 * OGRSpatialReference::morphToESRI().
11868
 */
11869
OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11870
11871
0
{
11872
0
    VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11873
11874
0
    return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11875
0
}
11876
11877
/************************************************************************/
11878
/*                           morphFromESRI()                            */
11879
/************************************************************************/
11880
11881
/**
11882
 * \brief Convert in place from ESRI WKT format.
11883
 *
11884
 * The value notes of this coordinate system are modified in various manners
11885
 * to adhere more closely to the WKT standard.  This mostly involves
11886
 * translating a variety of ESRI names for projections, arguments and
11887
 * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11888
 * translation of EPSG to WKT for the CT specification.
11889
 *
11890
 * \note Since GDAL 3.0, this function is essentially a no-operation, since
11891
 * morphing from ESRI is automatically done by importFromWkt(). Its only
11892
 * effect is to undo the effect of a potential prior call to morphToESRI().
11893
 *
11894
 * This does the same as the C function OSRMorphFromESRI().
11895
 *
11896
 * @return OGRERR_NONE unless something goes badly wrong.
11897
 * @deprecated
11898
 */
11899
11900
OGRErr OGRSpatialReference::morphFromESRI()
11901
11902
0
{
11903
0
    TAKE_OPTIONAL_LOCK();
11904
11905
0
    d->refreshProjObj();
11906
0
    d->setMorphToESRI(false);
11907
11908
0
    return OGRERR_NONE;
11909
0
}
11910
11911
/************************************************************************/
11912
/*                          OSRMorphFromESRI()                          */
11913
/************************************************************************/
11914
11915
/**
11916
 * \brief Convert in place from ESRI WKT format.
11917
 *
11918
 * This function is the same as the C++ method
11919
 * OGRSpatialReference::morphFromESRI().
11920
 */
11921
OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11922
11923
0
{
11924
0
    VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11925
11926
0
    return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11927
0
}
11928
11929
/************************************************************************/
11930
/*                            FindMatches()                             */
11931
/************************************************************************/
11932
11933
/**
11934
 * \brief Try to identify a match between the passed SRS and a related SRS
11935
 * in a catalog.
11936
 *
11937
 * Matching may be partial, or may fail.
11938
 * Returned entries will be sorted by decreasing match confidence (first
11939
 * entry has the highest match confidence).
11940
 *
11941
 * The exact way matching is done may change in future versions. Starting with
11942
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
11943
 *
11944
 * This method is the same as OSRFindMatches().
11945
 *
11946
 * @param papszOptions NULL terminated list of options or NULL
11947
 * @param pnEntries Output parameter. Number of values in the returned array.
11948
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11949
 * will be allocated to an array of *pnEntries whose values between 0 and 100
11950
 * indicate the confidence in the match. 100 is the highest confidence level.
11951
 * The array must be freed with CPLFree().
11952
 *
11953
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11954
 * with OSRFreeSRSArray()
11955
 *
11956
 *
11957
 * @see OGRSpatialReference::FindBestMatch()
11958
 */
11959
OGRSpatialReferenceH *
11960
OGRSpatialReference::FindMatches(CSLConstList papszOptions, int *pnEntries,
11961
                                 int **ppanMatchConfidence) const
11962
0
{
11963
0
    TAKE_OPTIONAL_LOCK();
11964
11965
0
    CPL_IGNORE_RET_VAL(papszOptions);
11966
11967
0
    if (pnEntries)
11968
0
        *pnEntries = 0;
11969
0
    if (ppanMatchConfidence)
11970
0
        *ppanMatchConfidence = nullptr;
11971
11972
0
    d->refreshProjObj();
11973
0
    if (!d->m_pj_crs)
11974
0
        return nullptr;
11975
11976
0
    int *panConfidence = nullptr;
11977
0
    auto ctxt = d->getPROJContext();
11978
0
    auto list =
11979
0
        proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11980
0
    if (!list)
11981
0
        return nullptr;
11982
11983
0
    const int nMatches = proj_list_get_count(list);
11984
11985
0
    if (pnEntries)
11986
0
        *pnEntries = static_cast<int>(nMatches);
11987
0
    OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11988
0
        CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11989
0
    if (ppanMatchConfidence)
11990
0
    {
11991
0
        *ppanMatchConfidence =
11992
0
            static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11993
0
    }
11994
11995
0
    bool bSortAgain = false;
11996
11997
0
    for (int i = 0; i < nMatches; i++)
11998
0
    {
11999
0
        PJ *obj = proj_list_get(ctxt, list, i);
12000
0
        CPLAssert(obj);
12001
0
        OGRSpatialReference *poSRS = new OGRSpatialReference();
12002
0
        poSRS->d->setPjCRS(obj);
12003
0
        pahRet[i] = ToHandle(poSRS);
12004
12005
        // Identify matches that only differ by axis order
12006
0
        if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
12007
0
            poSRS->GetAxesCount() == 2 &&
12008
0
            GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
12009
0
        {
12010
0
            OGRAxisOrientation eThisAxis0 = OAO_Other;
12011
0
            OGRAxisOrientation eThisAxis1 = OAO_Other;
12012
0
            OGRAxisOrientation eSRSAxis0 = OAO_Other;
12013
0
            OGRAxisOrientation eSRSAxis1 = OAO_Other;
12014
0
            GetAxis(nullptr, 0, &eThisAxis0);
12015
0
            GetAxis(nullptr, 1, &eThisAxis1);
12016
0
            poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
12017
0
            poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
12018
0
            if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
12019
0
                eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
12020
0
            {
12021
0
                auto pj_crs_normalized =
12022
0
                    proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
12023
0
                if (pj_crs_normalized)
12024
0
                {
12025
0
                    if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
12026
0
                                              PJ_COMP_EQUIVALENT))
12027
0
                    {
12028
0
                        bSortAgain = true;
12029
0
                        panConfidence[i] = 90;
12030
0
                        poSRS->SetDataAxisToSRSAxisMapping({2, 1});
12031
0
                    }
12032
0
                    proj_destroy(pj_crs_normalized);
12033
0
                }
12034
0
            }
12035
0
        }
12036
12037
0
        if (ppanMatchConfidence)
12038
0
            (*ppanMatchConfidence)[i] = panConfidence[i];
12039
0
    }
12040
12041
0
    if (bSortAgain)
12042
0
    {
12043
0
        std::vector<int> anIndices;
12044
0
        for (int i = 0; i < nMatches; ++i)
12045
0
            anIndices.push_back(i);
12046
12047
0
        std::stable_sort(anIndices.begin(), anIndices.end(),
12048
0
                         [&panConfidence](int i, int j)
12049
0
                         { return panConfidence[i] > panConfidence[j]; });
12050
12051
0
        OGRSpatialReferenceH *pahRetSorted =
12052
0
            static_cast<OGRSpatialReferenceH *>(
12053
0
                CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
12054
0
        for (int i = 0; i < nMatches; ++i)
12055
0
        {
12056
0
            pahRetSorted[i] = pahRet[anIndices[i]];
12057
0
            if (ppanMatchConfidence)
12058
0
                (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
12059
0
        }
12060
0
        CPLFree(pahRet);
12061
0
        pahRet = pahRetSorted;
12062
0
    }
12063
12064
0
    pahRet[nMatches] = nullptr;
12065
0
    proj_list_destroy(list);
12066
0
    proj_int_list_destroy(panConfidence);
12067
12068
0
    return pahRet;
12069
0
}
12070
12071
/************************************************************************/
12072
/*                          importFromEPSGA()                           */
12073
/************************************************************************/
12074
12075
/**
12076
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12077
 * code.
12078
 *
12079
 * This method will initialize the spatial reference based on the
12080
 * passed in EPSG CRS code found in the PROJ database.
12081
 *
12082
 * Since GDAL 3.0, this method is identical to importFromEPSG().
12083
 *
12084
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12085
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
12086
 * such method available for the CRS. This behavior might not always be
12087
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12088
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12089
 * The AddGuessedTOWGS84() method can also be used for that purpose.
12090
 *
12091
 * The method will also by default substitute a deprecated EPSG code by its
12092
 * non-deprecated replacement. If this behavior is not desired, the
12093
 * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
12094
 *
12095
 * This method is the same as the C function OSRImportFromEPSGA().
12096
 *
12097
 * @param nCode a CRS code.
12098
 *
12099
 * @return OGRERR_NONE on success, or an error code on failure.
12100
 */
12101
12102
OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
12103
12104
0
{
12105
0
    TAKE_OPTIONAL_LOCK();
12106
12107
0
    Clear();
12108
12109
0
    const char *pszUseNonDeprecated =
12110
0
        CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
12111
0
    const bool bUseNonDeprecated =
12112
0
        CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
12113
0
    const bool bAddTOWGS84 = CPLTestBool(
12114
0
        CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
12115
0
    auto tlsCache = OSRGetProjTLSCache();
12116
0
    if (tlsCache)
12117
0
    {
12118
0
        auto cachedObj =
12119
0
            tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
12120
0
        if (cachedObj)
12121
0
        {
12122
0
            d->setPjCRS(cachedObj);
12123
0
            return OGRERR_NONE;
12124
0
        }
12125
0
    }
12126
12127
0
    CPLString osCode;
12128
0
    osCode.Printf("%d", nCode);
12129
0
    PJ *obj;
12130
0
    constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
12131
0
    if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
12132
0
    {
12133
0
        obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12134
0
                                        osCode.c_str(), PJ_CATEGORY_CRS, true,
12135
0
                                        nullptr);
12136
0
        if (!obj)
12137
0
        {
12138
0
            return OGRERR_FAILURE;
12139
0
        }
12140
0
    }
12141
0
    else
12142
0
    {
12143
        // Likely to be an ESRI CRS...
12144
0
        CPLErr eLastErrorType = CE_None;
12145
0
        CPLErrorNum eLastErrorNum = CPLE_None;
12146
0
        std::string osLastErrorMsg;
12147
0
        bool bIsESRI = false;
12148
0
        {
12149
0
            CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
12150
0
            CPLErrorReset();
12151
0
            obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12152
0
                                            osCode.c_str(), PJ_CATEGORY_CRS,
12153
0
                                            true, nullptr);
12154
0
            if (!obj)
12155
0
            {
12156
0
                eLastErrorType = CPLGetLastErrorType();
12157
0
                eLastErrorNum = CPLGetLastErrorNo();
12158
0
                osLastErrorMsg = CPLGetLastErrorMsg();
12159
0
                obj = proj_create_from_database(d->getPROJContext(), "ESRI",
12160
0
                                                osCode.c_str(), PJ_CATEGORY_CRS,
12161
0
                                                true, nullptr);
12162
0
                if (obj)
12163
0
                    bIsESRI = true;
12164
0
            }
12165
0
        }
12166
0
        if (!obj)
12167
0
        {
12168
0
            if (eLastErrorType != CE_None)
12169
0
                CPLError(eLastErrorType, eLastErrorNum, "%s",
12170
0
                         osLastErrorMsg.c_str());
12171
0
            return OGRERR_FAILURE;
12172
0
        }
12173
0
        if (bIsESRI)
12174
0
        {
12175
0
            CPLError(CE_Warning, CPLE_AppDefined,
12176
0
                     "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
12177
0
                     "Assuming ESRI:%d was meant",
12178
0
                     nCode, nCode, nCode);
12179
0
        }
12180
0
    }
12181
12182
0
    if (bUseNonDeprecated && proj_is_deprecated(obj))
12183
0
    {
12184
0
        auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
12185
0
        if (list)
12186
0
        {
12187
0
            const auto count = proj_list_get_count(list);
12188
0
            if (count == 1)
12189
0
            {
12190
0
                auto nonDeprecated =
12191
0
                    proj_list_get(d->getPROJContext(), list, 0);
12192
0
                if (nonDeprecated)
12193
0
                {
12194
0
                    if (pszUseNonDeprecated == nullptr)
12195
0
                    {
12196
0
                        const char *pszNewAuth =
12197
0
                            proj_get_id_auth_name(nonDeprecated, 0);
12198
0
                        const char *pszNewCode =
12199
0
                            proj_get_id_code(nonDeprecated, 0);
12200
0
                        CPLError(CE_Warning, CPLE_AppDefined,
12201
0
                                 "CRS EPSG:%d is deprecated. "
12202
0
                                 "Its non-deprecated replacement %s:%s "
12203
0
                                 "will be used instead. "
12204
0
                                 "To use the original CRS, set the "
12205
0
                                 "OSR_USE_NON_DEPRECATED "
12206
0
                                 "configuration option to NO.",
12207
0
                                 nCode, pszNewAuth ? pszNewAuth : "(null)",
12208
0
                                 pszNewCode ? pszNewCode : "(null)");
12209
0
                    }
12210
0
                    proj_destroy(obj);
12211
0
                    obj = nonDeprecated;
12212
0
                }
12213
0
            }
12214
0
        }
12215
0
        proj_list_destroy(list);
12216
0
    }
12217
12218
0
    if (bAddTOWGS84)
12219
0
    {
12220
0
        auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12221
0
                                                           obj, nullptr);
12222
0
        if (boundCRS)
12223
0
        {
12224
0
            proj_destroy(obj);
12225
0
            obj = boundCRS;
12226
0
        }
12227
0
    }
12228
12229
0
    d->setPjCRS(obj);
12230
12231
0
    if (tlsCache)
12232
0
    {
12233
0
        tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12234
0
                                     obj);
12235
0
    }
12236
12237
0
    return OGRERR_NONE;
12238
0
}
12239
12240
/************************************************************************/
12241
/*                         AddGuessedTOWGS84()                          */
12242
/************************************************************************/
12243
12244
/**
12245
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12246
 * to WGS84.
12247
 *
12248
 * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12249
 * to WGS84 when there is one and only one such method available for the CRS.
12250
 * Note: this is more restrictive to how GDAL < 3 worked.
12251
 *
12252
 * This method is the same as the C function OSRAddGuessedTOWGS84().
12253
 *
12254
 * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12255
 * already a transformation to WGS84 or none matching could be found).
12256
 *
12257
 * @since GDAL 3.0.3
12258
 */
12259
OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12260
0
{
12261
0
    TAKE_OPTIONAL_LOCK();
12262
12263
0
    d->refreshProjObj();
12264
0
    if (!d->m_pj_crs)
12265
0
        return OGRERR_FAILURE;
12266
0
    auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12267
0
        d->getPROJContext(), d->m_pj_crs, false, true);
12268
0
    if (!boundCRS)
12269
0
    {
12270
0
        return OGRERR_FAILURE;
12271
0
    }
12272
0
    d->setPjCRS(boundCRS);
12273
0
    return OGRERR_NONE;
12274
0
}
12275
12276
/************************************************************************/
12277
/*                         OSRImportFromEPSGA()                         */
12278
/************************************************************************/
12279
12280
/**
12281
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12282
 * to WGS84.
12283
 *
12284
 * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12285
 *
12286
 * @since GDAL 3.0.3
12287
 */
12288
12289
OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12290
12291
0
{
12292
0
    VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12293
12294
0
    return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12295
0
}
12296
12297
/************************************************************************/
12298
/*                         OSRImportFromEPSGA()                         */
12299
/************************************************************************/
12300
12301
/**
12302
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12303
 * code.
12304
 *
12305
 * This function is the same as OGRSpatialReference::importFromEPSGA().
12306
 */
12307
12308
OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12309
12310
0
{
12311
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12312
12313
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12314
0
}
12315
12316
/************************************************************************/
12317
/*                           importFromEPSG()                           */
12318
/************************************************************************/
12319
12320
/**
12321
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12322
 * code.
12323
 *
12324
 * This method will initialize the spatial reference based on the
12325
 * passed in EPSG CRS code found in the PROJ database.
12326
 *
12327
 * This method is the same as the C function OSRImportFromEPSG().
12328
 *
12329
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12330
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
12331
 * such method available for the CRS. This behavior might not always be
12332
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12333
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12334
 *
12335
 * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12336
 *
12337
 * @return OGRERR_NONE on success, or an error code on failure.
12338
 */
12339
12340
OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12341
12342
0
{
12343
0
    return importFromEPSGA(nCode);
12344
0
}
12345
12346
/************************************************************************/
12347
/*                         OSRImportFromEPSG()                          */
12348
/************************************************************************/
12349
12350
/**
12351
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12352
 * code.
12353
 *
12354
 * This function is the same as OGRSpatialReference::importFromEPSG().
12355
 */
12356
12357
OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12358
12359
0
{
12360
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12361
12362
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12363
0
}
12364
12365
/************************************************************************/
12366
/*                        EPSGTreatsAsLatLong()                         */
12367
/************************************************************************/
12368
12369
/**
12370
 * \brief This method returns TRUE if this geographic coordinate
12371
 * system should be treated as having lat/long coordinate ordering.
12372
 *
12373
 * Currently this returns TRUE for all geographic coordinate systems
12374
 * with axes set defining it as lat, long (prior to GDAL 3.10, it
12375
 * also checked that the CRS had belonged to EPSG authority, but this check
12376
 * has now been removed).
12377
 *
12378
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12379
 * geographic CRS imported with importFromEPSG() would cause this method to
12380
 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12381
 * is now equivalent to importFromEPSGA().
12382
 *
12383
 * FALSE will be returned for all coordinate systems that are not geographic,
12384
 * or whose axes ordering is not latitude, longitude.
12385
 *
12386
 * This method is the same as the C function OSREPSGTreatsAsLatLong().
12387
 *
12388
 * @return TRUE or FALSE.
12389
 */
12390
12391
int OGRSpatialReference::EPSGTreatsAsLatLong() const
12392
12393
0
{
12394
0
    TAKE_OPTIONAL_LOCK();
12395
12396
0
    if (!IsGeographic())
12397
0
        return FALSE;
12398
12399
0
    d->demoteFromBoundCRS();
12400
12401
0
    bool ret = false;
12402
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12403
0
    {
12404
0
        auto horizCRS =
12405
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12406
0
        if (horizCRS)
12407
0
        {
12408
0
            auto cs =
12409
0
                proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12410
0
            if (cs)
12411
0
            {
12412
0
                const char *pszDirection = nullptr;
12413
0
                if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12414
0
                                          nullptr, &pszDirection, nullptr,
12415
0
                                          nullptr, nullptr, nullptr))
12416
0
                {
12417
0
                    if (EQUAL(pszDirection, "north"))
12418
0
                    {
12419
0
                        ret = true;
12420
0
                    }
12421
0
                }
12422
12423
0
                proj_destroy(cs);
12424
0
            }
12425
12426
0
            proj_destroy(horizCRS);
12427
0
        }
12428
0
    }
12429
0
    else
12430
0
    {
12431
0
        auto cs =
12432
0
            proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12433
0
        if (cs)
12434
0
        {
12435
0
            const char *pszDirection = nullptr;
12436
0
            if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12437
0
                                      nullptr, &pszDirection, nullptr, nullptr,
12438
0
                                      nullptr, nullptr))
12439
0
            {
12440
0
                if (EQUAL(pszDirection, "north"))
12441
0
                {
12442
0
                    ret = true;
12443
0
                }
12444
0
            }
12445
12446
0
            proj_destroy(cs);
12447
0
        }
12448
0
    }
12449
0
    d->undoDemoteFromBoundCRS();
12450
12451
0
    return ret;
12452
0
}
12453
12454
/************************************************************************/
12455
/*                       OSREPSGTreatsAsLatLong()                       */
12456
/************************************************************************/
12457
12458
/**
12459
 * \brief This function returns TRUE if this geographic coordinate
12460
 * system should be treated as having lat/long coordinate ordering.
12461
 *
12462
 * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12463
 */
12464
12465
int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12466
12467
0
{
12468
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12469
12470
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12471
0
}
12472
12473
/************************************************************************/
12474
/*                    EPSGTreatsAsNorthingEasting()                     */
12475
/************************************************************************/
12476
12477
/**
12478
 * \brief This method returns TRUE if this projected coordinate
12479
 * system should be treated as having northing/easting coordinate ordering.
12480
 *
12481
 * Currently this returns TRUE for all projected coordinate systems
12482
 * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12483
 * also checked that the CRS had belonged to EPSG authority, but this check
12484
 * has now been removed).
12485
 *
12486
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12487
 * projected CRS with northing, easting axis order imported with
12488
 * importFromEPSG() would cause this method to
12489
 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12490
 * is now equivalent to importFromEPSGA().
12491
 *
12492
 * FALSE will be returned for all coordinate systems that are not projected,
12493
 * or whose axes ordering is not northing, easting.
12494
 *
12495
 * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12496
 *
12497
 * @return TRUE or FALSE.
12498
 *
12499
 */
12500
12501
int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12502
12503
0
{
12504
0
    TAKE_OPTIONAL_LOCK();
12505
12506
0
    if (!IsProjected())
12507
0
        return FALSE;
12508
12509
0
    d->demoteFromBoundCRS();
12510
0
    PJ *projCRS;
12511
0
    const auto ctxt = d->getPROJContext();
12512
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12513
0
    {
12514
0
        projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12515
0
        if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12516
0
        {
12517
0
            d->undoDemoteFromBoundCRS();
12518
0
            proj_destroy(projCRS);
12519
0
            return FALSE;
12520
0
        }
12521
0
    }
12522
0
    else
12523
0
    {
12524
0
        projCRS = proj_clone(ctxt, d->m_pj_crs);
12525
0
    }
12526
12527
0
    bool ret = false;
12528
0
    auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12529
0
    proj_destroy(projCRS);
12530
0
    d->undoDemoteFromBoundCRS();
12531
12532
0
    if (cs)
12533
0
    {
12534
0
        ret = isNorthEastAxisOrder(ctxt, cs);
12535
0
        proj_destroy(cs);
12536
0
    }
12537
12538
0
    return ret;
12539
0
}
12540
12541
/************************************************************************/
12542
/*                   OSREPSGTreatsAsNorthingEasting()                   */
12543
/************************************************************************/
12544
12545
/**
12546
 * \brief This function returns TRUE if this projected coordinate
12547
 * system should be treated as having northing/easting coordinate ordering.
12548
 *
12549
 * This function is the same as
12550
 * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12551
 *
12552
 */
12553
12554
int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12555
12556
0
{
12557
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12558
12559
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12560
0
}
12561
12562
/************************************************************************/
12563
/*                     ImportFromESRIWisconsinWKT()                     */
12564
/*                                                                      */
12565
/*      Search a ESRI State Plane WKT and import it.                    */
12566
/************************************************************************/
12567
12568
// This is only used by the HFA driver and somewhat dubious we really need that
12569
// Coming from an old ESRI merge
12570
12571
OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12572
                                                       double centralMeridian,
12573
                                                       double latOfOrigin,
12574
                                                       const char *unitsName,
12575
                                                       const char *crsName)
12576
0
{
12577
0
    TAKE_OPTIONAL_LOCK();
12578
12579
0
    if (centralMeridian < -93 || centralMeridian > -87)
12580
0
        return OGRERR_FAILURE;
12581
0
    if (latOfOrigin < 40 || latOfOrigin > 47)
12582
0
        return OGRERR_FAILURE;
12583
12584
    // If the CS name is known.
12585
0
    if (!prjName && !unitsName && crsName)
12586
0
    {
12587
0
        const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12588
0
        PJ_OBJ_LIST *list = proj_create_from_name(
12589
0
            d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12590
0
        if (list)
12591
0
        {
12592
0
            if (proj_list_get_count(list) == 1)
12593
0
            {
12594
0
                auto crs = proj_list_get(d->getPROJContext(), list, 0);
12595
0
                if (crs)
12596
0
                {
12597
0
                    Clear();
12598
0
                    d->setPjCRS(crs);
12599
0
                    proj_list_destroy(list);
12600
0
                    return OGRERR_NONE;
12601
0
                }
12602
0
            }
12603
0
            proj_list_destroy(list);
12604
0
        }
12605
0
        return OGRERR_FAILURE;
12606
0
    }
12607
12608
0
    if (prjName == nullptr || unitsName == nullptr)
12609
0
    {
12610
0
        return OGRERR_FAILURE;
12611
0
    }
12612
12613
0
    const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12614
0
    PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12615
0
                                              "NAD_1983_HARN_WISCRS_", &type, 1,
12616
0
                                              true, 0, nullptr);
12617
0
    if (list)
12618
0
    {
12619
0
        const auto listSize = proj_list_get_count(list);
12620
0
        for (int i = 0; i < listSize; i++)
12621
0
        {
12622
0
            auto crs = proj_list_get(d->getPROJContext(), list, i);
12623
0
            if (!crs)
12624
0
            {
12625
0
                continue;
12626
0
            }
12627
12628
0
            auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12629
0
            if (!conv)
12630
0
            {
12631
0
                proj_destroy(crs);
12632
0
                continue;
12633
0
            }
12634
0
            const char *pszMethodCode = nullptr;
12635
0
            proj_coordoperation_get_method_info(
12636
0
                d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12637
0
            const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12638
0
            if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12639
0
                   nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12640
0
                  (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12641
0
                   nMethodCode ==
12642
0
                       EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12643
0
            {
12644
0
                proj_destroy(crs);
12645
0
                proj_destroy(conv);
12646
0
                continue;
12647
0
            }
12648
12649
0
            auto coordSys =
12650
0
                proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12651
0
            if (!coordSys)
12652
0
            {
12653
0
                proj_destroy(crs);
12654
0
                proj_destroy(conv);
12655
0
                continue;
12656
0
            }
12657
12658
0
            double dfConvFactor = 0.0;
12659
0
            proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12660
0
                                  nullptr, nullptr, &dfConvFactor, nullptr,
12661
0
                                  nullptr, nullptr);
12662
0
            proj_destroy(coordSys);
12663
12664
0
            if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12665
0
                (!EQUAL(unitsName, "meters") &&
12666
0
                 std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12667
0
                     1e-10))
12668
0
            {
12669
0
                proj_destroy(crs);
12670
0
                proj_destroy(conv);
12671
0
                continue;
12672
0
            }
12673
12674
0
            int idx_lat = proj_coordoperation_get_param_index(
12675
0
                d->getPROJContext(), conv,
12676
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12677
0
            double valueLat = -1000;
12678
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12679
0
                                          nullptr, nullptr, nullptr, &valueLat,
12680
0
                                          nullptr, nullptr, nullptr, nullptr,
12681
0
                                          nullptr, nullptr);
12682
0
            int idx_lon = proj_coordoperation_get_param_index(
12683
0
                d->getPROJContext(), conv,
12684
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12685
0
            double valueLong = -1000;
12686
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12687
0
                                          nullptr, nullptr, nullptr, &valueLong,
12688
0
                                          nullptr, nullptr, nullptr, nullptr,
12689
0
                                          nullptr, nullptr);
12690
0
            if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12691
0
                std::fabs(latOfOrigin - valueLat) <= 1e-10)
12692
0
            {
12693
0
                Clear();
12694
0
                d->setPjCRS(crs);
12695
0
                proj_list_destroy(list);
12696
0
                proj_destroy(conv);
12697
0
                return OGRERR_NONE;
12698
0
            }
12699
12700
0
            proj_destroy(crs);
12701
0
            proj_destroy(conv);
12702
0
        }
12703
0
        proj_list_destroy(list);
12704
0
    }
12705
12706
0
    return OGRERR_FAILURE;
12707
0
}
12708
12709
/************************************************************************/
12710
/*                       GetAxisMappingStrategy()                       */
12711
/************************************************************************/
12712
12713
/** \brief Return the data axis to CRS axis mapping strategy.
12714
 *
12715
 * <ul>
12716
 * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12717
 *     lat/long order, the data will still be long/lat ordered. Similarly for
12718
 *     a projected CRS with northing/easting order, the data will still be
12719
 *     easting/northing ordered.
12720
 * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12721
 *     the CRS axis.
12722
 * <li>OAMS_CUSTOM means that the data axis are customly defined with
12723
 *     SetDataAxisToSRSAxisMapping()
12724
 * </ul>
12725
 * @return the data axis to CRS axis mapping strategy.
12726
 * @since GDAL 3.0
12727
 */
12728
OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12729
0
{
12730
0
    TAKE_OPTIONAL_LOCK();
12731
12732
0
    return d->m_axisMappingStrategy;
12733
0
}
12734
12735
/************************************************************************/
12736
/*                     OSRGetAxisMappingStrategy()                      */
12737
/************************************************************************/
12738
12739
/** \brief Return the data axis to CRS axis mapping strategy.
12740
 *
12741
 * See OGRSpatialReference::GetAxisMappingStrategy()
12742
 * @since GDAL 3.0
12743
 */
12744
OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12745
0
{
12746
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12747
12748
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12749
0
}
12750
12751
/************************************************************************/
12752
/*                       SetAxisMappingStrategy()                       */
12753
/************************************************************************/
12754
12755
/** \brief Set the data axis to CRS axis mapping strategy.
12756
 *
12757
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12758
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12759
 * later being the default value when the option is not set) to control the
12760
 * value of the data axis to CRS axis mapping strategy when a
12761
 * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12762
 * override this default value.
12763
 *
12764
 * See OGRSpatialReference::GetAxisMappingStrategy()
12765
 * @since GDAL 3.0
12766
 */
12767
void OGRSpatialReference::SetAxisMappingStrategy(
12768
    OSRAxisMappingStrategy strategy)
12769
0
{
12770
0
    TAKE_OPTIONAL_LOCK();
12771
12772
0
    d->m_axisMappingStrategy = strategy;
12773
0
    d->refreshAxisMapping();
12774
0
}
12775
12776
/************************************************************************/
12777
/*                     OSRSetAxisMappingStrategy()                      */
12778
/************************************************************************/
12779
12780
/** \brief Set the data axis to CRS axis mapping strategy.
12781
 *
12782
 * See OGRSpatialReference::SetAxisMappingStrategy()
12783
 * @since GDAL 3.0
12784
 */
12785
void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12786
                               OSRAxisMappingStrategy strategy)
12787
0
{
12788
0
    VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12789
12790
0
    OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12791
0
}
12792
12793
/************************************************************************/
12794
/*                    GetDataAxisToSRSAxisMapping()                     */
12795
/************************************************************************/
12796
12797
/** \brief Return the data axis to SRS axis mapping.
12798
 *
12799
 * The number of elements of the vector will be the number of axis of the CRS.
12800
 * Values start at 1.
12801
 *
12802
 * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12803
 * for the first axis of the CRS.
12804
 *
12805
 * @since GDAL 3.0
12806
 */
12807
const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12808
0
{
12809
0
    TAKE_OPTIONAL_LOCK();
12810
12811
0
    return d->m_axisMapping;
12812
0
}
12813
12814
/************************************************************************/
12815
/*                   OSRGetDataAxisToSRSAxisMapping()                   */
12816
/************************************************************************/
12817
12818
/** \brief Return the data axis to SRS axis mapping.
12819
 *
12820
 * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12821
 *
12822
 * @since GDAL 3.0
12823
 */
12824
const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12825
                                          int *pnCount)
12826
0
{
12827
0
    VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12828
0
    VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12829
12830
0
    const auto &v =
12831
0
        OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12832
0
    *pnCount = static_cast<int>(v.size());
12833
0
    return v.data();
12834
0
}
12835
12836
/************************************************************************/
12837
/*                    SetDataAxisToSRSAxisMapping()                     */
12838
/************************************************************************/
12839
12840
/** \brief Set a custom data axis to CRS axis mapping.
12841
 *
12842
 * The number of elements of the mapping vector should be the number of axis
12843
 * of the CRS (as returned by GetAxesCount()) (although this method does not
12844
 * check that, beyond checking there are at least 2 elements, so that this
12845
 * method and setting the CRS can be done in any order).
12846
 * This is taken into account by OGRCoordinateTransformation to transform the
12847
 * order of coordinates to the order expected by the CRS before
12848
 * transformation, and back to the data order after transformation.
12849
 *
12850
 * The mapping[i] value (one based) represents the data axis number for the i(th)
12851
 * axis of the CRS. A negative value can also be used to ask for a sign
12852
 * reversal during coordinate transformation (to deal with northing vs southing,
12853
 * easting vs westing, heights vs depths).
12854
 *
12855
 * When used with OGRCoordinateTransformation,
12856
 * - the only valid values for mapping[0] (data axis number for the first axis
12857
 *   of the CRS) are 1, 2, -1, -2.
12858
 * - the only valid values for mapping[1] (data axis number for the second axis
12859
 *   of the CRS) are 1, 2, -1, -2.
12860
 *  - the only valid values mapping[2] are 3 or -3.
12861
 * Note: this method does not validate the values of mapping[].
12862
 *
12863
 * mapping=[2,1] typically expresses the inversion of axis between the data
12864
 * axis and the CRS axis for a 2D CRS.
12865
 *
12866
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12867
 *
12868
 * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12869
 *
12870
 * @param mapping The new data axis to CRS axis mapping.
12871
 *
12872
 * @since GDAL 3.0
12873
 * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12874
 */
12875
OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12876
    const std::vector<int> &mapping)
12877
0
{
12878
0
    TAKE_OPTIONAL_LOCK();
12879
12880
0
    if (mapping.size() < 2)
12881
0
        return OGRERR_FAILURE;
12882
0
    d->m_axisMappingStrategy = OAMS_CUSTOM;
12883
0
    d->m_axisMapping = mapping;
12884
0
    return OGRERR_NONE;
12885
0
}
12886
12887
/************************************************************************/
12888
/*                   OSRSetDataAxisToSRSAxisMapping()                   */
12889
/************************************************************************/
12890
12891
/** \brief Set a custom data axis to CRS axis mapping.
12892
 *
12893
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12894
 *
12895
 * This is the same as the C++ method
12896
 * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12897
 *
12898
 * @since GDAL 3.1
12899
 */
12900
OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12901
                                      int nMappingSize, const int *panMapping)
12902
0
{
12903
0
    VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12904
0
    VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12905
0
                      OGRERR_FAILURE);
12906
12907
0
    if (nMappingSize < 0)
12908
0
        return OGRERR_FAILURE;
12909
12910
0
    std::vector<int> mapping(nMappingSize);
12911
0
    if (nMappingSize)
12912
0
        memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12913
0
    return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12914
0
        mapping);
12915
0
}
12916
12917
/************************************************************************/
12918
/*                            GetAreaOfUse()                            */
12919
/************************************************************************/
12920
12921
/** \brief Return the area of use of the CRS.
12922
 *
12923
 * This method is the same as the OSRGetAreaOfUse() function.
12924
 *
12925
 * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12926
 * longitude, expressed in degree. Might be NULL. If the returned value is
12927
 * -1000, the bounding box is unknown.
12928
 * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12929
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12930
 * the bounding box is unknown.
12931
 * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12932
 * longitude, expressed in degree. Might be NULL. If the returned value is
12933
 * -1000, the bounding box is unknown.
12934
 * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12935
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12936
 * the bounding box is unknown.
12937
 * @param ppszAreaName Pointer to a string to receive the name of the area of
12938
 * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12939
 * invalidated by further calls.
12940
 * @return true in case of success
12941
 * @since GDAL 3.0
12942
 */
12943
bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12944
                                       double *pdfSouthLatitudeDeg,
12945
                                       double *pdfEastLongitudeDeg,
12946
                                       double *pdfNorthLatitudeDeg,
12947
                                       const char **ppszAreaName) const
12948
0
{
12949
0
    TAKE_OPTIONAL_LOCK();
12950
12951
0
    d->refreshProjObj();
12952
0
    if (!d->m_pj_crs)
12953
0
    {
12954
0
        return false;
12955
0
    }
12956
0
    d->demoteFromBoundCRS();
12957
0
    const char *pszAreaName = nullptr;
12958
0
    int bSuccess = proj_get_area_of_use(
12959
0
        d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12960
0
        pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12961
0
        &pszAreaName);
12962
0
    d->undoDemoteFromBoundCRS();
12963
0
    d->m_osAreaName = pszAreaName ? pszAreaName : "";
12964
0
    if (ppszAreaName)
12965
0
        *ppszAreaName = d->m_osAreaName.c_str();
12966
0
    return CPL_TO_BOOL(bSuccess);
12967
0
}
12968
12969
/************************************************************************/
12970
/*                            GetAreaOfUse()                            */
12971
/************************************************************************/
12972
12973
/** \brief Return the area of use of the CRS.
12974
 *
12975
 * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12976
 *
12977
 * @since GDAL 3.0
12978
 */
12979
int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12980
                    double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12981
                    double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12982
0
{
12983
0
    VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12984
12985
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12986
0
        pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12987
0
        pdfNorthLatitudeDeg, ppszAreaName);
12988
0
}
12989
12990
/************************************************************************/
12991
/*                   OSRGetCRSInfoListFromDatabase()                    */
12992
/************************************************************************/
12993
12994
/** \brief Enumerate CRS objects from the database.
12995
 *
12996
 * The returned object is an array of OSRCRSInfo* pointers, whose last
12997
 * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12998
 *
12999
 * @param pszAuthName Authority name, used to restrict the search.
13000
 * Or NULL for all authorities.
13001
 * @param params Additional criteria. Must be set to NULL for now.
13002
 * @param pnOutResultCount Output parameter pointing to an integer to receive
13003
 * the size of the result list. Might be NULL
13004
 * @return an array of OSRCRSInfo* pointers to be freed with
13005
 * OSRDestroyCRSInfoList(), or NULL in case of error.
13006
 *
13007
 * @since GDAL 3.0
13008
 */
13009
OSRCRSInfo **
13010
OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
13011
                              CPL_UNUSED const OSRCRSListParameters *params,
13012
                              int *pnOutResultCount)
13013
0
{
13014
0
    int nResultCount = 0;
13015
0
    auto projList = proj_get_crs_info_list_from_database(
13016
0
        OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
13017
0
    if (pnOutResultCount)
13018
0
        *pnOutResultCount = nResultCount;
13019
0
    if (!projList)
13020
0
    {
13021
0
        return nullptr;
13022
0
    }
13023
0
    auto res = new OSRCRSInfo *[nResultCount + 1];
13024
0
    for (int i = 0; i < nResultCount; i++)
13025
0
    {
13026
0
        res[i] = new OSRCRSInfo;
13027
0
        res[i]->pszAuthName = projList[i]->auth_name
13028
0
                                  ? CPLStrdup(projList[i]->auth_name)
13029
0
                                  : nullptr;
13030
0
        res[i]->pszCode =
13031
0
            projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
13032
0
        res[i]->pszName =
13033
0
            projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
13034
0
        res[i]->eType = OSR_CRS_TYPE_OTHER;
13035
0
        switch (projList[i]->type)
13036
0
        {
13037
0
            case PJ_TYPE_GEOGRAPHIC_2D_CRS:
13038
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
13039
0
                break;
13040
0
            case PJ_TYPE_GEOGRAPHIC_3D_CRS:
13041
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
13042
0
                break;
13043
0
            case PJ_TYPE_GEOCENTRIC_CRS:
13044
0
                res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
13045
0
                break;
13046
0
            case PJ_TYPE_PROJECTED_CRS:
13047
0
                res[i]->eType = OSR_CRS_TYPE_PROJECTED;
13048
0
                break;
13049
0
            case PJ_TYPE_VERTICAL_CRS:
13050
0
                res[i]->eType = OSR_CRS_TYPE_VERTICAL;
13051
0
                break;
13052
0
            case PJ_TYPE_COMPOUND_CRS:
13053
0
                res[i]->eType = OSR_CRS_TYPE_COMPOUND;
13054
0
                break;
13055
0
            default:
13056
0
                break;
13057
0
        }
13058
0
        res[i]->bDeprecated = projList[i]->deprecated;
13059
0
        res[i]->bBboxValid = projList[i]->bbox_valid;
13060
0
        res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
13061
0
        res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
13062
0
        res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
13063
0
        res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
13064
0
        res[i]->pszAreaName = projList[i]->area_name
13065
0
                                  ? CPLStrdup(projList[i]->area_name)
13066
0
                                  : nullptr;
13067
0
        res[i]->pszProjectionMethod =
13068
0
            projList[i]->projection_method_name
13069
0
                ? CPLStrdup(projList[i]->projection_method_name)
13070
0
                : nullptr;
13071
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
13072
0
        res[i]->pszCelestialBodyName =
13073
0
            projList[i]->celestial_body_name
13074
0
                ? CPLStrdup(projList[i]->celestial_body_name)
13075
0
                : nullptr;
13076
#else
13077
        res[i]->pszCelestialBodyName =
13078
            res[i]->pszAuthName && EQUAL(res[i]->pszAuthName, "EPSG")
13079
                ? CPLStrdup("Earth")
13080
                : nullptr;
13081
#endif
13082
0
    }
13083
0
    res[nResultCount] = nullptr;
13084
0
    proj_crs_info_list_destroy(projList);
13085
0
    return res;
13086
0
}
13087
13088
/************************************************************************/
13089
/*                       OSRDestroyCRSInfoList()                        */
13090
/************************************************************************/
13091
13092
/** \brief Destroy the result returned by
13093
 * OSRGetCRSInfoListFromDatabase().
13094
 *
13095
 * @since GDAL 3.0
13096
 */
13097
void OSRDestroyCRSInfoList(OSRCRSInfo **list)
13098
0
{
13099
0
    if (list)
13100
0
    {
13101
0
        for (int i = 0; list[i] != nullptr; i++)
13102
0
        {
13103
0
            CPLFree(list[i]->pszAuthName);
13104
0
            CPLFree(list[i]->pszCode);
13105
0
            CPLFree(list[i]->pszName);
13106
0
            CPLFree(list[i]->pszAreaName);
13107
0
            CPLFree(list[i]->pszProjectionMethod);
13108
0
            CPLFree(list[i]->pszCelestialBodyName);
13109
0
            delete list[i];
13110
0
        }
13111
0
        delete[] list;
13112
0
    }
13113
0
}
13114
13115
/************************************************************************/
13116
/*                  OSRGetAuthorityListFromDatabase()                   */
13117
/************************************************************************/
13118
13119
/** \brief Return the list of CRS authorities used in the PROJ database.
13120
 *
13121
 * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
13122
 *
13123
 * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
13124
 *
13125
 * @return nullptr in case of error, or a NULL terminated list of strings to
13126
 * free with CSLDestroy()
13127
 * @since GDAL 3.10
13128
 */
13129
char **OSRGetAuthorityListFromDatabase()
13130
0
{
13131
0
    PROJ_STRING_LIST list =
13132
0
        proj_get_authorities_from_database(OSRGetProjTLSContext());
13133
0
    if (!list)
13134
0
    {
13135
0
        return nullptr;
13136
0
    }
13137
0
    int count = 0;
13138
0
    while (list[count])
13139
0
        ++count;
13140
0
    char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
13141
0
    for (int i = 0; i < count; ++i)
13142
0
        res[i] = CPLStrdup(list[i]);
13143
0
    proj_string_list_destroy(list);
13144
0
    return res;
13145
0
}
13146
13147
/************************************************************************/
13148
/*                 UpdateCoordinateSystemFromGeogCRS()                  */
13149
/************************************************************************/
13150
13151
/*! @cond Doxygen_Suppress */
13152
/** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
13153
 *
13154
 * @since GDAL 3.1
13155
 */
13156
void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
13157
0
{
13158
0
    TAKE_OPTIONAL_LOCK();
13159
13160
0
    d->refreshProjObj();
13161
0
    if (!d->m_pj_crs)
13162
0
        return;
13163
0
    if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
13164
0
        return;
13165
0
    if (GetAxesCount() == 3)
13166
0
        return;
13167
0
    auto ctxt = d->getPROJContext();
13168
0
    auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
13169
0
    if (!baseCRS)
13170
0
        return;
13171
0
    auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
13172
0
    if (!baseCRSCS)
13173
0
    {
13174
0
        proj_destroy(baseCRS);
13175
0
        return;
13176
0
    }
13177
0
    if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
13178
0
    {
13179
0
        proj_destroy(baseCRSCS);
13180
0
        proj_destroy(baseCRS);
13181
0
        return;
13182
0
    }
13183
0
    auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
13184
0
    if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
13185
0
    {
13186
0
        proj_destroy(baseCRSCS);
13187
0
        proj_destroy(baseCRS);
13188
0
        proj_destroy(projCS);
13189
0
        return;
13190
0
    }
13191
13192
0
    PJ_AXIS_DESCRIPTION axis[3];
13193
0
    for (int i = 0; i < 3; i++)
13194
0
    {
13195
0
        const char *name = nullptr;
13196
0
        const char *abbreviation = nullptr;
13197
0
        const char *direction = nullptr;
13198
0
        double unit_conv_factor = 0;
13199
0
        const char *unit_name = nullptr;
13200
0
        proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13201
0
                              &abbreviation, &direction, &unit_conv_factor,
13202
0
                              &unit_name, nullptr, nullptr);
13203
0
        axis[i].name = CPLStrdup(name);
13204
0
        axis[i].abbreviation = CPLStrdup(abbreviation);
13205
0
        axis[i].direction = CPLStrdup(direction);
13206
0
        axis[i].unit_name = CPLStrdup(unit_name);
13207
0
        axis[i].unit_conv_factor = unit_conv_factor;
13208
0
        axis[i].unit_type = PJ_UT_LINEAR;
13209
0
    }
13210
0
    proj_destroy(baseCRSCS);
13211
0
    proj_destroy(projCS);
13212
0
    auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13213
0
    for (int i = 0; i < 3; i++)
13214
0
    {
13215
0
        CPLFree(axis[i].name);
13216
0
        CPLFree(axis[i].abbreviation);
13217
0
        CPLFree(axis[i].direction);
13218
0
        CPLFree(axis[i].unit_name);
13219
0
    }
13220
0
    if (!cs)
13221
0
    {
13222
0
        proj_destroy(baseCRS);
13223
0
        return;
13224
0
    }
13225
0
    auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13226
0
    auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13227
0
                                         conversion, cs);
13228
0
    proj_destroy(baseCRS);
13229
0
    proj_destroy(conversion);
13230
0
    proj_destroy(cs);
13231
0
    d->setPjCRS(crs);
13232
0
}
13233
13234
/*! @endcond */
13235
13236
/************************************************************************/
13237
/*                            PromoteTo3D()                             */
13238
/************************************************************************/
13239
13240
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13241
 *
13242
 * The new axis will be ellipsoidal height, oriented upwards, and with metre
13243
 * units.
13244
 *
13245
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13246
 * be used.
13247
 * @return OGRERR_NONE if no error occurred.
13248
 * @since GDAL 3.1 and PROJ 6.3
13249
 */
13250
OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13251
0
{
13252
0
    TAKE_OPTIONAL_LOCK();
13253
13254
0
    d->refreshProjObj();
13255
0
    if (!d->m_pj_crs)
13256
0
        return OGRERR_FAILURE;
13257
0
    auto newPj =
13258
0
        proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13259
0
    if (!newPj)
13260
0
        return OGRERR_FAILURE;
13261
0
    d->setPjCRS(newPj);
13262
0
    return OGRERR_NONE;
13263
0
}
13264
13265
/************************************************************************/
13266
/*                           OSRPromoteTo3D()                           */
13267
/************************************************************************/
13268
13269
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13270
 *
13271
 * See OGRSpatialReference::PromoteTo3D()
13272
 *
13273
 * @since GDAL 3.1 and PROJ 6.3
13274
 */
13275
OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13276
0
{
13277
0
    VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13278
13279
0
    return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13280
0
}
13281
13282
/************************************************************************/
13283
/*                             DemoteTo2D()                             */
13284
/************************************************************************/
13285
13286
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13287
 *
13288
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13289
 * be used.
13290
 * @return OGRERR_NONE if no error occurred.
13291
 * @since GDAL 3.2 and PROJ 6.3
13292
 */
13293
OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13294
0
{
13295
0
    TAKE_OPTIONAL_LOCK();
13296
13297
0
    d->refreshProjObj();
13298
0
    if (!d->m_pj_crs)
13299
0
        return OGRERR_FAILURE;
13300
0
    auto newPj =
13301
0
        proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13302
0
    if (!newPj)
13303
0
        return OGRERR_FAILURE;
13304
0
    d->setPjCRS(newPj);
13305
0
    return OGRERR_NONE;
13306
0
}
13307
13308
/************************************************************************/
13309
/*                           OSRDemoteTo2D()                            */
13310
/************************************************************************/
13311
13312
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13313
 *
13314
 * See OGRSpatialReference::DemoteTo2D()
13315
 *
13316
 * @since GDAL 3.2 and PROJ 6.3
13317
 */
13318
OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13319
0
{
13320
0
    VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13321
13322
0
    return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13323
0
}
13324
13325
/************************************************************************/
13326
/*                           GetEPSGGeogCS()                            */
13327
/************************************************************************/
13328
13329
/** Try to establish what the EPSG code for this coordinate systems
13330
 * GEOGCS might be.  Returns -1 if no reasonable guess can be made.
13331
 *
13332
 * @return EPSG code
13333
 */
13334
13335
int OGRSpatialReference::GetEPSGGeogCS() const
13336
13337
0
{
13338
0
    TAKE_OPTIONAL_LOCK();
13339
13340
    /* -------------------------------------------------------------------- */
13341
    /*      Check axis order.                                               */
13342
    /* -------------------------------------------------------------------- */
13343
0
    auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13344
0
    if (!poGeogCRS)
13345
0
        return -1;
13346
13347
0
    bool ret = false;
13348
0
    poGeogCRS->d->demoteFromBoundCRS();
13349
0
    auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13350
0
                                             poGeogCRS->d->m_pj_crs);
13351
0
    poGeogCRS->d->undoDemoteFromBoundCRS();
13352
0
    if (cs)
13353
0
    {
13354
0
        const char *pszDirection = nullptr;
13355
0
        if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13356
0
                                  &pszDirection, nullptr, nullptr, nullptr,
13357
0
                                  nullptr))
13358
0
        {
13359
0
            if (EQUAL(pszDirection, "north"))
13360
0
            {
13361
0
                ret = true;
13362
0
            }
13363
0
        }
13364
13365
0
        proj_destroy(cs);
13366
0
    }
13367
0
    if (!ret)
13368
0
        return -1;
13369
13370
    /* -------------------------------------------------------------------- */
13371
    /*      Do we already have it?                                          */
13372
    /* -------------------------------------------------------------------- */
13373
0
    const char *pszAuthName = GetAuthorityName("GEOGCS");
13374
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13375
0
        return atoi(GetAuthorityCode("GEOGCS"));
13376
13377
    /* -------------------------------------------------------------------- */
13378
    /*      Get the datum and geogcs names.                                 */
13379
    /* -------------------------------------------------------------------- */
13380
13381
0
    const char *pszGEOGCS = GetAttrValue("GEOGCS");
13382
0
    const char *pszDatum = GetAttrValue("DATUM");
13383
13384
    // We can only operate on coordinate systems with a geogcs.
13385
0
    OGRSpatialReference oSRSTmp;
13386
0
    if (pszGEOGCS == nullptr || pszDatum == nullptr)
13387
0
    {
13388
        // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13389
        // export to WKT1, so try to extract the geographic CRS through PROJ
13390
        // API with CopyGeogCSFrom() and get the nodes' values from it.
13391
0
        oSRSTmp.CopyGeogCSFrom(this);
13392
0
        pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13393
0
        pszDatum = oSRSTmp.GetAttrValue("DATUM");
13394
0
        if (pszGEOGCS == nullptr || pszDatum == nullptr)
13395
0
        {
13396
0
            return -1;
13397
0
        }
13398
0
    }
13399
13400
    // Lookup geographic CRS name
13401
0
    const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13402
0
    PJ_OBJ_LIST *list = proj_create_from_name(
13403
0
        d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13404
0
    if (list)
13405
0
    {
13406
0
        const auto listSize = proj_list_get_count(list);
13407
0
        if (listSize == 1)
13408
0
        {
13409
0
            auto crs = proj_list_get(d->getPROJContext(), list, 0);
13410
0
            if (crs)
13411
0
            {
13412
0
                pszAuthName = proj_get_id_auth_name(crs, 0);
13413
0
                const char *pszCode = proj_get_id_code(crs, 0);
13414
0
                if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13415
0
                {
13416
0
                    const int nCode = atoi(pszCode);
13417
0
                    proj_destroy(crs);
13418
0
                    proj_list_destroy(list);
13419
0
                    return nCode;
13420
0
                }
13421
0
                proj_destroy(crs);
13422
0
            }
13423
0
        }
13424
0
        proj_list_destroy(list);
13425
0
    }
13426
13427
    /* -------------------------------------------------------------------- */
13428
    /*      Is this a "well known" geographic coordinate system?            */
13429
    /* -------------------------------------------------------------------- */
13430
0
    const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13431
0
                      strstr(pszDatum, "WGS") ||
13432
0
                      strstr(pszGEOGCS, "World Geodetic System") ||
13433
0
                      strstr(pszGEOGCS, "World_Geodetic_System") ||
13434
0
                      strstr(pszDatum, "World Geodetic System") ||
13435
0
                      strstr(pszDatum, "World_Geodetic_System");
13436
13437
0
    const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13438
0
                      strstr(pszDatum, "NAD") ||
13439
0
                      strstr(pszGEOGCS, "North American") ||
13440
0
                      strstr(pszGEOGCS, "North_American") ||
13441
0
                      strstr(pszDatum, "North American") ||
13442
0
                      strstr(pszDatum, "North_American");
13443
13444
0
    if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13445
0
        return 4326;
13446
13447
0
    if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13448
0
        return 4322;
13449
13450
    // This is questionable as there are several 'flavors' of NAD83 that
13451
    // are not the same as 4269
13452
0
    if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13453
0
        return 4269;
13454
13455
0
    if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13456
0
        return 4267;
13457
13458
    /* -------------------------------------------------------------------- */
13459
    /*      If we know the datum, associate the most likely GCS with        */
13460
    /*      it.                                                             */
13461
    /* -------------------------------------------------------------------- */
13462
0
    const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13463
0
    pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13464
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13465
0
        GetPrimeMeridian() == 0.0)
13466
0
    {
13467
0
        const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13468
13469
0
        if (nDatum >= 6000 && nDatum <= 6999)
13470
0
            return nDatum - 2000;
13471
0
    }
13472
13473
0
    return -1;
13474
0
}
13475
13476
/************************************************************************/
13477
/*                         SetCoordinateEpoch()                         */
13478
/************************************************************************/
13479
13480
/** Set the coordinate epoch, as decimal year.
13481
 *
13482
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13483
 * change with time. To be unambiguous the coordinates must always be qualified
13484
 * with the epoch at which they are valid. The coordinate epoch is not
13485
 * necessarily the epoch at which the observation was collected.
13486
 *
13487
 * Pedantically the coordinate epoch of an observation belongs to the
13488
 * observation, and not to the CRS, however it is often more practical to
13489
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13490
 * CRS (see IsDynamic())
13491
 *
13492
 * This method is the same as the OSRSetCoordinateEpoch() function.
13493
 *
13494
 * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13495
 * @since OGR 3.4
13496
 */
13497
13498
void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13499
0
{
13500
0
    d->m_coordinateEpoch = dfCoordinateEpoch;
13501
0
}
13502
13503
/************************************************************************/
13504
/*                       OSRSetCoordinateEpoch()                        */
13505
/************************************************************************/
13506
13507
/** \brief Set the coordinate epoch, as decimal year.
13508
 *
13509
 * See OGRSpatialReference::SetCoordinateEpoch()
13510
 *
13511
 * @since OGR 3.4
13512
 */
13513
void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13514
0
{
13515
0
    VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13516
13517
0
    return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13518
0
        dfCoordinateEpoch);
13519
0
}
13520
13521
/************************************************************************/
13522
/*                         GetCoordinateEpoch()                         */
13523
/************************************************************************/
13524
13525
/** Return the coordinate epoch, as decimal year.
13526
 *
13527
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13528
 * change with time. To be unambiguous the coordinates must always be qualified
13529
 * with the epoch at which they are valid. The coordinate epoch is not
13530
 * necessarily the epoch at which the observation was collected.
13531
 *
13532
 * Pedantically the coordinate epoch of an observation belongs to the
13533
 * observation, and not to the CRS, however it is often more practical to
13534
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13535
 * CRS (see IsDynamic())
13536
 *
13537
 * This method is the same as the OSRGetCoordinateEpoch() function.
13538
 *
13539
 * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13540
 *                         if not set, or relevant.
13541
 * @since OGR 3.4
13542
 */
13543
13544
double OGRSpatialReference::GetCoordinateEpoch() const
13545
0
{
13546
0
    return d->m_coordinateEpoch;
13547
0
}
13548
13549
/************************************************************************/
13550
/*                       OSRGetCoordinateEpoch()                        */
13551
/************************************************************************/
13552
13553
/** \brief Get the coordinate epoch, as decimal year.
13554
 *
13555
 * See OGRSpatialReference::GetCoordinateEpoch()
13556
 *
13557
 * @since OGR 3.4
13558
 */
13559
double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13560
0
{
13561
0
    VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13562
13563
0
    return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13564
0
}