LCOV - code coverage report
Current view: top level - ogr - ogrspatialreference.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3172 3927 80.8 %
Date: 2024-05-04 12:52:34 Functions: 292 366 79.8 %

          Line data    Source code
       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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "ogr_spatialref.h"
      32             : 
      33             : #include <cmath>
      34             : #include <cstddef>
      35             : #include <cstdio>
      36             : #include <cstdlib>
      37             : #include <cstring>
      38             : #include <limits>
      39             : #include <string>
      40             : #include <mutex>
      41             : #include <set>
      42             : #include <vector>
      43             : 
      44             : #include "cpl_atomic_ops.h"
      45             : #include "cpl_conv.h"
      46             : #include "cpl_csv.h"
      47             : #include "cpl_error.h"
      48             : #include "cpl_error_internal.h"
      49             : #include "cpl_http.h"
      50             : #include "cpl_json.h"
      51             : #include "cpl_multiproc.h"
      52             : #include "cpl_string.h"
      53             : #include "cpl_vsi.h"
      54             : #include "ogr_core.h"
      55             : #include "ogr_p.h"
      56             : #include "ogr_proj_p.h"
      57             : #include "ogr_srs_api.h"
      58             : 
      59             : #include "proj.h"
      60             : #include "proj_experimental.h"
      61             : #include "proj_constants.h"
      62             : 
      63             : // Exists since 8.0.1
      64             : #ifndef PROJ_AT_LEAST_VERSION
      65             : #define PROJ_COMPUTE_VERSION(maj, min, patch)                                  \
      66             :     ((maj)*10000 + (min)*100 + (patch))
      67             : #define PROJ_VERSION_NUMBER                                                    \
      68             :     PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR,               \
      69             :                          PROJ_VERSION_PATCH)
      70             : #define PROJ_AT_LEAST_VERSION(maj, min, patch)                                 \
      71             :     (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
      72             : #endif
      73             : 
      74             : #define STRINGIFY(s) #s
      75             : #define XSTRINGIFY(s) STRINGIFY(s)
      76             : 
      77             : struct OGRSpatialReference::Private
      78             : {
      79             :     struct Listener : public OGR_SRSNode::Listener
      80             :     {
      81             :         OGRSpatialReference::Private *m_poObj = nullptr;
      82             : 
      83      192776 :         explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
      84             :         {
      85      192773 :         }
      86             : 
      87             :         Listener(const Listener &) = delete;
      88             :         Listener &operator=(const Listener &) = delete;
      89             : 
      90     1645250 :         void notifyChange(OGR_SRSNode *) override
      91             :         {
      92     1645250 :             m_poObj->nodesChanged();
      93     1645250 :         }
      94             :     };
      95             : 
      96             :     OGRSpatialReference *m_poSelf = nullptr;
      97             :     PJ *m_pj_crs = nullptr;
      98             : 
      99             :     // Temporary state used for object construction
     100             :     PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
     101             :     CPLString m_osPrimeMeridianName{};
     102             :     CPLString m_osAngularUnits{};
     103             :     CPLString m_osLinearUnits{};
     104             :     CPLString m_osAxisName[3]{};
     105             : 
     106             :     std::vector<std::string> m_wktImportWarnings{};
     107             :     std::vector<std::string> m_wktImportErrors{};
     108             :     CPLString m_osAreaName{};
     109             : 
     110             :     bool m_bNodesChanged = false;
     111             :     bool m_bNodesWKT2 = false;
     112             :     OGR_SRSNode *m_poRoot = nullptr;
     113             : 
     114             :     double dfFromGreenwich = 0.0;
     115             :     double dfToMeter = 0.0;
     116             :     double dfToDegrees = 0.0;
     117             :     double m_dfAngularUnitToRadian = 0.0;
     118             : 
     119             :     int nRefCount = 1;
     120             :     int bNormInfoSet = FALSE;
     121             : 
     122             :     PJ *m_pj_geod_base_crs_temp = nullptr;
     123             :     PJ *m_pj_proj_crs_cs_temp = nullptr;
     124             : 
     125             :     bool m_pj_crs_modified_during_demote = false;
     126             :     PJ *m_pj_bound_crs_target = nullptr;
     127             :     PJ *m_pj_bound_crs_co = nullptr;
     128             :     PJ *m_pj_crs_backup = nullptr;
     129             :     OGR_SRSNode *m_poRootBackup = nullptr;
     130             : 
     131             :     bool m_bMorphToESRI = false;
     132             :     bool m_bHasCenterLong = false;
     133             : 
     134             :     std::shared_ptr<Listener> m_poListener{};
     135             : 
     136             :     std::mutex m_mutex{};
     137             : 
     138             :     OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
     139             :     std::vector<int> m_axisMapping{1, 2, 3};
     140             : 
     141             :     double m_coordinateEpoch = 0;  // as decimal year
     142             : 
     143             :     explicit Private(OGRSpatialReference *poSelf);
     144             :     ~Private();
     145             :     Private(const Private &) = delete;
     146             :     Private &operator=(const Private &) = delete;
     147             : 
     148             :     void clear();
     149             :     void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
     150             :     void setRoot(OGR_SRSNode *poRoot);
     151             :     void refreshProjObj();
     152             :     void nodesChanged();
     153             :     void refreshRootFromProjObj();
     154             :     void invalidateNodes();
     155             : 
     156             :     void setMorphToESRI(bool b);
     157             : 
     158             :     PJ *getGeodBaseCRS();
     159             :     PJ *getProjCRSCoordSys();
     160             : 
     161             :     const char *getProjCRSName();
     162             :     OGRErr replaceConversionAndUnref(PJ *conv);
     163             : 
     164             :     void demoteFromBoundCRS();
     165             :     void undoDemoteFromBoundCRS();
     166             : 
     167      972868 :     PJ_CONTEXT *getPROJContext()
     168             :     {
     169      972868 :         return OSRGetProjTLSContext();
     170             :     }
     171             : 
     172             :     const char *nullifyTargetKeyIfPossible(const char *pszTargetKey);
     173             : 
     174             :     void refreshAxisMapping();
     175             : };
     176             : 
     177      192781 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
     178             : {
     179             :     const char *pszDefaultAMS =
     180      192781 :         CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
     181      192793 :     if (pszDefaultAMS)
     182             :     {
     183           1 :         if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
     184           0 :             return OAMS_AUTHORITY_COMPLIANT;
     185           1 :         else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
     186           1 :             return OAMS_TRADITIONAL_GIS_ORDER;
     187             :         else
     188             :         {
     189           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     190             :                      "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
     191             :                      pszDefaultAMS);
     192             :         }
     193             :     }
     194      192792 :     return OAMS_AUTHORITY_COMPLIANT;
     195             : }
     196             : 
     197      192784 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
     198             :     : m_poSelf(poSelf),
     199      192784 :       m_poListener(std::shared_ptr<Listener>(new Listener(this)))
     200             : {
     201             :     // Get the default value for m_axisMappingStrategy from the
     202             :     // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
     203      192746 :     m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
     204      192794 : }
     205             : 
     206      767855 : OGRSpatialReference::Private::~Private()
     207             : {
     208             :     // In case we destroy the object not in the thread that created it,
     209             :     // we need to reassign the PROJ context. Having the context bundled inside
     210             :     // PJ* deeply sucks...
     211      191972 :     auto ctxt = getPROJContext();
     212             : 
     213      191972 :     proj_assign_context(m_pj_crs, ctxt);
     214      191972 :     proj_destroy(m_pj_crs);
     215             : 
     216      191971 :     proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
     217      191971 :     proj_destroy(m_pj_geod_base_crs_temp);
     218             : 
     219      191971 :     proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
     220      191971 :     proj_destroy(m_pj_proj_crs_cs_temp);
     221             : 
     222      191971 :     proj_assign_context(m_pj_bound_crs_target, ctxt);
     223      191971 :     proj_destroy(m_pj_bound_crs_target);
     224             : 
     225      191971 :     proj_assign_context(m_pj_bound_crs_co, ctxt);
     226      191971 :     proj_destroy(m_pj_bound_crs_co);
     227             : 
     228      191970 :     proj_assign_context(m_pj_crs_backup, ctxt);
     229      191970 :     proj_destroy(m_pj_crs_backup);
     230             : 
     231      191970 :     delete m_poRootBackup;
     232      191970 :     delete m_poRoot;
     233      191963 : }
     234             : 
     235       96188 : void OGRSpatialReference::Private::clear()
     236             : {
     237       96188 :     proj_assign_context(m_pj_crs, getPROJContext());
     238       96188 :     proj_destroy(m_pj_crs);
     239       96188 :     m_pj_crs = nullptr;
     240             : 
     241       96188 :     delete m_poRoot;
     242       96188 :     m_poRoot = nullptr;
     243       96188 :     m_bNodesChanged = false;
     244             : 
     245       96188 :     m_wktImportWarnings.clear();
     246       96188 :     m_wktImportErrors.clear();
     247             : 
     248       96188 :     m_pj_crs_modified_during_demote = false;
     249       96188 :     m_pjType = PJ_TYPE_UNKNOWN;
     250       96188 :     m_osPrimeMeridianName.clear();
     251       96188 :     m_osAngularUnits.clear();
     252       96188 :     m_osLinearUnits.clear();
     253             : 
     254       96188 :     bNormInfoSet = FALSE;
     255       96188 :     dfFromGreenwich = 1.0;
     256       96188 :     dfToMeter = 1.0;
     257       96188 :     dfToDegrees = 1.0;
     258       96188 :     m_dfAngularUnitToRadian = 0.0;
     259             : 
     260       96188 :     m_bMorphToESRI = false;
     261       96188 :     m_bHasCenterLong = false;
     262             : 
     263       96188 :     m_coordinateEpoch = 0.0;
     264       96188 : }
     265             : 
     266       21020 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
     267             : {
     268       21020 :     m_poRoot = poRoot;
     269       21020 :     if (m_poRoot)
     270             :     {
     271       21020 :         m_poRoot->RegisterListener(m_poListener);
     272             :     }
     273       21020 :     nodesChanged();
     274       21020 : }
     275             : 
     276      158597 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
     277             :                                             bool doRefreshAxisMapping)
     278             : {
     279      158597 :     auto ctxt = getPROJContext();
     280             : 
     281             : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
     282             :     if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
     283             :     {
     284             :         const double dfEpoch =
     285             :             proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
     286             :         if (!std::isnan(dfEpoch))
     287             :         {
     288             :             m_poSelf->SetCoordinateEpoch(dfEpoch);
     289             :         }
     290             :         auto crs = proj_get_source_crs(ctxt, pj_crsIn);
     291             :         proj_destroy(pj_crsIn);
     292             :         pj_crsIn = crs;
     293             :     }
     294             : #endif
     295             : 
     296      158597 :     proj_assign_context(m_pj_crs, ctxt);
     297      158597 :     proj_destroy(m_pj_crs);
     298      158597 :     m_pj_crs = pj_crsIn;
     299      158597 :     if (m_pj_crs)
     300             :     {
     301      158545 :         m_pjType = proj_get_type(m_pj_crs);
     302             :     }
     303      158597 :     if (m_pj_crs_backup)
     304             :     {
     305          63 :         m_pj_crs_modified_during_demote = true;
     306             :     }
     307      158597 :     invalidateNodes();
     308      158597 :     if (doRefreshAxisMapping)
     309             :     {
     310      158535 :         refreshAxisMapping();
     311             :     }
     312      158597 : }
     313             : 
     314      534306 : void OGRSpatialReference::Private::refreshProjObj()
     315             : {
     316      534306 :     if (m_bNodesChanged && m_poRoot)
     317             :     {
     318        6613 :         char *pszWKT = nullptr;
     319        6613 :         m_poRoot->exportToWkt(&pszWKT);
     320        6613 :         auto poRootBackup = m_poRoot;
     321        6613 :         m_poRoot = nullptr;
     322        6613 :         const double dfCoordinateEpochBackup = m_coordinateEpoch;
     323        6613 :         clear();
     324        6613 :         m_coordinateEpoch = dfCoordinateEpochBackup;
     325        6613 :         m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
     326             : 
     327        6613 :         const char *const options[] = {
     328             :             "STRICT=NO",
     329             : #if PROJ_AT_LEAST_VERSION(9, 1, 0)
     330             :             "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
     331             : #endif
     332             :             nullptr
     333             :         };
     334        6613 :         PROJ_STRING_LIST warnings = nullptr;
     335        6613 :         PROJ_STRING_LIST errors = nullptr;
     336        6613 :         setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
     337             :                                       &warnings, &errors));
     338       12929 :         for (auto iter = warnings; iter && *iter; ++iter)
     339             :         {
     340        6316 :             m_wktImportWarnings.push_back(*iter);
     341             :         }
     342        6792 :         for (auto iter = errors; iter && *iter; ++iter)
     343             :         {
     344         179 :             m_wktImportErrors.push_back(*iter);
     345             :         }
     346        6613 :         proj_string_list_destroy(warnings);
     347        6613 :         proj_string_list_destroy(errors);
     348             : 
     349        6613 :         CPLFree(pszWKT);
     350             : 
     351        6613 :         m_poRoot = poRootBackup;
     352        6613 :         m_bNodesChanged = false;
     353             :     }
     354      534306 : }
     355             : 
     356       23110 : void OGRSpatialReference::Private::refreshRootFromProjObj()
     357             : {
     358       23110 :     CPLAssert(m_poRoot == nullptr);
     359             : 
     360       23110 :     if (m_pj_crs)
     361             :     {
     362       41958 :         CPLStringList aosOptions;
     363       20979 :         if (!m_bMorphToESRI)
     364             :         {
     365       20975 :             aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
     366       20975 :             aosOptions.SetNameValue("MULTILINE", "NO");
     367             :         }
     368       20979 :         aosOptions.SetNameValue("STRICT", "NO");
     369             : 
     370             :         const char *pszWKT;
     371             :         {
     372       20979 :             CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
     373       20979 :             pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
     374       20979 :                                  m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
     375       20979 :                                  aosOptions.List());
     376       20979 :             m_bNodesWKT2 = false;
     377             :         }
     378       20979 :         if (!m_bMorphToESRI && pszWKT == nullptr)
     379             :         {
     380          50 :             pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
     381          50 :                                  aosOptions.List());
     382          50 :             m_bNodesWKT2 = true;
     383             :         }
     384       20979 :         if (pszWKT)
     385             :         {
     386       20979 :             auto root = new OGR_SRSNode();
     387       20979 :             setRoot(root);
     388       20979 :             root->importFromWkt(&pszWKT);
     389       20979 :             m_bNodesChanged = false;
     390             :         }
     391             :     }
     392       23110 : }
     393             : 
     394      191313 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
     395             : {
     396      191313 :     const char *pszName1 = nullptr;
     397      191313 :     const char *pszDirection1 = nullptr;
     398      191313 :     proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
     399             :                           nullptr, nullptr, nullptr, nullptr);
     400      191313 :     const char *pszName2 = nullptr;
     401      191313 :     const char *pszDirection2 = nullptr;
     402      191313 :     proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
     403             :                           nullptr, nullptr, nullptr, nullptr);
     404      191313 :     if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
     405       95100 :         EQUAL(pszDirection2, "east"))
     406             :     {
     407       94558 :         return true;
     408             :     }
     409       96755 :     if (pszDirection1 && pszDirection2 &&
     410       96755 :         ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
     411       96230 :          (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
     412        1037 :         pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
     413         230 :         STARTS_WITH_CI(pszName2, "easting"))
     414             :     {
     415         230 :         return true;
     416             :     }
     417       96525 :     return false;
     418             : }
     419             : 
     420      237868 : void OGRSpatialReference::Private::refreshAxisMapping()
     421             : {
     422      237868 :     if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
     423       46867 :         return;
     424             : 
     425      191001 :     bool doUndoDemote = false;
     426      191001 :     if (m_pj_crs_backup == nullptr)
     427             :     {
     428      190938 :         doUndoDemote = true;
     429      190938 :         demoteFromBoundCRS();
     430             :     }
     431      191001 :     const auto ctxt = getPROJContext();
     432      191001 :     PJ *horizCRS = nullptr;
     433      191001 :     int axisCount = 0;
     434      191001 :     if (m_pjType == PJ_TYPE_VERTICAL_CRS)
     435             :     {
     436         217 :         axisCount = 1;
     437             :     }
     438      190784 :     else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
     439             :     {
     440        1051 :         horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
     441        1051 :         if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
     442             :         {
     443         222 :             auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
     444         222 :             if (baseCRS)
     445             :             {
     446         222 :                 proj_destroy(horizCRS);
     447         222 :                 horizCRS = baseCRS;
     448             :             }
     449             :         }
     450             : 
     451        1051 :         auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
     452        1051 :         if (vertCRS)
     453             :         {
     454        1048 :             if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
     455             :             {
     456         373 :                 auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
     457         373 :                 if (baseCRS)
     458             :                 {
     459         373 :                     proj_destroy(vertCRS);
     460         373 :                     vertCRS = baseCRS;
     461             :                 }
     462             :             }
     463             : 
     464        1048 :             auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
     465        1048 :             if (cs)
     466             :             {
     467        1048 :                 axisCount += proj_cs_get_axis_count(ctxt, cs);
     468        1048 :                 proj_destroy(cs);
     469             :             }
     470        1048 :             proj_destroy(vertCRS);
     471             :         }
     472             :     }
     473             :     else
     474             :     {
     475      189733 :         horizCRS = m_pj_crs;
     476             :     }
     477             : 
     478      191001 :     bool bSwitchForGisFriendlyOrder = false;
     479      191001 :     if (horizCRS)
     480             :     {
     481      190781 :         auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
     482      190781 :         if (cs)
     483             :         {
     484      190781 :             int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
     485      190781 :             axisCount += nHorizCSAxisCount;
     486      190781 :             if (nHorizCSAxisCount >= 2)
     487             :             {
     488      190771 :                 bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
     489             :             }
     490      190781 :             proj_destroy(cs);
     491             :         }
     492             :     }
     493      191001 :     if (horizCRS != m_pj_crs)
     494             :     {
     495        1268 :         proj_destroy(horizCRS);
     496             :     }
     497      191001 :     if (doUndoDemote)
     498             :     {
     499      190937 :         undoDemoteFromBoundCRS();
     500             :     }
     501             : 
     502      191001 :     m_axisMapping.resize(axisCount);
     503      191000 :     if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
     504       63104 :         !bSwitchForGisFriendlyOrder)
     505             :     {
     506      478423 :         for (int i = 0; i < axisCount; i++)
     507             :         {
     508      319381 :             m_axisMapping[i] = i + 1;
     509      159042 :         }
     510             :     }
     511             :     else
     512             :     {
     513       31959 :         m_axisMapping[0] = 2;
     514       31959 :         m_axisMapping[1] = 1;
     515       31959 :         if (axisCount == 3)
     516             :         {
     517         322 :             m_axisMapping[2] = 3;
     518             :         }
     519             :     }
     520             : }
     521             : 
     522     1666270 : void OGRSpatialReference::Private::nodesChanged()
     523             : {
     524     1666270 :     m_bNodesChanged = true;
     525     1666270 : }
     526             : 
     527      158892 : void OGRSpatialReference::Private::invalidateNodes()
     528             : {
     529      158892 :     delete m_poRoot;
     530      158892 :     m_poRoot = nullptr;
     531      158892 :     m_bNodesChanged = false;
     532      158892 : }
     533             : 
     534         295 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
     535             : {
     536         295 :     invalidateNodes();
     537         295 :     m_bMorphToESRI = b;
     538         295 : }
     539             : 
     540      487385 : void OGRSpatialReference::Private::demoteFromBoundCRS()
     541             : {
     542      487385 :     CPLAssert(m_pj_bound_crs_target == nullptr);
     543      487385 :     CPLAssert(m_pj_bound_crs_co == nullptr);
     544      487385 :     CPLAssert(m_poRootBackup == nullptr);
     545      487385 :     CPLAssert(m_pj_crs_backup == nullptr);
     546             : 
     547      487385 :     m_pj_crs_modified_during_demote = false;
     548             : 
     549      487385 :     if (m_pjType == PJ_TYPE_BOUND_CRS)
     550             :     {
     551        4336 :         auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
     552        4336 :         m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
     553        4336 :         m_pj_bound_crs_co =
     554        4336 :             proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
     555             : 
     556        4336 :         m_poRootBackup = m_poRoot;
     557        4336 :         m_poRoot = nullptr;
     558        4336 :         m_pj_crs_backup = m_pj_crs;
     559        4336 :         m_pj_crs = baseCRS;
     560        4336 :         m_pjType = proj_get_type(m_pj_crs);
     561             :     }
     562      487385 : }
     563             : 
     564      487385 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
     565             : {
     566      487385 :     if (m_pj_bound_crs_target)
     567             :     {
     568        4336 :         CPLAssert(m_poRoot == nullptr);
     569        4336 :         CPLAssert(m_pj_crs);
     570        4336 :         if (!m_pj_crs_modified_during_demote)
     571             :         {
     572        4274 :             proj_destroy(m_pj_crs);
     573        4274 :             m_pj_crs = m_pj_crs_backup;
     574        4274 :             m_pjType = proj_get_type(m_pj_crs);
     575        4274 :             m_poRoot = m_poRootBackup;
     576             :         }
     577             :         else
     578             :         {
     579          62 :             delete m_poRootBackup;
     580          62 :             m_poRootBackup = nullptr;
     581          62 :             proj_destroy(m_pj_crs_backup);
     582          62 :             m_pj_crs_backup = nullptr;
     583          62 :             setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
     584          62 :                                                m_pj_bound_crs_target,
     585          62 :                                                m_pj_bound_crs_co),
     586             :                      false);
     587             :         }
     588             :     }
     589             : 
     590      487385 :     m_poRootBackup = nullptr;
     591      487385 :     m_pj_crs_backup = nullptr;
     592      487385 :     proj_destroy(m_pj_bound_crs_target);
     593      487385 :     m_pj_bound_crs_target = nullptr;
     594      487385 :     proj_destroy(m_pj_bound_crs_co);
     595      487384 :     m_pj_bound_crs_co = nullptr;
     596      487384 :     m_pj_crs_modified_during_demote = false;
     597      487384 : }
     598             : 
     599       92396 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
     600             :     const char *pszTargetKey)
     601             : {
     602       92396 :     if (pszTargetKey)
     603             :     {
     604       39030 :         demoteFromBoundCRS();
     605       39030 :         if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
     606       24166 :              m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
     607       14938 :             EQUAL(pszTargetKey, "GEOGCS"))
     608             :         {
     609        3354 :             pszTargetKey = nullptr;
     610             :         }
     611       35676 :         else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
     612          20 :                  EQUAL(pszTargetKey, "GEOCCS"))
     613             :         {
     614           0 :             pszTargetKey = nullptr;
     615             :         }
     616       35676 :         else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
     617       22793 :                  EQUAL(pszTargetKey, "PROJCS"))
     618             :         {
     619        3253 :             pszTargetKey = nullptr;
     620             :         }
     621       32423 :         else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
     622           4 :                  EQUAL(pszTargetKey, "VERT_CS"))
     623             :         {
     624           2 :             pszTargetKey = nullptr;
     625             :         }
     626       39030 :         undoDemoteFromBoundCRS();
     627             :     }
     628       92396 :     return pszTargetKey;
     629             : }
     630             : 
     631        7650 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
     632             : {
     633        7650 :     if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
     634        7579 :         m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
     635             :     {
     636          71 :         return m_pj_crs;
     637             :     }
     638             : 
     639        7579 :     auto ctxt = getPROJContext();
     640        7579 :     if (m_pjType == PJ_TYPE_PROJECTED_CRS)
     641             :     {
     642        3412 :         proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
     643        3412 :         proj_destroy(m_pj_geod_base_crs_temp);
     644        3412 :         m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
     645        3412 :         return m_pj_geod_base_crs_temp;
     646             :     }
     647             : 
     648        4167 :     proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
     649        4167 :     proj_destroy(m_pj_geod_base_crs_temp);
     650        4167 :     auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
     651             :                                             nullptr, 0);
     652        4167 :     m_pj_geod_base_crs_temp = proj_create_geographic_crs(
     653             :         ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
     654             :         SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
     655             :         SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
     656        4167 :     proj_destroy(cs);
     657             : 
     658        4167 :     return m_pj_geod_base_crs_temp;
     659             : }
     660             : 
     661        4370 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
     662             : {
     663        4370 :     auto ctxt = getPROJContext();
     664        4370 :     if (m_pjType == PJ_TYPE_PROJECTED_CRS)
     665             :     {
     666        3399 :         proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
     667        3399 :         proj_destroy(m_pj_proj_crs_cs_temp);
     668        3399 :         m_pj_proj_crs_cs_temp =
     669        3399 :             proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
     670        3399 :         return m_pj_proj_crs_cs_temp;
     671             :     }
     672             : 
     673         971 :     proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
     674         971 :     proj_destroy(m_pj_proj_crs_cs_temp);
     675         971 :     m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
     676             :         ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
     677         971 :     return m_pj_proj_crs_cs_temp;
     678             : }
     679             : 
     680        4417 : const char *OGRSpatialReference::Private::getProjCRSName()
     681             : {
     682        4417 :     if (m_pjType == PJ_TYPE_PROJECTED_CRS)
     683             :     {
     684        3413 :         return proj_get_name(m_pj_crs);
     685             :     }
     686             : 
     687        1004 :     return "unnamed";
     688             : }
     689             : 
     690        1334 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
     691             : {
     692        1334 :     refreshProjObj();
     693             : 
     694        1334 :     demoteFromBoundCRS();
     695             : 
     696             :     auto projCRS =
     697        1334 :         proj_create_projected_crs(getPROJContext(), getProjCRSName(),
     698        1334 :                                   getGeodBaseCRS(), conv, getProjCRSCoordSys());
     699        1334 :     proj_destroy(conv);
     700             : 
     701        1334 :     setPjCRS(projCRS);
     702             : 
     703        1334 :     undoDemoteFromBoundCRS();
     704        1334 :     return OGRERR_NONE;
     705             : }
     706             : 
     707             : /************************************************************************/
     708             : /*                           ToPointer()                                */
     709             : /************************************************************************/
     710             : 
     711       19805 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
     712             : {
     713       19805 :     return OGRSpatialReference::FromHandle(hSRS);
     714             : }
     715             : 
     716             : /************************************************************************/
     717             : /*                           ToHandle()                                 */
     718             : /************************************************************************/
     719             : 
     720        3445 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
     721             : {
     722        3445 :     return OGRSpatialReference::ToHandle(poSRS);
     723             : }
     724             : 
     725             : /************************************************************************/
     726             : /*                           OGRsnPrintDouble()                         */
     727             : /************************************************************************/
     728             : 
     729             : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
     730             : 
     731         128 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
     732             : 
     733             : {
     734         128 :     CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
     735             : 
     736         128 :     const size_t nLen = strlen(pszStrBuf);
     737             : 
     738             :     // The following hack is intended to truncate some "precision" in cases
     739             :     // that appear to be roundoff error.
     740         128 :     if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
     741           8 :                       strcmp(pszStrBuf + nLen - 6, "000001") == 0))
     742             :     {
     743           0 :         CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
     744             :     }
     745             : 
     746             :     // Force to user periods regardless of locale.
     747         128 :     if (strchr(pszStrBuf, ',') != nullptr)
     748             :     {
     749           0 :         char *const pszDelim = strchr(pszStrBuf, ',');
     750           0 :         *pszDelim = '.';
     751             :     }
     752         128 : }
     753             : 
     754             : /************************************************************************/
     755             : /*                        OGRSpatialReference()                         */
     756             : /************************************************************************/
     757             : 
     758             : /**
     759             :  * \brief Constructor.
     760             :  *
     761             :  * This constructor takes an optional string argument which if passed
     762             :  * should be a WKT representation of an SRS.  Passing this is equivalent
     763             :  * to not passing it, and then calling importFromWkt() with the WKT string.
     764             :  *
     765             :  * Note that newly created objects are given a reference count of one.
     766             :  *
     767             :  * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
     768             :  * object are assumed to be in the order of the axis of the CRS definition
     769             :  (which
     770             :  * for example means latitude first, longitude second for geographic CRS
     771             :  belonging
     772             :  * to the EPSG authority). It is possible to define a data axis to CRS axis
     773             :  * mapping strategy with the SetAxisMappingStrategy() method.
     774             :  *
     775             :  * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
     776             :  * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
     777             :  later
     778             :  * being the default value when the option is not set) to control the value of
     779             :  the
     780             :  * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
     781             :  * created. Calling SetAxisMappingStrategy() will override this default value.
     782             : 
     783             :  * The C function OSRNewSpatialReference() does the same thing as this
     784             :  * constructor.
     785             :  *
     786             :  * @param pszWKT well known text definition to which the object should
     787             :  * be initialized, or NULL (the default).
     788             :  */
     789             : 
     790      190646 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
     791      190646 :     : d(new Private(this))
     792             : {
     793      190605 :     if (pszWKT != nullptr)
     794        1018 :         importFromWkt(pszWKT);
     795      190605 : }
     796             : 
     797             : /************************************************************************/
     798             : /*                       OSRNewSpatialReference()                       */
     799             : /************************************************************************/
     800             : 
     801             : /**
     802             :  * \brief Constructor.
     803             :  *
     804             :  * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
     805             :  * object are assumed to be in the order of the axis of the CRS definition
     806             :  * (which for example means latitude first, longitude second for geographic CRS
     807             :  * belonging to the EPSG authority). It is possible to define a data axis to CRS
     808             :  * axis mapping strategy with the SetAxisMappingStrategy() method.
     809             :  *
     810             :  * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
     811             :  * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
     812             :  * later being the default value when the option is not set) to control the
     813             :  * value of the data axis to CRS axis mapping strategy when a
     814             :  * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
     815             :  * override this default value.
     816             :  *
     817             :  * This function is the same as OGRSpatialReference::OGRSpatialReference()
     818             :  */
     819        2693 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
     820             : 
     821             : {
     822        2693 :     OGRSpatialReference *poSRS = new OGRSpatialReference();
     823             : 
     824        2693 :     if (pszWKT != nullptr && strlen(pszWKT) > 0)
     825             :     {
     826         307 :         if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
     827             :         {
     828           1 :             delete poSRS;
     829           1 :             poSRS = nullptr;
     830             :         }
     831             :     }
     832             : 
     833        2693 :     return ToHandle(poSRS);
     834             : }
     835             : 
     836             : /************************************************************************/
     837             : /*                        OGRSpatialReference()                         */
     838             : /************************************************************************/
     839             : 
     840             : /** Copy constructor. See also Clone().
     841             :  * @param oOther other spatial reference
     842             :  */
     843        2131 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
     844        2131 :     : d(new Private(this))
     845             : {
     846        2131 :     *this = oOther;
     847        2131 : }
     848             : 
     849             : /************************************************************************/
     850             : /*                        OGRSpatialReference()                         */
     851             : /************************************************************************/
     852             : 
     853             : /** Move constructor.
     854             :  * @param oOther other spatial reference
     855             :  */
     856          28 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
     857          28 :     : d(std::move(oOther.d))
     858             : {
     859          28 : }
     860             : 
     861             : /************************************************************************/
     862             : /*                        ~OGRSpatialReference()                        */
     863             : /************************************************************************/
     864             : 
     865             : /**
     866             :  * \brief OGRSpatialReference destructor.
     867             :  *
     868             :  * The C function OSRDestroySpatialReference() does the same thing as this
     869             :  * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
     870             :  *
     871             :  * @deprecated
     872             :  */
     873             : 
     874      237541 : OGRSpatialReference::~OGRSpatialReference()
     875             : 
     876             : {
     877      237540 : }
     878             : 
     879             : /************************************************************************/
     880             : /*                      DestroySpatialReference()                       */
     881             : /************************************************************************/
     882             : 
     883             : /**
     884             :  * \brief OGRSpatialReference destructor.
     885             :  *
     886             :  * This static method will destroy a OGRSpatialReference.  It is
     887             :  * equivalent to calling delete on the object, but it ensures that the
     888             :  * deallocation is properly executed within the OGR libraries heap on
     889             :  * platforms where this can matter (win32).
     890             :  *
     891             :  * This function is the same as OSRDestroySpatialReference()
     892             :  *
     893             :  * @param poSRS the object to delete
     894             :  *
     895             :  * @since GDAL 1.7.0
     896             :  */
     897             : 
     898           0 : void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
     899             : {
     900           0 :     delete poSRS;
     901           0 : }
     902             : 
     903             : /************************************************************************/
     904             : /*                     OSRDestroySpatialReference()                     */
     905             : /************************************************************************/
     906             : 
     907             : /**
     908             :  * \brief OGRSpatialReference destructor.
     909             :  *
     910             :  * This function is the same as OGRSpatialReference::~OGRSpatialReference()
     911             :  * and OGRSpatialReference::DestroySpatialReference()
     912             :  *
     913             :  * @param hSRS the object to delete
     914             :  */
     915        6114 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
     916             : 
     917             : {
     918        6114 :     delete ToPointer(hSRS);
     919        6114 : }
     920             : 
     921             : /************************************************************************/
     922             : /*                               Clear()                                */
     923             : /************************************************************************/
     924             : 
     925             : /**
     926             :  * \brief Wipe current definition.
     927             :  *
     928             :  * Returns OGRSpatialReference to a state with no definition, as it
     929             :  * exists when first created.  It does not affect reference counts.
     930             :  */
     931             : 
     932       89575 : void OGRSpatialReference::Clear()
     933             : 
     934             : {
     935       89575 :     d->clear();
     936       89575 : }
     937             : 
     938             : /************************************************************************/
     939             : /*                             operator=()                              */
     940             : /************************************************************************/
     941             : 
     942             : /** Assignment operator.
     943             :  * @param oSource SRS to assign to *this
     944             :  * @return *this
     945             :  */
     946             : OGRSpatialReference &
     947       17809 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
     948             : 
     949             : {
     950       17809 :     if (&oSource != this)
     951             :     {
     952       17808 :         Clear();
     953             : #ifdef CPPCHECK
     954             :         // Otherwise cppcheck would protest that nRefCount isn't modified
     955             :         d->nRefCount = (d->nRefCount + 1) - 1;
     956             : #endif
     957             : 
     958       17808 :         oSource.d->refreshProjObj();
     959       17808 :         if (oSource.d->m_pj_crs)
     960       17562 :             d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
     961       17808 :         if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
     962        9420 :             SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     963        8388 :         else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
     964         107 :             SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
     965             : 
     966       17808 :         d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
     967             :     }
     968             : 
     969       17809 :     return *this;
     970             : }
     971             : 
     972             : /************************************************************************/
     973             : /*                             operator=()                              */
     974             : /************************************************************************/
     975             : 
     976             : /** Move assignment operator.
     977             :  * @param oSource SRS to assign to *this
     978             :  * @return *this
     979             :  */
     980             : OGRSpatialReference &
     981        3784 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
     982             : 
     983             : {
     984        3784 :     if (&oSource != this)
     985             :     {
     986        3783 :         d = std::move(oSource.d);
     987             :     }
     988             : 
     989        3784 :     return *this;
     990             : }
     991             : 
     992             : /************************************************************************/
     993             : /*                             Reference()                              */
     994             : /************************************************************************/
     995             : 
     996             : /**
     997             :  * \brief Increments the reference count by one.
     998             :  *
     999             :  * The reference count is used keep track of the number of OGRGeometry objects
    1000             :  * referencing this SRS.
    1001             :  *
    1002             :  * The method does the same thing as the C function OSRReference().
    1003             :  *
    1004             :  * @return the updated reference count.
    1005             :  */
    1006             : 
    1007      448624 : int OGRSpatialReference::Reference()
    1008             : 
    1009             : {
    1010      448624 :     return CPLAtomicInc(&d->nRefCount);
    1011             : }
    1012             : 
    1013             : /************************************************************************/
    1014             : /*                            OSRReference()                            */
    1015             : /************************************************************************/
    1016             : 
    1017             : /**
    1018             :  * \brief Increments the reference count by one.
    1019             :  *
    1020             :  * This function is the same as OGRSpatialReference::Reference()
    1021             :  */
    1022         911 : int OSRReference(OGRSpatialReferenceH hSRS)
    1023             : 
    1024             : {
    1025         911 :     VALIDATE_POINTER1(hSRS, "OSRReference", 0);
    1026             : 
    1027         911 :     return ToPointer(hSRS)->Reference();
    1028             : }
    1029             : 
    1030             : /************************************************************************/
    1031             : /*                            Dereference()                             */
    1032             : /************************************************************************/
    1033             : 
    1034             : /**
    1035             :  * \brief Decrements the reference count by one.
    1036             :  *
    1037             :  * The method does the same thing as the C function OSRDereference().
    1038             :  *
    1039             :  * @return the updated reference count.
    1040             :  */
    1041             : 
    1042      482148 : int OGRSpatialReference::Dereference()
    1043             : 
    1044             : {
    1045      482148 :     if (d->nRefCount <= 0)
    1046           0 :         CPLDebug("OSR",
    1047             :                  "Dereference() called on an object with refcount %d,"
    1048             :                  "likely already destroyed!",
    1049           0 :                  d->nRefCount);
    1050      482148 :     return CPLAtomicDec(&d->nRefCount);
    1051             : }
    1052             : 
    1053             : /************************************************************************/
    1054             : /*                           OSRDereference()                           */
    1055             : /************************************************************************/
    1056             : 
    1057             : /**
    1058             :  * \brief Decrements the reference count by one.
    1059             :  *
    1060             :  * This function is the same as OGRSpatialReference::Dereference()
    1061             :  */
    1062           0 : int OSRDereference(OGRSpatialReferenceH hSRS)
    1063             : 
    1064             : {
    1065           0 :     VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
    1066             : 
    1067           0 :     return ToPointer(hSRS)->Dereference();
    1068             : }
    1069             : 
    1070             : /************************************************************************/
    1071             : /*                         GetReferenceCount()                          */
    1072             : /************************************************************************/
    1073             : 
    1074             : /**
    1075             :  * \brief Fetch current reference count.
    1076             :  *
    1077             :  * @return the current reference count.
    1078             :  */
    1079          96 : int OGRSpatialReference::GetReferenceCount() const
    1080             : {
    1081          96 :     return d->nRefCount;
    1082             : }
    1083             : 
    1084             : /************************************************************************/
    1085             : /*                              Release()                               */
    1086             : /************************************************************************/
    1087             : 
    1088             : /**
    1089             :  * \brief Decrements the reference count by one, and destroy if zero.
    1090             :  *
    1091             :  * The method does the same thing as the C function OSRRelease().
    1092             :  */
    1093             : 
    1094      479225 : void OGRSpatialReference::Release()
    1095             : 
    1096             : {
    1097      479225 :     if (Dereference() <= 0)
    1098       33468 :         delete this;
    1099      479225 : }
    1100             : 
    1101             : /************************************************************************/
    1102             : /*                             OSRRelease()                             */
    1103             : /************************************************************************/
    1104             : 
    1105             : /**
    1106             :  * \brief Decrements the reference count by one, and destroy if zero.
    1107             :  *
    1108             :  * This function is the same as OGRSpatialReference::Release()
    1109             :  */
    1110        5775 : void OSRRelease(OGRSpatialReferenceH hSRS)
    1111             : 
    1112             : {
    1113        5775 :     VALIDATE_POINTER0(hSRS, "OSRRelease");
    1114             : 
    1115        5775 :     ToPointer(hSRS)->Release();
    1116             : }
    1117             : 
    1118       64285 : OGR_SRSNode *OGRSpatialReference::GetRoot()
    1119             : {
    1120       64285 :     if (!d->m_poRoot)
    1121             :     {
    1122       20711 :         d->refreshRootFromProjObj();
    1123             :     }
    1124       64285 :     return d->m_poRoot;
    1125             : }
    1126             : 
    1127        6962 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
    1128             : {
    1129        6962 :     if (!d->m_poRoot)
    1130             :     {
    1131        2399 :         d->refreshRootFromProjObj();
    1132             :     }
    1133        6962 :     return d->m_poRoot;
    1134             : }
    1135             : 
    1136             : /************************************************************************/
    1137             : /*                              SetRoot()                               */
    1138             : /************************************************************************/
    1139             : 
    1140             : /**
    1141             :  * \brief Set the root SRS node.
    1142             :  *
    1143             :  * If the object has an existing tree of OGR_SRSNodes, they are destroyed
    1144             :  * as part of assigning the new root.  Ownership of the passed OGR_SRSNode is
    1145             :  * is assumed by the OGRSpatialReference.
    1146             :  *
    1147             :  * @param poNewRoot object to assign as root.
    1148             :  */
    1149             : 
    1150          41 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
    1151             : 
    1152             : {
    1153          41 :     if (d->m_poRoot != poNewRoot)
    1154             :     {
    1155          41 :         delete d->m_poRoot;
    1156          41 :         d->setRoot(poNewRoot);
    1157             :     }
    1158          41 : }
    1159             : 
    1160             : /************************************************************************/
    1161             : /*                            GetAttrNode()                             */
    1162             : /************************************************************************/
    1163             : 
    1164             : /**
    1165             :  * \brief Find named node in tree.
    1166             :  *
    1167             :  * This method does a pre-order traversal of the node tree searching for
    1168             :  * a node with this exact value (case insensitive), and returns it.  Leaf
    1169             :  * nodes are not considered, under the assumption that they are just
    1170             :  * attribute value nodes.
    1171             :  *
    1172             :  * If a node appears more than once in the tree (such as UNIT for instance),
    1173             :  * the first encountered will be returned.  Use GetNode() on a subtree to be
    1174             :  * more specific.
    1175             :  *
    1176             :  * @param pszNodePath the name of the node to search for.  May contain multiple
    1177             :  * components such as "GEOGCS|UNIT".
    1178             :  *
    1179             :  * @return a pointer to the node found, or NULL if none.
    1180             :  */
    1181             : 
    1182       61341 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
    1183             : 
    1184             : {
    1185       61341 :     if (strchr(pszNodePath, '|') == nullptr)
    1186             :     {
    1187             :         // Fast path
    1188       36055 :         OGR_SRSNode *poNode = GetRoot();
    1189       36055 :         if (poNode)
    1190       34871 :             poNode = poNode->GetNode(pszNodePath);
    1191       36055 :         return poNode;
    1192             :     }
    1193             : 
    1194             :     char **papszPathTokens =
    1195       25286 :         CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
    1196             : 
    1197       25286 :     if (CSLCount(papszPathTokens) < 1)
    1198             :     {
    1199           0 :         CSLDestroy(papszPathTokens);
    1200           0 :         return nullptr;
    1201             :     }
    1202             : 
    1203       25286 :     OGR_SRSNode *poNode = GetRoot();
    1204       77143 :     for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
    1205             :     {
    1206       51857 :         poNode = poNode->GetNode(papszPathTokens[i]);
    1207             :     }
    1208             : 
    1209       25286 :     CSLDestroy(papszPathTokens);
    1210             : 
    1211       25286 :     return poNode;
    1212             : }
    1213             : 
    1214             : /**
    1215             :  * \brief Find named node in tree.
    1216             :  *
    1217             :  * This method does a pre-order traversal of the node tree searching for
    1218             :  * a node with this exact value (case insensitive), and returns it.  Leaf
    1219             :  * nodes are not considered, under the assumption that they are just
    1220             :  * attribute value nodes.
    1221             :  *
    1222             :  * If a node appears more than once in the tree (such as UNIT for instance),
    1223             :  * the first encountered will be returned.  Use GetNode() on a subtree to be
    1224             :  * more specific.
    1225             :  *
    1226             :  * @param pszNodePath the name of the node to search for.  May contain multiple
    1227             :  * components such as "GEOGCS|UNIT".
    1228             :  *
    1229             :  * @return a pointer to the node found, or NULL if none.
    1230             :  */
    1231             : 
    1232             : const OGR_SRSNode *
    1233       54947 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
    1234             : 
    1235             : {
    1236             :     OGR_SRSNode *poNode =
    1237       54947 :         const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
    1238             : 
    1239       54947 :     return poNode;
    1240             : }
    1241             : 
    1242             : /************************************************************************/
    1243             : /*                            GetAttrValue()                            */
    1244             : /************************************************************************/
    1245             : 
    1246             : /**
    1247             :  * \brief Fetch indicated attribute of named node.
    1248             :  *
    1249             :  * This method uses GetAttrNode() to find the named node, and then extracts
    1250             :  * the value of the indicated child.  Thus a call to GetAttrValue("UNIT",1)
    1251             :  * would return the second child of the UNIT node, which is normally the
    1252             :  * length of the linear unit in meters.
    1253             :  *
    1254             :  * This method does the same thing as the C function OSRGetAttrValue().
    1255             :  *
    1256             :  * @param pszNodeName the tree node to look for (case insensitive).
    1257             :  * @param iAttr the child of the node to fetch (zero based).
    1258             :  *
    1259             :  * @return the requested value, or NULL if it fails for any reason.
    1260             :  */
    1261             : 
    1262       18184 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
    1263             :                                               int iAttr) const
    1264             : 
    1265             : {
    1266       18184 :     const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
    1267       18184 :     if (poNode == nullptr)
    1268             :     {
    1269        7061 :         if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
    1270             :         {
    1271           8 :             return GetAttrValue("METHOD", iAttr);
    1272             :         }
    1273        7053 :         else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
    1274             :         {
    1275           0 :             return GetAttrValue("PROJCRS|METHOD", iAttr);
    1276             :         }
    1277        7053 :         else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
    1278             :         {
    1279           1 :             return GetAttrValue("PROJCRS", iAttr);
    1280             :         }
    1281        7052 :         return nullptr;
    1282             :     }
    1283             : 
    1284       11123 :     if (iAttr < 0 || iAttr >= poNode->GetChildCount())
    1285           0 :         return nullptr;
    1286             : 
    1287       11123 :     return poNode->GetChild(iAttr)->GetValue();
    1288             : }
    1289             : 
    1290             : /************************************************************************/
    1291             : /*                          OSRGetAttrValue()                           */
    1292             : /************************************************************************/
    1293             : 
    1294             : /**
    1295             :  * \brief Fetch indicated attribute of named node.
    1296             :  *
    1297             :  * This function is the same as OGRSpatialReference::GetAttrValue()
    1298             :  */
    1299          62 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
    1300             :                                         const char *pszKey, int iChild)
    1301             : 
    1302             : {
    1303          62 :     VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
    1304             : 
    1305          62 :     return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
    1306             : }
    1307             : 
    1308             : /************************************************************************/
    1309             : /*                             GetName()                                */
    1310             : /************************************************************************/
    1311             : 
    1312             : /**
    1313             :  * \brief Return the CRS name.
    1314             :  *
    1315             :  * The returned value is only short lived and should not be used after other
    1316             :  * calls to methods on this object.
    1317             :  *
    1318             :  * @since GDAL 3.0
    1319             :  */
    1320             : 
    1321        4012 : const char *OGRSpatialReference::GetName() const
    1322             : {
    1323        4012 :     d->refreshProjObj();
    1324        4012 :     if (!d->m_pj_crs)
    1325         112 :         return nullptr;
    1326        3900 :     const char *pszName = proj_get_name(d->m_pj_crs);
    1327             : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
    1328             :     if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
    1329             :     {
    1330             :         // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
    1331             :         PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
    1332             :         if (baseCRS)
    1333             :         {
    1334             :             pszName = proj_get_name(baseCRS);
    1335             :             // pszName still remains valid after proj_destroy(), since
    1336             :             // d->m_pj_crs keeps a reference to the base CRS C++ object.
    1337             :             proj_destroy(baseCRS);
    1338             :         }
    1339             :     }
    1340             : #endif
    1341        3900 :     return pszName;
    1342             : }
    1343             : 
    1344             : /************************************************************************/
    1345             : /*                           OSRGetName()                               */
    1346             : /************************************************************************/
    1347             : 
    1348             : /**
    1349             :  * \brief Return the CRS name.
    1350             :  *
    1351             :  * The returned value is only short lived and should not be used after other
    1352             :  * calls to methods on this object.
    1353             :  *
    1354             :  * @since GDAL 3.0
    1355             :  */
    1356          38 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
    1357             : 
    1358             : {
    1359          38 :     VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
    1360             : 
    1361          38 :     return ToPointer(hSRS)->GetName();
    1362             : }
    1363             : 
    1364             : /************************************************************************/
    1365             : /*                               Clone()                                */
    1366             : /************************************************************************/
    1367             : 
    1368             : /**
    1369             :  * \brief Make a duplicate of this OGRSpatialReference.
    1370             :  *
    1371             :  * This method is the same as the C function OSRClone().
    1372             :  *
    1373             :  * @return a new SRS, which becomes the responsibility of the caller.
    1374             :  */
    1375             : 
    1376       22132 : OGRSpatialReference *OGRSpatialReference::Clone() const
    1377             : 
    1378             : {
    1379       22132 :     OGRSpatialReference *poNewRef = new OGRSpatialReference();
    1380             : 
    1381       22132 :     d->refreshProjObj();
    1382       22132 :     if (d->m_pj_crs != nullptr)
    1383       22081 :         poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
    1384       22132 :     if (d->m_bHasCenterLong && d->m_poRoot)
    1385             :     {
    1386           0 :         poNewRef->d->setRoot(d->m_poRoot->Clone());
    1387             :     }
    1388       22132 :     poNewRef->d->m_axisMapping = d->m_axisMapping;
    1389       22132 :     poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
    1390       22132 :     poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
    1391       22132 :     return poNewRef;
    1392             : }
    1393             : 
    1394             : /************************************************************************/
    1395             : /*                              OSRClone()                              */
    1396             : /************************************************************************/
    1397             : 
    1398             : /**
    1399             :  * \brief Make a duplicate of this OGRSpatialReference.
    1400             :  *
    1401             :  * This function is the same as OGRSpatialReference::Clone()
    1402             :  */
    1403         581 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
    1404             : 
    1405             : {
    1406         581 :     VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
    1407             : 
    1408         581 :     return ToHandle(ToPointer(hSRS)->Clone());
    1409             : }
    1410             : 
    1411             : /************************************************************************/
    1412             : /*                            dumpReadable()                            */
    1413             : /************************************************************************/
    1414             : 
    1415             : /** Dump pretty wkt to stdout, mostly for debugging.
    1416             :  */
    1417           0 : void OGRSpatialReference::dumpReadable()
    1418             : 
    1419             : {
    1420           0 :     char *pszPrettyWkt = nullptr;
    1421             : 
    1422           0 :     const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
    1423           0 :     exportToWkt(&pszPrettyWkt, apszOptions);
    1424           0 :     printf("%s\n", pszPrettyWkt); /*ok*/
    1425           0 :     CPLFree(pszPrettyWkt);
    1426           0 : }
    1427             : 
    1428             : /************************************************************************/
    1429             : /*                         exportToPrettyWkt()                          */
    1430             : /************************************************************************/
    1431             : 
    1432             : /**
    1433             :  * Convert this SRS into a nicely formatted WKT 1 string for display to a
    1434             :  * person.
    1435             :  *
    1436             :  * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
    1437             :  * Issues</a> page for implementation details of WKT 1 in OGR.
    1438             :  *
    1439             :  * Note that the returned WKT string should be freed with
    1440             :  * CPLFree() when no longer needed.  It is the responsibility of the caller.
    1441             :  *
    1442             :  * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
    1443             :  * option. Valid values are the one of the FORMAT option of
    1444             :  * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
    1445             :  *
    1446             :  * This method is the same as the C function OSRExportToPrettyWkt().
    1447             :  *
    1448             :  * @param ppszResult the resulting string is returned in this pointer.
    1449             :  * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
    1450             :  *   stripped off.
    1451             :  *
    1452             :  * @return OGRERR_NONE if successful.
    1453             :  */
    1454             : 
    1455          58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
    1456             :                                               int bSimplify) const
    1457             : 
    1458             : {
    1459         116 :     CPLStringList aosOptions;
    1460          58 :     aosOptions.SetNameValue("MULTILINE", "YES");
    1461          58 :     if (bSimplify)
    1462             :     {
    1463           0 :         aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
    1464             :     }
    1465         116 :     return exportToWkt(ppszResult, aosOptions.List());
    1466             : }
    1467             : 
    1468             : /************************************************************************/
    1469             : /*                        OSRExportToPrettyWkt()                        */
    1470             : /************************************************************************/
    1471             : 
    1472             : /**
    1473             :  * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
    1474             :  * person.
    1475             :  *
    1476             :  * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
    1477             :  * option. Valid values are the one of the FORMAT option of
    1478             :  * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
    1479             :  *
    1480             :  * This function is the same as OGRSpatialReference::exportToPrettyWkt().
    1481             :  */
    1482             : 
    1483          56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
    1484             :                                         char **ppszReturn, int bSimplify)
    1485             : 
    1486             : {
    1487          56 :     VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
    1488             : 
    1489          56 :     *ppszReturn = nullptr;
    1490             : 
    1491          56 :     return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
    1492             : }
    1493             : 
    1494             : /************************************************************************/
    1495             : /*                            exportToWkt()                             */
    1496             : /************************************************************************/
    1497             : 
    1498             : /**
    1499             :  * \brief Convert this SRS into WKT 1 format.
    1500             :  *
    1501             :  * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
    1502             :  * Issues</a> page for implementation details of WKT 1 in OGR.
    1503             :  *
    1504             :  * Note that the returned WKT string should be freed with
    1505             :  * CPLFree() when no longer needed.  It is the responsibility of the caller.
    1506             :  *
    1507             :  * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
    1508             :  * option. Valid values are the one of the FORMAT option of
    1509             :  * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
    1510             :  *
    1511             :  * This method is the same as the C function OSRExportToWkt().
    1512             :  *
    1513             :  * @param ppszResult the resulting string is returned in this pointer.
    1514             :  *
    1515             :  * @return OGRERR_NONE if successful.
    1516             :  */
    1517             : 
    1518       11954 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
    1519             : 
    1520             : {
    1521       11954 :     return exportToWkt(ppszResult, nullptr);
    1522             : }
    1523             : 
    1524             : /************************************************************************/
    1525             : /*                GDAL_proj_crs_create_bound_crs_to_WGS84()             */
    1526             : /************************************************************************/
    1527             : 
    1528         539 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
    1529             :                                                    bool onlyIfEPSGCode,
    1530             :                                                    bool canModifyHorizPart)
    1531             : {
    1532         539 :     PJ *ret = nullptr;
    1533         539 :     if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
    1534             :     {
    1535          13 :         auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
    1536          13 :         auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
    1537          13 :         if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
    1538          26 :             vertCRS &&
    1539          10 :             (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
    1540             :         {
    1541             :             auto boundHoriz =
    1542             :                 canModifyHorizPart
    1543           3 :                     ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
    1544           3 :                     : proj_clone(ctx, horizCRS);
    1545             :             auto boundVert =
    1546           3 :                 proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
    1547           3 :             if (boundHoriz && boundVert)
    1548             :             {
    1549           3 :                 ret = proj_create_compound_crs(ctx, proj_get_name(pj),
    1550             :                                                boundHoriz, boundVert);
    1551             :             }
    1552           3 :             proj_destroy(boundHoriz);
    1553           3 :             proj_destroy(boundVert);
    1554             :         }
    1555          13 :         proj_destroy(horizCRS);
    1556          13 :         proj_destroy(vertCRS);
    1557             :     }
    1558        1015 :     else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
    1559         489 :              (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
    1560             :     {
    1561         224 :         ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
    1562             :     }
    1563         539 :     return ret;
    1564             : }
    1565             : 
    1566             : /************************************************************************/
    1567             : /*                            exportToWkt()                             */
    1568             : /************************************************************************/
    1569             : 
    1570             : /**
    1571             :  * Convert this SRS into a WKT string.
    1572             :  *
    1573             :  * Note that the returned WKT string should be freed with
    1574             :  * CPLFree() when no longer needed.  It is the responsibility of the caller.
    1575             :  *
    1576             :  * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
    1577             :  * Issues</a> page for implementation details of WKT 1 in OGR.
    1578             :  *
    1579             :  * @param ppszResult the resulting string is returned in this pointer.
    1580             :  * @param papszOptions NULL terminated list of options, or NULL. Currently
    1581             :  * supported options are
    1582             :  * <ul>
    1583             :  * <li>MULTILINE=YES/NO. Defaults to NO.</li>
    1584             :  * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
    1585             :  *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
    1586             :  *     node is returned.
    1587             :  *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
    1588             :  *     node is returned.
    1589             :  *     WKT1 is an alias of WKT1_GDAL.
    1590             :  *     WKT2 will default to the latest revision implemented (currently
    1591             :  *     WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
    1592             :  * </li>
    1593             :  * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
    1594             :  * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
    1595             :  * be exported as a compound CRS whose vertical part represents an ellipsoidal
    1596             :  * height (for example for use with LAS 1.4 WKT1).
    1597             :  * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
    1598             :  * </ul>
    1599             :  *
    1600             :  * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
    1601             :  * configuration option is set to YES, when exporting to WKT1_GDAL, this method
    1602             :  * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
    1603             :  * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
    1604             :  * TOWGS84[] node may be added.
    1605             :  *
    1606             :  * @return OGRERR_NONE if successful.
    1607             :  * @since GDAL 3.0
    1608             :  */
    1609             : 
    1610       15430 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
    1611             :                                         const char *const *papszOptions) const
    1612             : {
    1613             :     // In the past calling this method was thread-safe, even if we never
    1614             :     // guaranteed it. Now proj_as_wkt() will cache the result internally,
    1615             :     // so this is no longer thread-safe.
    1616       30860 :     std::lock_guard<std::mutex> oLock(d->m_mutex);
    1617             : 
    1618       15430 :     d->refreshProjObj();
    1619       15430 :     if (!d->m_pj_crs)
    1620             :     {
    1621          21 :         *ppszResult = CPLStrdup("");
    1622          21 :         return OGRERR_FAILURE;
    1623             :     }
    1624             : 
    1625       15409 :     if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
    1626             :     {
    1627           0 :         return d->m_poRoot->exportToWkt(ppszResult);
    1628             :     }
    1629             : 
    1630       15409 :     auto ctxt = d->getPROJContext();
    1631       15409 :     auto wktFormat = PJ_WKT1_GDAL;
    1632             :     const char *pszFormat =
    1633       15409 :         CSLFetchNameValueDef(papszOptions, "FORMAT",
    1634             :                              CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
    1635       15409 :     if (EQUAL(pszFormat, "DEFAULT"))
    1636       13301 :         pszFormat = "";
    1637             : 
    1638       15409 :     if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
    1639             :     {
    1640         561 :         wktFormat = PJ_WKT1_ESRI;
    1641             :     }
    1642       14848 :     else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
    1643       14244 :              EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
    1644             :     {
    1645         609 :         wktFormat = PJ_WKT1_GDAL;
    1646             :     }
    1647       14239 :     else if (EQUAL(pszFormat, "WKT2_2015"))
    1648             :     {
    1649         232 :         wktFormat = PJ_WKT2_2015;
    1650             :     }
    1651       14007 :     else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
    1652       13729 :              EQUAL(pszFormat, "WKT2_2019"))
    1653             :     {
    1654         968 :         wktFormat = PJ_WKT2_2018;
    1655             :     }
    1656       13039 :     else if (pszFormat[0] == '\0')
    1657             :     {
    1658             :         // cppcheck-suppress knownConditionTrueFalse
    1659       13039 :         if (IsDerivedGeographic())
    1660             :         {
    1661           2 :             wktFormat = PJ_WKT2_2018;
    1662             :         }
    1663       25444 :         else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
    1664       12407 :                  GetAxesCount() == 3)
    1665             :         {
    1666          56 :             wktFormat = PJ_WKT2_2018;
    1667             :         }
    1668             :     }
    1669             :     else
    1670             :     {
    1671           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
    1672           0 :         *ppszResult = CPLStrdup("");
    1673           0 :         return OGRERR_FAILURE;
    1674             :     }
    1675             : 
    1676       30818 :     CPLStringList aosOptions;
    1677       15409 :     if (wktFormat != PJ_WKT1_ESRI)
    1678             :     {
    1679       14848 :         aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
    1680             :     }
    1681             :     aosOptions.SetNameValue(
    1682       15409 :         "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
    1683             : 
    1684       15409 :     const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
    1685             :         papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
    1686       15409 :     if (pszAllowEllpsHeightAsVertCS)
    1687             :     {
    1688             :         aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
    1689           0 :                                 pszAllowEllpsHeightAsVertCS);
    1690             :     }
    1691             : 
    1692       15409 :     PJ *boundCRS = nullptr;
    1693       28999 :     if (wktFormat == PJ_WKT1_GDAL &&
    1694       13590 :         CPLTestBool(CSLFetchNameValueDef(
    1695             :             papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
    1696             :             CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
    1697             :     {
    1698           0 :         boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
    1699           0 :             d->getPROJContext(), d->m_pj_crs, true, true);
    1700             :     }
    1701             : 
    1702       30818 :     std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
    1703       15409 :     CPLInstallErrorHandlerAccumulator(aoErrors);
    1704       15409 :     const char *pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
    1705       15409 :                                      wktFormat, aosOptions.List());
    1706       15409 :     CPLUninstallErrorHandlerAccumulator();
    1707       15411 :     for (const auto &oError : aoErrors)
    1708             :     {
    1709          32 :         if (pszFormat[0] == '\0' &&
    1710          14 :             (oError.msg.find("Unsupported conversion method") !=
    1711           2 :                  std::string::npos ||
    1712           2 :              oError.msg.find("can only be exported to WKT2") !=
    1713           0 :                  std::string::npos ||
    1714           0 :              oError.msg.find("can only be exported since WKT2:2019") !=
    1715             :                  std::string::npos))
    1716             :         {
    1717          14 :             CPLErrorReset();
    1718             :             // If we cannot export in the default mode (WKT1), retry with WKT2
    1719          14 :             pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
    1720          14 :                                  PJ_WKT2_2018, aosOptions.List());
    1721          14 :             break;
    1722             :         }
    1723           2 :         CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
    1724             :     }
    1725             : 
    1726       15409 :     if (!pszWKT)
    1727             :     {
    1728           2 :         *ppszResult = CPLStrdup("");
    1729           2 :         proj_destroy(boundCRS);
    1730           2 :         return OGRERR_FAILURE;
    1731             :     }
    1732             : 
    1733       15407 :     if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
    1734             :     {
    1735           5 :         OGR_SRSNode oRoot;
    1736           5 :         oRoot.importFromWkt(&pszWKT);
    1737           5 :         oRoot.StripNodes("AXIS");
    1738           5 :         if (EQUAL(pszFormat, "SFSQL"))
    1739             :         {
    1740           3 :             oRoot.StripNodes("TOWGS84");
    1741             :         }
    1742           5 :         oRoot.StripNodes("AUTHORITY");
    1743           5 :         oRoot.StripNodes("EXTENSION");
    1744             :         OGRErr eErr;
    1745           5 :         if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
    1746           2 :             eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
    1747             :         else
    1748           3 :             eErr = oRoot.exportToWkt(ppszResult);
    1749           5 :         proj_destroy(boundCRS);
    1750           5 :         return eErr;
    1751             :     }
    1752             : 
    1753       15402 :     *ppszResult = CPLStrdup(pszWKT);
    1754       15402 :     proj_destroy(boundCRS);
    1755       15402 :     return OGRERR_NONE;
    1756             : }
    1757             : 
    1758             : /************************************************************************/
    1759             : /*                            exportToWkt()                             */
    1760             : /************************************************************************/
    1761             : 
    1762             : /**
    1763             :  * Convert this SRS into a WKT string.
    1764             :  *
    1765             :  * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
    1766             :  * Issues</a> page for implementation details of WKT 1 in OGR.
    1767             :  *
    1768             :  * @param papszOptions NULL terminated list of options, or NULL. Currently
    1769             :  * supported options are
    1770             :  * <ul>
    1771             :  * <li>MULTILINE=YES/NO. Defaults to NO.</li>
    1772             :  * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
    1773             :  *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
    1774             :  *     node is returned.
    1775             :  *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
    1776             :  *     node is returned.
    1777             :  *     WKT1 is an alias of WKT1_GDAL.
    1778             :  *     WKT2 will default to the latest revision implemented (currently
    1779             :  *     WKT2_2019)
    1780             :  * </li>
    1781             :  * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
    1782             :  * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
    1783             :  * be exported as a compound CRS whose vertical part represents an ellipsoidal
    1784             :  * height (for example for use with LAS 1.4 WKT1).
    1785             :  * Requires PROJ 7.2.1.</li>
    1786             :  * </ul>
    1787             :  *
    1788             :  * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
    1789             :  * configuration option is set to YES, when exporting to WKT1_GDAL, this method
    1790             :  * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
    1791             :  * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
    1792             :  * TOWGS84[] node may be added.
    1793             :  *
    1794             :  * @return a non-empty string if successful.
    1795             :  * @since GDAL 3.9
    1796             :  */
    1797             : 
    1798             : std::string
    1799          92 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
    1800             : {
    1801          92 :     std::string osWKT;
    1802          92 :     char *pszWKT = nullptr;
    1803          92 :     if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
    1804          92 :         osWKT = pszWKT;
    1805          92 :     CPLFree(pszWKT);
    1806         184 :     return osWKT;
    1807             : }
    1808             : 
    1809             : /************************************************************************/
    1810             : /*                           OSRExportToWkt()                           */
    1811             : /************************************************************************/
    1812             : 
    1813             : /**
    1814             :  * \brief Convert this SRS into WKT 1 format.
    1815             :  *
    1816             :  * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
    1817             :  * Issues</a> page for implementation details of WKT in OGR.
    1818             :  *
    1819             :  * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
    1820             :  * option. Valid values are the one of the FORMAT option of
    1821             :  * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
    1822             :  *
    1823             :  * This function is the same as OGRSpatialReference::exportToWkt().
    1824             :  */
    1825             : 
    1826         830 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
    1827             : 
    1828             : {
    1829         830 :     VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
    1830             : 
    1831         830 :     *ppszReturn = nullptr;
    1832             : 
    1833         830 :     return ToPointer(hSRS)->exportToWkt(ppszReturn);
    1834             : }
    1835             : 
    1836             : /************************************************************************/
    1837             : /*                          OSRExportToWktEx()                          */
    1838             : /************************************************************************/
    1839             : 
    1840             : /**
    1841             :  * \brief Convert this SRS into WKT format.
    1842             :  *
    1843             :  * This function is the same as OGRSpatialReference::exportToWkt(char **
    1844             :  * ppszResult,const char* const* papszOptions ) const
    1845             :  *
    1846             :  * @since GDAL 3.0
    1847             :  */
    1848             : 
    1849         874 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
    1850             :                         const char *const *papszOptions)
    1851             : {
    1852         874 :     VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
    1853             : 
    1854         874 :     *ppszReturn = nullptr;
    1855             : 
    1856         874 :     return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
    1857             : }
    1858             : 
    1859             : /************************************************************************/
    1860             : /*                       exportToPROJJSON()                             */
    1861             : /************************************************************************/
    1862             : 
    1863             : /**
    1864             :  * Convert this SRS into a PROJJSON string.
    1865             :  *
    1866             :  * Note that the returned JSON string should be freed with
    1867             :  * CPLFree() when no longer needed.  It is the responsibility of the caller.
    1868             :  *
    1869             :  * @param ppszResult the resulting string is returned in this pointer.
    1870             :  * @param papszOptions NULL terminated list of options, or NULL. Currently
    1871             :  * supported options are
    1872             :  * <ul>
    1873             :  * <li>MULTILINE=YES/NO. Defaults to YES</li>
    1874             :  * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
    1875             :  * on).</li>
    1876             :  * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
    1877             :  * disable it.</li>
    1878             :  * </ul>
    1879             :  *
    1880             :  * @return OGRERR_NONE if successful.
    1881             :  * @since GDAL 3.1 and PROJ 6.2
    1882             :  */
    1883             : 
    1884        2077 : OGRErr OGRSpatialReference::exportToPROJJSON(
    1885             :     char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
    1886             : {
    1887        2077 :     d->refreshProjObj();
    1888        2077 :     if (!d->m_pj_crs)
    1889             :     {
    1890           0 :         *ppszResult = nullptr;
    1891           0 :         return OGRERR_FAILURE;
    1892             :     }
    1893             : 
    1894             :     const char *pszPROJJSON =
    1895        2077 :         proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
    1896             : 
    1897        2077 :     if (!pszPROJJSON)
    1898             :     {
    1899           0 :         *ppszResult = CPLStrdup("");
    1900           0 :         return OGRERR_FAILURE;
    1901             :     }
    1902             : 
    1903        2077 :     *ppszResult = CPLStrdup(pszPROJJSON);
    1904        2077 :     return OGRERR_NONE;
    1905             : }
    1906             : 
    1907             : /************************************************************************/
    1908             : /*                          OSRExportToPROJJSON()                       */
    1909             : /************************************************************************/
    1910             : 
    1911             : /**
    1912             :  * \brief Convert this SRS into PROJJSON format.
    1913             :  *
    1914             :  * This function is the same as OGRSpatialReference::exportToPROJJSON() const
    1915             :  *
    1916             :  * @since GDAL 3.1 and PROJ 6.2
    1917             :  */
    1918             : 
    1919          51 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
    1920             :                            const char *const *papszOptions)
    1921             : {
    1922          51 :     VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
    1923             : 
    1924          51 :     *ppszReturn = nullptr;
    1925             : 
    1926          51 :     return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
    1927             : }
    1928             : 
    1929             : /************************************************************************/
    1930             : /*                           importFromWkt()                            */
    1931             : /************************************************************************/
    1932             : 
    1933             : /**
    1934             :  * \brief Import from WKT string.
    1935             :  *
    1936             :  * This method will wipe the existing SRS definition, and
    1937             :  * reassign it based on the contents of the passed WKT string.  Only as
    1938             :  * much of the input string as needed to construct this SRS is consumed from
    1939             :  * the input string, and the input string pointer
    1940             :  * is then updated to point to the remaining (unused) input.
    1941             :  *
    1942             :  * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
    1943             :  * the CRS contained in it will be used to fill the OGRSpatialReference object,
    1944             :  * and the coordinate epoch potentially present used as the coordinate epoch
    1945             :  * property of the OGRSpatialReference object.
    1946             :  *
    1947             :  * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
    1948             :  * Issues</a> page for implementation details of WKT in OGR.
    1949             :  *
    1950             :  * This method is the same as the C function OSRImportFromWkt().
    1951             :  *
    1952             :  * @param ppszInput Pointer to pointer to input.  The pointer is updated to
    1953             :  * point to remaining unused input text.
    1954             :  *
    1955             :  * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
    1956             :  * fails for any reason.
    1957             :  * @since GDAL 2.3
    1958             :  */
    1959             : 
    1960       26449 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
    1961             : 
    1962             : {
    1963       26449 :     return importFromWkt(ppszInput, nullptr);
    1964             : }
    1965             : 
    1966             : /************************************************************************/
    1967             : /*                           importFromWkt()                            */
    1968             : /************************************************************************/
    1969             : 
    1970             : /*! @cond Doxygen_Suppress */
    1971             : 
    1972          16 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
    1973             :                                           CSLConstList papszOptions)
    1974             : 
    1975             : {
    1976          16 :     return importFromWkt(&pszInput, papszOptions);
    1977             : }
    1978             : 
    1979       26465 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
    1980             :                                           CSLConstList papszOptions)
    1981             : 
    1982             : {
    1983       26465 :     if (!ppszInput || !*ppszInput)
    1984           0 :         return OGRERR_FAILURE;
    1985             : 
    1986       26465 :     if (strlen(*ppszInput) > 100 * 1000 &&
    1987           0 :         CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
    1988             :     {
    1989           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1990             :                  "Suspiciously large input for importFromWkt(). Rejecting it. "
    1991             :                  "You can remove this limitation by definition the "
    1992             :                  "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
    1993           0 :         return OGRERR_FAILURE;
    1994             :     }
    1995             : 
    1996       26465 :     Clear();
    1997             : 
    1998       26465 :     bool canCache = false;
    1999       26465 :     auto tlsCache = OSRGetProjTLSCache();
    2000       52930 :     std::string osWkt;
    2001       26465 :     if (**ppszInput)
    2002             :     {
    2003       25927 :         osWkt = *ppszInput;
    2004       25927 :         auto cachedObj = tlsCache->GetPJForWKT(osWkt);
    2005       25927 :         if (cachedObj)
    2006             :         {
    2007       24262 :             d->setPjCRS(cachedObj);
    2008             :         }
    2009             :         else
    2010             :         {
    2011        3330 :             CPLStringList aosOptions(papszOptions);
    2012        1665 :             if (aosOptions.FetchNameValue("STRICT") == nullptr)
    2013        1665 :                 aosOptions.SetNameValue("STRICT", "NO");
    2014        1665 :             PROJ_STRING_LIST warnings = nullptr;
    2015        1665 :             PROJ_STRING_LIST errors = nullptr;
    2016        1665 :             auto ctxt = d->getPROJContext();
    2017        1665 :             auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
    2018             :                                            &warnings, &errors);
    2019        1665 :             d->setPjCRS(pj);
    2020             : 
    2021        1714 :             for (auto iter = warnings; iter && *iter; ++iter)
    2022             :             {
    2023          49 :                 d->m_wktImportWarnings.push_back(*iter);
    2024             :             }
    2025        1906 :             for (auto iter = errors; iter && *iter; ++iter)
    2026             :             {
    2027         241 :                 d->m_wktImportErrors.push_back(*iter);
    2028         241 :                 if (!d->m_pj_crs)
    2029             :                 {
    2030          35 :                     CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
    2031             :                 }
    2032             :             }
    2033        1665 :             if (warnings == nullptr && errors == nullptr)
    2034             :             {
    2035        1382 :                 canCache = true;
    2036             :             }
    2037        1665 :             proj_string_list_destroy(warnings);
    2038        1665 :             proj_string_list_destroy(errors);
    2039             :         }
    2040             :     }
    2041       26465 :     if (!d->m_pj_crs)
    2042         573 :         return OGRERR_CORRUPT_DATA;
    2043             : 
    2044             :     // Only accept CRS objects
    2045       25892 :     if (!proj_is_crs(d->m_pj_crs))
    2046             :     {
    2047           0 :         Clear();
    2048           0 :         return OGRERR_CORRUPT_DATA;
    2049             :     }
    2050             : 
    2051       25892 :     if (canCache)
    2052             :     {
    2053        1382 :         tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
    2054             :     }
    2055             : 
    2056       25892 :     if (strstr(*ppszInput, "CENTER_LONG"))
    2057             :     {
    2058           0 :         auto poRoot = new OGR_SRSNode();
    2059           0 :         d->setRoot(poRoot);
    2060           0 :         const char *pszTmp = *ppszInput;
    2061           0 :         poRoot->importFromWkt(&pszTmp);
    2062           0 :         d->m_bHasCenterLong = true;
    2063             :     }
    2064             : 
    2065             :     // TODO? we don't really update correctly since we assume that the
    2066             :     // passed string is only WKT.
    2067       25892 :     *ppszInput += strlen(*ppszInput);
    2068       25892 :     return OGRERR_NONE;
    2069             : 
    2070             : #if no_longer_implemented_for_now
    2071             :     /* -------------------------------------------------------------------- */
    2072             :     /*      The following seems to try and detect and unconsumed            */
    2073             :     /*      VERTCS[] coordinate system definition (ESRI style) and to       */
    2074             :     /*      import and attach it to the existing root.  Likely we will      */
    2075             :     /*      need to extend this somewhat to bring it into an acceptable     */
    2076             :     /*      OGRSpatialReference organization at some point.                 */
    2077             :     /* -------------------------------------------------------------------- */
    2078             :     if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
    2079             :     {
    2080             :         if (((*ppszInput)[0]) == ',')
    2081             :             (*ppszInput)++;
    2082             :         OGR_SRSNode *poNewChild = new OGR_SRSNode();
    2083             :         poRoot->AddChild(poNewChild);
    2084             :         return poNewChild->importFromWkt(ppszInput);
    2085             :     }
    2086             : #endif
    2087             : }
    2088             : 
    2089             : /*! @endcond */
    2090             : 
    2091             : /**
    2092             :  * \brief Import from WKT string.
    2093             :  *
    2094             :  * This method will wipe the existing SRS definition, and
    2095             :  * reassign it based on the contents of the passed WKT string.  Only as
    2096             :  * much of the input string as needed to construct this SRS is consumed from
    2097             :  * the input string, and the input string pointer
    2098             :  * is then updated to point to the remaining (unused) input.
    2099             :  *
    2100             :  * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
    2101             :  * Issues</a> page for implementation details of WKT in OGR.
    2102             :  *
    2103             :  * This method is the same as the C function OSRImportFromWkt().
    2104             :  *
    2105             :  * @param ppszInput Pointer to pointer to input.  The pointer is updated to
    2106             :  * point to remaining unused input text.
    2107             :  *
    2108             :  * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
    2109             :  * fails for any reason.
    2110             :  * @deprecated GDAL 2.3. Use importFromWkt(const char**) or importFromWkt(const
    2111             :  * char*)
    2112             :  */
    2113             : 
    2114           0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
    2115             : 
    2116             : {
    2117           0 :     return importFromWkt(const_cast<const char **>(ppszInput));
    2118             : }
    2119             : 
    2120             : /**
    2121             :  * \brief Import from WKT string.
    2122             :  *
    2123             :  * This method will wipe the existing SRS definition, and
    2124             :  * reassign it based on the contents of the passed WKT string.  Only as
    2125             :  * much of the input string as needed to construct this SRS is consumed from
    2126             :  * the input string, and the input string pointer
    2127             :  * is then updated to point to the remaining (unused) input.
    2128             :  *
    2129             :  * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
    2130             :  * Issues</a> page for implementation details of WKT in OGR.
    2131             :  *
    2132             :  * @param pszInput Input WKT
    2133             :  *
    2134             :  * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
    2135             :  * fails for any reason.
    2136             :  * @since GDAL 2.3
    2137             :  */
    2138             : 
    2139       26156 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
    2140             : {
    2141       26156 :     return importFromWkt(&pszInput);
    2142             : }
    2143             : 
    2144             : /************************************************************************/
    2145             : /*                              Validate()                              */
    2146             : /************************************************************************/
    2147             : 
    2148             : /**
    2149             :  * \brief Validate CRS imported with importFromWkt() or with modified with
    2150             :  * direct node manipulations. Otherwise the CRS should be always valid.
    2151             :  *
    2152             :  * This method attempts to verify that the spatial reference system is
    2153             :  * well formed, and consists of known tokens.  The validation is not
    2154             :  * comprehensive.
    2155             :  *
    2156             :  * This method is the same as the C function OSRValidate().
    2157             :  *
    2158             :  * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
    2159             :  * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
    2160             :  * but contains non-standard PROJECTION[] values.
    2161             :  */
    2162             : 
    2163         116 : OGRErr OGRSpatialReference::Validate() const
    2164             : 
    2165             : {
    2166         154 :     for (const auto &str : d->m_wktImportErrors)
    2167             :     {
    2168          38 :         CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
    2169             :     }
    2170         116 :     for (const auto &str : d->m_wktImportWarnings)
    2171             :     {
    2172           0 :         CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
    2173             :     }
    2174         116 :     if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
    2175             :     {
    2176          37 :         return OGRERR_CORRUPT_DATA;
    2177             :     }
    2178          79 :     if (!d->m_wktImportWarnings.empty())
    2179             :     {
    2180           0 :         return OGRERR_UNSUPPORTED_SRS;
    2181             :     }
    2182          79 :     return OGRERR_NONE;
    2183             : }
    2184             : 
    2185             : /************************************************************************/
    2186             : /*                            OSRValidate()                             */
    2187             : /************************************************************************/
    2188             : /**
    2189             :  * \brief Validate SRS tokens.
    2190             :  *
    2191             :  * This function is the same as the C++ method OGRSpatialReference::Validate().
    2192             :  */
    2193         114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
    2194             : 
    2195             : {
    2196         114 :     VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
    2197             : 
    2198         114 :     return OGRSpatialReference::FromHandle(hSRS)->Validate();
    2199             : }
    2200             : 
    2201             : /************************************************************************/
    2202             : /*                          OSRImportFromWkt()                          */
    2203             : /************************************************************************/
    2204             : 
    2205             : /**
    2206             :  * \brief Import from WKT string.
    2207             :  *
    2208             :  * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
    2209             :  * Issues</a> page for implementation details of WKT in OGR.
    2210             :  *
    2211             :  * This function is the same as OGRSpatialReference::importFromWkt().
    2212             :  */
    2213             : 
    2214         293 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
    2215             : 
    2216             : {
    2217         293 :     VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
    2218             : 
    2219         293 :     return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
    2220             : }
    2221             : 
    2222             : /************************************************************************/
    2223             : /*                              SetNode()                               */
    2224             : /************************************************************************/
    2225             : 
    2226             : /**
    2227             :  * \brief Set attribute value in spatial reference.
    2228             :  *
    2229             :  * Missing intermediate nodes in the path will be created if not already
    2230             :  * in existence.  If the attribute has no children one will be created and
    2231             :  * assigned the value otherwise the zeroth child will be assigned the value.
    2232             :  *
    2233             :  * This method does the same as the C function OSRSetAttrValue().
    2234             :  *
    2235             :  * @param pszNodePath full path to attribute to be set.  For instance
    2236             :  * "PROJCS|GEOGCS|UNIT".
    2237             :  *
    2238             :  * @param pszNewNodeValue value to be assigned to node, such as "meter".
    2239             :  * This may be NULL if you just want to force creation of the intermediate
    2240             :  * path.
    2241             :  *
    2242             :  * @return OGRERR_NONE on success.
    2243             :  */
    2244             : 
    2245         534 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
    2246             :                                     const char *pszNewNodeValue)
    2247             : 
    2248             : {
    2249             :     char **papszPathTokens =
    2250         534 :         CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
    2251             : 
    2252         534 :     if (CSLCount(papszPathTokens) < 1)
    2253             :     {
    2254           0 :         CSLDestroy(papszPathTokens);
    2255           0 :         return OGRERR_FAILURE;
    2256             :     }
    2257             : 
    2258         935 :     if (GetRoot() == nullptr ||
    2259         401 :         !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
    2260             :     {
    2261         241 :         if (EQUAL(papszPathTokens[0], "PROJCS") &&
    2262         104 :             CSLCount(papszPathTokens) == 1)
    2263             :         {
    2264         104 :             CSLDestroy(papszPathTokens);
    2265         104 :             return SetProjCS(pszNewNodeValue);
    2266             :         }
    2267             :         else
    2268             :         {
    2269          33 :             SetRoot(new OGR_SRSNode(papszPathTokens[0]));
    2270             :         }
    2271             :     }
    2272             : 
    2273         430 :     OGR_SRSNode *poNode = GetRoot();
    2274         667 :     for (int i = 1; papszPathTokens[i] != nullptr; i++)
    2275             :     {
    2276         237 :         int j = 0;  // Used after for.
    2277             : 
    2278         582 :         for (; j < poNode->GetChildCount(); j++)
    2279             :         {
    2280         534 :             if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
    2281             :             {
    2282         189 :                 poNode = poNode->GetChild(j);
    2283         189 :                 j = -1;
    2284         189 :                 break;
    2285             :             }
    2286             :         }
    2287             : 
    2288         237 :         if (j != -1)
    2289             :         {
    2290          48 :             OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
    2291          48 :             poNode->AddChild(poNewNode);
    2292          48 :             poNode = poNewNode;
    2293             :         }
    2294             :     }
    2295             : 
    2296         430 :     CSLDestroy(papszPathTokens);
    2297             : 
    2298         430 :     if (pszNewNodeValue != nullptr)
    2299             :     {
    2300         430 :         if (poNode->GetChildCount() > 0)
    2301         349 :             poNode->GetChild(0)->SetValue(pszNewNodeValue);
    2302             :         else
    2303          81 :             poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
    2304             :     };
    2305         430 :     return OGRERR_NONE;
    2306             : }
    2307             : 
    2308             : /************************************************************************/
    2309             : /*                          OSRSetAttrValue()                           */
    2310             : /************************************************************************/
    2311             : 
    2312             : /**
    2313             :  * \brief Set attribute value in spatial reference.
    2314             :  *
    2315             :  * This function is the same as OGRSpatialReference::SetNode()
    2316             :  */
    2317           1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
    2318             :                                    const char *pszPath, const char *pszValue)
    2319             : 
    2320             : {
    2321           1 :     VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
    2322             : 
    2323           1 :     return ToPointer(hSRS)->SetNode(pszPath, pszValue);
    2324             : }
    2325             : 
    2326             : /************************************************************************/
    2327             : /*                              SetNode()                               */
    2328             : /************************************************************************/
    2329             : 
    2330             : /**
    2331             :  * \brief Set attribute value in spatial reference.
    2332             :  *
    2333             :  * Missing intermediate nodes in the path will be created if not already
    2334             :  * in existence.  If the attribute has no children one will be created and
    2335             :  * assigned the value otherwise the zeroth child will be assigned the value.
    2336             :  *
    2337             :  * This method does the same as the C function OSRSetAttrValue().
    2338             :  *
    2339             :  * @param pszNodePath full path to attribute to be set.  For instance
    2340             :  * "PROJCS|GEOGCS|UNIT".
    2341             :  *
    2342             :  * @param dfValue value to be assigned to node.
    2343             :  *
    2344             :  * @return OGRERR_NONE on success.
    2345             :  */
    2346             : 
    2347           0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
    2348             : 
    2349             : {
    2350           0 :     char szValue[64] = {'\0'};
    2351             : 
    2352           0 :     if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
    2353           0 :         snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
    2354             :     else
    2355           0 :         OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
    2356             : 
    2357           0 :     return SetNode(pszNodePath, szValue);
    2358             : }
    2359             : 
    2360             : /************************************************************************/
    2361             : /*                          SetAngularUnits()                           */
    2362             : /************************************************************************/
    2363             : 
    2364             : /**
    2365             :  * \brief Set the angular units for the geographic coordinate system.
    2366             :  *
    2367             :  * This method creates a UNIT subnode with the specified values as a
    2368             :  * child of the GEOGCS node.
    2369             :  *
    2370             :  * This method does the same as the C function OSRSetAngularUnits().
    2371             :  *
    2372             :  * @param pszUnitsName the units name to be used.  Some preferred units
    2373             :  * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
    2374             :  *
    2375             :  * @param dfInRadians the value to multiple by an angle in the indicated
    2376             :  * units to transform to radians.  Some standard conversion factors can
    2377             :  * be found in ogr_srs_api.h.
    2378             :  *
    2379             :  * @return OGRERR_NONE on success.
    2380             :  */
    2381             : 
    2382         990 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
    2383             :                                             double dfInRadians)
    2384             : 
    2385             : {
    2386         990 :     d->bNormInfoSet = FALSE;
    2387             : 
    2388         990 :     d->refreshProjObj();
    2389         990 :     if (!d->m_pj_crs)
    2390           0 :         return OGRERR_FAILURE;
    2391         990 :     auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
    2392         990 :     if (!geodCRS)
    2393           0 :         return OGRERR_FAILURE;
    2394         990 :     proj_destroy(geodCRS);
    2395         990 :     d->demoteFromBoundCRS();
    2396         990 :     d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
    2397             :                                                pszUnitsName, dfInRadians,
    2398             :                                                nullptr, nullptr));
    2399         990 :     d->undoDemoteFromBoundCRS();
    2400             : 
    2401         990 :     d->m_osAngularUnits = pszUnitsName;
    2402         990 :     d->m_dfAngularUnitToRadian = dfInRadians;
    2403             : 
    2404         990 :     return OGRERR_NONE;
    2405             : }
    2406             : 
    2407             : /************************************************************************/
    2408             : /*                         OSRSetAngularUnits()                         */
    2409             : /************************************************************************/
    2410             : 
    2411             : /**
    2412             :  * \brief Set the angular units for the geographic coordinate system.
    2413             :  *
    2414             :  * This function is the same as OGRSpatialReference::SetAngularUnits()
    2415             :  */
    2416          36 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
    2417             :                           double dfInRadians)
    2418             : 
    2419             : {
    2420          36 :     VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
    2421             : 
    2422          36 :     return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
    2423             : }
    2424             : 
    2425             : /************************************************************************/
    2426             : /*                          GetAngularUnits()                           */
    2427             : /************************************************************************/
    2428             : 
    2429             : /**
    2430             :  * \brief Fetch angular geographic coordinate system units.
    2431             :  *
    2432             :  * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
    2433             :  * will be assumed.  This method only checks directly under the GEOGCS node
    2434             :  * for units.
    2435             :  *
    2436             :  * This method does the same thing as the C function OSRGetAngularUnits().
    2437             :  *
    2438             :  * @param ppszName a pointer to be updated with the pointer to the units name.
    2439             :  * The returned value remains internal to the OGRSpatialReference and should
    2440             :  * not be freed, or modified.  It may be invalidated on the next
    2441             :  * OGRSpatialReference call.
    2442             :  *
    2443             :  * @return the value to multiply by angular distances to transform them to
    2444             :  * radians.
    2445             :  * @since GDAL 2.3.0
    2446             :  */
    2447             : 
    2448        3521 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
    2449             : 
    2450             : {
    2451        3521 :     d->refreshProjObj();
    2452             : 
    2453        3521 :     if (!d->m_osAngularUnits.empty())
    2454             :     {
    2455         373 :         if (ppszName != nullptr)
    2456         209 :             *ppszName = d->m_osAngularUnits.c_str();
    2457         373 :         return d->m_dfAngularUnitToRadian;
    2458             :     }
    2459             : 
    2460             :     do
    2461             :     {
    2462        3148 :         if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
    2463             :         {
    2464         113 :             break;
    2465             :         }
    2466             : 
    2467             :         auto geodCRS =
    2468        3037 :             proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
    2469        3037 :         if (!geodCRS)
    2470             :         {
    2471           0 :             break;
    2472             :         }
    2473             :         auto coordSys =
    2474        3037 :             proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
    2475        3037 :         proj_destroy(geodCRS);
    2476        3037 :         if (!coordSys)
    2477             :         {
    2478           0 :             break;
    2479             :         }
    2480        3037 :         if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
    2481             :             PJ_CS_TYPE_ELLIPSOIDAL)
    2482             :         {
    2483           2 :             proj_destroy(coordSys);
    2484           2 :             break;
    2485             :         }
    2486             : 
    2487        3035 :         double dfConvFactor = 0.0;
    2488        3035 :         const char *pszUnitName = nullptr;
    2489        3035 :         if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
    2490             :                                    nullptr, nullptr, &dfConvFactor,
    2491             :                                    &pszUnitName, nullptr, nullptr))
    2492             :         {
    2493           0 :             proj_destroy(coordSys);
    2494           0 :             break;
    2495             :         }
    2496             : 
    2497        3035 :         d->m_osAngularUnits = pszUnitName;
    2498             : 
    2499        3035 :         proj_destroy(coordSys);
    2500        3035 :         d->m_dfAngularUnitToRadian = dfConvFactor;
    2501             :     } while (false);
    2502             : 
    2503        3148 :     if (d->m_osAngularUnits.empty())
    2504             :     {
    2505         113 :         d->m_osAngularUnits = "degree";
    2506         113 :         d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
    2507             :     }
    2508             : 
    2509        3148 :     if (ppszName != nullptr)
    2510        1909 :         *ppszName = d->m_osAngularUnits.c_str();
    2511        3148 :     return d->m_dfAngularUnitToRadian;
    2512             : }
    2513             : 
    2514             : /**
    2515             :  * \brief Fetch angular geographic coordinate system units.
    2516             :  *
    2517             :  * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
    2518             :  * will be assumed.  This method only checks directly under the GEOGCS node
    2519             :  * for units.
    2520             :  *
    2521             :  * This method does the same thing as the C function OSRGetAngularUnits().
    2522             :  *
    2523             :  * @param ppszName a pointer to be updated with the pointer to the units name.
    2524             :  * The returned value remains internal to the OGRSpatialReference and should
    2525             :  * not be freed, or modified.  It may be invalidated on the next
    2526             :  * OGRSpatialReference call.
    2527             :  *
    2528             :  * @return the value to multiply by angular distances to transform them to
    2529             :  * radians.
    2530             :  * @deprecated GDAL 2.3.0. Use GetAngularUnits(const char**) const.
    2531             :  */
    2532             : 
    2533           0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
    2534             : 
    2535             : {
    2536           0 :     return GetAngularUnits(const_cast<const char **>(ppszName));
    2537             : }
    2538             : 
    2539             : /************************************************************************/
    2540             : /*                         OSRGetAngularUnits()                         */
    2541             : /************************************************************************/
    2542             : 
    2543             : /**
    2544             :  * \brief Fetch angular geographic coordinate system units.
    2545             :  *
    2546             :  * This function is the same as OGRSpatialReference::GetAngularUnits()
    2547             :  */
    2548           1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
    2549             : 
    2550             : {
    2551           1 :     VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
    2552             : 
    2553           1 :     return ToPointer(hSRS)->GetAngularUnits(
    2554           1 :         const_cast<const char **>(ppszName));
    2555             : }
    2556             : 
    2557             : /************************************************************************/
    2558             : /*                 SetLinearUnitsAndUpdateParameters()                  */
    2559             : /************************************************************************/
    2560             : 
    2561             : /**
    2562             :  * \brief Set the linear units for the projection.
    2563             :  *
    2564             :  * This method creates a UNIT subnode with the specified values as a
    2565             :  * child of the PROJCS or LOCAL_CS node.   It works the same as the
    2566             :  * SetLinearUnits() method, but it also updates all existing linear
    2567             :  * projection parameter values from the old units to the new units.
    2568             :  *
    2569             :  * @param pszName the units name to be used.  Some preferred units
    2570             :  * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
    2571             :  * and SRS_UL_US_FOOT.
    2572             :  *
    2573             :  * @param dfInMeters the value to multiple by a length in the indicated
    2574             :  * units to transform to meters.  Some standard conversion factors can
    2575             :  * be found in ogr_srs_api.h.
    2576             :  *
    2577             :  * @param pszUnitAuthority Unit authority name. Or nullptr
    2578             :  *
    2579             :  * @param pszUnitCode Unit code. Or nullptr
    2580             :  *
    2581             :  * @return OGRERR_NONE on success.
    2582             :  */
    2583             : 
    2584          36 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
    2585             :     const char *pszName, double dfInMeters, const char *pszUnitAuthority,
    2586             :     const char *pszUnitCode)
    2587             : 
    2588             : {
    2589          36 :     if (dfInMeters <= 0.0)
    2590           0 :         return OGRERR_FAILURE;
    2591             : 
    2592          36 :     d->refreshProjObj();
    2593          36 :     if (!d->m_pj_crs)
    2594           0 :         return OGRERR_FAILURE;
    2595             : 
    2596          36 :     d->demoteFromBoundCRS();
    2597          36 :     if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
    2598             :     {
    2599          72 :         d->setPjCRS(proj_crs_alter_parameters_linear_unit(
    2600          36 :             d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
    2601             :             pszUnitAuthority, pszUnitCode, true));
    2602             :     }
    2603          36 :     d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
    2604             :                                               pszName, dfInMeters,
    2605             :                                               pszUnitAuthority, pszUnitCode));
    2606          36 :     d->undoDemoteFromBoundCRS();
    2607             : 
    2608          36 :     d->m_osLinearUnits = pszName;
    2609          36 :     d->dfToMeter = dfInMeters;
    2610             : 
    2611          36 :     return OGRERR_NONE;
    2612             : }
    2613             : 
    2614             : /************************************************************************/
    2615             : /*                OSRSetLinearUnitsAndUpdateParameters()                */
    2616             : /************************************************************************/
    2617             : 
    2618             : /**
    2619             :  * \brief Set the linear units for the projection.
    2620             :  *
    2621             :  * This function is the same as
    2622             :  *   OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
    2623             :  */
    2624           1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
    2625             :                                             const char *pszUnits,
    2626             :                                             double dfInMeters)
    2627             : 
    2628             : {
    2629           1 :     VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
    2630             :                       OGRERR_FAILURE);
    2631             : 
    2632           1 :     return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
    2633           1 :                                                               dfInMeters);
    2634             : }
    2635             : 
    2636             : /************************************************************************/
    2637             : /*                           SetLinearUnits()                           */
    2638             : /************************************************************************/
    2639             : 
    2640             : /**
    2641             :  * \brief Set the linear units for the projection.
    2642             :  *
    2643             :  * This method creates a UNIT subnode with the specified values as a
    2644             :  * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
    2645             :  * Geographic 3D CRS the vertical axis units will be set.
    2646             :  *
    2647             :  * This method does the same as the C function OSRSetLinearUnits().
    2648             :  *
    2649             :  * @param pszUnitsName the units name to be used.  Some preferred units
    2650             :  * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
    2651             :  * and SRS_UL_US_FOOT.
    2652             :  *
    2653             :  * @param dfInMeters the value to multiple by a length in the indicated
    2654             :  * units to transform to meters.  Some standard conversion factors can
    2655             :  * be found in ogr_srs_api.h.
    2656             :  *
    2657             :  * @return OGRERR_NONE on success.
    2658             :  */
    2659             : 
    2660        6281 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
    2661             :                                            double dfInMeters)
    2662             : 
    2663             : {
    2664        6281 :     return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
    2665             : }
    2666             : 
    2667             : /************************************************************************/
    2668             : /*                         OSRSetLinearUnits()                          */
    2669             : /************************************************************************/
    2670             : 
    2671             : /**
    2672             :  * \brief Set the linear units for the projection.
    2673             :  *
    2674             :  * This function is the same as OGRSpatialReference::SetLinearUnits()
    2675             :  */
    2676           7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
    2677             :                          double dfInMeters)
    2678             : 
    2679             : {
    2680           7 :     VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
    2681             : 
    2682           7 :     return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
    2683             : }
    2684             : 
    2685             : /************************************************************************/
    2686             : /*                        SetTargetLinearUnits()                        */
    2687             : /************************************************************************/
    2688             : 
    2689             : /**
    2690             :  * \brief Set the linear units for the projection.
    2691             :  *
    2692             :  * This method creates a UNIT subnode with the specified values as a
    2693             :  * child of the target node.
    2694             :  *
    2695             :  * This method does the same as the C function OSRSetTargetLinearUnits().
    2696             :  *
    2697             :  * @param pszTargetKey the keyword to set the linear units for.
    2698             :  * i.e. "PROJCS" or "VERT_CS"
    2699             :  *
    2700             :  * @param pszUnitsName the units name to be used.  Some preferred units
    2701             :  * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
    2702             :  * and SRS_UL_US_FOOT.
    2703             :  *
    2704             :  * @param dfInMeters the value to multiple by a length in the indicated
    2705             :  * units to transform to meters.  Some standard conversion factors can
    2706             :  * be found in ogr_srs_api.h.
    2707             :  *
    2708             :  * @param pszUnitAuthority Unit authority name. Or nullptr
    2709             :  *
    2710             :  * @param pszUnitCode Unit code. Or nullptr
    2711             :  *
    2712             :  * @return OGRERR_NONE on success.
    2713             :  *
    2714             :  * @since OGR 1.9.0
    2715             :  */
    2716             : 
    2717        9637 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
    2718             :                                                  const char *pszUnitsName,
    2719             :                                                  double dfInMeters,
    2720             :                                                  const char *pszUnitAuthority,
    2721             :                                                  const char *pszUnitCode)
    2722             : 
    2723             : {
    2724        9637 :     if (dfInMeters <= 0.0)
    2725           0 :         return OGRERR_FAILURE;
    2726             : 
    2727        9637 :     d->refreshProjObj();
    2728        9637 :     pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
    2729        9637 :     if (pszTargetKey == nullptr)
    2730             :     {
    2731        9637 :         if (!d->m_pj_crs)
    2732           0 :             return OGRERR_FAILURE;
    2733             : 
    2734        9637 :         d->demoteFromBoundCRS();
    2735        9637 :         if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
    2736             :         {
    2737       13508 :             d->setPjCRS(proj_crs_alter_parameters_linear_unit(
    2738        6754 :                 d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
    2739             :                 pszUnitAuthority, pszUnitCode, false));
    2740             :         }
    2741       19274 :         d->setPjCRS(proj_crs_alter_cs_linear_unit(
    2742        9637 :             d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
    2743             :             pszUnitAuthority, pszUnitCode));
    2744        9637 :         d->undoDemoteFromBoundCRS();
    2745             : 
    2746        9637 :         d->m_osLinearUnits = pszUnitsName;
    2747        9637 :         d->dfToMeter = dfInMeters;
    2748             : 
    2749        9637 :         return OGRERR_NONE;
    2750             :     }
    2751             : 
    2752           0 :     OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
    2753             : 
    2754           0 :     if (poCS == nullptr)
    2755           0 :         return OGRERR_FAILURE;
    2756             : 
    2757           0 :     char szValue[128] = {'\0'};
    2758           0 :     if (dfInMeters < std::numeric_limits<int>::max() &&
    2759           0 :         dfInMeters > std::numeric_limits<int>::min() &&
    2760           0 :         dfInMeters == static_cast<int>(dfInMeters))
    2761           0 :         snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
    2762             :     else
    2763           0 :         OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
    2764             : 
    2765           0 :     OGR_SRSNode *poUnits = nullptr;
    2766           0 :     if (poCS->FindChild("UNIT") >= 0)
    2767             :     {
    2768           0 :         poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
    2769           0 :         if (poUnits->GetChildCount() < 2)
    2770           0 :             return OGRERR_FAILURE;
    2771           0 :         poUnits->GetChild(0)->SetValue(pszUnitsName);
    2772           0 :         poUnits->GetChild(1)->SetValue(szValue);
    2773           0 :         if (poUnits->FindChild("AUTHORITY") != -1)
    2774           0 :             poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
    2775             :     }
    2776             :     else
    2777             :     {
    2778           0 :         poUnits = new OGR_SRSNode("UNIT");
    2779           0 :         poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
    2780           0 :         poUnits->AddChild(new OGR_SRSNode(szValue));
    2781             : 
    2782           0 :         poCS->AddChild(poUnits);
    2783             :     }
    2784             : 
    2785           0 :     return OGRERR_NONE;
    2786             : }
    2787             : 
    2788             : /************************************************************************/
    2789             : /*                         OSRSetLinearUnits()                          */
    2790             : /************************************************************************/
    2791             : 
    2792             : /**
    2793             :  * \brief Set the linear units for the target node.
    2794             :  *
    2795             :  * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
    2796             :  *
    2797             :  * @since OGR 1.9.0
    2798             :  */
    2799           1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
    2800             :                                const char *pszTargetKey, const char *pszUnits,
    2801             :                                double dfInMeters)
    2802             : 
    2803             : {
    2804           1 :     VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
    2805             : 
    2806           1 :     return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
    2807           1 :                                                  dfInMeters);
    2808             : }
    2809             : 
    2810             : /************************************************************************/
    2811             : /*                           GetLinearUnits()                           */
    2812             : /************************************************************************/
    2813             : 
    2814             : /**
    2815             :  * \brief Fetch linear projection units.
    2816             :  *
    2817             :  * If no units are available, a value of "Meters" and 1.0 will be assumed.
    2818             :  * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
    2819             :  * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
    2820             :  * axis units will be returned.
    2821             :  *
    2822             :  * This method does the same thing as the C function OSRGetLinearUnits()
    2823             :  *
    2824             :  * @param ppszName a pointer to be updated with the pointer to the units name.
    2825             :  * The returned value remains internal to the OGRSpatialReference and should
    2826             :  * not be freed, or modified.  It may be invalidated on the next
    2827             :  * OGRSpatialReference call.
    2828             :  *
    2829             :  * @return the value to multiply by linear distances to transform them to
    2830             :  * meters.
    2831             :  * @deprecated GDAL 2.3.0. Use GetLinearUnits(const char**) const.
    2832             :  */
    2833             : 
    2834           0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
    2835             : 
    2836             : {
    2837           0 :     return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
    2838             : }
    2839             : 
    2840             : /**
    2841             :  * \brief Fetch linear projection units.
    2842             :  *
    2843             :  * If no units are available, a value of "Meters" and 1.0 will be assumed.
    2844             :  * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
    2845             :  * for units.
    2846             :  *
    2847             :  * This method does the same thing as the C function OSRGetLinearUnits()
    2848             :  *
    2849             :  * @param ppszName a pointer to be updated with the pointer to the units name.
    2850             :  * The returned value remains internal to the OGRSpatialReference and should
    2851             :  * not be freed, or modified.  It may be invalidated on the next
    2852             :  * OGRSpatialReference call.
    2853             :  *
    2854             :  * @return the value to multiply by linear distances to transform them to
    2855             :  * meters.
    2856             :  * @since GDAL 2.3.0
    2857             :  */
    2858             : 
    2859       13375 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
    2860             : 
    2861             : {
    2862       13375 :     return GetTargetLinearUnits(nullptr, ppszName);
    2863             : }
    2864             : 
    2865             : /************************************************************************/
    2866             : /*                         OSRGetLinearUnits()                          */
    2867             : /************************************************************************/
    2868             : 
    2869             : /**
    2870             :  * \brief Fetch linear projection units.
    2871             :  *
    2872             :  * This function is the same as OGRSpatialReference::GetLinearUnits()
    2873             :  */
    2874         203 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
    2875             : 
    2876             : {
    2877         203 :     VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
    2878             : 
    2879         203 :     return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
    2880             : }
    2881             : 
    2882             : /************************************************************************/
    2883             : /*                        GetTargetLinearUnits()                        */
    2884             : /************************************************************************/
    2885             : 
    2886             : /**
    2887             :  * \brief Fetch linear units for target.
    2888             :  *
    2889             :  * If no units are available, a value of "Meters" and 1.0 will be assumed.
    2890             :  *
    2891             :  * This method does the same thing as the C function OSRGetTargetLinearUnits()
    2892             :  *
    2893             :  * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
    2894             :  * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
    2895             :  * GEOCCS, GEOGCS and VERT_CS are looked up)
    2896             :  * @param ppszName a pointer to be updated with the pointer to the units name.
    2897             :  * The returned value remains internal to the OGRSpatialReference and should not
    2898             :  * be freed, or modified.  It may be invalidated on the next
    2899             :  * OGRSpatialReference call. ppszName can be set to NULL.
    2900             :  *
    2901             :  * @return the value to multiply by linear distances to transform them to
    2902             :  * meters.
    2903             :  *
    2904             :  * @since OGR 1.9.0
    2905             :  * @deprecated GDAL 2.3.0. Use GetTargetLinearUnits(const char*, const char**)
    2906             :  * const.
    2907             :  */
    2908             : 
    2909       13614 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
    2910             :                                                  const char **ppszName) const
    2911             : 
    2912             : {
    2913       13614 :     d->refreshProjObj();
    2914             : 
    2915       13614 :     pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
    2916       13614 :     if (pszTargetKey == nullptr)
    2917             :     {
    2918             :         // Use cached result if available
    2919       13434 :         if (!d->m_osLinearUnits.empty())
    2920             :         {
    2921        6699 :             if (ppszName)
    2922        6070 :                 *ppszName = d->m_osLinearUnits.c_str();
    2923        6699 :             return d->dfToMeter;
    2924             :         }
    2925             : 
    2926             :         while (true)
    2927             :         {
    2928        6735 :             if (d->m_pj_crs == nullptr)
    2929             :             {
    2930         226 :                 break;
    2931             :             }
    2932             : 
    2933        6509 :             d->demoteFromBoundCRS();
    2934        6509 :             PJ *coordSys = nullptr;
    2935        6509 :             if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
    2936             :             {
    2937          30 :                 for (int iComponent = 0; iComponent < 2; iComponent++)
    2938             :                 {
    2939          30 :                     auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
    2940          30 :                                                        d->m_pj_crs, iComponent);
    2941          30 :                     if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
    2942             :                     {
    2943             :                         auto temp =
    2944           0 :                             proj_get_source_crs(d->getPROJContext(), subCRS);
    2945           0 :                         proj_destroy(subCRS);
    2946           0 :                         subCRS = temp;
    2947             :                     }
    2948          60 :                     if (subCRS &&
    2949          30 :                         (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
    2950          16 :                          proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
    2951          12 :                          proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
    2952             :                     {
    2953          24 :                         coordSys = proj_crs_get_coordinate_system(
    2954             :                             d->getPROJContext(), subCRS);
    2955          24 :                         proj_destroy(subCRS);
    2956          24 :                         break;
    2957             :                     }
    2958           6 :                     else if (subCRS)
    2959             :                     {
    2960           6 :                         proj_destroy(subCRS);
    2961             :                     }
    2962             :                 }
    2963          24 :                 if (coordSys == nullptr)
    2964             :                 {
    2965           0 :                     d->undoDemoteFromBoundCRS();
    2966           0 :                     break;
    2967             :                 }
    2968             :             }
    2969             :             else
    2970             :             {
    2971        6485 :                 coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
    2972        6485 :                                                           d->m_pj_crs);
    2973             :             }
    2974             : 
    2975        6509 :             d->undoDemoteFromBoundCRS();
    2976        6509 :             if (!coordSys)
    2977             :             {
    2978           0 :                 break;
    2979             :             }
    2980        6509 :             auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
    2981             : 
    2982        6509 :             if (csType != PJ_CS_TYPE_CARTESIAN &&
    2983        1303 :                 csType != PJ_CS_TYPE_VERTICAL &&
    2984           0 :                 csType != PJ_CS_TYPE_ELLIPSOIDAL &&
    2985             :                 csType != PJ_CS_TYPE_SPHERICAL)
    2986             :             {
    2987           0 :                 proj_destroy(coordSys);
    2988           0 :                 break;
    2989             :             }
    2990             : 
    2991        6509 :             int axis = 0;
    2992             : 
    2993        6509 :             if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
    2994             :                 csType == PJ_CS_TYPE_SPHERICAL)
    2995             :             {
    2996             :                 const int axisCount =
    2997        1303 :                     proj_cs_get_axis_count(d->getPROJContext(), coordSys);
    2998             : 
    2999        1303 :                 if (axisCount == 3)
    3000             :                 {
    3001           4 :                     axis = 2;
    3002             :                 }
    3003             :                 else
    3004             :                 {
    3005        1299 :                     proj_destroy(coordSys);
    3006        1299 :                     break;
    3007             :                 }
    3008             :             }
    3009             : 
    3010        5210 :             double dfConvFactor = 0.0;
    3011        5210 :             const char *pszUnitName = nullptr;
    3012        5210 :             if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
    3013             :                                        nullptr, nullptr, nullptr, &dfConvFactor,
    3014             :                                        &pszUnitName, nullptr, nullptr))
    3015             :             {
    3016           0 :                 proj_destroy(coordSys);
    3017           0 :                 break;
    3018             :             }
    3019             : 
    3020        5210 :             d->m_osLinearUnits = pszUnitName;
    3021        5210 :             d->dfToMeter = dfConvFactor;
    3022        5210 :             if (ppszName)
    3023         951 :                 *ppszName = d->m_osLinearUnits.c_str();
    3024             : 
    3025        5210 :             proj_destroy(coordSys);
    3026        5210 :             return dfConvFactor;
    3027             :         }
    3028             : 
    3029        1525 :         d->m_osLinearUnits = "unknown";
    3030        1525 :         d->dfToMeter = 1.0;
    3031             : 
    3032        1525 :         if (ppszName != nullptr)
    3033        1346 :             *ppszName = d->m_osLinearUnits.c_str();
    3034        1525 :         return 1.0;
    3035             :     }
    3036             : 
    3037         180 :     const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
    3038             : 
    3039         180 :     if (ppszName != nullptr)
    3040          34 :         *ppszName = "unknown";
    3041             : 
    3042         180 :     if (poCS == nullptr)
    3043         145 :         return 1.0;
    3044             : 
    3045         105 :     for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
    3046             :     {
    3047         105 :         const OGR_SRSNode *poChild = poCS->GetChild(iChild);
    3048             : 
    3049         105 :         if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
    3050             :         {
    3051          35 :             if (ppszName != nullptr)
    3052          34 :                 *ppszName = poChild->GetChild(0)->GetValue();
    3053             : 
    3054          35 :             return CPLAtof(poChild->GetChild(1)->GetValue());
    3055             :         }
    3056             :     }
    3057             : 
    3058           0 :     return 1.0;
    3059             : }
    3060             : 
    3061             : /**
    3062             :  * \brief Fetch linear units for target.
    3063             :  *
    3064             :  * If no units are available, a value of "Meters" and 1.0 will be assumed.
    3065             :  *
    3066             :  * This method does the same thing as the C function OSRGetTargetLinearUnits()
    3067             :  *
    3068             :  * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
    3069             :  * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
    3070             :  * GEOCCS and VERT_CS are looked up)
    3071             :  * @param ppszName a pointer to be updated with the pointer to the units name.
    3072             :  * The returned value remains internal to the OGRSpatialReference and should not
    3073             :  * be freed, or modified.  It may be invalidated on the next
    3074             :  * OGRSpatialReference call. ppszName can be set to NULL.
    3075             :  *
    3076             :  * @return the value to multiply by linear distances to transform them to
    3077             :  * meters.
    3078             :  *
    3079             :  * @since GDAL 2.3.0
    3080             :  */
    3081             : 
    3082           0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
    3083             :                                                  char **ppszName) const
    3084             : 
    3085             : {
    3086           0 :     return GetTargetLinearUnits(pszTargetKey,
    3087           0 :                                 const_cast<const char **>(ppszName));
    3088             : }
    3089             : 
    3090             : /************************************************************************/
    3091             : /*                      OSRGetTargetLinearUnits()                       */
    3092             : /************************************************************************/
    3093             : 
    3094             : /**
    3095             :  * \brief Fetch linear projection units.
    3096             :  *
    3097             :  * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
    3098             :  *
    3099             :  * @since OGR 1.9.0
    3100             :  */
    3101           4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
    3102             :                                const char *pszTargetKey, char **ppszName)
    3103             : 
    3104             : {
    3105           4 :     VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
    3106             : 
    3107           4 :     return ToPointer(hSRS)->GetTargetLinearUnits(
    3108           4 :         pszTargetKey, const_cast<const char **>(ppszName));
    3109             : }
    3110             : 
    3111             : /************************************************************************/
    3112             : /*                          GetPrimeMeridian()                          */
    3113             : /************************************************************************/
    3114             : 
    3115             : /**
    3116             :  * \brief Fetch prime meridian info.
    3117             :  *
    3118             :  * Returns the offset of the prime meridian from greenwich in degrees,
    3119             :  * and the prime meridian name (if requested).   If no PRIMEM value exists
    3120             :  * in the coordinate system definition a value of "Greenwich" and an
    3121             :  * offset of 0.0 is assumed.
    3122             :  *
    3123             :  * If the prime meridian name is returned, the pointer is to an internal
    3124             :  * copy of the name. It should not be freed, altered or depended on after
    3125             :  * the next OGR call.
    3126             :  *
    3127             :  * This method is the same as the C function OSRGetPrimeMeridian().
    3128             :  *
    3129             :  * @param ppszName return location for prime meridian name.  If NULL, name
    3130             :  * is not returned.
    3131             :  *
    3132             :  * @return the offset to the GEOGCS prime meridian from greenwich in decimal
    3133             :  * degrees.
    3134             :  * @deprecated GDAL 2.3.0. Use GetPrimeMeridian(const char**) const.
    3135             :  */
    3136             : 
    3137        1313 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
    3138             : 
    3139             : {
    3140        1313 :     d->refreshProjObj();
    3141             : 
    3142        1313 :     if (!d->m_osPrimeMeridianName.empty())
    3143             :     {
    3144          39 :         if (ppszName != nullptr)
    3145           1 :             *ppszName = d->m_osPrimeMeridianName.c_str();
    3146          39 :         return d->dfFromGreenwich;
    3147             :     }
    3148             : 
    3149             :     while (true)
    3150             :     {
    3151        1274 :         if (!d->m_pj_crs)
    3152           0 :             break;
    3153             : 
    3154        1274 :         auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
    3155        1274 :         if (!pm)
    3156           0 :             break;
    3157             : 
    3158        1274 :         d->m_osPrimeMeridianName = proj_get_name(pm);
    3159        1274 :         if (ppszName)
    3160          30 :             *ppszName = d->m_osPrimeMeridianName.c_str();
    3161        1274 :         double dfLongitude = 0.0;
    3162        1274 :         double dfConvFactor = 0.0;
    3163        1274 :         proj_prime_meridian_get_parameters(
    3164             :             d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
    3165        1274 :         proj_destroy(pm);
    3166        2548 :         d->dfFromGreenwich =
    3167        1274 :             dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
    3168        1274 :         return d->dfFromGreenwich;
    3169             :     }
    3170             : 
    3171           0 :     d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
    3172           0 :     d->dfFromGreenwich = 0.0;
    3173           0 :     if (ppszName != nullptr)
    3174           0 :         *ppszName = d->m_osPrimeMeridianName.c_str();
    3175           0 :     return d->dfFromGreenwich;
    3176             : }
    3177             : 
    3178             : /**
    3179             :  * \brief Fetch prime meridian info.
    3180             :  *
    3181             :  * Returns the offset of the prime meridian from greenwich in degrees,
    3182             :  * and the prime meridian name (if requested).   If no PRIMEM value exists
    3183             :  * in the coordinate system definition a value of "Greenwich" and an
    3184             :  * offset of 0.0 is assumed.
    3185             :  *
    3186             :  * If the prime meridian name is returned, the pointer is to an internal
    3187             :  * copy of the name. It should not be freed, altered or depended on after
    3188             :  * the next OGR call.
    3189             :  *
    3190             :  * This method is the same as the C function OSRGetPrimeMeridian().
    3191             :  *
    3192             :  * @param ppszName return location for prime meridian name.  If NULL, name
    3193             :  * is not returned.
    3194             :  *
    3195             :  * @return the offset to the GEOGCS prime meridian from greenwich in decimal
    3196             :  * degrees.
    3197             :  * @since GDAL 2.3.0
    3198             :  */
    3199             : 
    3200           0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
    3201             : 
    3202             : {
    3203           0 :     return GetPrimeMeridian(const_cast<const char **>(ppszName));
    3204             : }
    3205             : 
    3206             : /************************************************************************/
    3207             : /*                        OSRGetPrimeMeridian()                         */
    3208             : /************************************************************************/
    3209             : 
    3210             : /**
    3211             :  * \brief Fetch prime meridian info.
    3212             :  *
    3213             :  * This function is the same as OGRSpatialReference::GetPrimeMeridian()
    3214             :  */
    3215           1 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
    3216             : 
    3217             : {
    3218           1 :     VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
    3219             : 
    3220           1 :     return ToPointer(hSRS)->GetPrimeMeridian(
    3221           1 :         const_cast<const char **>(ppszName));
    3222             : }
    3223             : 
    3224             : /************************************************************************/
    3225             : /*                             SetGeogCS()                              */
    3226             : /************************************************************************/
    3227             : 
    3228             : /**
    3229             :  * \brief Set geographic coordinate system.
    3230             :  *
    3231             :  * This method is used to set the datum, ellipsoid, prime meridian and
    3232             :  * angular units for a geographic coordinate system.  It can be used on its
    3233             :  * own to establish a geographic spatial reference, or applied to a
    3234             :  * projected coordinate system to establish the underlying geographic
    3235             :  * coordinate system.
    3236             :  *
    3237             :  * This method does the same as the C function OSRSetGeogCS().
    3238             :  *
    3239             :  * @param pszGeogName user visible name for the geographic coordinate system
    3240             :  * (not to serve as a key).
    3241             :  *
    3242             :  * @param pszDatumName key name for this datum.  The OpenGIS specification
    3243             :  * lists some known values, and otherwise EPSG datum names with a standard
    3244             :  * transformation are considered legal keys.
    3245             :  *
    3246             :  * @param pszSpheroidName user visible spheroid name (not to serve as a key)
    3247             :  *
    3248             :  * @param dfSemiMajor the semi major axis of the spheroid.
    3249             :  *
    3250             :  * @param dfInvFlattening the inverse flattening for the spheroid.
    3251             :  * This can be computed from the semi minor axis as
    3252             :  * 1/f = 1.0 / (1.0 - semiminor/semimajor).
    3253             :  *
    3254             :  * @param pszPMName the name of the prime meridian (not to serve as a key)
    3255             :  * If this is NULL a default value of "Greenwich" will be used.
    3256             :  *
    3257             :  * @param dfPMOffset the longitude of Greenwich relative to this prime
    3258             :  * meridian. Always in Degrees
    3259             :  *
    3260             :  * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
    3261             :  * standard names).  If NULL a value of "degrees" will be assumed.
    3262             :  *
    3263             :  * @param dfConvertToRadians value to multiply angular units by to transform
    3264             :  * them to radians.  A value of SRS_UA_DEGREE_CONV will be used if
    3265             :  * pszAngularUnits is NULL.
    3266             :  *
    3267             :  * @return OGRERR_NONE on success.
    3268             :  */
    3269             : 
    3270        6195 : OGRErr OGRSpatialReference::SetGeogCS(
    3271             :     const char *pszGeogName, const char *pszDatumName,
    3272             :     const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
    3273             :     const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
    3274             :     double dfConvertToRadians)
    3275             : 
    3276             : {
    3277        6195 :     d->bNormInfoSet = FALSE;
    3278        6195 :     d->m_osAngularUnits.clear();
    3279        6195 :     d->m_dfAngularUnitToRadian = 0.0;
    3280        6195 :     d->m_osPrimeMeridianName.clear();
    3281        6195 :     d->dfFromGreenwich = 0.0;
    3282             : 
    3283             :     /* -------------------------------------------------------------------- */
    3284             :     /*      For a geocentric coordinate system we want to set the datum     */
    3285             :     /*      and ellipsoid based on the GEOGCS.  Create the GEOGCS in a      */
    3286             :     /*      temporary srs and use the copy method which has special         */
    3287             :     /*      handling for GEOCCS.                                            */
    3288             :     /* -------------------------------------------------------------------- */
    3289        6195 :     if (IsGeocentric())
    3290             :     {
    3291           4 :         OGRSpatialReference oGCS;
    3292             : 
    3293           2 :         oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
    3294             :                        dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
    3295             :                        dfConvertToRadians);
    3296           2 :         return CopyGeogCSFrom(&oGCS);
    3297             :     }
    3298             : 
    3299        6193 :     auto cs = proj_create_ellipsoidal_2D_cs(
    3300             :         d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
    3301             :         dfConvertToRadians);
    3302             :     // Prime meridian expressed in Degree
    3303        6193 :     auto obj = proj_create_geographic_crs(
    3304             :         d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
    3305             :         dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
    3306        6193 :     proj_destroy(cs);
    3307             : 
    3308       10084 :     if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
    3309        3891 :         d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
    3310             :     {
    3311        2302 :         d->setPjCRS(obj);
    3312             :     }
    3313        3891 :     else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
    3314             :     {
    3315        7782 :         d->setPjCRS(
    3316        3891 :             proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
    3317        3891 :         proj_destroy(obj);
    3318             :     }
    3319             :     else
    3320             :     {
    3321           0 :         proj_destroy(obj);
    3322             :     }
    3323             : 
    3324        6193 :     return OGRERR_NONE;
    3325             : }
    3326             : 
    3327             : /************************************************************************/
    3328             : /*                            OSRSetGeogCS()                            */
    3329             : /************************************************************************/
    3330             : 
    3331             : /**
    3332             :  * \brief Set geographic coordinate system.
    3333             :  *
    3334             :  * This function is the same as OGRSpatialReference::SetGeogCS()
    3335             :  */
    3336          26 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
    3337             :                     const char *pszDatumName, const char *pszSpheroidName,
    3338             :                     double dfSemiMajor, double dfInvFlattening,
    3339             :                     const char *pszPMName, double dfPMOffset,
    3340             :                     const char *pszAngularUnits, double dfConvertToRadians)
    3341             : 
    3342             : {
    3343          26 :     VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
    3344             : 
    3345          26 :     return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
    3346             :                                       pszSpheroidName, dfSemiMajor,
    3347             :                                       dfInvFlattening, pszPMName, dfPMOffset,
    3348          26 :                                       pszAngularUnits, dfConvertToRadians);
    3349             : }
    3350             : 
    3351             : /************************************************************************/
    3352             : /*                         SetWellKnownGeogCS()                         */
    3353             : /************************************************************************/
    3354             : 
    3355             : /**
    3356             :  * \brief Set a GeogCS based on well known name.
    3357             :  *
    3358             :  * This may be called on an empty OGRSpatialReference to make a geographic
    3359             :  * coordinate system, or on something with an existing PROJCS node to
    3360             :  * set the underlying geographic coordinate system of a projected coordinate
    3361             :  * system.
    3362             :  *
    3363             :  * The following well known text values are currently supported,
    3364             :  * Except for "EPSG:n", the others are without dependency on EPSG data files:
    3365             :  * <ul>
    3366             :  * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
    3367             :  * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
    3368             :  * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
    3369             :  * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
    3370             :  * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
    3371             :  * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
    3372             :  * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
    3373             :  * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
    3374             :  * </ul>
    3375             :  *
    3376             :  * @param pszName name of well known geographic coordinate system.
    3377             :  * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
    3378             :  * recognised, the target object is already initialized, or an EPSG value
    3379             :  * can't be successfully looked up.
    3380             :  */
    3381             : 
    3382       12787 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
    3383             : 
    3384             : {
    3385             :     /* -------------------------------------------------------------------- */
    3386             :     /*      Check for EPSG authority numbers.                               */
    3387             :     /* -------------------------------------------------------------------- */
    3388       12787 :     if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
    3389             :     {
    3390          84 :         OGRSpatialReference oSRS2;
    3391          42 :         const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
    3392          42 :         if (eErr != OGRERR_NONE)
    3393           0 :             return eErr;
    3394             : 
    3395          42 :         if (!oSRS2.IsGeographic())
    3396           0 :             return OGRERR_FAILURE;
    3397             : 
    3398          42 :         return CopyGeogCSFrom(&oSRS2);
    3399             :     }
    3400             : 
    3401             :     /* -------------------------------------------------------------------- */
    3402             :     /*      Check for simple names.                                         */
    3403             :     /* -------------------------------------------------------------------- */
    3404       12745 :     const char *pszWKT = nullptr;
    3405             : 
    3406       12745 :     if (EQUAL(pszName, "WGS84"))
    3407             :     {
    3408       11927 :         pszWKT = SRS_WKT_WGS84_LAT_LONG;
    3409             :     }
    3410         818 :     else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
    3411             :     {
    3412          94 :         pszWKT =
    3413             :             "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
    3414             :             "ELLIPSOID[\"WGS "
    3415             :             "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
    3416             :             "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
    3417             :             "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
    3418             :             "ANGLEUNIT[\"degree\",0.0174532925199433]],"
    3419             :             "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
    3420             :             "ANGLEUNIT[\"degree\",0.0174532925199433]],"
    3421             :             "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
    3422             :             "ID[\"OGC\",\"CRS84\"]]";
    3423             :     }
    3424         724 :     else if (EQUAL(pszName, "WGS72"))
    3425          48 :         pszWKT =
    3426             :             "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
    3427             :             "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
    3428             :             "AUTHORITY[\"EPSG\",\"6322\"]],"
    3429             :             "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
    3430             :             "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
    3431             :             "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
    3432             :             "AUTHORITY[\"EPSG\",\"4322\"]]";
    3433             : 
    3434         676 :     else if (EQUAL(pszName, "NAD27"))
    3435         175 :         pszWKT =
    3436             :             "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
    3437             :             "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
    3438             :             "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
    3439             :             "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
    3440             :             "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
    3441             :             "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
    3442             :             "AUTHORITY[\"EPSG\",\"4267\"]]";
    3443             : 
    3444         501 :     else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
    3445           0 :         pszWKT =
    3446             :             "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
    3447             :             "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
    3448             :             "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
    3449             :             "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
    3450             :             "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
    3451             :             "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
    3452             : 
    3453         501 :     else if (EQUAL(pszName, "NAD83"))
    3454         497 :         pszWKT =
    3455             :             "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
    3456             :             "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
    3457             :             "AUTHORITY[\"EPSG\",\"7019\"]],"
    3458             :             "AUTHORITY[\"EPSG\",\"6269\"]],"
    3459             :             "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
    3460             :             "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
    3461             :             "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
    3462             :             "\"EPSG\",\"4269\"]]";
    3463             : 
    3464           4 :     else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
    3465           0 :         pszWKT =
    3466             :             "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
    3467             :             "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
    3468             :             "AUTHORITY[\"EPSG\",\"7019\"]],"
    3469             :             "AUTHORITY[\"EPSG\",\"6269\"]],"
    3470             :             "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
    3471             :             "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
    3472             :             "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
    3473             : 
    3474             :     else
    3475           4 :         return OGRERR_FAILURE;
    3476             : 
    3477             :     /* -------------------------------------------------------------------- */
    3478             :     /*      Import the WKT                                                  */
    3479             :     /* -------------------------------------------------------------------- */
    3480       25482 :     OGRSpatialReference oSRS2;
    3481       12741 :     const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
    3482       12741 :     if (eErr != OGRERR_NONE)
    3483           0 :         return eErr;
    3484             : 
    3485             :     /* -------------------------------------------------------------------- */
    3486             :     /*      Copy over.                                                      */
    3487             :     /* -------------------------------------------------------------------- */
    3488       12741 :     return CopyGeogCSFrom(&oSRS2);
    3489             : }
    3490             : 
    3491             : /************************************************************************/
    3492             : /*                       OSRSetWellKnownGeogCS()                        */
    3493             : /************************************************************************/
    3494             : 
    3495             : /**
    3496             :  * \brief Set a GeogCS based on well known name.
    3497             :  *
    3498             :  * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
    3499             :  */
    3500         116 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
    3501             : 
    3502             : {
    3503         116 :     VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
    3504             : 
    3505         116 :     return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
    3506             : }
    3507             : 
    3508             : /************************************************************************/
    3509             : /*                           CopyGeogCSFrom()                           */
    3510             : /************************************************************************/
    3511             : 
    3512             : /**
    3513             :  * \brief Copy GEOGCS from another OGRSpatialReference.
    3514             :  *
    3515             :  * The GEOGCS information is copied into this OGRSpatialReference from another.
    3516             :  * If this object has a PROJCS root already, the GEOGCS is installed within
    3517             :  * it, otherwise it is installed as the root.
    3518             :  *
    3519             :  * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
    3520             :  *
    3521             :  * @return OGRERR_NONE on success or an error code.
    3522             :  */
    3523             : 
    3524       13336 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
    3525             : 
    3526             : {
    3527       13336 :     d->bNormInfoSet = FALSE;
    3528       13336 :     d->m_osAngularUnits.clear();
    3529       13336 :     d->m_dfAngularUnitToRadian = 0.0;
    3530       13336 :     d->m_osPrimeMeridianName.clear();
    3531       13336 :     d->dfFromGreenwich = 0.0;
    3532             : 
    3533       13336 :     d->refreshProjObj();
    3534       13336 :     poSrcSRS->d->refreshProjObj();
    3535       13336 :     if (!poSrcSRS->d->m_pj_crs)
    3536             :     {
    3537           0 :         return OGRERR_FAILURE;
    3538             :     }
    3539             :     auto geodCRS =
    3540       13336 :         proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
    3541       13336 :     if (!geodCRS)
    3542             :     {
    3543           0 :         return OGRERR_FAILURE;
    3544             :     }
    3545             : 
    3546             :     /* -------------------------------------------------------------------- */
    3547             :     /*      Handle geocentric coordinate systems specially.  We just        */
    3548             :     /*      want to copy the DATUM.                                         */
    3549             :     /* -------------------------------------------------------------------- */
    3550       13336 :     if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
    3551             :     {
    3552           3 :         auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
    3553             : #if PROJ_VERSION_MAJOR > 7 ||                                                  \
    3554             :     (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
    3555             :         if (datum == nullptr)
    3556             :         {
    3557             :             datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
    3558             :         }
    3559             : #endif
    3560           3 :         if (datum == nullptr)
    3561             :         {
    3562           0 :             proj_destroy(geodCRS);
    3563           0 :             return OGRERR_FAILURE;
    3564             :         }
    3565             : 
    3566           3 :         const char *pszUnitName = nullptr;
    3567           3 :         double unitConvFactor = GetLinearUnits(&pszUnitName);
    3568             : 
    3569           3 :         auto pj_crs = proj_create_geocentric_crs_from_datum(
    3570           3 :             d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
    3571             :             unitConvFactor);
    3572           3 :         proj_destroy(datum);
    3573             : 
    3574           3 :         d->setPjCRS(pj_crs);
    3575             :     }
    3576             : 
    3577       13333 :     else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
    3578             :     {
    3579         300 :         auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
    3580         300 :                                                   d->m_pj_crs, geodCRS);
    3581         300 :         d->setPjCRS(pj_crs);
    3582             :     }
    3583             : 
    3584             :     else
    3585             :     {
    3586       13033 :         d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
    3587             :     }
    3588             : 
    3589             :     // Apply TOWGS84 of source CRS
    3590       13336 :     if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
    3591             :     {
    3592             :         auto target =
    3593           1 :             proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
    3594           1 :         auto co = proj_crs_get_coordoperation(d->getPROJContext(),
    3595           1 :                                               poSrcSRS->d->m_pj_crs);
    3596           1 :         d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
    3597             :                                               target, co));
    3598           1 :         proj_destroy(target);
    3599           1 :         proj_destroy(co);
    3600             :     }
    3601             : 
    3602       13336 :     proj_destroy(geodCRS);
    3603             : 
    3604       13336 :     return OGRERR_NONE;
    3605             : }
    3606             : 
    3607             : /************************************************************************/
    3608             : /*                         OSRCopyGeogCSFrom()                          */
    3609             : /************************************************************************/
    3610             : 
    3611             : /**
    3612             :  * \brief Copy GEOGCS from another OGRSpatialReference.
    3613             :  *
    3614             :  * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
    3615             :  */
    3616           1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
    3617             :                          const OGRSpatialReferenceH hSrcSRS)
    3618             : 
    3619             : {
    3620           1 :     VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
    3621           1 :     VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
    3622             : 
    3623           1 :     return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
    3624             : }
    3625             : 
    3626             : /************************************************************************/
    3627             : /*                   SET_FROM_USER_INPUT_LIMITATIONS_get()              */
    3628             : /************************************************************************/
    3629             : 
    3630             : /** Limitations for OGRSpatialReference::SetFromUserInput().
    3631             :  *
    3632             :  * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
    3633             :  */
    3634             : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
    3635             :     "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
    3636             : 
    3637             : /**
    3638             :  * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
    3639             :  */
    3640        2331 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
    3641             : {
    3642        2331 :     return SET_FROM_USER_INPUT_LIMITATIONS;
    3643             : }
    3644             : 
    3645             : /************************************************************************/
    3646             : /*                      RemoveIDFromMemberOfEnsembles()                 */
    3647             : /************************************************************************/
    3648             : 
    3649             : // cppcheck-suppress constParameterReference
    3650          73 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
    3651             : {
    3652             :     // Remove "id" from members of datum ensembles for compatibility with
    3653             :     // older PROJ versions
    3654             :     // Cf https://github.com/opengeospatial/geoparquet/discussions/110
    3655             :     // and https://github.com/OSGeo/PROJ/pull/3221
    3656          73 :     if (obj.GetType() == CPLJSONObject::Type::Object)
    3657             :     {
    3658          89 :         for (auto &subObj : obj.GetChildren())
    3659             :         {
    3660          70 :             RemoveIDFromMemberOfEnsembles(subObj);
    3661             :         }
    3662             :     }
    3663          60 :     else if (obj.GetType() == CPLJSONObject::Type::Array &&
    3664          60 :              obj.GetName() == "members")
    3665             :     {
    3666          18 :         for (auto &subObj : obj.ToArray())
    3667             :         {
    3668          15 :             if (subObj.GetType() == CPLJSONObject::Type::Object)
    3669             :             {
    3670          14 :                 subObj.Delete("id");
    3671             :             }
    3672             :         }
    3673             :     }
    3674          73 : }
    3675             : 
    3676             : /************************************************************************/
    3677             : /*                          SetFromUserInput()                          */
    3678             : /************************************************************************/
    3679             : 
    3680             : /**
    3681             :  * \brief Set spatial reference from various text formats.
    3682             :  *
    3683             :  * This method will examine the provided input, and try to deduce the
    3684             :  * format, and then use it to initialize the spatial reference system.  It
    3685             :  * may take the following forms:
    3686             :  *
    3687             :  * <ol>
    3688             :  * <li> Well Known Text definition - passed on to importFromWkt().
    3689             :  * <li> "EPSG:n" - number passed on to importFromEPSG().
    3690             :  * <li> "EPSGA:n" - number passed on to importFromEPSGA().
    3691             :  * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
    3692             :  * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
    3693             :  * <li> PROJ.4 definitions - passed on to importFromProj4().
    3694             :  * <li> filename - file read for WKT, XML or PROJ.4 definition.
    3695             :  * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
    3696             :  * WGS84 or WGS72.
    3697             :  * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
    3698             :  * <li> PROJJSON (PROJ &gt;= 6.2)
    3699             :  * </ol>
    3700             :  *
    3701             :  * It is expected that this method will be extended in the future to support
    3702             :  * XML and perhaps a simplified "minilanguage" for indicating common UTM and
    3703             :  * State Plane definitions.
    3704             :  *
    3705             :  * This method is intended to be flexible, but by its nature it is
    3706             :  * imprecise as it must guess information about the format intended.  When
    3707             :  * possible applications should call the specific method appropriate if the
    3708             :  * input is known to be in a particular format.
    3709             :  *
    3710             :  * This method does the same thing as the OSRSetFromUserInput() function.
    3711             :  *
    3712             :  * @param pszDefinition text definition to try to deduce SRS from.
    3713             :  *
    3714             :  * @return OGRERR_NONE on success, or an error code if the name isn't
    3715             :  * recognised, the definition is corrupt, or an EPSG value can't be
    3716             :  * successfully looked up.
    3717             :  */
    3718             : 
    3719       13189 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
    3720             : {
    3721       13189 :     return SetFromUserInput(pszDefinition, nullptr);
    3722             : }
    3723             : 
    3724             : /**
    3725             :  * \brief Set spatial reference from various text formats.
    3726             :  *
    3727             :  * This method will examine the provided input, and try to deduce the
    3728             :  * format, and then use it to initialize the spatial reference system.  It
    3729             :  * may take the following forms:
    3730             :  *
    3731             :  * <ol>
    3732             :  * <li> Well Known Text definition - passed on to importFromWkt().
    3733             :  * <li> "EPSG:n" - number passed on to importFromEPSG().
    3734             :  * <li> "EPSGA:n" - number passed on to importFromEPSGA().
    3735             :  * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
    3736             :  * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
    3737             :  * <li> PROJ.4 definitions - passed on to importFromProj4().
    3738             :  * <li> filename - file read for WKT, XML or PROJ.4 definition.
    3739             :  * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
    3740             :  * WGS84 or WGS72.
    3741             :  * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
    3742             :  * <li> PROJJSON (PROJ &gt;= 6.2)
    3743             :  * </ol>
    3744             :  *
    3745             :  * It is expected that this method will be extended in the future to support
    3746             :  * XML and perhaps a simplified "minilanguage" for indicating common UTM and
    3747             :  * State Plane definitions.
    3748             :  *
    3749             :  * This method is intended to be flexible, but by its nature it is
    3750             :  * imprecise as it must guess information about the format intended.  When
    3751             :  * possible applications should call the specific method appropriate if the
    3752             :  * input is known to be in a particular format.
    3753             :  *
    3754             :  * This method does the same thing as the OSRSetFromUserInput() and
    3755             :  * OSRSetFromUserInputEx() functions.
    3756             :  *
    3757             :  * @param pszDefinition text definition to try to deduce SRS from.
    3758             :  *
    3759             :  * @param papszOptions NULL terminated list of options, or NULL.
    3760             :  * <ol>
    3761             :  * <li> ALLOW_NETWORK_ACCESS=YES/NO.
    3762             :  *      Whether http:// or https:// access is allowed. Defaults to YES.
    3763             :  * <li> ALLOW_FILE_ACCESS=YES/NO.
    3764             :  *      Whether reading a file using the Virtual File System layer is allowed
    3765             :  *      (can also involve network access). Defaults to YES.
    3766             :  * </ol>
    3767             :  *
    3768             :  * @return OGRERR_NONE on success, or an error code if the name isn't
    3769             :  * recognised, the definition is corrupt, or an EPSG value can't be
    3770             :  * successfully looked up.
    3771             :  */
    3772             : 
    3773       16915 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
    3774             :                                              CSLConstList papszOptions)
    3775             : {
    3776             :     // Skip leading white space
    3777       16915 :     while (isspace(static_cast<unsigned char>(*pszDefinition)))
    3778           2 :         pszDefinition++;
    3779             : 
    3780       16913 :     if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
    3781             :     {
    3782           1 :         pszDefinition += 6;
    3783             :     }
    3784             : 
    3785             :     /* -------------------------------------------------------------------- */
    3786             :     /*      Is it a recognised syntax?                                      */
    3787             :     /* -------------------------------------------------------------------- */
    3788       16913 :     const char *const wktKeywords[] = {
    3789             :         // WKT1
    3790             :         "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
    3791             :         // WKT2"
    3792             :         "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
    3793             :         "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
    3794             :         "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
    3795      208473 :     for (const char *keyword : wktKeywords)
    3796             :     {
    3797      199321 :         if (STARTS_WITH_CI(pszDefinition, keyword))
    3798             :         {
    3799        7761 :             return importFromWkt(pszDefinition);
    3800             :         }
    3801             :     }
    3802             : 
    3803        9152 :     const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
    3804        9152 :     if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
    3805             :     {
    3806        7029 :         OGRErr eStatus = OGRERR_NONE;
    3807             : 
    3808        7029 :         if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
    3809             :         {
    3810             :             // Use proj_create() as it allows things like EPSG:3157+4617
    3811             :             // that are not normally supported by the below code that
    3812             :             // builds manually a compound CRS
    3813          57 :             PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
    3814          57 :             if (!pj)
    3815             :             {
    3816           0 :                 return OGRERR_FAILURE;
    3817             :             }
    3818          57 :             Clear();
    3819          57 :             d->setPjCRS(pj);
    3820          57 :             return OGRERR_NONE;
    3821             :         }
    3822             :         else
    3823             :         {
    3824             :             eStatus =
    3825        6972 :                 importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
    3826             :         }
    3827             : 
    3828        6972 :         return eStatus;
    3829             :     }
    3830             : 
    3831        2123 :     if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
    3832        1487 :         STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
    3833        1486 :         STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
    3834        1428 :         STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
    3835        1428 :         STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
    3836        1428 :         STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
    3837         695 :         return importFromURN(pszDefinition);
    3838             : 
    3839        1428 :     if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
    3840        1426 :         STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
    3841        1425 :         STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
    3842         842 :         STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
    3843         841 :         STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
    3844         587 :         return importFromCRSURL(pszDefinition);
    3845             : 
    3846         841 :     if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
    3847           1 :         return importFromWMSAUTO(pszDefinition);
    3848             : 
    3849             :     // WMS/WCS OGC codes like OGC:CRS84.
    3850         840 :     if (EQUAL(pszDefinition, "OGC:CRS84"))
    3851          23 :         return SetWellKnownGeogCS(pszDefinition + 4);
    3852             : 
    3853         817 :     if (STARTS_WITH_CI(pszDefinition, "CRS:"))
    3854           1 :         return SetWellKnownGeogCS(pszDefinition);
    3855             : 
    3856         816 :     if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
    3857             :     {
    3858           0 :         char *pszFile = CPLStrdup(pszDefinition + 5);
    3859           0 :         char *pszCode = strstr(pszFile, ",") + 1;
    3860             : 
    3861           0 :         pszCode[-1] = '\0';
    3862             : 
    3863           0 :         OGRErr err = importFromDict(pszFile, pszCode);
    3864           0 :         CPLFree(pszFile);
    3865             : 
    3866           0 :         return err;
    3867             :     }
    3868             : 
    3869         816 :     if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
    3870         814 :         EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
    3871             :     {
    3872         247 :         Clear();
    3873         247 :         return SetWellKnownGeogCS(pszDefinition);
    3874             :     }
    3875             : 
    3876             :     // PROJJSON
    3877         569 :     if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
    3878          44 :         (strstr(pszDefinition, "GeodeticCRS") ||
    3879          44 :          strstr(pszDefinition, "GeographicCRS") ||
    3880          16 :          strstr(pszDefinition, "ProjectedCRS") ||
    3881           0 :          strstr(pszDefinition, "VerticalCRS") ||
    3882           0 :          strstr(pszDefinition, "BoundCRS") ||
    3883           0 :          strstr(pszDefinition, "CompoundCRS") ||
    3884           0 :          strstr(pszDefinition, "DerivedGeodeticCRS") ||
    3885           0 :          strstr(pszDefinition, "DerivedGeographicCRS") ||
    3886           0 :          strstr(pszDefinition, "DerivedProjectedCRS") ||
    3887           0 :          strstr(pszDefinition, "DerivedVerticalCRS") ||
    3888           0 :          strstr(pszDefinition, "EngineeringCRS") ||
    3889           0 :          strstr(pszDefinition, "DerivedEngineeringCRS") ||
    3890           0 :          strstr(pszDefinition, "ParametricCRS") ||
    3891           0 :          strstr(pszDefinition, "DerivedParametricCRS") ||
    3892           0 :          strstr(pszDefinition, "TemporalCRS") ||
    3893           0 :          strstr(pszDefinition, "DerivedTemporalCRS")))
    3894             :     {
    3895             :         PJ *pj;
    3896          44 :         if (strstr(pszDefinition, "datum_ensemble") != nullptr)
    3897             :         {
    3898             :             // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
    3899             :             // a unknown id.
    3900           3 :             CPLJSONDocument oCRSDoc;
    3901           3 :             if (!oCRSDoc.LoadMemory(pszDefinition))
    3902           0 :                 return OGRERR_CORRUPT_DATA;
    3903           3 :             CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
    3904           3 :             RemoveIDFromMemberOfEnsembles(oCRSRoot);
    3905           3 :             pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
    3906             :         }
    3907             :         else
    3908             :         {
    3909          41 :             pj = proj_create(d->getPROJContext(), pszDefinition);
    3910             :         }
    3911          44 :         if (!pj)
    3912             :         {
    3913           2 :             return OGRERR_FAILURE;
    3914             :         }
    3915          42 :         Clear();
    3916          42 :         d->setPjCRS(pj);
    3917          42 :         return OGRERR_NONE;
    3918             :     }
    3919             : 
    3920         525 :     if (strstr(pszDefinition, "+proj") != nullptr ||
    3921         195 :         strstr(pszDefinition, "+init") != nullptr)
    3922         330 :         return importFromProj4(pszDefinition);
    3923             : 
    3924         195 :     if (STARTS_WITH_CI(pszDefinition, "http://") ||
    3925         195 :         STARTS_WITH_CI(pszDefinition, "https://"))
    3926             :     {
    3927           1 :         if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    3928             :                                              "ALLOW_NETWORK_ACCESS", "YES")))
    3929           0 :             return importFromUrl(pszDefinition);
    3930             : 
    3931           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    3932             :                  "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
    3933             :                  pszDefinition);
    3934           1 :         return OGRERR_FAILURE;
    3935             :     }
    3936             : 
    3937         194 :     if (EQUAL(pszDefinition, "osgb:BNG"))
    3938             :     {
    3939          13 :         return importFromEPSG(27700);
    3940             :     }
    3941             : 
    3942             :     // Used by German CityGML files
    3943         181 :     if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
    3944             :     {
    3945             :         // "ETRS89 / UTM Zone 32N + DHHN92 height"
    3946           0 :         return SetFromUserInput("EPSG:25832+5783");
    3947             :     }
    3948         181 :     else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
    3949             :     {
    3950             :         // "ETRS89 / UTM Zone 32N + DHHN2016 height"
    3951           4 :         return SetFromUserInput("EPSG:25832+7837");
    3952             :     }
    3953             : 
    3954             :     // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
    3955         177 :     const char *pszDot = strrchr(pszDefinition, ':');
    3956         177 :     if (pszDot)
    3957             :     {
    3958         107 :         CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
    3959             :         auto authorities =
    3960         107 :             proj_get_authorities_from_database(d->getPROJContext());
    3961         107 :         if (authorities)
    3962             :         {
    3963         107 :             std::set<std::string> aosCandidateAuthorities;
    3964         232 :             for (auto iter = authorities; *iter; ++iter)
    3965             :             {
    3966         229 :                 if (*iter == osPrefix)
    3967             :                 {
    3968         104 :                     aosCandidateAuthorities.clear();
    3969         104 :                     aosCandidateAuthorities.insert(*iter);
    3970         104 :                     break;
    3971             :                 }
    3972             :                 // Deal with "IAU_2015" as authority in the list and input
    3973             :                 // "IAU:code"
    3974         125 :                 else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
    3975         125 :                              0 &&
    3976           0 :                          (*iter)[osPrefix.size()] == '_')
    3977             :                 {
    3978           0 :                     aosCandidateAuthorities.insert(*iter);
    3979             :                 }
    3980             :                 // Deal with "IAU_2015" as authority in the list and input
    3981             :                 // "IAU:2015:code"
    3982         250 :                 else if (osPrefix.find(':') != std::string::npos &&
    3983         125 :                          osPrefix.size() == strlen(*iter) &&
    3984         125 :                          CPLString(osPrefix).replaceAll(':', '_') == *iter)
    3985             :                 {
    3986           0 :                     aosCandidateAuthorities.clear();
    3987           0 :                     aosCandidateAuthorities.insert(*iter);
    3988           0 :                     break;
    3989             :                 }
    3990             :             }
    3991             : 
    3992         107 :             proj_string_list_destroy(authorities);
    3993             : 
    3994         107 :             if (!aosCandidateAuthorities.empty())
    3995             :             {
    3996         104 :                 auto obj = proj_create_from_database(
    3997             :                     d->getPROJContext(),
    3998         104 :                     aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
    3999             :                     PJ_CATEGORY_CRS, false, nullptr);
    4000         104 :                 if (!obj)
    4001             :                 {
    4002           3 :                     return OGRERR_FAILURE;
    4003             :                 }
    4004         101 :                 Clear();
    4005         101 :                 d->setPjCRS(obj);
    4006         101 :                 return OGRERR_NONE;
    4007             :             }
    4008             :         }
    4009             :     }
    4010             : 
    4011             :     /* -------------------------------------------------------------------- */
    4012             :     /*      Try to open it as a file.                                       */
    4013             :     /* -------------------------------------------------------------------- */
    4014          73 :     if (!CPLTestBool(
    4015             :             CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
    4016             :     {
    4017             :         VSIStatBufL sStat;
    4018          24 :         if (STARTS_WITH(pszDefinition, "/vsi") ||
    4019          12 :             VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
    4020             :         {
    4021           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4022             :                      "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
    4023             :                      pszDefinition);
    4024           0 :             return OGRERR_FAILURE;
    4025             :         }
    4026             :         // We used to silently return an error without a CE_Failure message
    4027             :         // Cf https://github.com/Toblerity/Fiona/issues/1063
    4028          12 :         return OGRERR_CORRUPT_DATA;
    4029             :     }
    4030             : 
    4031         122 :     CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
    4032          61 :     VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
    4033          61 :     if (fp == nullptr)
    4034          56 :         return OGRERR_CORRUPT_DATA;
    4035             : 
    4036           5 :     const size_t nBufMax = 100000;
    4037           5 :     char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
    4038           5 :     const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
    4039           5 :     VSIFCloseL(fp);
    4040             : 
    4041           5 :     if (nBytes == nBufMax - 1)
    4042             :     {
    4043           0 :         CPLDebug("OGR",
    4044             :                  "OGRSpatialReference::SetFromUserInput(%s), opened file "
    4045             :                  "but it is to large for our generous buffer.  Is it really "
    4046             :                  "just a WKT definition?",
    4047             :                  pszDefinition);
    4048           0 :         CPLFree(pszBuffer);
    4049           0 :         return OGRERR_FAILURE;
    4050             :     }
    4051             : 
    4052           5 :     pszBuffer[nBytes] = '\0';
    4053             : 
    4054           5 :     char *pszBufPtr = pszBuffer;
    4055           5 :     while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
    4056           0 :         pszBufPtr++;
    4057             : 
    4058           5 :     OGRErr err = OGRERR_NONE;
    4059           5 :     if (pszBufPtr[0] == '<')
    4060           0 :         err = importFromXML(pszBufPtr);
    4061           5 :     else if ((strstr(pszBuffer, "+proj") != nullptr ||
    4062           5 :               strstr(pszBuffer, "+init") != nullptr) &&
    4063           0 :              (strstr(pszBuffer, "EXTENSION") == nullptr &&
    4064           0 :               strstr(pszBuffer, "extension") == nullptr))
    4065           0 :         err = importFromProj4(pszBufPtr);
    4066             :     else
    4067             :     {
    4068           5 :         if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
    4069             :         {
    4070           3 :             pszBufPtr += 6;
    4071             :         }
    4072             : 
    4073             :         // coverity[tainted_data]
    4074           5 :         err = importFromWkt(pszBufPtr);
    4075             :     }
    4076             : 
    4077           5 :     CPLFree(pszBuffer);
    4078             : 
    4079           5 :     return err;
    4080             : }
    4081             : 
    4082             : /************************************************************************/
    4083             : /*                        OSRSetFromUserInput()                         */
    4084             : /************************************************************************/
    4085             : 
    4086             : /**
    4087             :  * \brief Set spatial reference from various text formats.
    4088             :  *
    4089             :  * This function is the same as OGRSpatialReference::SetFromUserInput()
    4090             :  *
    4091             :  * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
    4092             :  */
    4093         236 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
    4094             :                                        const char *pszDef)
    4095             : 
    4096             : {
    4097         236 :     VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
    4098             : 
    4099         236 :     return ToPointer(hSRS)->SetFromUserInput(pszDef);
    4100             : }
    4101             : 
    4102             : /************************************************************************/
    4103             : /*                       OSRSetFromUserInputEx()                        */
    4104             : /************************************************************************/
    4105             : 
    4106             : /**
    4107             :  * \brief Set spatial reference from various text formats.
    4108             :  *
    4109             :  * This function is the same as OGRSpatialReference::SetFromUserInput().
    4110             :  *
    4111             :  * @since GDAL 3.9
    4112             :  */
    4113         676 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
    4114             :                              CSLConstList papszOptions)
    4115             : 
    4116             : {
    4117         676 :     VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
    4118             : 
    4119         676 :     return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
    4120             : }
    4121             : 
    4122             : /************************************************************************/
    4123             : /*                          ImportFromUrl()                             */
    4124             : /************************************************************************/
    4125             : 
    4126             : /**
    4127             :  * \brief Set spatial reference from a URL.
    4128             :  *
    4129             :  * This method will download the spatial reference at a given URL and
    4130             :  * feed it into SetFromUserInput for you.
    4131             :  *
    4132             :  * This method does the same thing as the OSRImportFromUrl() function.
    4133             :  *
    4134             :  * @param pszUrl text definition to try to deduce SRS from.
    4135             :  *
    4136             :  * @return OGRERR_NONE on success, or an error code with the curl
    4137             :  * error message if it is unable to download data.
    4138             :  */
    4139             : 
    4140           5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
    4141             : 
    4142             : {
    4143           5 :     if (!STARTS_WITH_CI(pszUrl, "http://") &&
    4144           3 :         !STARTS_WITH_CI(pszUrl, "https://"))
    4145             :     {
    4146           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    4147             :                  "The given string is not recognized as a URL"
    4148             :                  "starting with 'http://' -- %s",
    4149             :                  pszUrl);
    4150           2 :         return OGRERR_FAILURE;
    4151             :     }
    4152             : 
    4153             :     /* -------------------------------------------------------------------- */
    4154             :     /*      Fetch the result.                                               */
    4155             :     /* -------------------------------------------------------------------- */
    4156           3 :     CPLErrorReset();
    4157             : 
    4158           6 :     std::string osUrl(pszUrl);
    4159             :     // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
    4160             :     // as a valid URL since we used a "Accept: application/x-ogcwkt" header
    4161             :     // to query WKT. To allow a static server to be used, rather append a
    4162             :     // "ogcwkt/" suffix.
    4163           2 :     for (const char *pszPrefix : {"https://spatialreference.org/ref/",
    4164           5 :                                   "http://spatialreference.org/ref/"})
    4165             :     {
    4166           5 :         if (STARTS_WITH(pszUrl, pszPrefix))
    4167             :         {
    4168             :             const CPLStringList aosTokens(
    4169           6 :                 CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
    4170           3 :             if (aosTokens.size() == 2)
    4171             :             {
    4172           2 :                 osUrl = "https://spatialreference.org/ref/";
    4173           2 :                 osUrl += aosTokens[0];  // authority
    4174           2 :                 osUrl += '/';
    4175           2 :                 osUrl += aosTokens[1];  // code
    4176           2 :                 osUrl += "/ogcwkt/";
    4177             :             }
    4178           3 :             break;
    4179             :         }
    4180             :     }
    4181             : 
    4182           3 :     const char *pszTimeout = "TIMEOUT=10";
    4183           3 :     char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
    4184             : 
    4185           3 :     CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
    4186             : 
    4187             :     /* -------------------------------------------------------------------- */
    4188             :     /*      Try to handle errors.                                           */
    4189             :     /* -------------------------------------------------------------------- */
    4190             : 
    4191           3 :     if (psResult == nullptr)
    4192           0 :         return OGRERR_FAILURE;
    4193           6 :     if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
    4194           3 :         psResult->pabyData == nullptr)
    4195             :     {
    4196           0 :         if (CPLGetLastErrorNo() == 0)
    4197             :         {
    4198           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4199             :                      "No data was returned from the given URL");
    4200             :         }
    4201           0 :         CPLHTTPDestroyResult(psResult);
    4202           0 :         return OGRERR_FAILURE;
    4203             :     }
    4204             : 
    4205           3 :     if (psResult->nStatus != 0)
    4206             :     {
    4207           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
    4208             :                  psResult->nStatus, psResult->pszErrBuf);
    4209           0 :         CPLHTTPDestroyResult(psResult);
    4210           0 :         return OGRERR_FAILURE;
    4211             :     }
    4212             : 
    4213           3 :     const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
    4214           3 :     if (STARTS_WITH_CI(pszData, "http://") ||
    4215           3 :         STARTS_WITH_CI(pszData, "https://"))
    4216             :     {
    4217           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4218             :                  "The data that was downloaded also starts with 'http://' "
    4219             :                  "and cannot be passed into SetFromUserInput.  Is this "
    4220             :                  "really a spatial reference definition? ");
    4221           0 :         CPLHTTPDestroyResult(psResult);
    4222           0 :         return OGRERR_FAILURE;
    4223             :     }
    4224           3 :     if (OGRERR_NONE != SetFromUserInput(pszData))
    4225             :     {
    4226           0 :         CPLHTTPDestroyResult(psResult);
    4227           0 :         return OGRERR_FAILURE;
    4228             :     }
    4229             : 
    4230           3 :     CPLHTTPDestroyResult(psResult);
    4231           3 :     return OGRERR_NONE;
    4232             : }
    4233             : 
    4234             : /************************************************************************/
    4235             : /*                        OSRimportFromUrl()                            */
    4236             : /************************************************************************/
    4237             : 
    4238             : /**
    4239             :  * \brief Set spatial reference from a URL.
    4240             :  *
    4241             :  * This function is the same as OGRSpatialReference::importFromUrl()
    4242             :  */
    4243           3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
    4244             : 
    4245             : {
    4246           3 :     VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
    4247             : 
    4248           3 :     return ToPointer(hSRS)->importFromUrl(pszUrl);
    4249             : }
    4250             : 
    4251             : /************************************************************************/
    4252             : /*                         importFromURNPart()                          */
    4253             : /************************************************************************/
    4254        1443 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
    4255             :                                               const char *pszCode,
    4256             :                                               const char *pszURN)
    4257             : {
    4258             : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
    4259             :     (void)this;
    4260             :     (void)pszAuthority;
    4261             :     (void)pszCode;
    4262             :     (void)pszURN;
    4263             :     return OGRERR_FAILURE;
    4264             : #else
    4265             :     /* -------------------------------------------------------------------- */
    4266             :     /*      Is this an EPSG code? Note that we import it with EPSG          */
    4267             :     /*      preferred axis ordering for geographic coordinate systems.      */
    4268             :     /* -------------------------------------------------------------------- */
    4269        1443 :     if (STARTS_WITH_CI(pszAuthority, "EPSG"))
    4270        1370 :         return importFromEPSGA(atoi(pszCode));
    4271             : 
    4272             :     /* -------------------------------------------------------------------- */
    4273             :     /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
    4274             :     /* -------------------------------------------------------------------- */
    4275          73 :     if (STARTS_WITH_CI(pszAuthority, "IAU"))
    4276           0 :         return importFromDict("IAU2000.wkt", pszCode);
    4277             : 
    4278             :     /* -------------------------------------------------------------------- */
    4279             :     /*      Is this an OGC code?                                            */
    4280             :     /* -------------------------------------------------------------------- */
    4281          73 :     if (!STARTS_WITH_CI(pszAuthority, "OGC"))
    4282             :     {
    4283           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4284             :                  "URN %s has unrecognized authority.", pszURN);
    4285           1 :         return OGRERR_FAILURE;
    4286             :     }
    4287             : 
    4288          72 :     if (STARTS_WITH_CI(pszCode, "CRS84"))
    4289          66 :         return SetWellKnownGeogCS(pszCode);
    4290           6 :     else if (STARTS_WITH_CI(pszCode, "CRS83"))
    4291           0 :         return SetWellKnownGeogCS(pszCode);
    4292           6 :     else if (STARTS_WITH_CI(pszCode, "CRS27"))
    4293           0 :         return SetWellKnownGeogCS(pszCode);
    4294           6 :     else if (STARTS_WITH_CI(pszCode, "84"))  // urn:ogc:def:crs:OGC:2:84
    4295           4 :         return SetWellKnownGeogCS("CRS84");
    4296             : 
    4297             :     /* -------------------------------------------------------------------- */
    4298             :     /*      Handle auto codes.  We need to convert from format              */
    4299             :     /*      AUTO42001:99:8888 to format AUTO:42001,99,8888.                 */
    4300             :     /* -------------------------------------------------------------------- */
    4301           2 :     else if (STARTS_WITH_CI(pszCode, "AUTO"))
    4302             :     {
    4303           2 :         char szWMSAuto[100] = {'\0'};
    4304             : 
    4305           2 :         if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
    4306           0 :             return OGRERR_FAILURE;
    4307             : 
    4308           2 :         snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
    4309          28 :         for (int i = 5; szWMSAuto[i] != '\0'; i++)
    4310             :         {
    4311          26 :             if (szWMSAuto[i] == ':')
    4312           4 :                 szWMSAuto[i] = ',';
    4313             :         }
    4314             : 
    4315           2 :         return importFromWMSAUTO(szWMSAuto);
    4316             :     }
    4317             : 
    4318             :     /* -------------------------------------------------------------------- */
    4319             :     /*      Not a recognise OGC item.                                       */
    4320             :     /* -------------------------------------------------------------------- */
    4321           0 :     CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
    4322             :              pszURN);
    4323             : 
    4324           0 :     return OGRERR_FAILURE;
    4325             : #endif
    4326             : }
    4327             : 
    4328             : /************************************************************************/
    4329             : /*                           importFromURN()                            */
    4330             : /*                                                                      */
    4331             : /*      See OGC recommendation paper 06-023r1 or later for details.     */
    4332             : /************************************************************************/
    4333             : 
    4334             : /**
    4335             :  * \brief Initialize from OGC URN.
    4336             :  *
    4337             :  * Initializes this spatial reference from a coordinate system defined
    4338             :  * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
    4339             :  * paper 06-023r1.  Currently EPSG and OGC authority values are supported,
    4340             :  * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
    4341             :  *
    4342             :  * This method is also support through SetFromUserInput() which can
    4343             :  * normally be used for URNs.
    4344             :  *
    4345             :  * @param pszURN the urn string.
    4346             :  *
    4347             :  * @return OGRERR_NONE on success or an error code.
    4348             :  */
    4349             : 
    4350         756 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
    4351             : 
    4352             : {
    4353             : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
    4354             : 
    4355             :     // PROJ 8.2.0 has support for IAU codes now.
    4356             : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
    4357             :     /* -------------------------------------------------------------------- */
    4358             :     /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
    4359             :     /* -------------------------------------------------------------------- */
    4360             :     const char *pszIAU = strstr(pszURN, "IAU");
    4361             :     if (pszIAU)
    4362             :     {
    4363             :         const char *pszCode = strchr(pszIAU, ':');
    4364             :         if (pszCode)
    4365             :         {
    4366             :             ++pszCode;
    4367             :             if (*pszCode == ':')
    4368             :                 ++pszCode;
    4369             :             return importFromDict("IAU2000.wkt", pszCode);
    4370             :         }
    4371             :     }
    4372             : #endif
    4373             : 
    4374             :     if (strlen(pszURN) >= 1000)
    4375             :     {
    4376             :         CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
    4377             :         return OGRERR_CORRUPT_DATA;
    4378             :     }
    4379             :     auto obj = proj_create(d->getPROJContext(), pszURN);
    4380             :     if (!obj)
    4381             :     {
    4382             :         return OGRERR_FAILURE;
    4383             :     }
    4384             :     Clear();
    4385             :     d->setPjCRS(obj);
    4386             :     return OGRERR_NONE;
    4387             : #else
    4388         756 :     const char *pszCur = nullptr;
    4389             : 
    4390         756 :     if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
    4391         691 :         pszCur = pszURN + 16;
    4392          65 :     else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
    4393           1 :         pszCur = pszURN + 20;
    4394          64 :     else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
    4395          62 :         pszCur = pszURN + 18;
    4396           2 :     else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
    4397           0 :         pszCur = pszURN + 16;
    4398           2 :     else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
    4399           0 :         pszCur = pszURN + 20;
    4400             :     else
    4401             :     {
    4402           2 :         CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
    4403             :                  pszURN);
    4404           2 :         return OGRERR_FAILURE;
    4405             :     }
    4406             : 
    4407             :     /* -------------------------------------------------------------------- */
    4408             :     /*      Clear any existing definition.                                  */
    4409             :     /* -------------------------------------------------------------------- */
    4410         754 :     Clear();
    4411             : 
    4412             :     /* -------------------------------------------------------------------- */
    4413             :     /*      Find code (ignoring version) out of string like:                */
    4414             :     /*                                                                      */
    4415             :     /*      authority:[version]:code                                        */
    4416             :     /* -------------------------------------------------------------------- */
    4417         754 :     const char *pszAuthority = pszCur;
    4418             : 
    4419             :     // skip authority
    4420        3757 :     while (*pszCur != ':' && *pszCur)
    4421        3003 :         pszCur++;
    4422         754 :     if (*pszCur == ':')
    4423         754 :         pszCur++;
    4424             : 
    4425             :     // skip version
    4426         754 :     const char *pszBeforeVersion = pszCur;
    4427        1034 :     while (*pszCur != ':' && *pszCur)
    4428         280 :         pszCur++;
    4429         754 :     if (*pszCur == ':')
    4430         726 :         pszCur++;
    4431             :     else
    4432             :         // We come here in the case, the content to parse is authority:code
    4433             :         // (instead of authority::code) which is probably illegal according to
    4434             :         // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
    4435             :         // for example in what is returned by GeoServer.
    4436          28 :         pszCur = pszBeforeVersion;
    4437             : 
    4438         754 :     const char *pszCode = pszCur;
    4439             : 
    4440         754 :     const char *pszComma = strchr(pszCur, ',');
    4441         754 :     if (pszComma == nullptr)
    4442         753 :         return importFromURNPart(pszAuthority, pszCode, pszURN);
    4443             : 
    4444             :     // There's a second part with the vertical SRS.
    4445           1 :     pszCur = pszComma + 1;
    4446           1 :     if (!STARTS_WITH(pszCur, "crs:"))
    4447             :     {
    4448           0 :         CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
    4449             :                  pszURN);
    4450           0 :         return OGRERR_FAILURE;
    4451             :     }
    4452             : 
    4453           1 :     pszCur += 4;
    4454             : 
    4455           1 :     char *pszFirstCode = CPLStrdup(pszCode);
    4456           1 :     pszFirstCode[pszComma - pszCode] = '\0';
    4457           1 :     OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
    4458           1 :     CPLFree(pszFirstCode);
    4459             : 
    4460             :     // Do we want to turn this into a compound definition
    4461             :     // with a vertical datum?
    4462           1 :     if (eStatus != OGRERR_NONE)
    4463           0 :         return eStatus;
    4464             : 
    4465             :     /* -------------------------------------------------------------------- */
    4466             :     /*      Find code (ignoring version) out of string like:                */
    4467             :     /*                                                                      */
    4468             :     /*      authority:[version]:code                                        */
    4469             :     /* -------------------------------------------------------------------- */
    4470           1 :     pszAuthority = pszCur;
    4471             : 
    4472             :     // skip authority
    4473           5 :     while (*pszCur != ':' && *pszCur)
    4474           4 :         pszCur++;
    4475           1 :     if (*pszCur == ':')
    4476           1 :         pszCur++;
    4477             : 
    4478             :     // skip version
    4479           1 :     pszBeforeVersion = pszCur;
    4480           1 :     while (*pszCur != ':' && *pszCur)
    4481           0 :         pszCur++;
    4482           1 :     if (*pszCur == ':')
    4483           1 :         pszCur++;
    4484             :     else
    4485           0 :         pszCur = pszBeforeVersion;
    4486             : 
    4487           1 :     pszCode = pszCur;
    4488             : 
    4489           2 :     OGRSpatialReference oVertSRS;
    4490           1 :     eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
    4491           1 :     if (eStatus == OGRERR_NONE)
    4492             :     {
    4493           1 :         OGRSpatialReference oHorizSRS(*this);
    4494             : 
    4495           1 :         Clear();
    4496             : 
    4497           1 :         oHorizSRS.d->refreshProjObj();
    4498           1 :         oVertSRS.d->refreshProjObj();
    4499           1 :         if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
    4500           0 :             return OGRERR_FAILURE;
    4501             : 
    4502           1 :         const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
    4503           1 :         const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
    4504             : 
    4505           2 :         CPLString osName = pszHorizName ? pszHorizName : "";
    4506           1 :         osName += " + ";
    4507           1 :         osName += pszVertName ? pszVertName : "";
    4508             : 
    4509           1 :         SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
    4510             :     }
    4511             : 
    4512           1 :     return eStatus;
    4513             : #endif
    4514             : }
    4515             : 
    4516             : /************************************************************************/
    4517             : /*                           importFromCRSURL()                         */
    4518             : /*                                                                      */
    4519             : /*      See OGC Best Practice document 11-135 for details.              */
    4520             : /************************************************************************/
    4521             : 
    4522             : /**
    4523             :  * \brief Initialize from OGC URL.
    4524             :  *
    4525             :  * Initializes this spatial reference from a coordinate system defined
    4526             :  * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
    4527             :  * paper 11-135.  Currently EPSG and OGC authority values are supported,
    4528             :  * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
    4529             :  *
    4530             :  * This method is also supported through SetFromUserInput() which can
    4531             :  * normally be used for URLs.
    4532             :  *
    4533             :  * @param pszURL the URL string.
    4534             :  *
    4535             :  * @return OGRERR_NONE on success or an error code.
    4536             :  */
    4537             : 
    4538         689 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
    4539             : 
    4540             : {
    4541             : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
    4542             :     if (strlen(pszURL) >= 10000)
    4543             :     {
    4544             :         CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
    4545             :         return OGRERR_CORRUPT_DATA;
    4546             :     }
    4547             : 
    4548             :     PJ *obj;
    4549             : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
    4550             :     if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
    4551             :     {
    4552             :         obj = proj_create(
    4553             :             d->getPROJContext(),
    4554             :             CPLSPrintf("IAU:%s",
    4555             :                        pszURL +
    4556             :                            strlen("http://www.opengis.net/def/crs/IAU/0/")));
    4557             :     }
    4558             :     else
    4559             : #endif
    4560             :     {
    4561             :         obj = proj_create(d->getPROJContext(), pszURL);
    4562             :     }
    4563             :     if (!obj)
    4564             :     {
    4565             :         return OGRERR_FAILURE;
    4566             :     }
    4567             :     Clear();
    4568             :     d->setPjCRS(obj);
    4569             :     return OGRERR_NONE;
    4570             : #else
    4571         689 :     const char *pszCur = nullptr;
    4572             : 
    4573         689 :     if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
    4574           2 :         pszCur = pszURL + 26;
    4575         687 :     else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
    4576           1 :         pszCur = pszURL + 27;
    4577         686 :     else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
    4578         685 :         pszCur = pszURL + 30;
    4579           1 :     else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
    4580           1 :         pszCur = pszURL + 31;
    4581           0 :     else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
    4582           0 :         pszCur = pszURL + 23;
    4583             :     else
    4584             :     {
    4585           0 :         CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
    4586             :                  pszURL);
    4587           0 :         return OGRERR_FAILURE;
    4588             :     }
    4589             : 
    4590         689 :     if (*pszCur == '\0')
    4591             :     {
    4592           0 :         CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
    4593           0 :         return OGRERR_FAILURE;
    4594             :     }
    4595             : 
    4596             :     /* -------------------------------------------------------------------- */
    4597             :     /*      Clear any existing definition.                                  */
    4598             :     /* -------------------------------------------------------------------- */
    4599         689 :     Clear();
    4600             : 
    4601         689 :     if (STARTS_WITH_CI(pszCur, "-compound?1="))
    4602             :     {
    4603             :         /* --------------------------------------------------------------------
    4604             :          */
    4605             :         /*      It's a compound CRS, of the form: */
    4606             :         /*                                                                      */
    4607             :         /*      http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
    4608             :         /* --------------------------------------------------------------------
    4609             :          */
    4610           1 :         pszCur += 12;
    4611             : 
    4612             :         // Extract each component CRS URL.
    4613           1 :         int iComponentUrl = 2;
    4614             : 
    4615           2 :         CPLString osName = "";
    4616           1 :         Clear();
    4617             : 
    4618           3 :         while (iComponentUrl != -1)
    4619             :         {
    4620           2 :             char searchStr[15] = {};
    4621           2 :             snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
    4622             : 
    4623           2 :             const char *pszUrlEnd = strstr(pszCur, searchStr);
    4624             : 
    4625             :             // Figure out the next component URL.
    4626           2 :             char *pszComponentUrl = nullptr;
    4627             : 
    4628           2 :             if (pszUrlEnd)
    4629             :             {
    4630           1 :                 size_t nLen = pszUrlEnd - pszCur;
    4631           1 :                 pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
    4632           1 :                 strncpy(pszComponentUrl, pszCur, nLen);
    4633           1 :                 pszComponentUrl[nLen] = '\0';
    4634             : 
    4635           1 :                 ++iComponentUrl;
    4636           1 :                 pszCur += nLen + strlen(searchStr);
    4637             :             }
    4638             :             else
    4639             :             {
    4640           1 :                 if (iComponentUrl == 2)
    4641             :                 {
    4642           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    4643             :                              "Compound CRS URLs must have at least two "
    4644             :                              "component CRSs.");
    4645           0 :                     return OGRERR_FAILURE;
    4646             :                 }
    4647             :                 else
    4648             :                 {
    4649           1 :                     pszComponentUrl = CPLStrdup(pszCur);
    4650             :                     // no more components
    4651           1 :                     iComponentUrl = -1;
    4652             :                 }
    4653             :             }
    4654             : 
    4655           2 :             OGRSpatialReference oComponentSRS;
    4656           2 :             OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
    4657             : 
    4658           2 :             CPLFree(pszComponentUrl);
    4659           2 :             pszComponentUrl = nullptr;
    4660             : 
    4661           2 :             if (eStatus == OGRERR_NONE)
    4662             :             {
    4663           2 :                 if (osName.length() != 0)
    4664             :                 {
    4665           1 :                     osName += " + ";
    4666             :                 }
    4667           2 :                 osName += oComponentSRS.GetRoot()->GetValue();
    4668           2 :                 SetNode("COMPD_CS", osName);
    4669           2 :                 GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
    4670             :             }
    4671             :             else
    4672           0 :                 return eStatus;
    4673             :         }
    4674             : 
    4675           1 :         return OGRERR_NONE;
    4676             :     }
    4677             : 
    4678             :     /* -------------------------------------------------------------------- */
    4679             :     /*      It's a normal CRS URL, of the form:                             */
    4680             :     /*                                                                      */
    4681             :     /*      http://opengis.net/def/crs/AUTHORITY/VERSION/CODE               */
    4682             :     /* -------------------------------------------------------------------- */
    4683         688 :     ++pszCur;
    4684         688 :     const char *pszAuthority = pszCur;
    4685             : 
    4686             :     // skip authority
    4687      103377 :     while (*pszCur != '/' && *pszCur)
    4688      102689 :         pszCur++;
    4689         688 :     if (*pszCur == '/')
    4690         687 :         pszCur++;
    4691             : 
    4692             :     // skip version
    4693        1493 :     while (*pszCur != '/' && *pszCur)
    4694         805 :         pszCur++;
    4695         688 :     if (*pszCur == '/')
    4696         687 :         pszCur++;
    4697             : 
    4698         688 :     const char *pszCode = pszCur;
    4699             : 
    4700         688 :     return importFromURNPart(pszAuthority, pszCode, pszURL);
    4701             : #endif
    4702             : }
    4703             : 
    4704             : /************************************************************************/
    4705             : /*                         importFromWMSAUTO()                          */
    4706             : /************************************************************************/
    4707             : 
    4708             : /**
    4709             :  * \brief Initialize from WMSAUTO string.
    4710             :  *
    4711             :  * Note that the WMS 1.3 specification does not include the
    4712             :  * units code, while apparently earlier specs do.  We try to
    4713             :  * guess around this.
    4714             :  *
    4715             :  * @param pszDefinition the WMSAUTO string
    4716             :  *
    4717             :  * @return OGRERR_NONE on success or an error code.
    4718             :  */
    4719           3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
    4720             : 
    4721             : {
    4722             : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
    4723             :     if (strlen(pszDefinition) >= 10000)
    4724             :     {
    4725             :         CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
    4726             :         return OGRERR_CORRUPT_DATA;
    4727             :     }
    4728             : 
    4729             :     auto obj = proj_create(d->getPROJContext(), pszDefinition);
    4730             :     if (!obj)
    4731             :     {
    4732             :         return OGRERR_FAILURE;
    4733             :     }
    4734             :     Clear();
    4735             :     d->setPjCRS(obj);
    4736             :     return OGRERR_NONE;
    4737             : #else
    4738             :     int nProjId, nUnitsId;
    4739           3 :     double dfRefLong, dfRefLat = 0.0;
    4740             : 
    4741             :     /* -------------------------------------------------------------------- */
    4742             :     /*      Tokenize                                                        */
    4743             :     /* -------------------------------------------------------------------- */
    4744           3 :     if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
    4745           3 :         pszDefinition += 5;
    4746             : 
    4747             :     char **papszTokens =
    4748           3 :         CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
    4749             : 
    4750           3 :     if (CSLCount(papszTokens) == 4)
    4751             :     {
    4752           0 :         nProjId = atoi(papszTokens[0]);
    4753           0 :         nUnitsId = atoi(papszTokens[1]);
    4754           0 :         dfRefLong = CPLAtof(papszTokens[2]);
    4755           0 :         dfRefLat = CPLAtof(papszTokens[3]);
    4756             :     }
    4757           3 :     else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
    4758             :     {
    4759           0 :         nProjId = atoi(papszTokens[0]);
    4760           0 :         nUnitsId = atoi(papszTokens[1]);
    4761           0 :         dfRefLong = CPLAtof(papszTokens[2]);
    4762           0 :         dfRefLat = 0.0;
    4763             :     }
    4764           3 :     else if (CSLCount(papszTokens) == 3)
    4765             :     {
    4766           2 :         nProjId = atoi(papszTokens[0]);
    4767           2 :         nUnitsId = 9001;
    4768           2 :         dfRefLong = CPLAtof(papszTokens[1]);
    4769           2 :         dfRefLat = CPLAtof(papszTokens[2]);
    4770             :     }
    4771           1 :     else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
    4772             :     {
    4773           0 :         nProjId = atoi(papszTokens[0]);
    4774           0 :         nUnitsId = 9001;
    4775           0 :         dfRefLong = CPLAtof(papszTokens[1]);
    4776             :     }
    4777             :     else
    4778             :     {
    4779           1 :         CSLDestroy(papszTokens);
    4780           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4781             :                  "AUTO projection has wrong number of arguments, expected\n"
    4782             :                  "AUTO:proj_id,units_id,ref_long,ref_lat or"
    4783             :                  "AUTO:proj_id,ref_long,ref_lat");
    4784           1 :         return OGRERR_FAILURE;
    4785             :     }
    4786             : 
    4787           2 :     CSLDestroy(papszTokens);
    4788           2 :     papszTokens = nullptr;
    4789             : 
    4790             :     /* -------------------------------------------------------------------- */
    4791             :     /*      Build coordsys.                                                 */
    4792             :     /* -------------------------------------------------------------------- */
    4793           2 :     Clear();
    4794             : 
    4795             :     /* -------------------------------------------------------------------- */
    4796             :     /*      Set WGS84.                                                      */
    4797             :     /* -------------------------------------------------------------------- */
    4798           2 :     SetWellKnownGeogCS("WGS84");
    4799             : 
    4800           2 :     switch (nProjId)
    4801             :     {
    4802           2 :         case 42001:  // Auto UTM
    4803           2 :             SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
    4804             :                    dfRefLat >= 0.0);
    4805           2 :             break;
    4806             : 
    4807           0 :         case 42002:  // Auto TM (strangely very UTM-like).
    4808           0 :             SetTM(0, dfRefLong, 0.9996, 500000.0,
    4809             :                   (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
    4810           0 :             break;
    4811             : 
    4812           0 :         case 42003:  // Auto Orthographic.
    4813           0 :             SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
    4814           0 :             break;
    4815             : 
    4816           0 :         case 42004:  // Auto Equirectangular
    4817           0 :             SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
    4818           0 :             break;
    4819             : 
    4820           0 :         case 42005:
    4821           0 :             SetMollweide(dfRefLong, 0.0, 0.0);
    4822           0 :             break;
    4823             : 
    4824           0 :         default:
    4825           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4826             :                      "Unsupported projection id in importFromWMSAUTO(): %d",
    4827             :                      nProjId);
    4828           0 :             return OGRERR_FAILURE;
    4829             :     }
    4830             : 
    4831             :     /* -------------------------------------------------------------------- */
    4832             :     /*      Set units.                                                      */
    4833             :     /* -------------------------------------------------------------------- */
    4834             : 
    4835           2 :     switch (nUnitsId)
    4836             :     {
    4837           2 :         case 9001:
    4838           2 :             SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
    4839           2 :             break;
    4840             : 
    4841           0 :         case 9002:
    4842           0 :             SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
    4843           0 :             break;
    4844             : 
    4845           0 :         case 9003:
    4846           0 :             SetTargetLinearUnits(nullptr, "US survey foot",
    4847             :                                  CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
    4848           0 :             break;
    4849             : 
    4850           0 :         default:
    4851           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4852             :                      "Unsupported units code (%d).", nUnitsId);
    4853           0 :             return OGRERR_FAILURE;
    4854             :             break;
    4855             :     }
    4856             : 
    4857           2 :     return OGRERR_NONE;
    4858             : #endif
    4859             : }
    4860             : 
    4861             : /************************************************************************/
    4862             : /*                            GetSemiMajor()                            */
    4863             : /************************************************************************/
    4864             : 
    4865             : /**
    4866             :  * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
    4867             :  *
    4868             :  * This method does the same thing as the C function OSRGetSemiMajor().
    4869             :  *
    4870             :  * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
    4871             :  * can be found.
    4872             :  *
    4873             :  * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
    4874             :  */
    4875             : 
    4876        4847 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
    4877             : 
    4878             : {
    4879        4847 :     if (pnErr != nullptr)
    4880        2253 :         *pnErr = OGRERR_FAILURE;
    4881             : 
    4882        4847 :     d->refreshProjObj();
    4883        4847 :     if (!d->m_pj_crs)
    4884         111 :         return SRS_WGS84_SEMIMAJOR;
    4885             : 
    4886        4736 :     auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
    4887        4736 :     if (!ellps)
    4888           4 :         return SRS_WGS84_SEMIMAJOR;
    4889             : 
    4890        4732 :     double dfSemiMajor = 0.0;
    4891        4732 :     proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
    4892             :                                   nullptr, nullptr, nullptr);
    4893        4732 :     proj_destroy(ellps);
    4894             : 
    4895        4732 :     if (dfSemiMajor > 0)
    4896             :     {
    4897        4732 :         if (pnErr != nullptr)
    4898        2140 :             *pnErr = OGRERR_NONE;
    4899        4732 :         return dfSemiMajor;
    4900             :     }
    4901             : 
    4902           0 :     return SRS_WGS84_SEMIMAJOR;
    4903             : }
    4904             : 
    4905             : /************************************************************************/
    4906             : /*                          OSRGetSemiMajor()                           */
    4907             : /************************************************************************/
    4908             : 
    4909             : /**
    4910             :  * \brief Get spheroid semi major axis.
    4911             :  *
    4912             :  * This function is the same as OGRSpatialReference::GetSemiMajor()
    4913             :  */
    4914          58 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
    4915             : 
    4916             : {
    4917          58 :     VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
    4918             : 
    4919          58 :     return ToPointer(hSRS)->GetSemiMajor(pnErr);
    4920             : }
    4921             : 
    4922             : /************************************************************************/
    4923             : /*                          GetInvFlattening()                          */
    4924             : /************************************************************************/
    4925             : 
    4926             : /**
    4927             :  * \brief Get spheroid inverse flattening.
    4928             :  *
    4929             :  * This method does the same thing as the C function OSRGetInvFlattening().
    4930             :  *
    4931             :  * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
    4932             :  * can be found.
    4933             :  *
    4934             :  * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
    4935             :  */
    4936             : 
    4937        3242 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
    4938             : 
    4939             : {
    4940        3242 :     if (pnErr != nullptr)
    4941        2189 :         *pnErr = OGRERR_FAILURE;
    4942             : 
    4943        3242 :     d->refreshProjObj();
    4944        3242 :     if (!d->m_pj_crs)
    4945         111 :         return SRS_WGS84_INVFLATTENING;
    4946             : 
    4947        3131 :     auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
    4948        3131 :     if (!ellps)
    4949           2 :         return SRS_WGS84_INVFLATTENING;
    4950             : 
    4951        3129 :     double dfInvFlattening = -1.0;
    4952        3129 :     proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
    4953             :                                   nullptr, &dfInvFlattening);
    4954        3129 :     proj_destroy(ellps);
    4955             : 
    4956        3129 :     if (dfInvFlattening >= 0.0)
    4957             :     {
    4958        3129 :         if (pnErr != nullptr)
    4959        2078 :             *pnErr = OGRERR_NONE;
    4960        3129 :         return dfInvFlattening;
    4961             :     }
    4962             : 
    4963           0 :     return SRS_WGS84_INVFLATTENING;
    4964             : }
    4965             : 
    4966             : /************************************************************************/
    4967             : /*                        OSRGetInvFlattening()                         */
    4968             : /************************************************************************/
    4969             : 
    4970             : /**
    4971             :  * \brief Get spheroid inverse flattening.
    4972             :  *
    4973             :  * This function is the same as OGRSpatialReference::GetInvFlattening()
    4974             :  */
    4975          11 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
    4976             : 
    4977             : {
    4978          11 :     VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
    4979             : 
    4980          11 :     return ToPointer(hSRS)->GetInvFlattening(pnErr);
    4981             : }
    4982             : 
    4983             : /************************************************************************/
    4984             : /*                           GetEccentricity()                          */
    4985             : /************************************************************************/
    4986             : 
    4987             : /**
    4988             :  * \brief Get spheroid eccentricity
    4989             :  *
    4990             :  * @return eccentricity (or -1 in case of error)
    4991             :  * @since GDAL 2.3
    4992             :  */
    4993             : 
    4994           0 : double OGRSpatialReference::GetEccentricity() const
    4995             : 
    4996             : {
    4997           0 :     OGRErr eErr = OGRERR_NONE;
    4998           0 :     const double dfInvFlattening = GetInvFlattening(&eErr);
    4999           0 :     if (eErr != OGRERR_NONE)
    5000             :     {
    5001           0 :         return -1.0;
    5002             :     }
    5003           0 :     if (dfInvFlattening == 0.0)
    5004           0 :         return 0.0;
    5005           0 :     if (dfInvFlattening < 0.5)
    5006           0 :         return -1.0;
    5007           0 :     return sqrt(2.0 / dfInvFlattening -
    5008           0 :                 1.0 / (dfInvFlattening * dfInvFlattening));
    5009             : }
    5010             : 
    5011             : /************************************************************************/
    5012             : /*                      GetSquaredEccentricity()                        */
    5013             : /************************************************************************/
    5014             : 
    5015             : /**
    5016             :  * \brief Get spheroid squared eccentricity
    5017             :  *
    5018             :  * @return squared eccentricity (or -1 in case of error)
    5019             :  * @since GDAL 2.3
    5020             :  */
    5021             : 
    5022           0 : double OGRSpatialReference::GetSquaredEccentricity() const
    5023             : 
    5024             : {
    5025           0 :     OGRErr eErr = OGRERR_NONE;
    5026           0 :     const double dfInvFlattening = GetInvFlattening(&eErr);
    5027           0 :     if (eErr != OGRERR_NONE)
    5028             :     {
    5029           0 :         return -1.0;
    5030             :     }
    5031           0 :     if (dfInvFlattening == 0.0)
    5032           0 :         return 0.0;
    5033           0 :     if (dfInvFlattening < 0.5)
    5034           0 :         return -1.0;
    5035           0 :     return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
    5036             : }
    5037             : 
    5038             : /************************************************************************/
    5039             : /*                            GetSemiMinor()                            */
    5040             : /************************************************************************/
    5041             : 
    5042             : /**
    5043             :  * \brief Get spheroid semi minor axis.
    5044             :  *
    5045             :  * This method does the same thing as the C function OSRGetSemiMinor().
    5046             :  *
    5047             :  * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
    5048             :  * can be found.
    5049             :  *
    5050             :  * @return semi-minor axis, or WGS84 semi minor if it can't be found.
    5051             :  */
    5052             : 
    5053         630 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
    5054             : 
    5055             : {
    5056         630 :     const double dfSemiMajor = GetSemiMajor(pnErr);
    5057         630 :     const double dfInvFlattening = GetInvFlattening(pnErr);
    5058             : 
    5059         630 :     return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
    5060             : }
    5061             : 
    5062             : /************************************************************************/
    5063             : /*                          OSRGetSemiMinor()                           */
    5064             : /************************************************************************/
    5065             : 
    5066             : /**
    5067             :  * \brief Get spheroid semi minor axis.
    5068             :  *
    5069             :  * This function is the same as OGRSpatialReference::GetSemiMinor()
    5070             :  */
    5071           4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
    5072             : 
    5073             : {
    5074           4 :     VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
    5075             : 
    5076           4 :     return ToPointer(hSRS)->GetSemiMinor(pnErr);
    5077             : }
    5078             : 
    5079             : /************************************************************************/
    5080             : /*                             SetLocalCS()                             */
    5081             : /************************************************************************/
    5082             : 
    5083             : /**
    5084             :  * \brief Set the user visible LOCAL_CS name.
    5085             :  *
    5086             :  * This method is the same as the C function OSRSetLocalCS().
    5087             :  *
    5088             :  * This method will ensure a LOCAL_CS node is created as the root,
    5089             :  * and set the provided name on it.  It must be used before SetLinearUnits().
    5090             :  *
    5091             :  * @param pszName the user visible name to assign.  Not used as a key.
    5092             :  *
    5093             :  * @return OGRERR_NONE on success.
    5094             :  */
    5095             : 
    5096        2881 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
    5097             : 
    5098             : {
    5099        2881 :     if (d->m_pjType == PJ_TYPE_UNKNOWN ||
    5100           0 :         d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
    5101             :     {
    5102        2881 :         d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
    5103             :     }
    5104             :     else
    5105             :     {
    5106           0 :         CPLDebug("OGR",
    5107             :                  "OGRSpatialReference::SetLocalCS(%s) failed.  "
    5108             :                  "It appears an incompatible object already exists.",
    5109             :                  pszName);
    5110           0 :         return OGRERR_FAILURE;
    5111             :     }
    5112             : 
    5113        2881 :     return OGRERR_NONE;
    5114             : }
    5115             : 
    5116             : /************************************************************************/
    5117             : /*                           OSRSetLocalCS()                            */
    5118             : /************************************************************************/
    5119             : 
    5120             : /**
    5121             :  * \brief Set the user visible LOCAL_CS name.
    5122             :  *
    5123             :  * This function is the same as OGRSpatialReference::SetLocalCS()
    5124             :  */
    5125           1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
    5126             : 
    5127             : {
    5128           1 :     VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
    5129             : 
    5130           1 :     return ToPointer(hSRS)->SetLocalCS(pszName);
    5131             : }
    5132             : 
    5133             : /************************************************************************/
    5134             : /*                             SetGeocCS()                              */
    5135             : /************************************************************************/
    5136             : 
    5137             : /**
    5138             :  * \brief Set the user visible GEOCCS name.
    5139             :  *
    5140             :  * This method is the same as the C function OSRSetGeocCS().
    5141             : 
    5142             :  * This method will ensure a GEOCCS node is created as the root,
    5143             :  * and set the provided name on it.  If used on a GEOGCS coordinate system,
    5144             :  * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
    5145             :  * the GEOGCS.
    5146             :  *
    5147             :  * @param pszName the user visible name to assign.  Not used as a key.
    5148             :  *
    5149             :  * @return OGRERR_NONE on success.
    5150             :  *
    5151             :  * @since OGR 1.9.0
    5152             :  */
    5153             : 
    5154           6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
    5155             : 
    5156             : {
    5157           6 :     OGRErr eErr = OGRERR_NONE;
    5158           6 :     d->refreshProjObj();
    5159           6 :     d->demoteFromBoundCRS();
    5160           6 :     if (d->m_pjType == PJ_TYPE_UNKNOWN)
    5161             :     {
    5162           3 :         d->setPjCRS(proj_create_geocentric_crs(
    5163             :             d->getPROJContext(), pszName, "World Geodetic System 1984",
    5164             :             "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
    5165             :             SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
    5166             :             "Metre", 1.0));
    5167             :     }
    5168           3 :     else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
    5169             :     {
    5170           1 :         d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
    5171             :     }
    5172           3 :     else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
    5173           1 :              d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
    5174             :     {
    5175           1 :         auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
    5176             : #if PROJ_VERSION_MAJOR > 7 ||                                                  \
    5177             :     (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
    5178             :         if (datum == nullptr)
    5179             :         {
    5180             :             datum =
    5181             :                 proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
    5182             :         }
    5183             : #endif
    5184           1 :         if (datum == nullptr)
    5185             :         {
    5186           0 :             d->undoDemoteFromBoundCRS();
    5187           0 :             return OGRERR_FAILURE;
    5188             :         }
    5189             : 
    5190           1 :         auto pj_crs = proj_create_geocentric_crs_from_datum(
    5191           1 :             d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
    5192             :             0.0);
    5193           1 :         d->setPjCRS(pj_crs);
    5194             : 
    5195           1 :         proj_destroy(datum);
    5196             :     }
    5197             :     else
    5198             :     {
    5199           1 :         CPLDebug("OGR",
    5200             :                  "OGRSpatialReference::SetGeocCS(%s) failed.  "
    5201             :                  "It appears an incompatible object already exists.",
    5202             :                  pszName);
    5203           1 :         eErr = OGRERR_FAILURE;
    5204             :     }
    5205           6 :     d->undoDemoteFromBoundCRS();
    5206             : 
    5207           6 :     return eErr;
    5208             : }
    5209             : 
    5210             : /************************************************************************/
    5211             : /*                            OSRSetGeocCS()                            */
    5212             : /************************************************************************/
    5213             : 
    5214             : /**
    5215             :  * \brief Set the user visible PROJCS name.
    5216             :  *
    5217             :  * This function is the same as OGRSpatialReference::SetGeocCS()
    5218             :  *
    5219             :  * @since OGR 1.9.0
    5220             :  */
    5221           4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
    5222             : 
    5223             : {
    5224           4 :     VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
    5225             : 
    5226           4 :     return ToPointer(hSRS)->SetGeocCS(pszName);
    5227             : }
    5228             : 
    5229             : /************************************************************************/
    5230             : /*                             SetVertCS()                              */
    5231             : /************************************************************************/
    5232             : 
    5233             : /**
    5234             :  * \brief Set the user visible VERT_CS name.
    5235             :  *
    5236             :  * This method is the same as the C function OSRSetVertCS().
    5237             : 
    5238             :  * This method will ensure a VERT_CS node is created if needed.  If the
    5239             :  * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
    5240             :  * turned into a COMPD_CS.
    5241             :  *
    5242             :  * @param pszVertCSName the user visible name of the vertical coordinate
    5243             :  * system. Not used as a key.
    5244             :  *
    5245             :  * @param pszVertDatumName the user visible name of the vertical datum.  It
    5246             :  * is helpful if this matches the EPSG name.
    5247             :  *
    5248             :  * @param nVertDatumType the OGC vertical datum type. Ignored
    5249             :  *
    5250             :  * @return OGRERR_NONE on success.
    5251             :  *
    5252             :  * @since OGR 1.9.0
    5253             :  */
    5254             : 
    5255           1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
    5256             :                                       const char *pszVertDatumName,
    5257             :                                       int nVertDatumType)
    5258             : 
    5259             : {
    5260           1 :     CPL_IGNORE_RET_VAL(nVertDatumType);
    5261             : 
    5262           1 :     d->refreshProjObj();
    5263             : 
    5264           1 :     auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
    5265             :                                             pszVertDatumName, nullptr, 0.0);
    5266             : 
    5267             :     /* -------------------------------------------------------------------- */
    5268             :     /*      Handle the case where we want to make a compound coordinate     */
    5269             :     /*      system.                                                         */
    5270             :     /* -------------------------------------------------------------------- */
    5271           1 :     if (IsProjected() || IsGeographic())
    5272             :     {
    5273           1 :         auto compoundCRS = proj_create_compound_crs(
    5274           1 :             d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
    5275           1 :         proj_destroy(vertCRS);
    5276           1 :         d->setPjCRS(compoundCRS);
    5277             :     }
    5278             :     else
    5279             :     {
    5280           0 :         d->setPjCRS(vertCRS);
    5281             :     }
    5282           1 :     return OGRERR_NONE;
    5283             : }
    5284             : 
    5285             : /************************************************************************/
    5286             : /*                            OSRSetVertCS()                            */
    5287             : /************************************************************************/
    5288             : 
    5289             : /**
    5290             :  * \brief Setup the vertical coordinate system.
    5291             :  *
    5292             :  * This function is the same as OGRSpatialReference::SetVertCS()
    5293             :  *
    5294             :  * @since OGR 1.9.0
    5295             :  */
    5296           0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
    5297             :                     const char *pszVertDatumName, int nVertDatumType)
    5298             : 
    5299             : {
    5300           0 :     VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
    5301             : 
    5302           0 :     return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
    5303           0 :                                       nVertDatumType);
    5304             : }
    5305             : 
    5306             : /************************************************************************/
    5307             : /*                           SetCompoundCS()                            */
    5308             : /************************************************************************/
    5309             : 
    5310             : /**
    5311             :  * \brief Setup a compound coordinate system.
    5312             :  *
    5313             :  * This method is the same as the C function OSRSetCompoundCS().
    5314             : 
    5315             :  * This method is replace the current SRS with a COMPD_CS coordinate system
    5316             :  * consisting of the passed in horizontal and vertical coordinate systems.
    5317             :  *
    5318             :  * @param pszName the name of the compound coordinate system.
    5319             :  *
    5320             :  * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
    5321             :  *
    5322             :  * @param poVertSRS the vertical SRS (VERT_CS).
    5323             :  *
    5324             :  * @return OGRERR_NONE on success.
    5325             :  */
    5326             : 
    5327          92 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
    5328             :                                           const OGRSpatialReference *poHorizSRS,
    5329             :                                           const OGRSpatialReference *poVertSRS)
    5330             : 
    5331             : {
    5332             :     /* -------------------------------------------------------------------- */
    5333             :     /*      Verify these are legal horizontal and vertical coordinate       */
    5334             :     /*      systems.                                                        */
    5335             :     /* -------------------------------------------------------------------- */
    5336          92 :     if (!poVertSRS->IsVertical())
    5337             :     {
    5338           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5339             :                  "SetCompoundCS() fails, vertical component is not VERT_CS.");
    5340           0 :         return OGRERR_FAILURE;
    5341             :     }
    5342          92 :     if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
    5343             :     {
    5344           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5345             :                  "SetCompoundCS() fails, horizontal component is not PROJCS or "
    5346             :                  "GEOGCS.");
    5347           0 :         return OGRERR_FAILURE;
    5348             :     }
    5349             : 
    5350             :     /* -------------------------------------------------------------------- */
    5351             :     /*      Replace with compound srs.                                      */
    5352             :     /* -------------------------------------------------------------------- */
    5353          92 :     Clear();
    5354             : 
    5355          92 :     auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
    5356          92 :                                                 poHorizSRS->d->m_pj_crs,
    5357          92 :                                                 poVertSRS->d->m_pj_crs);
    5358          92 :     d->setPjCRS(compoundCRS);
    5359             : 
    5360          92 :     return OGRERR_NONE;
    5361             : }
    5362             : 
    5363             : /************************************************************************/
    5364             : /*                          OSRSetCompoundCS()                          */
    5365             : /************************************************************************/
    5366             : 
    5367             : /**
    5368             :  * \brief Setup a compound coordinate system.
    5369             :  *
    5370             :  * This function is the same as OGRSpatialReference::SetCompoundCS()
    5371             :  */
    5372           8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
    5373             :                         OGRSpatialReferenceH hHorizSRS,
    5374             :                         OGRSpatialReferenceH hVertSRS)
    5375             : 
    5376             : {
    5377           8 :     VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
    5378           8 :     VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
    5379           8 :     VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
    5380             : 
    5381          16 :     return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
    5382          16 :                                           ToPointer(hVertSRS));
    5383             : }
    5384             : 
    5385             : /************************************************************************/
    5386             : /*                             SetProjCS()                              */
    5387             : /************************************************************************/
    5388             : 
    5389             : /**
    5390             :  * \brief Set the user visible PROJCS name.
    5391             :  *
    5392             :  * This method is the same as the C function OSRSetProjCS().
    5393             :  *
    5394             :  * This method will ensure a PROJCS node is created as the root,
    5395             :  * and set the provided name on it.  If used on a GEOGCS coordinate system,
    5396             :  * the GEOGCS node will be demoted to be a child of the new PROJCS root.
    5397             :  *
    5398             :  * @param pszName the user visible name to assign.  Not used as a key.
    5399             :  *
    5400             :  * @return OGRERR_NONE on success.
    5401             :  */
    5402             : 
    5403        3714 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
    5404             : 
    5405             : {
    5406        3714 :     d->refreshProjObj();
    5407        3714 :     d->demoteFromBoundCRS();
    5408        3714 :     if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
    5409             :     {
    5410         480 :         d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
    5411             :     }
    5412             :     else
    5413             :     {
    5414        3234 :         auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
    5415             :                                                 nullptr, nullptr, nullptr,
    5416             :                                                 nullptr, nullptr, 0, nullptr);
    5417        3234 :         auto cs = proj_create_cartesian_2D_cs(
    5418             :             d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
    5419             : 
    5420        3234 :         auto projCRS = proj_create_projected_crs(
    5421        3234 :             d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
    5422        3234 :         proj_destroy(dummyConv);
    5423        3234 :         proj_destroy(cs);
    5424             : 
    5425        3234 :         d->setPjCRS(projCRS);
    5426             :     }
    5427        3714 :     d->undoDemoteFromBoundCRS();
    5428        3714 :     return OGRERR_NONE;
    5429             : }
    5430             : 
    5431             : /************************************************************************/
    5432             : /*                            OSRSetProjCS()                            */
    5433             : /************************************************************************/
    5434             : 
    5435             : /**
    5436             :  * \brief Set the user visible PROJCS name.
    5437             :  *
    5438             :  * This function is the same as OGRSpatialReference::SetProjCS()
    5439             :  */
    5440           7 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
    5441             : 
    5442             : {
    5443           7 :     VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
    5444             : 
    5445           7 :     return ToPointer(hSRS)->SetProjCS(pszName);
    5446             : }
    5447             : 
    5448             : /************************************************************************/
    5449             : /*                           SetProjection()                            */
    5450             : /************************************************************************/
    5451             : 
    5452             : /**
    5453             :  * \brief Set a projection name.
    5454             :  *
    5455             :  * This method is the same as the C function OSRSetProjection().
    5456             :  *
    5457             :  * @param pszProjection the projection name, which should be selected from
    5458             :  * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
    5459             :  *
    5460             :  * @return OGRERR_NONE on success.
    5461             :  */
    5462             : 
    5463          23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
    5464             : 
    5465             : {
    5466          23 :     OGR_SRSNode *poGeogCS = nullptr;
    5467             : 
    5468          23 :     if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
    5469             :     {
    5470           4 :         poGeogCS = d->m_poRoot;
    5471           4 :         d->m_poRoot = nullptr;
    5472             :     }
    5473             : 
    5474          23 :     if (!GetAttrNode("PROJCS"))
    5475             :     {
    5476          11 :         SetNode("PROJCS", "unnamed");
    5477             :     }
    5478             : 
    5479          23 :     const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
    5480          23 :     if (eErr != OGRERR_NONE)
    5481           0 :         return eErr;
    5482             : 
    5483          23 :     if (poGeogCS != nullptr)
    5484           4 :         d->m_poRoot->InsertChild(poGeogCS, 1);
    5485             : 
    5486          23 :     return OGRERR_NONE;
    5487             : }
    5488             : 
    5489             : /************************************************************************/
    5490             : /*                            OSRSetProjection()                        */
    5491             : /************************************************************************/
    5492             : 
    5493             : /**
    5494             :  * \brief Set a projection name.
    5495             :  *
    5496             :  * This function is the same as OGRSpatialReference::SetProjection()
    5497             :  */
    5498           0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
    5499             : 
    5500             : {
    5501           0 :     VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
    5502             : 
    5503           0 :     return ToPointer(hSRS)->SetProjection(pszProjection);
    5504             : }
    5505             : 
    5506             : /************************************************************************/
    5507             : /*                      GetWKT2ProjectionMethod()                       */
    5508             : /************************************************************************/
    5509             : 
    5510             : /**
    5511             :  * \brief Returns info on the projection method, based on WKT2 naming
    5512             :  * conventions.
    5513             :  *
    5514             :  * The returned strings are short lived and should be considered to be
    5515             :  * invalidated by any further call to the GDAL API.
    5516             :  *
    5517             :  * @param[out] ppszMethodName Pointer to a string that will receive the
    5518             :  * projection method name.
    5519             :  * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
    5520             :  * receive the name of the authority that defines the projection method.
    5521             :  * *ppszMethodAuthName may be nullptr if the projection method is not linked to
    5522             :  * an authority.
    5523             :  * @param[out] ppszMethodCode null pointer, or pointer to a string that will
    5524             :  * receive the code that defines the projection method.
    5525             :  * *ppszMethodCode may be nullptr if the projection method is not linked to
    5526             :  * an authority.
    5527             :  *
    5528             :  * @return OGRERR_NONE on success.
    5529             :  */
    5530             : OGRErr
    5531           1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
    5532             :                                              const char **ppszMethodAuthName,
    5533             :                                              const char **ppszMethodCode) const
    5534             : {
    5535           1 :     auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
    5536           1 :     if (!conv)
    5537           0 :         return OGRERR_FAILURE;
    5538           1 :     const char *pszTmpMethodName = "";
    5539           1 :     const char *pszTmpMethodAuthName = "";
    5540           1 :     const char *pszTmpMethodCode = "";
    5541           1 :     int ret = proj_coordoperation_get_method_info(
    5542             :         d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
    5543             :         &pszTmpMethodCode);
    5544             :     // "Internalize" temporary strings returned by PROJ
    5545           1 :     CPLAssert(pszTmpMethodName);
    5546           1 :     if (ppszMethodName)
    5547           1 :         *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
    5548           1 :     if (ppszMethodAuthName)
    5549           0 :         *ppszMethodAuthName = pszTmpMethodAuthName
    5550           0 :                                   ? CPLSPrintf("%s", pszTmpMethodAuthName)
    5551           0 :                                   : nullptr;
    5552           1 :     if (ppszMethodCode)
    5553           0 :         *ppszMethodCode =
    5554           0 :             pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
    5555           1 :     proj_destroy(conv);
    5556           1 :     return ret ? OGRERR_NONE : OGRERR_FAILURE;
    5557             : }
    5558             : 
    5559             : /************************************************************************/
    5560             : /*                            SetProjParm()                             */
    5561             : /************************************************************************/
    5562             : 
    5563             : /**
    5564             :  * \brief Set a projection parameter value.
    5565             :  *
    5566             :  * Adds a new PARAMETER under the PROJCS with the indicated name and value.
    5567             :  *
    5568             :  * This method is the same as the C function OSRSetProjParm().
    5569             :  *
    5570             :  * Please check https://gdal.org/proj_list pages for
    5571             :  * legal parameter names for specific projections.
    5572             :  *
    5573             :  *
    5574             :  * @param pszParamName the parameter name, which should be selected from
    5575             :  * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
    5576             :  *
    5577             :  * @param dfValue value to assign.
    5578             :  *
    5579             :  * @return OGRERR_NONE on success.
    5580             :  */
    5581             : 
    5582         133 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
    5583             :                                         double dfValue)
    5584             : 
    5585             : {
    5586         133 :     OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
    5587             : 
    5588         133 :     if (poPROJCS == nullptr)
    5589           5 :         return OGRERR_FAILURE;
    5590             : 
    5591         128 :     char szValue[64] = {'\0'};
    5592         128 :     OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
    5593             : 
    5594             :     /* -------------------------------------------------------------------- */
    5595             :     /*      Try to find existing parameter with this name.                  */
    5596             :     /* -------------------------------------------------------------------- */
    5597        1040 :     for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
    5598             :     {
    5599         953 :         OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
    5600             : 
    5601        1256 :         if (EQUAL(poParam->GetValue(), "PARAMETER") &&
    5602        1256 :             poParam->GetChildCount() == 2 &&
    5603         303 :             EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
    5604             :         {
    5605          41 :             poParam->GetChild(1)->SetValue(szValue);
    5606          41 :             return OGRERR_NONE;
    5607             :         }
    5608             :     }
    5609             : 
    5610             :     /* -------------------------------------------------------------------- */
    5611             :     /*      Otherwise create a new parameter and append.                    */
    5612             :     /* -------------------------------------------------------------------- */
    5613          87 :     OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
    5614          87 :     poParam->AddChild(new OGR_SRSNode(pszParamName));
    5615          87 :     poParam->AddChild(new OGR_SRSNode(szValue));
    5616             : 
    5617          87 :     poPROJCS->AddChild(poParam);
    5618             : 
    5619          87 :     return OGRERR_NONE;
    5620             : }
    5621             : 
    5622             : /************************************************************************/
    5623             : /*                           OSRSetProjParm()                           */
    5624             : /************************************************************************/
    5625             : 
    5626             : /**
    5627             :  * \brief Set a projection parameter value.
    5628             :  *
    5629             :  * This function is the same as OGRSpatialReference::SetProjParm()
    5630             :  */
    5631           0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
    5632             :                       double dfValue)
    5633             : 
    5634             : {
    5635           0 :     VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
    5636             : 
    5637           0 :     return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
    5638             : }
    5639             : 
    5640             : /************************************************************************/
    5641             : /*                            FindProjParm()                            */
    5642             : /************************************************************************/
    5643             : 
    5644             : /**
    5645             :  * \brief Return the child index of the named projection parameter on
    5646             :  * its parent PROJCS node.
    5647             :  *
    5648             :  * @param pszParameter projection parameter to look for
    5649             :  * @param poPROJCS projection CS node to look in. If NULL is passed,
    5650             :  *        the PROJCS node of the SpatialReference object will be searched.
    5651             :  *
    5652             :  * @return the child index of the named projection parameter. -1 on failure
    5653             :  */
    5654        4316 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
    5655             :                                       const OGR_SRSNode *poPROJCS) const
    5656             : 
    5657             : {
    5658        4316 :     if (poPROJCS == nullptr)
    5659           0 :         poPROJCS = GetAttrNode("PROJCS");
    5660             : 
    5661        4316 :     if (poPROJCS == nullptr)
    5662           0 :         return -1;
    5663             : 
    5664             :     /* -------------------------------------------------------------------- */
    5665             :     /*      Search for requested parameter.                                 */
    5666             :     /* -------------------------------------------------------------------- */
    5667        4316 :     bool bIsWKT2 = false;
    5668       27604 :     for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
    5669             :     {
    5670       27193 :         const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
    5671             : 
    5672       27193 :         if (poParameter->GetChildCount() >= 2)
    5673             :         {
    5674       18586 :             const char *pszValue = poParameter->GetValue();
    5675       31215 :             if (EQUAL(pszValue, "PARAMETER") &&
    5676       12629 :                 EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
    5677             :                       pszParameter))
    5678             :             {
    5679        3905 :                 return iChild;
    5680             :             }
    5681       14681 :             else if (EQUAL(pszValue, "METHOD"))
    5682             :             {
    5683          25 :                 bIsWKT2 = true;
    5684             :             }
    5685             :         }
    5686             :     }
    5687             : 
    5688             :     /* -------------------------------------------------------------------- */
    5689             :     /*      Try similar names, for selected parameters.                     */
    5690             :     /* -------------------------------------------------------------------- */
    5691         411 :     if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
    5692             :     {
    5693         184 :         if (bIsWKT2)
    5694             :         {
    5695           4 :             int iChild = FindProjParm(
    5696             :                 EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
    5697           4 :             if (iChild == -1)
    5698           3 :                 iChild = FindProjParm(
    5699             :                     EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
    5700           4 :             return iChild;
    5701             :         }
    5702         180 :         return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
    5703             :     }
    5704             : 
    5705         227 :     if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
    5706             :     {
    5707          27 :         if (bIsWKT2)
    5708             :         {
    5709           5 :             int iChild = FindProjParm(
    5710             :                 EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
    5711           5 :             if (iChild == -1)
    5712           0 :                 iChild = FindProjParm(
    5713             :                     EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
    5714           5 :             return iChild;
    5715             :         }
    5716          22 :         int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
    5717          22 :         if (iChild == -1)
    5718           0 :             iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
    5719          22 :         return iChild;
    5720             :     }
    5721             : 
    5722         200 :     return -1;
    5723             : }
    5724             : 
    5725             : /************************************************************************/
    5726             : /*                            GetProjParm()                             */
    5727             : /************************************************************************/
    5728             : 
    5729             : /**
    5730             :  * \brief Fetch a projection parameter value.
    5731             :  *
    5732             :  * NOTE: This code should be modified to translate non degree angles into
    5733             :  * degrees based on the GEOGCS unit.  This has not yet been done.
    5734             :  *
    5735             :  * This method is the same as the C function OSRGetProjParm().
    5736             :  *
    5737             :  * @param pszName the name of the parameter to fetch, from the set of
    5738             :  * SRS_PP codes in ogr_srs_api.h.
    5739             :  *
    5740             :  * @param dfDefaultValue the value to return if this parameter doesn't exist.
    5741             :  *
    5742             :  * @param pnErr place to put error code on failure.  Ignored if NULL.
    5743             :  *
    5744             :  * @return value of parameter.
    5745             :  */
    5746             : 
    5747        4351 : double OGRSpatialReference::GetProjParm(const char *pszName,
    5748             :                                         double dfDefaultValue,
    5749             :                                         OGRErr *pnErr) const
    5750             : 
    5751             : {
    5752        4351 :     d->refreshProjObj();
    5753        4351 :     GetRoot();  // force update of d->m_bNodesWKT2
    5754             : 
    5755        4351 :     if (pnErr != nullptr)
    5756        3395 :         *pnErr = OGRERR_NONE;
    5757             : 
    5758             :     /* -------------------------------------------------------------------- */
    5759             :     /*      Find the desired parameter.                                     */
    5760             :     /* -------------------------------------------------------------------- */
    5761             :     const OGR_SRSNode *poPROJCS =
    5762        4351 :         GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
    5763        4351 :     if (poPROJCS == nullptr)
    5764             :     {
    5765         249 :         if (pnErr != nullptr)
    5766         248 :             *pnErr = OGRERR_FAILURE;
    5767         249 :         return dfDefaultValue;
    5768             :     }
    5769             : 
    5770        4102 :     const int iChild = FindProjParm(pszName, poPROJCS);
    5771        4102 :     if (iChild == -1)
    5772             :     {
    5773         197 :         if (IsProjected() && GetAxesCount() == 3)
    5774             :         {
    5775           3 :             OGRSpatialReference *poSRSTmp = Clone();
    5776           3 :             poSRSTmp->DemoteTo2D(nullptr);
    5777             :             const double dfRet =
    5778           3 :                 poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
    5779           3 :             delete poSRSTmp;
    5780           3 :             return dfRet;
    5781             :         }
    5782             : 
    5783         194 :         if (pnErr != nullptr)
    5784         172 :             *pnErr = OGRERR_FAILURE;
    5785         194 :         return dfDefaultValue;
    5786             :     }
    5787             : 
    5788        3905 :     const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
    5789        3905 :     return CPLAtof(poParameter->GetChild(1)->GetValue());
    5790             : }
    5791             : 
    5792             : /************************************************************************/
    5793             : /*                           OSRGetProjParm()                           */
    5794             : /************************************************************************/
    5795             : 
    5796             : /**
    5797             :  * \brief Fetch a projection parameter value.
    5798             :  *
    5799             :  * This function is the same as OGRSpatialReference::GetProjParm()
    5800             :  */
    5801          91 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
    5802             :                       double dfDefaultValue, OGRErr *pnErr)
    5803             : 
    5804             : {
    5805          91 :     VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
    5806             : 
    5807          91 :     return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
    5808             : }
    5809             : 
    5810             : /************************************************************************/
    5811             : /*                          GetNormProjParm()                           */
    5812             : /************************************************************************/
    5813             : 
    5814             : /**
    5815             :  * \brief Fetch a normalized projection parameter value.
    5816             :  *
    5817             :  * This method is the same as GetProjParm() except that the value of
    5818             :  * the parameter is "normalized" into degrees or meters depending on
    5819             :  * whether it is linear or angular.
    5820             :  *
    5821             :  * This method is the same as the C function OSRGetNormProjParm().
    5822             :  *
    5823             :  * @param pszName the name of the parameter to fetch, from the set of
    5824             :  * SRS_PP codes in ogr_srs_api.h.
    5825             :  *
    5826             :  * @param dfDefaultValue the value to return if this parameter doesn't exist.
    5827             :  *
    5828             :  * @param pnErr place to put error code on failure.  Ignored if NULL.
    5829             :  *
    5830             :  * @return value of parameter.
    5831             :  */
    5832             : 
    5833        3369 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
    5834             :                                             double dfDefaultValue,
    5835             :                                             OGRErr *pnErr) const
    5836             : 
    5837             : {
    5838        3369 :     GetNormInfo();
    5839             : 
    5840        3369 :     OGRErr nError = OGRERR_NONE;
    5841        3369 :     double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
    5842        3369 :     if (pnErr != nullptr)
    5843           0 :         *pnErr = nError;
    5844             : 
    5845             :     // If we got the default just return it unadjusted.
    5846        3369 :     if (nError != OGRERR_NONE)
    5847         420 :         return dfRawResult;
    5848             : 
    5849        2949 :     if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
    5850           4 :         dfRawResult *= d->dfToDegrees;
    5851             : 
    5852        2949 :     if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
    5853           5 :         return dfRawResult * d->dfToMeter;
    5854             : 
    5855        2944 :     return dfRawResult;
    5856             : }
    5857             : 
    5858             : /************************************************************************/
    5859             : /*                         OSRGetNormProjParm()                         */
    5860             : /************************************************************************/
    5861             : 
    5862             : /**
    5863             :  * \brief This function is the same as OGRSpatialReference::
    5864             :  *
    5865             :  * This function is the same as OGRSpatialReference::GetNormProjParm()
    5866             :  */
    5867           1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
    5868             :                           double dfDefaultValue, OGRErr *pnErr)
    5869             : 
    5870             : {
    5871           1 :     VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
    5872             : 
    5873           1 :     return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
    5874             : }
    5875             : 
    5876             : /************************************************************************/
    5877             : /*                          SetNormProjParm()                           */
    5878             : /************************************************************************/
    5879             : 
    5880             : /**
    5881             :  * \brief Set a projection parameter with a normalized value.
    5882             :  *
    5883             :  * This method is the same as SetProjParm() except that the value of
    5884             :  * the parameter passed in is assumed to be in "normalized" form (decimal
    5885             :  * degrees for angular values, meters for linear values.  The values are
    5886             :  * converted in a form suitable for the GEOGCS and linear units in effect.
    5887             :  *
    5888             :  * This method is the same as the C function OSRSetNormProjParm().
    5889             :  *
    5890             :  * @param pszName the parameter name, which should be selected from
    5891             :  * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
    5892             :  *
    5893             :  * @param dfValue value to assign.
    5894             :  *
    5895             :  * @return OGRERR_NONE on success.
    5896             :  */
    5897             : 
    5898          91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
    5899             : 
    5900             : {
    5901          91 :     GetNormInfo();
    5902             : 
    5903          91 :     if (d->dfToDegrees != 0.0 &&
    5904          91 :         (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
    5905           0 :         IsAngularParameter(pszName))
    5906             :     {
    5907           0 :         dfValue /= d->dfToDegrees;
    5908             :     }
    5909          95 :     else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
    5910           4 :              IsLinearParameter(pszName))
    5911           4 :         dfValue /= d->dfToMeter;
    5912             : 
    5913          91 :     return SetProjParm(pszName, dfValue);
    5914             : }
    5915             : 
    5916             : /************************************************************************/
    5917             : /*                         OSRSetNormProjParm()                         */
    5918             : /************************************************************************/
    5919             : 
    5920             : /**
    5921             :  * \brief Set a projection parameter with a normalized value.
    5922             :  *
    5923             :  * This function is the same as OGRSpatialReference::SetNormProjParm()
    5924             :  */
    5925           0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
    5926             :                           double dfValue)
    5927             : 
    5928             : {
    5929           0 :     VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
    5930             : 
    5931           0 :     return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
    5932             : }
    5933             : 
    5934             : /************************************************************************/
    5935             : /*                               SetTM()                                */
    5936             : /************************************************************************/
    5937             : 
    5938         397 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
    5939             :                                   double dfScale, double dfFalseEasting,
    5940             :                                   double dfFalseNorthing)
    5941             : 
    5942             : {
    5943         397 :     return d->replaceConversionAndUnref(
    5944             :         proj_create_conversion_transverse_mercator(
    5945             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
    5946         397 :             dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    5947             : }
    5948             : 
    5949             : /************************************************************************/
    5950             : /*                              OSRSetTM()                              */
    5951             : /************************************************************************/
    5952             : 
    5953           5 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
    5954             :                 double dfCenterLong, double dfScale, double dfFalseEasting,
    5955             :                 double dfFalseNorthing)
    5956             : 
    5957             : {
    5958           5 :     VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
    5959             : 
    5960           5 :     return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
    5961           5 :                                   dfFalseEasting, dfFalseNorthing);
    5962             : }
    5963             : 
    5964             : /************************************************************************/
    5965             : /*                            SetTMVariant()                            */
    5966             : /************************************************************************/
    5967             : 
    5968           0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
    5969             :                                          double dfCenterLat,
    5970             :                                          double dfCenterLong, double dfScale,
    5971             :                                          double dfFalseEasting,
    5972             :                                          double dfFalseNorthing)
    5973             : 
    5974             : {
    5975           0 :     SetProjection(pszVariantName);
    5976           0 :     SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
    5977           0 :     SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
    5978           0 :     SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
    5979           0 :     SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
    5980           0 :     SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
    5981             : 
    5982           0 :     return OGRERR_NONE;
    5983             : }
    5984             : 
    5985             : /************************************************************************/
    5986             : /*                          OSRSetTMVariant()                           */
    5987             : /************************************************************************/
    5988             : 
    5989           0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
    5990             :                        double dfCenterLat, double dfCenterLong, double dfScale,
    5991             :                        double dfFalseEasting, double dfFalseNorthing)
    5992             : 
    5993             : {
    5994           0 :     VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
    5995             : 
    5996           0 :     return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
    5997             :                                          dfCenterLong, dfScale, dfFalseEasting,
    5998           0 :                                          dfFalseNorthing);
    5999             : }
    6000             : 
    6001             : /************************************************************************/
    6002             : /*                              SetTMSO()                               */
    6003             : /************************************************************************/
    6004             : 
    6005           3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
    6006             :                                     double dfScale, double dfFalseEasting,
    6007             :                                     double dfFalseNorthing)
    6008             : 
    6009             : {
    6010           3 :     auto conv = proj_create_conversion_transverse_mercator_south_oriented(
    6011             :         d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
    6012             :         dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
    6013             : 
    6014           3 :     const char *pszName = nullptr;
    6015           3 :     double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
    6016           3 :     CPLString osName = pszName ? pszName : "";
    6017             : 
    6018           3 :     d->refreshProjObj();
    6019             : 
    6020           3 :     d->demoteFromBoundCRS();
    6021             : 
    6022           3 :     auto cs = proj_create_cartesian_2D_cs(
    6023             :         d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
    6024           3 :         !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
    6025             :     auto projCRS =
    6026           3 :         proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
    6027           3 :                                   d->getGeodBaseCRS(), conv, cs);
    6028           3 :     proj_destroy(conv);
    6029           3 :     proj_destroy(cs);
    6030             : 
    6031           3 :     d->setPjCRS(projCRS);
    6032             : 
    6033           3 :     d->undoDemoteFromBoundCRS();
    6034             : 
    6035           6 :     return OGRERR_NONE;
    6036             : }
    6037             : 
    6038             : /************************************************************************/
    6039             : /*                             OSRSetTMSO()                             */
    6040             : /************************************************************************/
    6041             : 
    6042           0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6043             :                   double dfCenterLong, double dfScale, double dfFalseEasting,
    6044             :                   double dfFalseNorthing)
    6045             : 
    6046             : {
    6047           0 :     VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
    6048             : 
    6049           0 :     return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
    6050           0 :                                     dfFalseEasting, dfFalseNorthing);
    6051             : }
    6052             : 
    6053             : /************************************************************************/
    6054             : /*                              SetTPED()                               */
    6055             : /************************************************************************/
    6056             : 
    6057           1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
    6058             :                                     double dfLat2, double dfLong2,
    6059             :                                     double dfFalseEasting,
    6060             :                                     double dfFalseNorthing)
    6061             : 
    6062             : {
    6063           1 :     return d->replaceConversionAndUnref(
    6064             :         proj_create_conversion_two_point_equidistant(
    6065             :             d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
    6066           1 :             dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6067             : }
    6068             : 
    6069             : /************************************************************************/
    6070             : /*                             OSRSetTPED()                             */
    6071             : /************************************************************************/
    6072             : 
    6073           0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
    6074             :                   double dfLat2, double dfLong2, double dfFalseEasting,
    6075             :                   double dfFalseNorthing)
    6076             : 
    6077             : {
    6078           0 :     VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
    6079             : 
    6080           0 :     return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
    6081           0 :                                     dfFalseEasting, dfFalseNorthing);
    6082             : }
    6083             : 
    6084             : /************************************************************************/
    6085             : /*                               SetTMG()                               */
    6086             : /************************************************************************/
    6087             : 
    6088           0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
    6089             :                                    double dfFalseEasting,
    6090             :                                    double dfFalseNorthing)
    6091             : 
    6092             : {
    6093           0 :     return d->replaceConversionAndUnref(
    6094             :         proj_create_conversion_tunisia_mapping_grid(
    6095             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
    6096           0 :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6097             : }
    6098             : 
    6099             : /************************************************************************/
    6100             : /*                             OSRSetTMG()                              */
    6101             : /************************************************************************/
    6102             : 
    6103           0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6104             :                  double dfCenterLong, double dfFalseEasting,
    6105             :                  double dfFalseNorthing)
    6106             : 
    6107             : {
    6108           0 :     VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
    6109             : 
    6110           0 :     return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
    6111           0 :                                    dfFalseNorthing);
    6112             : }
    6113             : 
    6114             : /************************************************************************/
    6115             : /*                              SetACEA()                               */
    6116             : /************************************************************************/
    6117             : 
    6118          40 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
    6119             :                                     double dfCenterLat, double dfCenterLong,
    6120             :                                     double dfFalseEasting,
    6121             :                                     double dfFalseNorthing)
    6122             : 
    6123             : {
    6124             :     // Note different order of parameters. The one in PROJ is conformant with
    6125             :     // EPSG
    6126          40 :     return d->replaceConversionAndUnref(
    6127             :         proj_create_conversion_albers_equal_area(
    6128             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
    6129          40 :             dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6130             : }
    6131             : 
    6132             : /************************************************************************/
    6133             : /*                             OSRSetACEA()                             */
    6134             : /************************************************************************/
    6135             : 
    6136           0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
    6137             :                   double dfCenterLat, double dfCenterLong,
    6138             :                   double dfFalseEasting, double dfFalseNorthing)
    6139             : 
    6140             : {
    6141           0 :     VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
    6142             : 
    6143           0 :     return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
    6144           0 :                                     dfFalseEasting, dfFalseNorthing);
    6145             : }
    6146             : 
    6147             : /************************************************************************/
    6148             : /*                               SetAE()                                */
    6149             : /************************************************************************/
    6150             : 
    6151          19 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
    6152             :                                   double dfFalseEasting, double dfFalseNorthing)
    6153             : 
    6154             : {
    6155          19 :     return d->replaceConversionAndUnref(
    6156             :         proj_create_conversion_azimuthal_equidistant(
    6157             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
    6158          19 :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6159             : }
    6160             : 
    6161             : /************************************************************************/
    6162             : /*                              OSRSetAE()                              */
    6163             : /************************************************************************/
    6164             : 
    6165           0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6166             :                 double dfCenterLong, double dfFalseEasting,
    6167             :                 double dfFalseNorthing)
    6168             : 
    6169             : {
    6170           0 :     VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
    6171             : 
    6172           0 :     return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
    6173           0 :                                   dfFalseNorthing);
    6174             : }
    6175             : 
    6176             : /************************************************************************/
    6177             : /*                              SetBonne()                              */
    6178             : /************************************************************************/
    6179             : 
    6180           1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
    6181             :                                      double dfFalseEasting,
    6182             :                                      double dfFalseNorthing)
    6183             : 
    6184             : {
    6185           1 :     return d->replaceConversionAndUnref(proj_create_conversion_bonne(
    6186             :         d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
    6187           1 :         dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6188             : }
    6189             : 
    6190             : /************************************************************************/
    6191             : /*                            OSRSetBonne()                             */
    6192             : /************************************************************************/
    6193             : 
    6194           0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
    6195             :                    double dfCentralMeridian, double dfFalseEasting,
    6196             :                    double dfFalseNorthing)
    6197             : 
    6198             : {
    6199           0 :     VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
    6200             : 
    6201           0 :     return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
    6202           0 :                                      dfFalseNorthing);
    6203             : }
    6204             : 
    6205             : /************************************************************************/
    6206             : /*                               SetCEA()                               */
    6207             : /************************************************************************/
    6208             : 
    6209           4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
    6210             :                                    double dfFalseEasting,
    6211             :                                    double dfFalseNorthing)
    6212             : 
    6213             : {
    6214           4 :     return d->replaceConversionAndUnref(
    6215             :         proj_create_conversion_lambert_cylindrical_equal_area(
    6216             :             d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
    6217           4 :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6218             : }
    6219             : 
    6220             : /************************************************************************/
    6221             : /*                             OSRSetCEA()                              */
    6222             : /************************************************************************/
    6223             : 
    6224           0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
    6225             :                  double dfCentralMeridian, double dfFalseEasting,
    6226             :                  double dfFalseNorthing)
    6227             : 
    6228             : {
    6229           0 :     VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
    6230             : 
    6231           0 :     return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
    6232           0 :                                    dfFalseNorthing);
    6233             : }
    6234             : 
    6235             : /************************************************************************/
    6236             : /*                               SetCS()                                */
    6237             : /************************************************************************/
    6238             : 
    6239           5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
    6240             :                                   double dfFalseEasting, double dfFalseNorthing)
    6241             : 
    6242             : {
    6243           5 :     return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
    6244             :         d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
    6245           5 :         dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6246             : }
    6247             : 
    6248             : /************************************************************************/
    6249             : /*                              OSRSetCS()                              */
    6250             : /************************************************************************/
    6251             : 
    6252           0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6253             :                 double dfCenterLong, double dfFalseEasting,
    6254             :                 double dfFalseNorthing)
    6255             : 
    6256             : {
    6257           0 :     VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
    6258             : 
    6259           0 :     return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
    6260           0 :                                   dfFalseNorthing);
    6261             : }
    6262             : 
    6263             : /************************************************************************/
    6264             : /*                               SetEC()                                */
    6265             : /************************************************************************/
    6266             : 
    6267           7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
    6268             :                                   double dfCenterLat, double dfCenterLong,
    6269             :                                   double dfFalseEasting, double dfFalseNorthing)
    6270             : 
    6271             : {
    6272             :     // Note: different order of arguments
    6273           7 :     return d->replaceConversionAndUnref(
    6274             :         proj_create_conversion_equidistant_conic(
    6275             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
    6276           7 :             dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6277             : }
    6278             : 
    6279             : /************************************************************************/
    6280             : /*                              OSRSetEC()                              */
    6281             : /************************************************************************/
    6282             : 
    6283           0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
    6284             :                 double dfCenterLat, double dfCenterLong, double dfFalseEasting,
    6285             :                 double dfFalseNorthing)
    6286             : 
    6287             : {
    6288           0 :     VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
    6289             : 
    6290           0 :     return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
    6291           0 :                                   dfFalseEasting, dfFalseNorthing);
    6292             : }
    6293             : 
    6294             : /************************************************************************/
    6295             : /*                             SetEckert()                              */
    6296             : /************************************************************************/
    6297             : 
    6298          10 : OGRErr OGRSpatialReference::SetEckert(int nVariation,  // 1-6.
    6299             :                                       double dfCentralMeridian,
    6300             :                                       double dfFalseEasting,
    6301             :                                       double dfFalseNorthing)
    6302             : 
    6303             : {
    6304             :     PJ *conv;
    6305          10 :     if (nVariation == 1)
    6306             :     {
    6307           1 :         conv = proj_create_conversion_eckert_i(
    6308             :             d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
    6309             :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
    6310             :     }
    6311           9 :     else if (nVariation == 2)
    6312             :     {
    6313           1 :         conv = proj_create_conversion_eckert_ii(
    6314             :             d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
    6315             :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
    6316             :     }
    6317           8 :     else if (nVariation == 3)
    6318             :     {
    6319           1 :         conv = proj_create_conversion_eckert_iii(
    6320             :             d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
    6321             :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
    6322             :     }
    6323           7 :     else if (nVariation == 4)
    6324             :     {
    6325           3 :         conv = proj_create_conversion_eckert_iv(
    6326             :             d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
    6327             :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
    6328             :     }
    6329           4 :     else if (nVariation == 5)
    6330             :     {
    6331           1 :         conv = proj_create_conversion_eckert_v(
    6332             :             d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
    6333             :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
    6334             :     }
    6335           3 :     else if (nVariation == 6)
    6336             :     {
    6337           3 :         conv = proj_create_conversion_eckert_vi(
    6338             :             d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
    6339             :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
    6340             :     }
    6341             :     else
    6342             :     {
    6343           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6344             :                  "Unsupported Eckert variation (%d).", nVariation);
    6345           0 :         return OGRERR_UNSUPPORTED_SRS;
    6346             :     }
    6347             : 
    6348          10 :     return d->replaceConversionAndUnref(conv);
    6349             : }
    6350             : 
    6351             : /************************************************************************/
    6352             : /*                            OSRSetEckert()                            */
    6353             : /************************************************************************/
    6354             : 
    6355           0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
    6356             :                     double dfCentralMeridian, double dfFalseEasting,
    6357             :                     double dfFalseNorthing)
    6358             : 
    6359             : {
    6360           0 :     VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
    6361             : 
    6362           0 :     return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
    6363           0 :                                       dfFalseEasting, dfFalseNorthing);
    6364             : }
    6365             : 
    6366             : /************************************************************************/
    6367             : /*                            SetEckertIV()                             */
    6368             : /*                                                                      */
    6369             : /*      Deprecated                                                      */
    6370             : /************************************************************************/
    6371             : 
    6372           2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
    6373             :                                         double dfFalseEasting,
    6374             :                                         double dfFalseNorthing)
    6375             : 
    6376             : {
    6377           2 :     return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
    6378             : }
    6379             : 
    6380             : /************************************************************************/
    6381             : /*                           OSRSetEckertIV()                           */
    6382             : /************************************************************************/
    6383             : 
    6384           0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
    6385             :                       double dfFalseEasting, double dfFalseNorthing)
    6386             : 
    6387             : {
    6388           0 :     VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
    6389             : 
    6390           0 :     return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
    6391           0 :                                         dfFalseNorthing);
    6392             : }
    6393             : 
    6394             : /************************************************************************/
    6395             : /*                            SetEckertVI()                             */
    6396             : /*                                                                      */
    6397             : /*      Deprecated                                                      */
    6398             : /************************************************************************/
    6399             : 
    6400           2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
    6401             :                                         double dfFalseEasting,
    6402             :                                         double dfFalseNorthing)
    6403             : 
    6404             : {
    6405           2 :     return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
    6406             : }
    6407             : 
    6408             : /************************************************************************/
    6409             : /*                           OSRSetEckertVI()                           */
    6410             : /************************************************************************/
    6411             : 
    6412           0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
    6413             :                       double dfFalseEasting, double dfFalseNorthing)
    6414             : 
    6415             : {
    6416           0 :     VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
    6417             : 
    6418           0 :     return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
    6419           0 :                                         dfFalseNorthing);
    6420             : }
    6421             : 
    6422             : /************************************************************************/
    6423             : /*                         SetEquirectangular()                         */
    6424             : /************************************************************************/
    6425             : 
    6426           2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
    6427             :                                                double dfCenterLong,
    6428             :                                                double dfFalseEasting,
    6429             :                                                double dfFalseNorthing)
    6430             : 
    6431             : {
    6432           2 :     if (dfCenterLat == 0.0)
    6433             :     {
    6434           0 :         return d->replaceConversionAndUnref(
    6435             :             proj_create_conversion_equidistant_cylindrical(
    6436             :                 d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
    6437           0 :                 dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6438             :     }
    6439             : 
    6440             :     // Non-standard extension with non-zero latitude of origin
    6441           2 :     SetProjection(SRS_PT_EQUIRECTANGULAR);
    6442           2 :     SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
    6443           2 :     SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
    6444           2 :     SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
    6445           2 :     SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
    6446             : 
    6447           2 :     return OGRERR_NONE;
    6448             : }
    6449             : 
    6450             : /************************************************************************/
    6451             : /*                       OSRSetEquirectangular()                        */
    6452             : /************************************************************************/
    6453             : 
    6454           0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6455             :                              double dfCenterLong, double dfFalseEasting,
    6456             :                              double dfFalseNorthing)
    6457             : 
    6458             : {
    6459           0 :     VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
    6460             : 
    6461           0 :     return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
    6462           0 :                                                dfFalseEasting, dfFalseNorthing);
    6463             : }
    6464             : 
    6465             : /************************************************************************/
    6466             : /*                         SetEquirectangular2()                        */
    6467             : /* Generalized form                                                     */
    6468             : /************************************************************************/
    6469             : 
    6470         174 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
    6471             :                                                 double dfCenterLong,
    6472             :                                                 double dfStdParallel1,
    6473             :                                                 double dfFalseEasting,
    6474             :                                                 double dfFalseNorthing)
    6475             : 
    6476             : {
    6477         174 :     if (dfCenterLat == 0.0)
    6478             :     {
    6479         169 :         return d->replaceConversionAndUnref(
    6480             :             proj_create_conversion_equidistant_cylindrical(
    6481             :                 d->getPROJContext(), dfStdParallel1, dfCenterLong,
    6482         169 :                 dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6483             :     }
    6484             : 
    6485             :     // Non-standard extension with non-zero latitude of origin
    6486           5 :     SetProjection(SRS_PT_EQUIRECTANGULAR);
    6487           5 :     SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
    6488           5 :     SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
    6489           5 :     SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
    6490           5 :     SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
    6491           5 :     SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
    6492             : 
    6493           5 :     return OGRERR_NONE;
    6494             : }
    6495             : 
    6496             : /************************************************************************/
    6497             : /*                       OSRSetEquirectangular2()                       */
    6498             : /************************************************************************/
    6499             : 
    6500           3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6501             :                               double dfCenterLong, double dfStdParallel1,
    6502             :                               double dfFalseEasting, double dfFalseNorthing)
    6503             : 
    6504             : {
    6505           3 :     VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
    6506             : 
    6507           3 :     return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
    6508             :                                                 dfStdParallel1, dfFalseEasting,
    6509           3 :                                                 dfFalseNorthing);
    6510             : }
    6511             : 
    6512             : /************************************************************************/
    6513             : /*                               SetGS()                                */
    6514             : /************************************************************************/
    6515             : 
    6516           5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
    6517             :                                   double dfFalseEasting, double dfFalseNorthing)
    6518             : 
    6519             : {
    6520           5 :     return d->replaceConversionAndUnref(proj_create_conversion_gall(
    6521             :         d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
    6522           5 :         nullptr, 0.0, nullptr, 0.0));
    6523             : }
    6524             : 
    6525             : /************************************************************************/
    6526             : /*                              OSRSetGS()                              */
    6527             : /************************************************************************/
    6528             : 
    6529           2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
    6530             :                 double dfFalseEasting, double dfFalseNorthing)
    6531             : 
    6532             : {
    6533           2 :     VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
    6534             : 
    6535           2 :     return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
    6536           2 :                                   dfFalseNorthing);
    6537             : }
    6538             : 
    6539             : /************************************************************************/
    6540             : /*                               SetGH()                                */
    6541             : /************************************************************************/
    6542             : 
    6543           0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
    6544             :                                   double dfFalseEasting, double dfFalseNorthing)
    6545             : 
    6546             : {
    6547           0 :     return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
    6548             :         d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
    6549           0 :         nullptr, 0.0, nullptr, 0.0));
    6550             : }
    6551             : 
    6552             : /************************************************************************/
    6553             : /*                              OSRSetGH()                              */
    6554             : /************************************************************************/
    6555             : 
    6556           0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
    6557             :                 double dfFalseEasting, double dfFalseNorthing)
    6558             : 
    6559             : {
    6560           0 :     VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
    6561             : 
    6562           0 :     return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
    6563           0 :                                   dfFalseNorthing);
    6564             : }
    6565             : 
    6566             : /************************************************************************/
    6567             : /*                              SetIGH()                                */
    6568             : /************************************************************************/
    6569             : 
    6570           0 : OGRErr OGRSpatialReference::SetIGH()
    6571             : 
    6572             : {
    6573           0 :     return d->replaceConversionAndUnref(
    6574             :         proj_create_conversion_interrupted_goode_homolosine(
    6575           0 :             d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
    6576             : }
    6577             : 
    6578             : /************************************************************************/
    6579             : /*                              OSRSetIGH()                             */
    6580             : /************************************************************************/
    6581             : 
    6582           0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
    6583             : 
    6584             : {
    6585           0 :     VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
    6586             : 
    6587           0 :     return ToPointer(hSRS)->SetIGH();
    6588             : }
    6589             : 
    6590             : /************************************************************************/
    6591             : /*                              SetGEOS()                               */
    6592             : /************************************************************************/
    6593             : 
    6594           3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
    6595             :                                     double dfSatelliteHeight,
    6596             :                                     double dfFalseEasting,
    6597             :                                     double dfFalseNorthing)
    6598             : 
    6599             : {
    6600           3 :     return d->replaceConversionAndUnref(
    6601             :         proj_create_conversion_geostationary_satellite_sweep_y(
    6602             :             d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
    6603           3 :             dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6604             : }
    6605             : 
    6606             : /************************************************************************/
    6607             : /*                              OSRSetGEOS()                             */
    6608             : /************************************************************************/
    6609             : 
    6610           0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
    6611             :                   double dfSatelliteHeight, double dfFalseEasting,
    6612             :                   double dfFalseNorthing)
    6613             : 
    6614             : {
    6615           0 :     VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
    6616             : 
    6617           0 :     return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
    6618           0 :                                     dfFalseEasting, dfFalseNorthing);
    6619             : }
    6620             : 
    6621             : /************************************************************************/
    6622             : /*                       SetGaussSchreiberTMercator()                   */
    6623             : /************************************************************************/
    6624             : 
    6625           0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
    6626             :                                                        double dfCenterLong,
    6627             :                                                        double dfScale,
    6628             :                                                        double dfFalseEasting,
    6629             :                                                        double dfFalseNorthing)
    6630             : 
    6631             : {
    6632           0 :     return d->replaceConversionAndUnref(
    6633             :         proj_create_conversion_gauss_schreiber_transverse_mercator(
    6634             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
    6635           0 :             dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6636             : }
    6637             : 
    6638             : /************************************************************************/
    6639             : /*                     OSRSetGaussSchreiberTMercator()                  */
    6640             : /************************************************************************/
    6641             : 
    6642           0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
    6643             :                                      double dfCenterLat, double dfCenterLong,
    6644             :                                      double dfScale, double dfFalseEasting,
    6645             :                                      double dfFalseNorthing)
    6646             : 
    6647             : {
    6648           0 :     VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
    6649             : 
    6650           0 :     return ToPointer(hSRS)->SetGaussSchreiberTMercator(
    6651           0 :         dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
    6652             : }
    6653             : 
    6654             : /************************************************************************/
    6655             : /*                            SetGnomonic()                             */
    6656             : /************************************************************************/
    6657             : 
    6658           2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
    6659             :                                         double dfFalseEasting,
    6660             :                                         double dfFalseNorthing)
    6661             : 
    6662             : {
    6663           2 :     return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
    6664             :         d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
    6665           2 :         dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6666             : }
    6667             : 
    6668             : /************************************************************************/
    6669             : /*                           OSRSetGnomonic()                           */
    6670             : /************************************************************************/
    6671             : 
    6672           0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6673             :                       double dfCenterLong, double dfFalseEasting,
    6674             :                       double dfFalseNorthing)
    6675             : 
    6676             : {
    6677           0 :     VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
    6678             : 
    6679           0 :     return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
    6680           0 :                                         dfFalseEasting, dfFalseNorthing);
    6681             : }
    6682             : 
    6683             : /************************************************************************/
    6684             : /*                              SetHOMAC()                              */
    6685             : /************************************************************************/
    6686             : 
    6687             : /**
    6688             :  * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
    6689             :  * azimuth angle.
    6690             :  *
    6691             :  * This projection corresponds to EPSG projection method 9815, also
    6692             :  * sometimes known as hotine oblique mercator (variant B).
    6693             :  *
    6694             :  * This method does the same thing as the C function OSRSetHOMAC().
    6695             :  *
    6696             :  * @param dfCenterLat Latitude of the projection origin.
    6697             :  * @param dfCenterLong Longitude of the projection origin.
    6698             :  * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
    6699             :  * centerline.
    6700             :  * @param dfRectToSkew Angle from Rectified to Skew Grid
    6701             :  * @param dfScale Scale factor applies to the projection origin.
    6702             :  * @param dfFalseEasting False easting.
    6703             :  * @param dfFalseNorthing False northing.
    6704             :  *
    6705             :  * @return OGRERR_NONE on success.
    6706             :  */
    6707             : 
    6708           4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
    6709             :                                      double dfAzimuth, double dfRectToSkew,
    6710             :                                      double dfScale, double dfFalseEasting,
    6711             :                                      double dfFalseNorthing)
    6712             : 
    6713             : {
    6714           4 :     return d->replaceConversionAndUnref(
    6715             :         proj_create_conversion_hotine_oblique_mercator_variant_b(
    6716             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
    6717             :             dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
    6718           4 :             0.0, nullptr, 0.0));
    6719             : }
    6720             : 
    6721             : /************************************************************************/
    6722             : /*                            OSRSetHOMAC()                             */
    6723             : /************************************************************************/
    6724             : 
    6725             : /**
    6726             :  * \brief Set an Oblique Mercator projection using azimuth angle.
    6727             :  *
    6728             :  * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
    6729             :  */
    6730           0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6731             :                    double dfCenterLong, double dfAzimuth, double dfRectToSkew,
    6732             :                    double dfScale, double dfFalseEasting,
    6733             :                    double dfFalseNorthing)
    6734             : 
    6735             : {
    6736           0 :     VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
    6737             : 
    6738           0 :     return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
    6739             :                                      dfRectToSkew, dfScale, dfFalseEasting,
    6740           0 :                                      dfFalseNorthing);
    6741             : }
    6742             : 
    6743             : /************************************************************************/
    6744             : /*                               SetHOM()                               */
    6745             : /************************************************************************/
    6746             : 
    6747             : /**
    6748             :  * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
    6749             :  *
    6750             :  * This projection corresponds to EPSG projection method 9812, also
    6751             :  * sometimes known as hotine oblique mercator (variant A)..
    6752             :  *
    6753             :  * This method does the same thing as the C function OSRSetHOM().
    6754             :  *
    6755             :  * @param dfCenterLat Latitude of the projection origin.
    6756             :  * @param dfCenterLong Longitude of the projection origin.
    6757             :  * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
    6758             :  * centerline.
    6759             :  * @param dfRectToSkew Angle from Rectified to Skew Grid
    6760             :  * @param dfScale Scale factor applies to the projection origin.
    6761             :  * @param dfFalseEasting False easting.
    6762             :  * @param dfFalseNorthing False northing.
    6763             :  *
    6764             :  * @return OGRERR_NONE on success.
    6765             :  */
    6766             : 
    6767          12 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
    6768             :                                    double dfAzimuth, double dfRectToSkew,
    6769             :                                    double dfScale, double dfFalseEasting,
    6770             :                                    double dfFalseNorthing)
    6771             : 
    6772             : {
    6773          12 :     return d->replaceConversionAndUnref(
    6774             :         proj_create_conversion_hotine_oblique_mercator_variant_a(
    6775             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
    6776             :             dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
    6777          12 :             0.0, nullptr, 0.0));
    6778             : }
    6779             : 
    6780             : /************************************************************************/
    6781             : /*                             OSRSetHOM()                              */
    6782             : /************************************************************************/
    6783             : /**
    6784             :  * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
    6785             :  *
    6786             :  * This is the same as the C++ method OGRSpatialReference::SetHOM()
    6787             :  */
    6788           0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6789             :                  double dfCenterLong, double dfAzimuth, double dfRectToSkew,
    6790             :                  double dfScale, double dfFalseEasting, double dfFalseNorthing)
    6791             : 
    6792             : {
    6793           0 :     VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
    6794             : 
    6795           0 :     return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
    6796             :                                    dfRectToSkew, dfScale, dfFalseEasting,
    6797           0 :                                    dfFalseNorthing);
    6798             : }
    6799             : 
    6800             : /************************************************************************/
    6801             : /*                             SetHOM2PNO()                             */
    6802             : /************************************************************************/
    6803             : 
    6804             : /**
    6805             :  * \brief Set a Hotine Oblique Mercator projection using two points on
    6806             :  * projection centerline.
    6807             :  *
    6808             :  * This method does the same thing as the C function OSRSetHOM2PNO().
    6809             :  *
    6810             :  * @param dfCenterLat Latitude of the projection origin.
    6811             :  * @param dfLat1 Latitude of the first point on center line.
    6812             :  * @param dfLong1 Longitude of the first point on center line.
    6813             :  * @param dfLat2 Latitude of the second point on center line.
    6814             :  * @param dfLong2 Longitude of the second point on center line.
    6815             :  * @param dfScale Scale factor applies to the projection origin.
    6816             :  * @param dfFalseEasting False easting.
    6817             :  * @param dfFalseNorthing False northing.
    6818             :  *
    6819             :  * @return OGRERR_NONE on success.
    6820             :  */
    6821             : 
    6822           3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
    6823             :                                        double dfLong1, double dfLat2,
    6824             :                                        double dfLong2, double dfScale,
    6825             :                                        double dfFalseEasting,
    6826             :                                        double dfFalseNorthing)
    6827             : 
    6828             : {
    6829           3 :     return d->replaceConversionAndUnref(
    6830             :         proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
    6831             :             d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
    6832             :             dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
    6833           3 :             0.0));
    6834             : }
    6835             : 
    6836             : /************************************************************************/
    6837             : /*                           OSRSetHOM2PNO()                            */
    6838             : /************************************************************************/
    6839             : /**
    6840             :  * \brief Set a Hotine Oblique Mercator projection using two points on
    6841             :  *  projection centerline.
    6842             :  *
    6843             :  * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
    6844             :  */
    6845           0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6846             :                      double dfLat1, double dfLong1, double dfLat2,
    6847             :                      double dfLong2, double dfScale, double dfFalseEasting,
    6848             :                      double dfFalseNorthing)
    6849             : 
    6850             : {
    6851           0 :     VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
    6852             : 
    6853           0 :     return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
    6854             :                                        dfLong2, dfScale, dfFalseEasting,
    6855           0 :                                        dfFalseNorthing);
    6856             : }
    6857             : 
    6858             : /************************************************************************/
    6859             : /*                               SetLOM()                               */
    6860             : /************************************************************************/
    6861             : 
    6862             : /**
    6863             :  * \brief Set a Laborde Oblique Mercator projection.
    6864             :  *
    6865             :  * @param dfCenterLat Latitude of the projection origin.
    6866             :  * @param dfCenterLong Longitude of the projection origin.
    6867             :  * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
    6868             :  * centerline.
    6869             :  * @param dfScale Scale factor on the initiali line
    6870             :  * @param dfFalseEasting False easting.
    6871             :  * @param dfFalseNorthing False northing.
    6872             :  *
    6873             :  * @return OGRERR_NONE on success.
    6874             :  */
    6875             : 
    6876           0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
    6877             :                                    double dfAzimuth, double dfScale,
    6878             :                                    double dfFalseEasting,
    6879             :                                    double dfFalseNorthing)
    6880             : 
    6881             : {
    6882           0 :     return d->replaceConversionAndUnref(
    6883             :         proj_create_conversion_laborde_oblique_mercator(
    6884             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
    6885           0 :             dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6886             : }
    6887             : 
    6888             : /************************************************************************/
    6889             : /*                            SetIWMPolyconic()                         */
    6890             : /************************************************************************/
    6891             : 
    6892           0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
    6893             :                                             double dfCenterLong,
    6894             :                                             double dfFalseEasting,
    6895             :                                             double dfFalseNorthing)
    6896             : 
    6897             : {
    6898           0 :     return d->replaceConversionAndUnref(
    6899             :         proj_create_conversion_international_map_world_polyconic(
    6900             :             d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
    6901           0 :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
    6902             : }
    6903             : 
    6904             : /************************************************************************/
    6905             : /*                          OSRSetIWMPolyconic()                        */
    6906             : /************************************************************************/
    6907             : 
    6908           0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
    6909             :                           double dfLat2, double dfCenterLong,
    6910             :                           double dfFalseEasting, double dfFalseNorthing)
    6911             : 
    6912             : {
    6913           0 :     VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
    6914             : 
    6915           0 :     return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
    6916           0 :                                             dfFalseEasting, dfFalseNorthing);
    6917             : }
    6918             : 
    6919             : /************************************************************************/
    6920             : /*                             SetKrovak()                              */
    6921             : /************************************************************************/
    6922             : 
    6923             : /** Krovak east-north projection.
    6924             :  *
    6925             :  * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
    6926             :  * to PROJ and should be respectively set to 30.28813972222222 and 78.5
    6927             :  */
    6928           3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
    6929             :                                       double dfAzimuth,
    6930             :                                       double dfPseudoStdParallel1,
    6931             :                                       double dfScale, double dfFalseEasting,
    6932             :                                       double dfFalseNorthing)
    6933             : 
    6934             : {
    6935           3 :     return d->replaceConversionAndUnref(
    6936             :         proj_create_conversion_krovak_north_oriented(
    6937             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
    6938             :             dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
    6939           3 :             nullptr, 0.0, nullptr, 0.0));
    6940             : }
    6941             : 
    6942             : /************************************************************************/
    6943             : /*                            OSRSetKrovak()                            */
    6944             : /************************************************************************/
    6945             : 
    6946           0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
    6947             :                     double dfCenterLong, double dfAzimuth,
    6948             :                     double dfPseudoStdParallel1, double dfScale,
    6949             :                     double dfFalseEasting, double dfFalseNorthing)
    6950             : 
    6951             : {
    6952           0 :     VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
    6953             : 
    6954           0 :     return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
    6955             :                                       dfPseudoStdParallel1, dfScale,
    6956           0 :                                       dfFalseEasting, dfFalseNorthing);
    6957             : }
    6958             : 
    6959             : /************************************************************************/
    6960             : /*                              SetLAEA()                               */
    6961             : /************************************************************************/
    6962             : 
    6963          13 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
    6964             :                                     double dfFalseEasting,
    6965             :                                     double dfFalseNorthing)
    6966             : 
    6967             : {
    6968          13 :     auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
    6969             :         d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
    6970             :         dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
    6971             : 
    6972          13 :     const char *pszName = nullptr;
    6973          13 :     double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
    6974          13 :     CPLString osName = pszName ? pszName : "";
    6975             : 
    6976          13 :     d->refreshProjObj();
    6977             : 
    6978          13 :     d->demoteFromBoundCRS();
    6979             : 
    6980          13 :     auto cs = proj_create_cartesian_2D_cs(
    6981             :         d->getPROJContext(),
    6982          13 :         std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
    6983             :             ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
    6984           0 :         : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
    6985          10 :             ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
    6986             :             : PJ_CART2D_EASTING_NORTHING,
    6987          13 :         !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
    6988             :     auto projCRS =
    6989          13 :         proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
    6990          13 :                                   d->getGeodBaseCRS(), conv, cs);
    6991          13 :     proj_destroy(conv);
    6992          13 :     proj_destroy(cs);
    6993             : 
    6994          13 :     d->setPjCRS(projCRS);
    6995             : 
    6996          13 :     d->undoDemoteFromBoundCRS();
    6997             : 
    6998          26 :     return OGRERR_NONE;
    6999             : }
    7000             : 
    7001             : /************************************************************************/
    7002             : /*                             OSRSetLAEA()                             */
    7003             : /************************************************************************/
    7004             : 
    7005           0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
    7006             :                   double dfCenterLong, double dfFalseEasting,
    7007             :                   double dfFalseNorthing)
    7008             : 
    7009             : {
    7010           0 :     VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
    7011             : 
    7012           0 :     return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
    7013           0 :                                     dfFalseNorthing);
    7014             : }
    7015             : 
    7016             : /************************************************************************/
    7017             : /*                               SetLCC()                               */
    7018             : /************************************************************************/
    7019             : 
    7020         145 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
    7021             :                                    double dfCenterLat, double dfCenterLong,
    7022             :                                    double dfFalseEasting,
    7023             :                                    double dfFalseNorthing)
    7024             : 
    7025             : {
    7026         145 :     return d->replaceConversionAndUnref(
    7027             :         proj_create_conversion_lambert_conic_conformal_2sp(
    7028             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
    7029         145 :             dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
    7030             : }
    7031             : 
    7032             : /************************************************************************/
    7033             : /*                             OSRSetLCC()                              */
    7034             : /************************************************************************/
    7035             : 
    7036           3 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
    7037             :                  double dfCenterLat, double dfCenterLong, double dfFalseEasting,
    7038             :                  double dfFalseNorthing)
    7039             : 
    7040             : {
    7041           3 :     VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
    7042             : 
    7043           3 :     return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
    7044           3 :                                    dfFalseEasting, dfFalseNorthing);
    7045             : }
    7046             : 
    7047             : /************************************************************************/
    7048             : /*                             SetLCC1SP()                              */
    7049             : /************************************************************************/
    7050             : 
    7051           7 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
    7052             :                                       double dfScale, double dfFalseEasting,
    7053             :                                       double dfFalseNorthing)
    7054             : 
    7055             : {
    7056           7 :     return d->replaceConversionAndUnref(
    7057             :         proj_create_conversion_lambert_conic_conformal_1sp(
    7058             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
    7059           7 :             dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
    7060             : }
    7061             : 
    7062             : /************************************************************************/
    7063             : /*                            OSRSetLCC1SP()                            */
    7064             : /************************************************************************/
    7065             : 
    7066           0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
    7067             :                     double dfCenterLong, double dfScale, double dfFalseEasting,
    7068             :                     double dfFalseNorthing)
    7069             : 
    7070             : {
    7071           0 :     VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
    7072             : 
    7073           0 :     return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
    7074           0 :                                       dfFalseEasting, dfFalseNorthing);
    7075             : }
    7076             : 
    7077             : /************************************************************************/
    7078             : /*                              SetLCCB()                               */
    7079             : /************************************************************************/
    7080             : 
    7081           2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
    7082             :                                     double dfCenterLat, double dfCenterLong,
    7083             :                                     double dfFalseEasting,
    7084             :                                     double dfFalseNorthing)
    7085             : 
    7086             : {
    7087           2 :     return d->replaceConversionAndUnref(
    7088             :         proj_create_conversion_lambert_conic_conformal_2sp_belgium(
    7089             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
    7090           2 :             dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
    7091             : }
    7092             : 
    7093             : /************************************************************************/
    7094             : /*                             OSRSetLCCB()                             */
    7095             : /************************************************************************/
    7096             : 
    7097           0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
    7098             :                   double dfCenterLat, double dfCenterLong,
    7099             :                   double dfFalseEasting, double dfFalseNorthing)
    7100             : 
    7101             : {
    7102           0 :     VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
    7103             : 
    7104           0 :     return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
    7105           0 :                                     dfFalseEasting, dfFalseNorthing);
    7106             : }
    7107             : 
    7108             : /************************************************************************/
    7109             : /*                               SetMC()                                */
    7110             : /************************************************************************/
    7111             : 
    7112           4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
    7113             :                                   double dfFalseEasting, double dfFalseNorthing)
    7114             : 
    7115             : {
    7116             :     (void)dfCenterLat;  // ignored
    7117             : 
    7118           4 :     return d->replaceConversionAndUnref(
    7119             :         proj_create_conversion_miller_cylindrical(
    7120             :             d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
    7121           4 :             nullptr, 0, nullptr, 0));
    7122             : }
    7123             : 
    7124             : /************************************************************************/
    7125             : /*                              OSRSetMC()                              */
    7126             : /************************************************************************/
    7127             : 
    7128           0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
    7129             :                 double dfCenterLong, double dfFalseEasting,
    7130             :                 double dfFalseNorthing)
    7131             : 
    7132             : {
    7133           0 :     VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
    7134             : 
    7135           0 :     return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
    7136           0 :                                   dfFalseNorthing);
    7137             : }
    7138             : 
    7139             : /************************************************************************/
    7140             : /*                            SetMercator()                             */
    7141             : /************************************************************************/
    7142             : 
    7143          56 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
    7144             :                                         double dfScale, double dfFalseEasting,
    7145             :                                         double dfFalseNorthing)
    7146             : 
    7147             : {
    7148          56 :     if (dfCenterLat != 0.0 && dfScale == 1.0)
    7149             :     {
    7150             :         // Not sure this is correct, but this is how it has been used
    7151             :         // historically
    7152           0 :         return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
    7153           0 :                               dfFalseNorthing);
    7154             :     }
    7155          56 :     return d->replaceConversionAndUnref(
    7156             :         proj_create_conversion_mercator_variant_a(
    7157             :             d->getPROJContext(),
    7158             :             dfCenterLat,  // should be zero
    7159             :             dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
    7160          56 :             nullptr, 0));
    7161             : }
    7162             : 
    7163             : /************************************************************************/
    7164             : /*                           OSRSetMercator()                           */
    7165             : /************************************************************************/
    7166             : 
    7167           2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
    7168             :                       double dfCenterLong, double dfScale,
    7169             :                       double dfFalseEasting, double dfFalseNorthing)
    7170             : 
    7171             : {
    7172           2 :     VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
    7173             : 
    7174           2 :     return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
    7175           2 :                                         dfFalseEasting, dfFalseNorthing);
    7176             : }
    7177             : 
    7178             : /************************************************************************/
    7179             : /*                           SetMercator2SP()                           */
    7180             : /************************************************************************/
    7181             : 
    7182          30 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
    7183             :                                            double dfCenterLong,
    7184             :                                            double dfFalseEasting,
    7185             :                                            double dfFalseNorthing)
    7186             : 
    7187             : {
    7188          30 :     if (dfCenterLat == 0.0)
    7189             :     {
    7190          29 :         return d->replaceConversionAndUnref(
    7191             :             proj_create_conversion_mercator_variant_b(
    7192             :                 d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
    7193          29 :                 dfFalseNorthing, nullptr, 0, nullptr, 0));
    7194             :     }
    7195             : 
    7196           1 :     SetProjection(SRS_PT_MERCATOR_2SP);
    7197             : 
    7198           1 :     SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
    7199           1 :     SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
    7200           1 :     SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
    7201           1 :     SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
    7202           1 :     SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
    7203             : 
    7204           1 :     return OGRERR_NONE;
    7205             : }
    7206             : 
    7207             : /************************************************************************/
    7208             : /*                         OSRSetMercator2SP()                          */
    7209             : /************************************************************************/
    7210             : 
    7211           1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
    7212             :                          double dfCenterLat, double dfCenterLong,
    7213             :                          double dfFalseEasting, double dfFalseNorthing)
    7214             : 
    7215             : {
    7216           1 :     VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
    7217             : 
    7218           1 :     return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
    7219           1 :                                            dfFalseEasting, dfFalseNorthing);
    7220             : }
    7221             : 
    7222             : /************************************************************************/
    7223             : /*                            SetMollweide()                            */
    7224             : /************************************************************************/
    7225             : 
    7226           3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
    7227             :                                          double dfFalseEasting,
    7228             :                                          double dfFalseNorthing)
    7229             : 
    7230             : {
    7231           3 :     return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
    7232             :         d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
    7233           3 :         nullptr, 0, nullptr, 0));
    7234             : }
    7235             : 
    7236             : /************************************************************************/
    7237             : /*                          OSRSetMollweide()                           */
    7238             : /************************************************************************/
    7239             : 
    7240           0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
    7241             :                        double dfFalseEasting, double dfFalseNorthing)
    7242             : 
    7243             : {
    7244           0 :     VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
    7245             : 
    7246           0 :     return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
    7247           0 :                                          dfFalseNorthing);
    7248             : }
    7249             : 
    7250             : /************************************************************************/
    7251             : /*                              SetNZMG()                               */
    7252             : /************************************************************************/
    7253             : 
    7254           6 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
    7255             :                                     double dfFalseEasting,
    7256             :                                     double dfFalseNorthing)
    7257             : 
    7258             : {
    7259           6 :     return d->replaceConversionAndUnref(
    7260             :         proj_create_conversion_new_zealand_mapping_grid(
    7261             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
    7262           6 :             dfFalseNorthing, nullptr, 0, nullptr, 0));
    7263             : }
    7264             : 
    7265             : /************************************************************************/
    7266             : /*                             OSRSetNZMG()                             */
    7267             : /************************************************************************/
    7268             : 
    7269           0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
    7270             :                   double dfCenterLong, double dfFalseEasting,
    7271             :                   double dfFalseNorthing)
    7272             : 
    7273             : {
    7274           0 :     VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
    7275             : 
    7276           0 :     return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
    7277           0 :                                     dfFalseNorthing);
    7278             : }
    7279             : 
    7280             : /************************************************************************/
    7281             : /*                               SetOS()                                */
    7282             : /************************************************************************/
    7283             : 
    7284           6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
    7285             :                                   double dfScale, double dfFalseEasting,
    7286             :                                   double dfFalseNorthing)
    7287             : 
    7288             : {
    7289           6 :     return d->replaceConversionAndUnref(
    7290             :         proj_create_conversion_oblique_stereographic(
    7291             :             d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
    7292           6 :             dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
    7293             : }
    7294             : 
    7295             : /************************************************************************/
    7296             : /*                              OSRSetOS()                              */
    7297             : /************************************************************************/
    7298             : 
    7299           0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
    7300             :                 double dfCMeridian, double dfScale, double dfFalseEasting,
    7301             :                 double dfFalseNorthing)
    7302             : 
    7303             : {
    7304           0 :     VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
    7305             : 
    7306           0 :     return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
    7307           0 :                                   dfFalseEasting, dfFalseNorthing);
    7308             : }
    7309             : 
    7310             : /************************************************************************/
    7311             : /*                          SetOrthographic()                           */
    7312             : /************************************************************************/
    7313             : 
    7314           7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
    7315             :                                             double dfCenterLong,
    7316             :                                             double dfFalseEasting,
    7317             :                                             double dfFalseNorthing)
    7318             : 
    7319             : {
    7320           7 :     return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
    7321             :         d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
    7322           7 :         dfFalseNorthing, nullptr, 0, nullptr, 0));
    7323             : }
    7324             : 
    7325             : /************************************************************************/
    7326             : /*                         OSRSetOrthographic()                         */
    7327             : /************************************************************************/
    7328             : 
    7329           1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
    7330             :                           double dfCenterLong, double dfFalseEasting,
    7331             :                           double dfFalseNorthing)
    7332             : 
    7333             : {
    7334           1 :     VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
    7335             : 
    7336           1 :     return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
    7337           1 :                                             dfFalseEasting, dfFalseNorthing);
    7338             : }
    7339             : 
    7340             : /************************************************************************/
    7341             : /*                            SetPolyconic()                            */
    7342             : /************************************************************************/
    7343             : 
    7344           7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
    7345             :                                          double dfCenterLong,
    7346             :                                          double dfFalseEasting,
    7347             :                                          double dfFalseNorthing)
    7348             : 
    7349             : {
    7350             :     // note: it seems that by some definitions this should include a
    7351             :     //       scale_factor parameter.
    7352           7 :     return d->replaceConversionAndUnref(
    7353             :         proj_create_conversion_american_polyconic(
    7354             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
    7355           7 :             dfFalseNorthing, nullptr, 0, nullptr, 0));
    7356             : }
    7357             : 
    7358             : /************************************************************************/
    7359             : /*                          OSRSetPolyconic()                           */
    7360             : /************************************************************************/
    7361             : 
    7362           0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
    7363             :                        double dfCenterLong, double dfFalseEasting,
    7364             :                        double dfFalseNorthing)
    7365             : 
    7366             : {
    7367           0 :     VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
    7368             : 
    7369           0 :     return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
    7370           0 :                                          dfFalseEasting, dfFalseNorthing);
    7371             : }
    7372             : 
    7373             : /************************************************************************/
    7374             : /*                               SetPS()                                */
    7375             : /************************************************************************/
    7376             : 
    7377             : /** Sets a Polar Stereographic projection.
    7378             :  *
    7379             :  * Two variants are possible:
    7380             :  * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
    7381             :  *   interpreted as the latitude of origin, combined with the scale factor
    7382             :  * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
    7383             :  *   is interpreted as the latitude of true scale. In that situation, dfScale
    7384             :  *   must be set to 1 (it is ignored in the projection parameters)
    7385             :  */
    7386          30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
    7387             :                                   double dfScale, double dfFalseEasting,
    7388             :                                   double dfFalseNorthing)
    7389             : 
    7390             : {
    7391             :     PJ *conv;
    7392          30 :     if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
    7393             :     {
    7394          20 :         conv = proj_create_conversion_polar_stereographic_variant_b(
    7395             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
    7396             :             dfFalseNorthing, nullptr, 0, nullptr, 0);
    7397             :     }
    7398             :     else
    7399             :     {
    7400          10 :         conv = proj_create_conversion_polar_stereographic_variant_a(
    7401             :             d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
    7402             :             dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
    7403             :     }
    7404             : 
    7405          30 :     const char *pszName = nullptr;
    7406          30 :     double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
    7407          30 :     CPLString osName = pszName ? pszName : "";
    7408             : 
    7409          30 :     d->refreshProjObj();
    7410             : 
    7411          30 :     d->demoteFromBoundCRS();
    7412             : 
    7413          30 :     auto cs = proj_create_cartesian_2D_cs(
    7414             :         d->getPROJContext(),
    7415             :         dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
    7416             :                         : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
    7417          30 :         !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
    7418             :     auto projCRS =
    7419          30 :         proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
    7420          30 :                                   d->getGeodBaseCRS(), conv, cs);
    7421          30 :     proj_destroy(conv);
    7422          30 :     proj_destroy(cs);
    7423             : 
    7424          30 :     d->setPjCRS(projCRS);
    7425             : 
    7426          30 :     d->undoDemoteFromBoundCRS();
    7427             : 
    7428          60 :     return OGRERR_NONE;
    7429             : }
    7430             : 
    7431             : /************************************************************************/
    7432             : /*                              OSRSetPS()                              */
    7433             : /************************************************************************/
    7434             : 
    7435           1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
    7436             :                 double dfCenterLong, double dfScale, double dfFalseEasting,
    7437             :                 double dfFalseNorthing)
    7438             : 
    7439             : {
    7440           1 :     VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
    7441             : 
    7442           1 :     return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
    7443           1 :                                   dfFalseEasting, dfFalseNorthing);
    7444             : }
    7445             : 
    7446             : /************************************************************************/
    7447             : /*                            SetRobinson()                             */
    7448             : /************************************************************************/
    7449             : 
    7450           4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
    7451             :                                         double dfFalseEasting,
    7452             :                                         double dfFalseNorthing)
    7453             : 
    7454             : {
    7455           4 :     return d->replaceConversionAndUnref(proj_create_conversion_robinson(
    7456             :         d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
    7457           4 :         nullptr, 0, nullptr, 0));
    7458             : }
    7459             : 
    7460             : /************************************************************************/
    7461             : /*                           OSRSetRobinson()                           */
    7462             : /************************************************************************/
    7463             : 
    7464           0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
    7465             :                       double dfFalseEasting, double dfFalseNorthing)
    7466             : 
    7467             : {
    7468           0 :     VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
    7469             : 
    7470           0 :     return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
    7471           0 :                                         dfFalseNorthing);
    7472             : }
    7473             : 
    7474             : /************************************************************************/
    7475             : /*                           SetSinusoidal()                            */
    7476             : /************************************************************************/
    7477             : 
    7478          33 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
    7479             :                                           double dfFalseEasting,
    7480             :                                           double dfFalseNorthing)
    7481             : 
    7482             : {
    7483          33 :     return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
    7484             :         d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
    7485          33 :         nullptr, 0, nullptr, 0));
    7486             : }
    7487             : 
    7488             : /************************************************************************/
    7489             : /*                          OSRSetSinusoidal()                          */
    7490             : /************************************************************************/
    7491             : 
    7492           1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
    7493             :                         double dfFalseEasting, double dfFalseNorthing)
    7494             : 
    7495             : {
    7496           1 :     VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
    7497             : 
    7498           1 :     return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
    7499           1 :                                           dfFalseNorthing);
    7500             : }
    7501             : 
    7502             : /************************************************************************/
    7503             : /*                          SetStereographic()                          */
    7504             : /************************************************************************/
    7505             : 
    7506           2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
    7507             :                                              double dfCMeridian, double dfScale,
    7508             :                                              double dfFalseEasting,
    7509             :                                              double dfFalseNorthing)
    7510             : 
    7511             : {
    7512           2 :     return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
    7513             :         d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
    7514           2 :         dfFalseNorthing, nullptr, 0, nullptr, 0));
    7515             : }
    7516             : 
    7517             : /************************************************************************/
    7518             : /*                        OSRSetStereographic()                         */
    7519             : /************************************************************************/
    7520             : 
    7521           0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
    7522             :                            double dfCMeridian, double dfScale,
    7523             :                            double dfFalseEasting, double dfFalseNorthing)
    7524             : 
    7525             : {
    7526           0 :     VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
    7527             : 
    7528           0 :     return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
    7529           0 :                                              dfFalseEasting, dfFalseNorthing);
    7530             : }
    7531             : 
    7532             : /************************************************************************/
    7533             : /*                               SetSOC()                               */
    7534             : /*                                                                      */
    7535             : /*      NOTE: This definition isn't really used in practice any more    */
    7536             : /*      and should be considered deprecated.  It seems that swiss       */
    7537             : /*      oblique mercator is now define as Hotine_Oblique_Mercator       */
    7538             : /*      with an azimuth of 90 and a rectified_grid_angle of 90.  See    */
    7539             : /*      EPSG:2056 and Bug 423.                                          */
    7540             : /************************************************************************/
    7541             : 
    7542           2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
    7543             :                                    double dfCentralMeridian,
    7544             :                                    double dfFalseEasting,
    7545             :                                    double dfFalseNorthing)
    7546             : 
    7547             : {
    7548           2 :     return d->replaceConversionAndUnref(
    7549             :         proj_create_conversion_hotine_oblique_mercator_variant_b(
    7550             :             d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
    7551             :             90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
    7552           2 :             0.0));
    7553             : #if 0
    7554             :     SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
    7555             :     SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
    7556             :     SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
    7557             :     SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
    7558             :     SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
    7559             : 
    7560             :     return OGRERR_NONE;
    7561             : #endif
    7562             : }
    7563             : 
    7564             : /************************************************************************/
    7565             : /*                             OSRSetSOC()                              */
    7566             : /************************************************************************/
    7567             : 
    7568           0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
    7569             :                  double dfCentralMeridian, double dfFalseEasting,
    7570             :                  double dfFalseNorthing)
    7571             : 
    7572             : {
    7573           0 :     VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
    7574             : 
    7575           0 :     return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
    7576           0 :                                    dfFalseEasting, dfFalseNorthing);
    7577             : }
    7578             : 
    7579             : /************************************************************************/
    7580             : /*                               SetVDG()                               */
    7581             : /************************************************************************/
    7582             : 
    7583           2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
    7584             :                                    double dfFalseNorthing)
    7585             : 
    7586             : {
    7587           2 :     return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
    7588             :         d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
    7589           2 :         nullptr, 0, nullptr, 0));
    7590             : }
    7591             : 
    7592             : /************************************************************************/
    7593             : /*                             OSRSetVDG()                              */
    7594             : /************************************************************************/
    7595             : 
    7596           0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
    7597             :                  double dfFalseEasting, double dfFalseNorthing)
    7598             : 
    7599             : {
    7600           0 :     VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
    7601             : 
    7602           0 :     return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
    7603           0 :                                    dfFalseNorthing);
    7604             : }
    7605             : 
    7606             : /************************************************************************/
    7607             : /*                               SetUTM()                               */
    7608             : /************************************************************************/
    7609             : 
    7610             : /**
    7611             :  * \brief Set UTM projection definition.
    7612             :  *
    7613             :  * This will generate a projection definition with the full set of
    7614             :  * transverse mercator projection parameters for the given UTM zone.
    7615             :  * If no PROJCS[] description is set yet, one will be set to look
    7616             :  * like "UTM Zone %d, {Northern, Southern} Hemisphere".
    7617             :  *
    7618             :  * This method is the same as the C function OSRSetUTM().
    7619             :  *
    7620             :  * @param nZone UTM zone.
    7621             :  *
    7622             :  * @param bNorth TRUE for northern hemisphere, or FALSE for southern
    7623             :  * hemisphere.
    7624             :  *
    7625             :  * @return OGRERR_NONE on success.
    7626             :  */
    7627             : 
    7628         331 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
    7629             : 
    7630             : {
    7631         331 :     if (nZone < 0 || nZone > 60)
    7632             :     {
    7633           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
    7634           0 :         return OGRERR_FAILURE;
    7635             :     }
    7636             : 
    7637         331 :     return d->replaceConversionAndUnref(
    7638         331 :         proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
    7639             : }
    7640             : 
    7641             : /************************************************************************/
    7642             : /*                             OSRSetUTM()                              */
    7643             : /************************************************************************/
    7644             : 
    7645             : /**
    7646             :  * \brief Set UTM projection definition.
    7647             :  *
    7648             :  * This is the same as the C++ method OGRSpatialReference::SetUTM()
    7649             :  */
    7650          20 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
    7651             : 
    7652             : {
    7653          20 :     VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
    7654             : 
    7655          20 :     return ToPointer(hSRS)->SetUTM(nZone, bNorth);
    7656             : }
    7657             : 
    7658             : /************************************************************************/
    7659             : /*                             GetUTMZone()                             */
    7660             : /*                                                                      */
    7661             : /*      Returns zero if it isn't UTM.                                   */
    7662             : /************************************************************************/
    7663             : 
    7664             : /**
    7665             :  * \brief Get utm zone information.
    7666             :  *
    7667             :  * This is the same as the C function OSRGetUTMZone().
    7668             :  *
    7669             :  * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
    7670             :  * zone which is negative in the southern hemisphere instead of having the
    7671             :  * pbNorth flag used in the C and C++ interface.
    7672             :  *
    7673             :  * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
    7674             :  * FALSE if southern.
    7675             :  *
    7676             :  * @return UTM zone number or zero if this isn't a UTM definition.
    7677             :  */
    7678             : 
    7679         612 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
    7680             : 
    7681             : {
    7682         612 :     if (IsProjected() && GetAxesCount() == 3)
    7683             :     {
    7684           1 :         OGRSpatialReference *poSRSTmp = Clone();
    7685           1 :         poSRSTmp->DemoteTo2D(nullptr);
    7686           1 :         const int nZone = poSRSTmp->GetUTMZone(pbNorth);
    7687           1 :         delete poSRSTmp;
    7688           1 :         return nZone;
    7689             :     }
    7690             : 
    7691         611 :     const char *pszProjection = GetAttrValue("PROJECTION");
    7692             : 
    7693         611 :     if (pszProjection == nullptr ||
    7694         507 :         !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
    7695         296 :         return 0;
    7696             : 
    7697         315 :     if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
    7698           5 :         return 0;
    7699             : 
    7700         310 :     if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
    7701           9 :         return 0;
    7702             : 
    7703         301 :     if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
    7704           3 :         return 0;
    7705             : 
    7706         298 :     const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
    7707             : 
    7708         298 :     if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
    7709           0 :         return 0;
    7710             : 
    7711         298 :     if (pbNorth != nullptr)
    7712         237 :         *pbNorth = (dfFalseNorthing == 0);
    7713             : 
    7714             :     const double dfCentralMeridian =
    7715         298 :         GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
    7716         298 :     const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
    7717             : 
    7718         298 :     if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
    7719         894 :         CPLIsNan(dfZone) ||
    7720         298 :         std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
    7721           0 :         return 0;
    7722             : 
    7723         298 :     return static_cast<int>(dfZone);
    7724             : }
    7725             : 
    7726             : /************************************************************************/
    7727             : /*                           OSRGetUTMZone()                            */
    7728             : /************************************************************************/
    7729             : 
    7730             : /**
    7731             :  * \brief Get utm zone information.
    7732             :  *
    7733             :  * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
    7734             :  */
    7735           6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
    7736             : 
    7737             : {
    7738           6 :     VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
    7739             : 
    7740           6 :     return ToPointer(hSRS)->GetUTMZone(pbNorth);
    7741             : }
    7742             : 
    7743             : /************************************************************************/
    7744             : /*                             SetWagner()                              */
    7745             : /************************************************************************/
    7746             : 
    7747           0 : OGRErr OGRSpatialReference::SetWagner(int nVariation,  // 1--7.
    7748             :                                       double dfCenterLat, double dfFalseEasting,
    7749             :                                       double dfFalseNorthing)
    7750             : 
    7751             : {
    7752             :     PJ *conv;
    7753           0 :     if (nVariation == 1)
    7754             :     {
    7755           0 :         conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
    7756             :                                                dfFalseEasting, dfFalseNorthing,
    7757             :                                                nullptr, 0.0, nullptr, 0.0);
    7758             :     }
    7759           0 :     else if (nVariation == 2)
    7760             :     {
    7761           0 :         conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
    7762             :                                                 dfFalseEasting, dfFalseNorthing,
    7763             :                                                 nullptr, 0.0, nullptr, 0.0);
    7764             :     }
    7765           0 :     else if (nVariation == 3)
    7766             :     {
    7767           0 :         conv = proj_create_conversion_wagner_iii(
    7768             :             d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
    7769             :             dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
    7770             :     }
    7771           0 :     else if (nVariation == 4)
    7772             :     {
    7773           0 :         conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
    7774             :                                                 dfFalseEasting, dfFalseNorthing,
    7775             :                                                 nullptr, 0.0, nullptr, 0.0);
    7776             :     }
    7777           0 :     else if (nVariation == 5)
    7778             :     {
    7779           0 :         conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
    7780             :                                                dfFalseEasting, dfFalseNorthing,
    7781             :                                                nullptr, 0.0, nullptr, 0.0);
    7782             :     }
    7783           0 :     else if (nVariation == 6)
    7784             :     {
    7785           0 :         conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
    7786             :                                                 dfFalseEasting, dfFalseNorthing,
    7787             :                                                 nullptr, 0.0, nullptr, 0.0);
    7788             :     }
    7789           0 :     else if (nVariation == 7)
    7790             :     {
    7791           0 :         conv = proj_create_conversion_wagner_vii(
    7792             :             d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
    7793             :             0.0, nullptr, 0.0);
    7794             :     }
    7795             :     else
    7796             :     {
    7797           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7798             :                  "Unsupported Wagner variation (%d).", nVariation);
    7799           0 :         return OGRERR_UNSUPPORTED_SRS;
    7800             :     }
    7801             : 
    7802           0 :     return d->replaceConversionAndUnref(conv);
    7803             : }
    7804             : 
    7805             : /************************************************************************/
    7806             : /*                            OSRSetWagner()                            */
    7807             : /************************************************************************/
    7808             : 
    7809           0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
    7810             :                     double dfCenterLat, double dfFalseEasting,
    7811             :                     double dfFalseNorthing)
    7812             : 
    7813             : {
    7814           0 :     VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
    7815             : 
    7816           0 :     return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
    7817           0 :                                       dfFalseNorthing);
    7818             : }
    7819             : 
    7820             : /************************************************************************/
    7821             : /*                            SetQSC()                     */
    7822             : /************************************************************************/
    7823             : 
    7824           0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
    7825             : {
    7826           0 :     return d->replaceConversionAndUnref(
    7827             :         proj_create_conversion_quadrilateralized_spherical_cube(
    7828             :             d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
    7829           0 :             0, nullptr, 0));
    7830             : }
    7831             : 
    7832             : /************************************************************************/
    7833             : /*                           OSRSetQSC()                   */
    7834             : /************************************************************************/
    7835             : 
    7836           0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
    7837             :                  double dfCenterLong)
    7838             : 
    7839             : {
    7840           0 :     VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
    7841             : 
    7842           0 :     return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
    7843             : }
    7844             : 
    7845             : /************************************************************************/
    7846             : /*                            SetSCH()                     */
    7847             : /************************************************************************/
    7848             : 
    7849           0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
    7850             :                                    double dfPegHeading, double dfPegHgt)
    7851             : 
    7852             : {
    7853           0 :     return d->replaceConversionAndUnref(
    7854             :         proj_create_conversion_spherical_cross_track_height(
    7855             :             d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
    7856           0 :             nullptr, 0, nullptr, 0));
    7857             : }
    7858             : 
    7859             : /************************************************************************/
    7860             : /*                           OSRSetSCH()                   */
    7861             : /************************************************************************/
    7862             : 
    7863           0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
    7864             :                  double dfPegHeading, double dfPegHgt)
    7865             : 
    7866             : {
    7867           0 :     VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
    7868             : 
    7869           0 :     return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
    7870             : }
    7871             : 
    7872             : /************************************************************************/
    7873             : /*                         SetVerticalPerspective()                     */
    7874             : /************************************************************************/
    7875             : 
    7876           3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
    7877             :     double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
    7878             :     double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
    7879             : {
    7880           3 :     return d->replaceConversionAndUnref(
    7881             :         proj_create_conversion_vertical_perspective(
    7882             :             d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
    7883             :             dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
    7884           3 :             dfFalseNorthing, nullptr, 0, nullptr, 0));
    7885             : }
    7886             : 
    7887             : /************************************************************************/
    7888             : /*                       OSRSetVerticalPerspective()                    */
    7889             : /************************************************************************/
    7890             : 
    7891           1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
    7892             :                                  double dfTopoOriginLat, double dfTopoOriginLon,
    7893             :                                  double dfTopoOriginHeight,
    7894             :                                  double dfViewPointHeight,
    7895             :                                  double dfFalseEasting, double dfFalseNorthing)
    7896             : 
    7897             : {
    7898           1 :     VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
    7899             : 
    7900           1 :     return ToPointer(hSRS)->SetVerticalPerspective(
    7901             :         dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
    7902           1 :         dfFalseEasting, dfFalseNorthing);
    7903             : }
    7904             : 
    7905             : /************************************************************************/
    7906             : /*             SetDerivedGeogCRSWithPoleRotationGRIBConvention()        */
    7907             : /************************************************************************/
    7908             : 
    7909           2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
    7910             :     const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
    7911             :     double dfAxisRotation)
    7912             : {
    7913           2 :     d->refreshProjObj();
    7914           2 :     if (!d->m_pj_crs)
    7915           0 :         return OGRERR_FAILURE;
    7916           2 :     if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
    7917           0 :         return OGRERR_FAILURE;
    7918           2 :     auto ctxt = d->getPROJContext();
    7919           2 :     auto conv = proj_create_conversion_pole_rotation_grib_convention(
    7920             :         ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
    7921           2 :     auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
    7922           4 :     d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
    7923           2 :                                                    d->m_pj_crs, conv, cs));
    7924           2 :     proj_destroy(conv);
    7925           2 :     proj_destroy(cs);
    7926           2 :     return OGRERR_NONE;
    7927             : }
    7928             : 
    7929             : /************************************************************************/
    7930             : /*         SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention()        */
    7931             : /************************************************************************/
    7932             : 
    7933           3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
    7934             :     const char *pszCRSName, double dfGridNorthPoleLat,
    7935             :     double dfGridNorthPoleLon, double dfNorthPoleGridLon)
    7936             : {
    7937             : #if PROJ_VERSION_MAJOR > 8 ||                                                  \
    7938             :     (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
    7939             :     d->refreshProjObj();
    7940             :     if (!d->m_pj_crs)
    7941             :         return OGRERR_FAILURE;
    7942             :     if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
    7943             :         return OGRERR_FAILURE;
    7944             :     auto ctxt = d->getPROJContext();
    7945             :     auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
    7946             :         ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
    7947             :         nullptr, 0);
    7948             :     auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
    7949             :     d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
    7950             :                                                    d->m_pj_crs, conv, cs));
    7951             :     proj_destroy(conv);
    7952             :     proj_destroy(cs);
    7953             :     return OGRERR_NONE;
    7954             : #else
    7955             :     (void)pszCRSName;
    7956           3 :     SetProjection("Rotated_pole");
    7957           3 :     SetExtension(
    7958             :         "PROJCS", "PROJ4",
    7959             :         CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.18g +o_lon_p=%.18g "
    7960             :                    "+o_lat_p=%.18g +a=%.18g +b=%.18g "
    7961             :                    "+to_meter=0.0174532925199433 "
    7962             :                    "+wktext",
    7963             :                    180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
    7964             :                    dfGridNorthPoleLat, GetSemiMajor(nullptr),
    7965             :                    GetSemiMinor(nullptr)));
    7966           3 :     return OGRERR_NONE;
    7967             : #endif
    7968             : }
    7969             : 
    7970             : /************************************************************************/
    7971             : /*                            SetAuthority()                            */
    7972             : /************************************************************************/
    7973             : 
    7974             : /**
    7975             :  * \brief Set the authority for a node.
    7976             :  *
    7977             :  * This method is the same as the C function OSRSetAuthority().
    7978             :  *
    7979             :  * @param pszTargetKey the partial or complete path to the node to
    7980             :  * set an authority on.  i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
    7981             :  *
    7982             :  * @param pszAuthority authority name, such as "EPSG".
    7983             :  *
    7984             :  * @param nCode code for value with this authority.
    7985             :  *
    7986             :  * @return OGRERR_NONE on success.
    7987             :  */
    7988             : 
    7989        9393 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
    7990             :                                          const char *pszAuthority, int nCode)
    7991             : 
    7992             : {
    7993        9393 :     d->refreshProjObj();
    7994        9393 :     pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
    7995             : 
    7996        9393 :     if (pszTargetKey == nullptr)
    7997             :     {
    7998         224 :         if (!d->m_pj_crs)
    7999           0 :             return OGRERR_FAILURE;
    8000         224 :         CPLString osCode;
    8001         224 :         osCode.Printf("%d", nCode);
    8002         224 :         d->demoteFromBoundCRS();
    8003         224 :         d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
    8004             :                                   pszAuthority, osCode.c_str()));
    8005         224 :         d->undoDemoteFromBoundCRS();
    8006         224 :         return OGRERR_NONE;
    8007             :     }
    8008             : 
    8009        9169 :     d->demoteFromBoundCRS();
    8010        9169 :     if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
    8011             :     {
    8012        3036 :         CPLString osCode;
    8013        3036 :         osCode.Printf("%d", nCode);
    8014             :         auto newGeogCRS =
    8015        3036 :             proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
    8016             :                           pszAuthority, osCode.c_str());
    8017             : 
    8018             :         auto conv =
    8019        3036 :             proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
    8020             : 
    8021        3036 :         auto projCRS = proj_create_projected_crs(
    8022             :             d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
    8023        3036 :             d->getProjCRSCoordSys());
    8024             : 
    8025             :         // Preserve existing id on the PROJCRS
    8026        3036 :         const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
    8027        3036 :         const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
    8028        3036 :         if (pszProjCRSAuthName && pszProjCRSCode)
    8029             :         {
    8030             :             auto projCRSWithId =
    8031           0 :                 proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
    8032             :                               pszProjCRSCode);
    8033           0 :             proj_destroy(projCRS);
    8034           0 :             projCRS = projCRSWithId;
    8035             :         }
    8036             : 
    8037        3036 :         proj_destroy(newGeogCRS);
    8038        3036 :         proj_destroy(conv);
    8039             : 
    8040        3036 :         d->setPjCRS(projCRS);
    8041        3036 :         d->undoDemoteFromBoundCRS();
    8042        3036 :         return OGRERR_NONE;
    8043             :     }
    8044        6133 :     d->undoDemoteFromBoundCRS();
    8045             : 
    8046             :     /* -------------------------------------------------------------------- */
    8047             :     /*      Find the node below which the authority should be put.          */
    8048             :     /* -------------------------------------------------------------------- */
    8049        6133 :     OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
    8050             : 
    8051        6133 :     if (poNode == nullptr)
    8052           0 :         return OGRERR_FAILURE;
    8053             : 
    8054             :     /* -------------------------------------------------------------------- */
    8055             :     /*      If there is an existing AUTHORITY child blow it away before     */
    8056             :     /*      trying to set a new one.                                        */
    8057             :     /* -------------------------------------------------------------------- */
    8058        6133 :     int iOldChild = poNode->FindChild("AUTHORITY");
    8059        6133 :     if (iOldChild != -1)
    8060           5 :         poNode->DestroyChild(iOldChild);
    8061             : 
    8062             :     /* -------------------------------------------------------------------- */
    8063             :     /*      Create a new authority node.                                    */
    8064             :     /* -------------------------------------------------------------------- */
    8065        6133 :     char szCode[32] = {};
    8066             : 
    8067        6133 :     snprintf(szCode, sizeof(szCode), "%d", nCode);
    8068             : 
    8069        6133 :     OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
    8070        6133 :     poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
    8071        6133 :     poAuthNode->AddChild(new OGR_SRSNode(szCode));
    8072             : 
    8073        6133 :     poNode->AddChild(poAuthNode);
    8074             : 
    8075        6133 :     return OGRERR_NONE;
    8076             : }
    8077             : 
    8078             : /************************************************************************/
    8079             : /*                          OSRSetAuthority()                           */
    8080             : /************************************************************************/
    8081             : 
    8082             : /**
    8083             :  * \brief Set the authority for a node.
    8084             :  *
    8085             :  * This function is the same as OGRSpatialReference::SetAuthority().
    8086             :  */
    8087           0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
    8088             :                        const char *pszAuthority, int nCode)
    8089             : 
    8090             : {
    8091           0 :     VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
    8092             : 
    8093           0 :     return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
    8094             : }
    8095             : 
    8096             : /************************************************************************/
    8097             : /*                          GetAuthorityCode()                          */
    8098             : /************************************************************************/
    8099             : 
    8100             : /**
    8101             :  * \brief Get the authority code for a node.
    8102             :  *
    8103             :  * This method is used to query an AUTHORITY[] node from within the
    8104             :  * WKT tree, and fetch the code value.
    8105             :  *
    8106             :  * While in theory values may be non-numeric, for the EPSG authority all
    8107             :  * code values should be integral.
    8108             :  *
    8109             :  * This method is the same as the C function OSRGetAuthorityCode().
    8110             :  *
    8111             :  * @param pszTargetKey the partial or complete path to the node to
    8112             :  * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
    8113             :  * search for an authority node on the root element.
    8114             :  *
    8115             :  * @return value code from authority node, or NULL on failure.  The value
    8116             :  * returned is internal and should not be freed or modified.
    8117             :  */
    8118             : 
    8119             : const char *
    8120       20674 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
    8121             : 
    8122             : {
    8123       20674 :     d->refreshProjObj();
    8124       20674 :     const char *pszInputTargetKey = pszTargetKey;
    8125       20674 :     pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
    8126       20674 :     if (pszTargetKey == nullptr)
    8127             :     {
    8128       15463 :         if (!d->m_pj_crs)
    8129             :         {
    8130          13 :             return nullptr;
    8131             :         }
    8132       15450 :         d->demoteFromBoundCRS();
    8133       15450 :         auto ret = proj_get_id_code(d->m_pj_crs, 0);
    8134       15450 :         if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
    8135             :         {
    8136        1103 :             auto ctxt = d->getPROJContext();
    8137        1103 :             auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
    8138        1103 :             if (cs)
    8139             :             {
    8140        1103 :                 const int axisCount = proj_cs_get_axis_count(ctxt, cs);
    8141        1103 :                 proj_destroy(cs);
    8142        1103 :                 if (axisCount == 3)
    8143             :                 {
    8144             :                     // This might come from a COMPD_CS with a VERT_DATUM type =
    8145             :                     // 2002 in which case, using the WKT1 representation will
    8146             :                     // enable us to recover the EPSG code.
    8147          14 :                     pszTargetKey = pszInputTargetKey;
    8148             :                 }
    8149             :             }
    8150             :         }
    8151       15450 :         d->undoDemoteFromBoundCRS();
    8152       15450 :         if (ret != nullptr || pszTargetKey == nullptr)
    8153             :         {
    8154       15450 :             return ret;
    8155             :         }
    8156             :     }
    8157             : 
    8158             :     // Special key for that context
    8159        5215 :     else if (EQUAL(pszTargetKey, "HORIZCRS") &&
    8160           4 :              d->m_pjType == PJ_TYPE_COMPOUND_CRS)
    8161             :     {
    8162           4 :         auto ctxt = d->getPROJContext();
    8163           4 :         auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
    8164           4 :         if (crs)
    8165             :         {
    8166           4 :             const char *ret = proj_get_id_code(crs, 0);
    8167           4 :             if (ret)
    8168           4 :                 ret = CPLSPrintf("%s", ret);
    8169           4 :             proj_destroy(crs);
    8170           4 :             return ret;
    8171             :         }
    8172             :     }
    8173        5211 :     else if (EQUAL(pszTargetKey, "VERTCRS") &&
    8174           4 :              d->m_pjType == PJ_TYPE_COMPOUND_CRS)
    8175             :     {
    8176           4 :         auto ctxt = d->getPROJContext();
    8177           4 :         auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
    8178           4 :         if (crs)
    8179             :         {
    8180           4 :             const char *ret = proj_get_id_code(crs, 0);
    8181           4 :             if (ret)
    8182           4 :                 ret = CPLSPrintf("%s", ret);
    8183           4 :             proj_destroy(crs);
    8184           4 :             return ret;
    8185             :         }
    8186             :     }
    8187             : 
    8188             :     /* -------------------------------------------------------------------- */
    8189             :     /*      Find the node below which the authority should be put.          */
    8190             :     /* -------------------------------------------------------------------- */
    8191        5203 :     const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
    8192             : 
    8193        5203 :     if (poNode == nullptr)
    8194          84 :         return nullptr;
    8195             : 
    8196             :     /* -------------------------------------------------------------------- */
    8197             :     /*      Fetch AUTHORITY child if there is one.                          */
    8198             :     /* -------------------------------------------------------------------- */
    8199        5119 :     if (poNode->FindChild("AUTHORITY") == -1)
    8200         182 :         return nullptr;
    8201             : 
    8202        4937 :     poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
    8203             : 
    8204             :     /* -------------------------------------------------------------------- */
    8205             :     /*      Create a new authority node.                                    */
    8206             :     /* -------------------------------------------------------------------- */
    8207        4937 :     if (poNode->GetChildCount() < 2)
    8208           0 :         return nullptr;
    8209             : 
    8210        4937 :     return poNode->GetChild(1)->GetValue();
    8211             : }
    8212             : 
    8213             : /************************************************************************/
    8214             : /*                          OSRGetAuthorityCode()                       */
    8215             : /************************************************************************/
    8216             : 
    8217             : /**
    8218             :  * \brief Get the authority code for a node.
    8219             :  *
    8220             :  * This function is the same as OGRSpatialReference::GetAuthorityCode().
    8221             :  */
    8222         643 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
    8223             :                                 const char *pszTargetKey)
    8224             : 
    8225             : {
    8226         643 :     VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
    8227             : 
    8228         643 :     return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
    8229             : }
    8230             : 
    8231             : /************************************************************************/
    8232             : /*                          GetAuthorityName()                          */
    8233             : /************************************************************************/
    8234             : 
    8235             : /**
    8236             :  * \brief Get the authority name for a node.
    8237             :  *
    8238             :  * This method is used to query an AUTHORITY[] node from within the
    8239             :  * WKT tree, and fetch the authority name value.
    8240             :  *
    8241             :  * The most common authority is "EPSG".
    8242             :  *
    8243             :  * This method is the same as the C function OSRGetAuthorityName().
    8244             :  *
    8245             :  * @param pszTargetKey the partial or complete path to the node to
    8246             :  * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
    8247             :  * search for an authority node on the root element.
    8248             :  *
    8249             :  * @return value code from authority node, or NULL on failure. The value
    8250             :  * returned is internal and should not be freed or modified.
    8251             :  */
    8252             : 
    8253             : const char *
    8254       33958 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
    8255             : 
    8256             : {
    8257       33958 :     d->refreshProjObj();
    8258       33958 :     const char *pszInputTargetKey = pszTargetKey;
    8259       33958 :     pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
    8260       33958 :     if (pszTargetKey == nullptr)
    8261             :     {
    8262       16097 :         if (!d->m_pj_crs)
    8263             :         {
    8264          14 :             return nullptr;
    8265             :         }
    8266       16083 :         d->demoteFromBoundCRS();
    8267       16083 :         auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
    8268       16083 :         if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
    8269             :         {
    8270         793 :             auto ctxt = d->getPROJContext();
    8271         793 :             auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
    8272         793 :             if (cs)
    8273             :             {
    8274         793 :                 const int axisCount = proj_cs_get_axis_count(ctxt, cs);
    8275         793 :                 proj_destroy(cs);
    8276         793 :                 if (axisCount == 3)
    8277             :                 {
    8278             :                     // This might come from a COMPD_CS with a VERT_DATUM type =
    8279             :                     // 2002 in which case, using the WKT1 representation will
    8280             :                     // enable us to recover the EPSG code.
    8281          14 :                     pszTargetKey = pszInputTargetKey;
    8282             :                 }
    8283             :             }
    8284             :         }
    8285       16083 :         d->undoDemoteFromBoundCRS();
    8286       16083 :         if (ret != nullptr || pszTargetKey == nullptr)
    8287             :         {
    8288       16083 :             return ret;
    8289             :         }
    8290             :     }
    8291             : 
    8292             :     // Special key for that context
    8293       17865 :     else if (EQUAL(pszTargetKey, "HORIZCRS") &&
    8294           4 :              d->m_pjType == PJ_TYPE_COMPOUND_CRS)
    8295             :     {
    8296           4 :         auto ctxt = d->getPROJContext();
    8297           4 :         auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
    8298           4 :         if (crs)
    8299             :         {
    8300           4 :             const char *ret = proj_get_id_auth_name(crs, 0);
    8301           4 :             if (ret)
    8302           4 :                 ret = CPLSPrintf("%s", ret);
    8303           4 :             proj_destroy(crs);
    8304           4 :             return ret;
    8305             :         }
    8306             :     }
    8307       17861 :     else if (EQUAL(pszTargetKey, "VERTCRS") &&
    8308           4 :              d->m_pjType == PJ_TYPE_COMPOUND_CRS)
    8309             :     {
    8310           4 :         auto ctxt = d->getPROJContext();
    8311           4 :         auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
    8312           4 :         if (crs)
    8313             :         {
    8314           4 :             const char *ret = proj_get_id_auth_name(crs, 0);
    8315           4 :             if (ret)
    8316           4 :                 ret = CPLSPrintf("%s", ret);
    8317           4 :             proj_destroy(crs);
    8318           4 :             return ret;
    8319             :         }
    8320             :     }
    8321             : 
    8322             :     /* -------------------------------------------------------------------- */
    8323             :     /*      Find the node below which the authority should be put.          */
    8324             :     /* -------------------------------------------------------------------- */
    8325       17853 :     const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
    8326             : 
    8327       17853 :     if (poNode == nullptr)
    8328        7038 :         return nullptr;
    8329             : 
    8330             :     /* -------------------------------------------------------------------- */
    8331             :     /*      Fetch AUTHORITY child if there is one.                          */
    8332             :     /* -------------------------------------------------------------------- */
    8333       10815 :     if (poNode->FindChild("AUTHORITY") == -1)
    8334        1477 :         return nullptr;
    8335             : 
    8336        9338 :     poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
    8337             : 
    8338             :     /* -------------------------------------------------------------------- */
    8339             :     /*      Create a new authority node.                                    */
    8340             :     /* -------------------------------------------------------------------- */
    8341        9338 :     if (poNode->GetChildCount() < 2)
    8342           0 :         return nullptr;
    8343             : 
    8344        9338 :     return poNode->GetChild(0)->GetValue();
    8345             : }
    8346             : 
    8347             : /************************************************************************/
    8348             : /*                        OSRGetAuthorityName()                         */
    8349             : /************************************************************************/
    8350             : 
    8351             : /**
    8352             :  * \brief Get the authority name for a node.
    8353             :  *
    8354             :  * This function is the same as OGRSpatialReference::GetAuthorityName().
    8355             :  */
    8356         221 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
    8357             :                                 const char *pszTargetKey)
    8358             : 
    8359             : {
    8360         221 :     VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
    8361             : 
    8362         221 :     return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
    8363             : }
    8364             : 
    8365             : /************************************************************************/
    8366             : /*                          GetOGCURN()                                 */
    8367             : /************************************************************************/
    8368             : 
    8369             : /**
    8370             :  * \brief Get a OGC URN string describing the CRS, when possible
    8371             :  *
    8372             :  * This method assumes that the CRS has a top-level identifier, or is
    8373             :  * a compound CRS whose horizontal and vertical parts have a top-level
    8374             :  * identifier.
    8375             :  *
    8376             :  * @return a string to free with CPLFree(), or nulptr when no result can be
    8377             :  * generated
    8378             :  *
    8379             :  * @since GDAL 3.5
    8380             :  */
    8381             : 
    8382          53 : char *OGRSpatialReference::GetOGCURN() const
    8383             : 
    8384             : {
    8385          53 :     const char *pszAuthName = GetAuthorityName(nullptr);
    8386          53 :     const char *pszAuthCode = GetAuthorityCode(nullptr);
    8387          53 :     if (pszAuthName && pszAuthCode)
    8388          50 :         return CPLStrdup(
    8389          50 :             CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
    8390           3 :     if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
    8391           2 :         return nullptr;
    8392           1 :     auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
    8393           1 :     auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
    8394           1 :     char *pszRet = nullptr;
    8395           1 :     if (horizCRS && vertCRS)
    8396             :     {
    8397           1 :         auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
    8398           1 :         auto horizAuthCode = proj_get_id_code(horizCRS, 0);
    8399           1 :         auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
    8400           1 :         auto vertAuthCode = proj_get_id_code(vertCRS, 0);
    8401           1 :         if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
    8402             :         {
    8403           1 :             pszRet = CPLStrdup(CPLSPrintf(
    8404             :                 "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
    8405             :                 horizAuthCode, vertAuthName, vertAuthCode));
    8406             :         }
    8407             :     }
    8408           1 :     proj_destroy(horizCRS);
    8409           1 :     proj_destroy(vertCRS);
    8410           1 :     return pszRet;
    8411             : }
    8412             : 
    8413             : /************************************************************************/
    8414             : /*                           StripVertical()                            */
    8415             : /************************************************************************/
    8416             : 
    8417             : /**
    8418             :  * \brief Convert a compound cs into a horizontal CS.
    8419             :  *
    8420             :  * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
    8421             :  * nodes are stripped resulting and only the horizontal coordinate system
    8422             :  * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
    8423             :  *
    8424             :  * If this is not a compound coordinate system then nothing is changed.
    8425             :  *
    8426             :  * This method is the same as the C function OSRStripVertical().
    8427             :  *
    8428             :  * @since OGR 1.8.0
    8429             :  */
    8430             : 
    8431          41 : OGRErr OGRSpatialReference::StripVertical()
    8432             : 
    8433             : {
    8434          41 :     d->refreshProjObj();
    8435          41 :     d->demoteFromBoundCRS();
    8436          41 :     if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
    8437             :     {
    8438           0 :         d->undoDemoteFromBoundCRS();
    8439           0 :         return OGRERR_NONE;
    8440             :     }
    8441          41 :     auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
    8442          41 :     if (!horizCRS)
    8443             :     {
    8444           0 :         d->undoDemoteFromBoundCRS();
    8445           0 :         return OGRERR_FAILURE;
    8446             :     }
    8447             : 
    8448          41 :     bool reuseExistingBoundCRS = false;
    8449          41 :     if (d->m_pj_bound_crs_target)
    8450             :     {
    8451           2 :         auto type = proj_get_type(d->m_pj_bound_crs_target);
    8452           4 :         reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
    8453           4 :                                 type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
    8454             :                                 type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
    8455             :     }
    8456             : 
    8457          41 :     if (reuseExistingBoundCRS)
    8458             :     {
    8459           2 :         auto newBoundCRS = proj_crs_create_bound_crs(
    8460           2 :             d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
    8461           2 :             d->m_pj_bound_crs_co);
    8462           2 :         proj_destroy(horizCRS);
    8463           2 :         d->undoDemoteFromBoundCRS();
    8464           2 :         d->setPjCRS(newBoundCRS);
    8465             :     }
    8466             :     else
    8467             :     {
    8468          39 :         d->undoDemoteFromBoundCRS();
    8469          39 :         d->setPjCRS(horizCRS);
    8470             :     }
    8471             : 
    8472          41 :     return OGRERR_NONE;
    8473             : }
    8474             : 
    8475             : /************************************************************************/
    8476             : /*                            OSRStripVertical()                             */
    8477             : /************************************************************************/
    8478             : /**
    8479             :  * \brief Convert a compound cs into a horizontal CS.
    8480             :  *
    8481             :  * This function is the same as the C++ method
    8482             :  * OGRSpatialReference::StripVertical().
    8483             :  */
    8484           1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
    8485             : 
    8486             : {
    8487           1 :     VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
    8488             : 
    8489           1 :     return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
    8490             : }
    8491             : 
    8492             : /************************************************************************/
    8493             : /*                   StripTOWGS84IfKnownDatumAndAllowed()               */
    8494             : /************************************************************************/
    8495             : 
    8496             : /**
    8497             :  * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
    8498             :  *        and this is allowed by the user.
    8499             :  *
    8500             :  * The default behavior is to remove TOWGS84 information if the CRS has a
    8501             :  * known horizontal datum. This can be disabled by setting the
    8502             :  * OSR_STRIP_TOWGS84 configuration option to NO.
    8503             :  *
    8504             :  * @return true if TOWGS84 has been removed.
    8505             :  * @since OGR 3.1.0
    8506             :  */
    8507             : 
    8508        5744 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
    8509             : {
    8510        5744 :     if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
    8511             :     {
    8512        5741 :         if (StripTOWGS84IfKnownDatum())
    8513             :         {
    8514           6 :             CPLDebug("OSR", "TOWGS84 information has been removed. "
    8515             :                             "It can be kept by setting the OSR_STRIP_TOWGS84 "
    8516             :                             "configuration option to NO");
    8517           6 :             return true;
    8518             :         }
    8519             :     }
    8520        5738 :     return false;
    8521             : }
    8522             : 
    8523             : /************************************************************************/
    8524             : /*                      StripTOWGS84IfKnownDatum()                      */
    8525             : /************************************************************************/
    8526             : 
    8527             : /**
    8528             :  * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
    8529             :  *
    8530             :  * @return true if TOWGS84 has been removed.
    8531             :  * @since OGR 3.1.0
    8532             :  */
    8533             : 
    8534        5747 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
    8535             : 
    8536             : {
    8537        5747 :     d->refreshProjObj();
    8538        5747 :     if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
    8539             :     {
    8540        5726 :         return false;
    8541             :     }
    8542          21 :     auto ctxt = d->getPROJContext();
    8543          21 :     auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
    8544          21 :     if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
    8545             :     {
    8546           1 :         proj_destroy(baseCRS);
    8547           1 :         return false;
    8548             :     }
    8549             : 
    8550             :     // Known base CRS code ? Return base CRS
    8551          20 :     const char *pszCode = proj_get_id_code(baseCRS, 0);
    8552          20 :     if (pszCode)
    8553             :     {
    8554           2 :         d->setPjCRS(baseCRS);
    8555           2 :         return true;
    8556             :     }
    8557             : 
    8558          18 :     auto datum = proj_crs_get_datum(ctxt, baseCRS);
    8559             : #if PROJ_VERSION_MAJOR > 7 ||                                                  \
    8560             :     (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
    8561             :     if (datum == nullptr)
    8562             :     {
    8563             :         datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
    8564             :     }
    8565             : #endif
    8566          18 :     if (!datum)
    8567             :     {
    8568           0 :         proj_destroy(baseCRS);
    8569           0 :         return false;
    8570             :     }
    8571             : 
    8572             :     // Known datum code ? Return base CRS
    8573          18 :     pszCode = proj_get_id_code(datum, 0);
    8574          18 :     if (pszCode)
    8575             :     {
    8576           3 :         proj_destroy(datum);
    8577           3 :         d->setPjCRS(baseCRS);
    8578           3 :         return true;
    8579             :     }
    8580             : 
    8581          15 :     const char *name = proj_get_name(datum);
    8582          15 :     if (EQUAL(name, "unknown"))
    8583             :     {
    8584           1 :         proj_destroy(datum);
    8585           1 :         proj_destroy(baseCRS);
    8586           1 :         return false;
    8587             :     }
    8588          14 :     const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
    8589             :     PJ_OBJ_LIST *list =
    8590          14 :         proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
    8591             : 
    8592          14 :     bool knownDatumName = false;
    8593          14 :     if (list)
    8594             :     {
    8595          14 :         if (proj_list_get_count(list) == 1)
    8596             :         {
    8597           4 :             knownDatumName = true;
    8598             :         }
    8599          14 :         proj_list_destroy(list);
    8600             :     }
    8601             : 
    8602          14 :     proj_destroy(datum);
    8603          14 :     if (knownDatumName)
    8604             :     {
    8605           4 :         d->setPjCRS(baseCRS);
    8606           4 :         return true;
    8607             :     }
    8608          10 :     proj_destroy(baseCRS);
    8609          10 :     return false;
    8610             : }
    8611             : 
    8612             : /************************************************************************/
    8613             : /*                             IsCompound()                             */
    8614             : /************************************************************************/
    8615             : 
    8616             : /**
    8617             :  * \brief Check if coordinate system is compound.
    8618             :  *
    8619             :  * This method is the same as the C function OSRIsCompound().
    8620             :  *
    8621             :  * @return TRUE if this is rooted with a COMPD_CS node.
    8622             :  */
    8623             : 
    8624       29443 : int OGRSpatialReference::IsCompound() const
    8625             : 
    8626             : {
    8627       29443 :     d->refreshProjObj();
    8628       29443 :     d->demoteFromBoundCRS();
    8629       29443 :     bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
    8630       29443 :     d->undoDemoteFromBoundCRS();
    8631       29443 :     return isCompound;
    8632             : }
    8633             : 
    8634             : /************************************************************************/
    8635             : /*                           OSRIsCompound()                            */
    8636             : /************************************************************************/
    8637             : 
    8638             : /**
    8639             :  * \brief Check if the coordinate system is compound.
    8640             :  *
    8641             :  * This function is the same as OGRSpatialReference::IsCompound().
    8642             :  */
    8643           5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
    8644             : 
    8645             : {
    8646           5 :     VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
    8647             : 
    8648           5 :     return ToPointer(hSRS)->IsCompound();
    8649             : }
    8650             : 
    8651             : /************************************************************************/
    8652             : /*                            IsProjected()                             */
    8653             : /************************************************************************/
    8654             : 
    8655             : /**
    8656             :  * \brief Check if projected coordinate system.
    8657             :  *
    8658             :  * This method is the same as the C function OSRIsProjected().
    8659             :  *
    8660             :  * @return TRUE if this contains a PROJCS node indicating a it is a
    8661             :  * projected coordinate system. Also if it is a CompoundCRS made of a
    8662             :  * ProjectedCRS
    8663             :  */
    8664             : 
    8665       40772 : int OGRSpatialReference::IsProjected() const
    8666             : 
    8667             : {
    8668       40772 :     d->refreshProjObj();
    8669       40772 :     d->demoteFromBoundCRS();
    8670       40772 :     bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
    8671       40772 :     if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
    8672             :     {
    8673             :         auto horizCRS =
    8674         141 :             proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
    8675         141 :         if (horizCRS)
    8676             :         {
    8677         141 :             auto horizCRSType = proj_get_type(horizCRS);
    8678         141 :             isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
    8679         141 :             if (horizCRSType == PJ_TYPE_BOUND_CRS)
    8680             :             {
    8681           6 :                 auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
    8682           6 :                 if (base)
    8683             :                 {
    8684           6 :                     isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
    8685           6 :                     proj_destroy(base);
    8686             :                 }
    8687             :             }
    8688         141 :             proj_destroy(horizCRS);
    8689             :         }
    8690             :     }
    8691       40772 :     d->undoDemoteFromBoundCRS();
    8692       40772 :     return isProjected;
    8693             : }
    8694             : 
    8695             : /************************************************************************/
    8696             : /*                           OSRIsProjected()                           */
    8697             : /************************************************************************/
    8698             : /**
    8699             :  * \brief Check if projected coordinate system.
    8700             :  *
    8701             :  * This function is the same as OGRSpatialReference::IsProjected().
    8702             :  */
    8703         190 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
    8704             : 
    8705             : {
    8706         190 :     VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
    8707             : 
    8708         190 :     return ToPointer(hSRS)->IsProjected();
    8709             : }
    8710             : 
    8711             : /************************************************************************/
    8712             : /*                            IsGeocentric()                            */
    8713             : /************************************************************************/
    8714             : 
    8715             : /**
    8716             :  * \brief Check if geocentric coordinate system.
    8717             :  *
    8718             :  * This method is the same as the C function OSRIsGeocentric().
    8719             :  *
    8720             :  * @return TRUE if this contains a GEOCCS node indicating a it is a
    8721             :  * geocentric coordinate system.
    8722             :  *
    8723             :  * @since OGR 1.9.0
    8724             :  */
    8725             : 
    8726       10966 : int OGRSpatialReference::IsGeocentric() const
    8727             : 
    8728             : {
    8729       10966 :     d->refreshProjObj();
    8730       10966 :     d->demoteFromBoundCRS();
    8731       10966 :     bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
    8732       10966 :     d->undoDemoteFromBoundCRS();
    8733       10966 :     return isGeocentric;
    8734             : }
    8735             : 
    8736             : /************************************************************************/
    8737             : /*                           OSRIsGeocentric()                          */
    8738             : /************************************************************************/
    8739             : /**
    8740             :  * \brief Check if geocentric coordinate system.
    8741             :  *
    8742             :  * This function is the same as OGRSpatialReference::IsGeocentric().
    8743             :  *
    8744             :  * @since OGR 1.9.0
    8745             :  */
    8746           2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
    8747             : 
    8748             : {
    8749           2 :     VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
    8750             : 
    8751           2 :     return ToPointer(hSRS)->IsGeocentric();
    8752             : }
    8753             : 
    8754             : /************************************************************************/
    8755             : /*                            IsEmpty()                                 */
    8756             : /************************************************************************/
    8757             : 
    8758             : /**
    8759             :  * \brief Return if the SRS is not set.
    8760             :  */
    8761             : 
    8762       46790 : bool OGRSpatialReference::IsEmpty() const
    8763             : {
    8764       46790 :     d->refreshProjObj();
    8765       46790 :     return d->m_pj_crs == nullptr;
    8766             : }
    8767             : 
    8768             : /************************************************************************/
    8769             : /*                            IsGeographic()                            */
    8770             : /************************************************************************/
    8771             : 
    8772             : /**
    8773             :  * \brief Check if geographic coordinate system.
    8774             :  *
    8775             :  * This method is the same as the C function OSRIsGeographic().
    8776             :  *
    8777             :  * @return TRUE if this spatial reference is geographic ... that is the
    8778             :  * root is a GEOGCS node. Also if it is a CompoundCRS made of a
    8779             :  * GeographicCRS
    8780             :  */
    8781             : 
    8782       50661 : int OGRSpatialReference::IsGeographic() const
    8783             : 
    8784             : {
    8785       50661 :     d->refreshProjObj();
    8786       50661 :     d->demoteFromBoundCRS();
    8787       70638 :     bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
    8788       19977 :                   d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
    8789       50661 :     if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
    8790             :     {
    8791             :         auto horizCRS =
    8792         291 :             proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
    8793         291 :         if (horizCRS)
    8794             :         {
    8795         291 :             auto horizCRSType = proj_get_type(horizCRS);
    8796         291 :             isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
    8797             :                      horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
    8798         291 :             if (horizCRSType == PJ_TYPE_BOUND_CRS)
    8799             :             {
    8800          13 :                 auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
    8801          13 :                 if (base)
    8802             :                 {
    8803          13 :                     horizCRSType = proj_get_type(base);
    8804          13 :                     isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
    8805             :                              horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
    8806          13 :                     proj_destroy(base);
    8807             :                 }
    8808             :             }
    8809         291 :             proj_destroy(horizCRS);
    8810             :         }
    8811             :     }
    8812       50661 :     d->undoDemoteFromBoundCRS();
    8813       50661 :     return isGeog;
    8814             : }
    8815             : 
    8816             : /************************************************************************/
    8817             : /*                          OSRIsGeographic()                           */
    8818             : /************************************************************************/
    8819             : /**
    8820             :  * \brief Check if geographic coordinate system.
    8821             :  *
    8822             :  * This function is the same as OGRSpatialReference::IsGeographic().
    8823             :  */
    8824         134 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
    8825             : 
    8826             : {
    8827         134 :     VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
    8828             : 
    8829         134 :     return ToPointer(hSRS)->IsGeographic();
    8830             : }
    8831             : 
    8832             : /************************************************************************/
    8833             : /*                      IsDerivedGeographic()                           */
    8834             : /************************************************************************/
    8835             : 
    8836             : /**
    8837             :  * \brief Check if the CRS is a derived geographic coordinate system.
    8838             :  * (for example a rotated long/lat grid)
    8839             :  *
    8840             :  * This method is the same as the C function OSRIsDerivedGeographic().
    8841             :  *
    8842             :  * @since GDAL 3.1.0 and PROJ 6.3.0
    8843             :  */
    8844             : 
    8845       13758 : int OGRSpatialReference::IsDerivedGeographic() const
    8846             : 
    8847             : {
    8848       13758 :     d->refreshProjObj();
    8849       13758 :     d->demoteFromBoundCRS();
    8850       22586 :     const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
    8851        8828 :                         d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
    8852             :     const bool isDerivedGeographic =
    8853       13758 :         isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
    8854       13758 :     d->undoDemoteFromBoundCRS();
    8855       13758 :     return isDerivedGeographic ? TRUE : FALSE;
    8856             : }
    8857             : 
    8858             : /************************************************************************/
    8859             : /*                      OSRIsDerivedGeographic()                        */
    8860             : /************************************************************************/
    8861             : /**
    8862             :  * \brief Check if the CRS is a derived geographic coordinate system.
    8863             :  * (for example a rotated long/lat grid)
    8864             :  *
    8865             :  * This function is the same as OGRSpatialReference::IsDerivedGeographic().
    8866             :  */
    8867           1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
    8868             : 
    8869             : {
    8870           1 :     VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
    8871             : 
    8872           1 :     return ToPointer(hSRS)->IsDerivedGeographic();
    8873             : }
    8874             : 
    8875             : /************************************************************************/
    8876             : /*                      IsDerivedProjected()                            */
    8877             : /************************************************************************/
    8878             : 
    8879             : /**
    8880             :  * \brief Check if the CRS is a derived projected coordinate system.
    8881             :  *
    8882             :  * This method is the same as the C function OSRIsDerivedGeographic().
    8883             :  *
    8884             :  * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
    8885             :  */
    8886             : 
    8887           0 : int OGRSpatialReference::IsDerivedProjected() const
    8888             : 
    8889             : {
    8890             : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
    8891             :     d->refreshProjObj();
    8892             :     d->demoteFromBoundCRS();
    8893             :     const bool isDerivedProjected =
    8894             :         d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
    8895             :     d->undoDemoteFromBoundCRS();
    8896             :     return isDerivedProjected ? TRUE : FALSE;
    8897             : #else
    8898           0 :     return FALSE;
    8899             : #endif
    8900             : }
    8901             : 
    8902             : /************************************************************************/
    8903             : /*                      OSRIsDerivedProjected()                         */
    8904             : /************************************************************************/
    8905             : /**
    8906             :  * \brief Check if the CRS is a derived projected coordinate system.
    8907             :  *
    8908             :  * This function is the same as OGRSpatialReference::IsDerivedProjected().
    8909             :  *
    8910             :  * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
    8911             :  */
    8912           0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
    8913             : 
    8914             : {
    8915           0 :     VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
    8916             : 
    8917           0 :     return ToPointer(hSRS)->IsDerivedProjected();
    8918             : }
    8919             : 
    8920             : /************************************************************************/
    8921             : /*                              IsLocal()                               */
    8922             : /************************************************************************/
    8923             : 
    8924             : /**
    8925             :  * \brief Check if local coordinate system.
    8926             :  *
    8927             :  * This method is the same as the C function OSRIsLocal().
    8928             :  *
    8929             :  * @return TRUE if this spatial reference is local ... that is the
    8930             :  * root is a LOCAL_CS node.
    8931             :  */
    8932             : 
    8933        5577 : int OGRSpatialReference::IsLocal() const
    8934             : 
    8935             : {
    8936        5577 :     d->refreshProjObj();
    8937        5577 :     return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
    8938             : }
    8939             : 
    8940             : /************************************************************************/
    8941             : /*                          OSRIsLocal()                                */
    8942             : /************************************************************************/
    8943             : /**
    8944             :  * \brief Check if local coordinate system.
    8945             :  *
    8946             :  * This function is the same as OGRSpatialReference::IsLocal().
    8947             :  */
    8948           8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
    8949             : 
    8950             : {
    8951           8 :     VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
    8952             : 
    8953           8 :     return ToPointer(hSRS)->IsLocal();
    8954             : }
    8955             : 
    8956             : /************************************************************************/
    8957             : /*                            IsVertical()                              */
    8958             : /************************************************************************/
    8959             : 
    8960             : /**
    8961             :  * \brief Check if vertical coordinate system.
    8962             :  *
    8963             :  * This method is the same as the C function OSRIsVertical().
    8964             :  *
    8965             :  * @return TRUE if this contains a VERT_CS node indicating a it is a
    8966             :  * vertical coordinate system. Also if it is a CompoundCRS made of a
    8967             :  * VerticalCRS
    8968             :  *
    8969             :  * @since OGR 1.8.0
    8970             :  */
    8971             : 
    8972        4589 : int OGRSpatialReference::IsVertical() const
    8973             : 
    8974             : {
    8975        4589 :     d->refreshProjObj();
    8976        4589 :     d->demoteFromBoundCRS();
    8977        4589 :     bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
    8978        4589 :     if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
    8979             :     {
    8980             :         auto vertCRS =
    8981          33 :             proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
    8982          33 :         if (vertCRS)
    8983             :         {
    8984          33 :             const auto vertCRSType = proj_get_type(vertCRS);
    8985          33 :             isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
    8986          33 :             if (vertCRSType == PJ_TYPE_BOUND_CRS)
    8987             :             {
    8988           0 :                 auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
    8989           0 :                 if (base)
    8990             :                 {
    8991           0 :                     isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
    8992           0 :                     proj_destroy(base);
    8993             :                 }
    8994             :             }
    8995          33 :             proj_destroy(vertCRS);
    8996             :         }
    8997             :     }
    8998        4589 :     d->undoDemoteFromBoundCRS();
    8999        4589 :     return isVertical;
    9000             : }
    9001             : 
    9002             : /************************************************************************/
    9003             : /*                           OSRIsVertical()                            */
    9004             : /************************************************************************/
    9005             : /**
    9006             :  * \brief Check if vertical coordinate system.
    9007             :  *
    9008             :  * This function is the same as OGRSpatialReference::IsVertical().
    9009             :  *
    9010             :  * @since OGR 1.8.0
    9011             :  */
    9012           0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
    9013             : 
    9014             : {
    9015           0 :     VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
    9016             : 
    9017           0 :     return ToPointer(hSRS)->IsVertical();
    9018             : }
    9019             : 
    9020             : /************************************************************************/
    9021             : /*                            IsDynamic()                               */
    9022             : /************************************************************************/
    9023             : 
    9024             : /**
    9025             :  * \brief Check if a CRS is a dynamic CRS.
    9026             :  *
    9027             :  * A dynamic CRS relies on a dynamic datum, that is a datum that is not
    9028             :  * plate-fixed.
    9029             :  *
    9030             :  * This method is the same as the C function OSRIsDynamic().
    9031             :  *
    9032             :  * @return true if the CRS is dynamic
    9033             :  *
    9034             :  * @since OGR 3.4.0
    9035             :  *
    9036             :  * @see HasPointMotionOperation()
    9037             :  */
    9038             : 
    9039        9762 : bool OGRSpatialReference::IsDynamic() const
    9040             : 
    9041             : {
    9042        9762 :     bool isDynamic = false;
    9043        9762 :     d->refreshProjObj();
    9044        9762 :     d->demoteFromBoundCRS();
    9045        9762 :     auto ctxt = d->getPROJContext();
    9046        9762 :     PJ *horiz = nullptr;
    9047        9762 :     if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
    9048             :     {
    9049          96 :         horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
    9050             :     }
    9051        9666 :     else if (d->m_pj_crs)
    9052             :     {
    9053        9623 :         horiz = proj_clone(ctxt, d->m_pj_crs);
    9054             :     }
    9055        9762 :     if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
    9056             :     {
    9057           6 :         auto baseCRS = proj_get_source_crs(ctxt, horiz);
    9058           6 :         if (baseCRS)
    9059             :         {
    9060           6 :             proj_destroy(horiz);
    9061           6 :             horiz = baseCRS;
    9062             :         }
    9063             :     }
    9064        9762 :     auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
    9065        9762 :     if (datum)
    9066             :     {
    9067        9708 :         const auto type = proj_get_type(datum);
    9068        9708 :         isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
    9069             :                     type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
    9070        9708 :         if (!isDynamic)
    9071             :         {
    9072        9708 :             const char *auth_name = proj_get_id_auth_name(datum, 0);
    9073        9708 :             const char *code = proj_get_id_code(datum, 0);
    9074        9708 :             if (auth_name && code && EQUAL(auth_name, "EPSG") &&
    9075        9319 :                 EQUAL(code, "6326"))
    9076             :             {
    9077        5138 :                 isDynamic = true;
    9078             :             }
    9079             :         }
    9080        9708 :         proj_destroy(datum);
    9081             :     }
    9082             : #if PROJ_VERSION_MAJOR > 7 ||                                                  \
    9083             :     (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
    9084             :     else
    9085             :     {
    9086             :         auto ensemble =
    9087             :             horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
    9088             :         if (ensemble)
    9089             :         {
    9090             :             auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
    9091             :             if (member)
    9092             :             {
    9093             :                 const auto type = proj_get_type(member);
    9094             :                 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
    9095             :                             type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
    9096             :                 proj_destroy(member);
    9097             :             }
    9098             :             proj_destroy(ensemble);
    9099             :         }
    9100             :     }
    9101             : #endif
    9102        9762 :     proj_destroy(horiz);
    9103        9762 :     d->undoDemoteFromBoundCRS();
    9104        9762 :     return isDynamic;
    9105             : }
    9106             : 
    9107             : /************************************************************************/
    9108             : /*                           OSRIsDynamic()                             */
    9109             : /************************************************************************/
    9110             : /**
    9111             :  * \brief Check if a CRS is a dynamic CRS.
    9112             :  *
    9113             :  * A dynamic CRS relies on a dynamic datum, that is a datum that is not
    9114             :  * plate-fixed.
    9115             :  *
    9116             :  * This function is the same as OGRSpatialReference::IsDynamic().
    9117             :  *
    9118             :  * @since OGR 3.4.0
    9119             :  */
    9120           0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
    9121             : 
    9122             : {
    9123           0 :     VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
    9124             : 
    9125           0 :     return ToPointer(hSRS)->IsDynamic();
    9126             : }
    9127             : 
    9128             : /************************************************************************/
    9129             : /*                         HasPointMotionOperation()                    */
    9130             : /************************************************************************/
    9131             : 
    9132             : /**
    9133             :  * \brief Check if a CRS has at least an associated point motion operation.
    9134             :  *
    9135             :  * Some CRS are not formally declared as dynamic, but may behave as such
    9136             :  * in practice due to the prsence of point motion operation, to perform
    9137             :  * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
    9138             :  *
    9139             :  * @return true if the CRS has at least an associated point motion operation.
    9140             :  *
    9141             :  * @since OGR 3.8.0 and PROJ 9.4.0
    9142             :  *
    9143             :  * @see IsDynamic()
    9144             :  */
    9145             : 
    9146           5 : bool OGRSpatialReference::HasPointMotionOperation() const
    9147             : 
    9148             : {
    9149             : #if PROJ_VERSION_MAJOR > 9 ||                                                  \
    9150             :     (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
    9151             :     d->refreshProjObj();
    9152             :     d->demoteFromBoundCRS();
    9153             :     auto ctxt = d->getPROJContext();
    9154             :     auto res =
    9155             :         CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
    9156             :     d->undoDemoteFromBoundCRS();
    9157             :     return res;
    9158             : #else
    9159           5 :     return false;
    9160             : #endif
    9161             : }
    9162             : 
    9163             : /************************************************************************/
    9164             : /*                      OSRHasPointMotionOperation()                    */
    9165             : /************************************************************************/
    9166             : 
    9167             : /**
    9168             :  * \brief Check if a CRS has at least an associated point motion operation.
    9169             :  *
    9170             :  * Some CRS are not formally declared as dynamic, but may behave as such
    9171             :  * in practice due to the prsence of point motion operation, to perform
    9172             :  * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
    9173             :  *
    9174             :  * This function is the same as OGRSpatialReference::HasPointMotionOperation().
    9175             :  *
    9176             :  * @since OGR 3.8.0 and PROJ 9.4.0
    9177             :  */
    9178           0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
    9179             : 
    9180             : {
    9181           0 :     VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
    9182             : 
    9183           0 :     return ToPointer(hSRS)->HasPointMotionOperation();
    9184             : }
    9185             : 
    9186             : /************************************************************************/
    9187             : /*                            CloneGeogCS()                             */
    9188             : /************************************************************************/
    9189             : 
    9190             : /**
    9191             :  * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
    9192             :  * object.
    9193             :  *
    9194             :  * @return a new SRS, which becomes the responsibility of the caller.
    9195             :  */
    9196        3284 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
    9197             : 
    9198             : {
    9199        3284 :     d->refreshProjObj();
    9200        3284 :     if (d->m_pj_crs)
    9201             :     {
    9202        3284 :         if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
    9203           0 :             return nullptr;
    9204             : 
    9205             :         auto geodCRS =
    9206        3284 :             proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
    9207        3284 :         if (geodCRS)
    9208             :         {
    9209        3284 :             OGRSpatialReference *poNewSRS = new OGRSpatialReference();
    9210        3284 :             if (d->m_pjType == PJ_TYPE_BOUND_CRS)
    9211             :             {
    9212             :                 PJ *hub_crs =
    9213          81 :                     proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
    9214          81 :                 PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
    9215          81 :                                                      d->m_pj_crs);
    9216          81 :                 auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
    9217             :                                                       geodCRS, hub_crs, co);
    9218          81 :                 proj_destroy(geodCRS);
    9219          81 :                 geodCRS = temp;
    9220          81 :                 proj_destroy(hub_crs);
    9221          81 :                 proj_destroy(co);
    9222             :             }
    9223             : 
    9224             :             /* --------------------------------------------------------------------
    9225             :              */
    9226             :             /*      We have to reconstruct the GEOGCS node for geocentric */
    9227             :             /*      coordinate systems. */
    9228             :             /* --------------------------------------------------------------------
    9229             :              */
    9230        3284 :             if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
    9231             :             {
    9232           0 :                 auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
    9233             : #if PROJ_VERSION_MAJOR > 7 ||                                                  \
    9234             :     (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
    9235             :                 if (datum == nullptr)
    9236             :                 {
    9237             :                     datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
    9238             :                                                         geodCRS);
    9239             :                 }
    9240             : #endif
    9241           0 :                 if (datum)
    9242             :                 {
    9243           0 :                     auto cs = proj_create_ellipsoidal_2D_cs(
    9244             :                         d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
    9245             :                         nullptr, 0);
    9246           0 :                     auto temp = proj_create_geographic_crs_from_datum(
    9247             :                         d->getPROJContext(), "unnamed", datum, cs);
    9248           0 :                     proj_destroy(datum);
    9249           0 :                     proj_destroy(cs);
    9250           0 :                     proj_destroy(geodCRS);
    9251           0 :                     geodCRS = temp;
    9252             :                 }
    9253             :             }
    9254             : 
    9255        3284 :             poNewSRS->d->setPjCRS(geodCRS);
    9256        3284 :             if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
    9257        2283 :                 poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    9258        3284 :             return poNewSRS;
    9259             :         }
    9260             :     }
    9261           0 :     return nullptr;
    9262             : }
    9263             : 
    9264             : /************************************************************************/
    9265             : /*                           OSRCloneGeogCS()                           */
    9266             : /************************************************************************/
    9267             : /**
    9268             :  * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
    9269             :  * object.
    9270             :  *
    9271             :  * This function is the same as OGRSpatialReference::CloneGeogCS().
    9272             :  */
    9273         143 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
    9274             : 
    9275             : {
    9276         143 :     VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
    9277             : 
    9278         143 :     return ToHandle(ToPointer(hSource)->CloneGeogCS());
    9279             : }
    9280             : 
    9281             : /************************************************************************/
    9282             : /*                            IsSameGeogCS()                            */
    9283             : /************************************************************************/
    9284             : 
    9285             : /**
    9286             :  * \brief Do the GeogCS'es match?
    9287             :  *
    9288             :  * This method is the same as the C function OSRIsSameGeogCS().
    9289             :  *
    9290             :  * @param poOther the SRS being compared against.
    9291             :  *
    9292             :  * @return TRUE if they are the same or FALSE otherwise.
    9293             :  */
    9294             : 
    9295        5305 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
    9296             : 
    9297             : {
    9298        5305 :     return IsSameGeogCS(poOther, nullptr);
    9299             : }
    9300             : 
    9301             : /**
    9302             :  * \brief Do the GeogCS'es match?
    9303             :  *
    9304             :  * This method is the same as the C function OSRIsSameGeogCS().
    9305             :  *
    9306             :  * @param poOther the SRS being compared against.
    9307             :  * @param papszOptions options. ignored
    9308             :  *
    9309             :  * @return TRUE if they are the same or FALSE otherwise.
    9310             :  */
    9311             : 
    9312        5305 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
    9313             :                                       const char *const *papszOptions) const
    9314             : 
    9315             : {
    9316        5305 :     CPL_IGNORE_RET_VAL(papszOptions);
    9317             : 
    9318        5305 :     d->refreshProjObj();
    9319        5305 :     poOther->d->refreshProjObj();
    9320        5305 :     if (!d->m_pj_crs || !poOther->d->m_pj_crs)
    9321           0 :         return FALSE;
    9322        5305 :     if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
    9323        5305 :         d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
    9324       15915 :         poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
    9325        5305 :         poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
    9326             :     {
    9327           0 :         return FALSE;
    9328             :     }
    9329             : 
    9330        5305 :     auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
    9331             :     auto otherGeodCRS =
    9332        5305 :         proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
    9333        5305 :     if (!geodCRS || !otherGeodCRS)
    9334             :     {
    9335           0 :         proj_destroy(geodCRS);
    9336           0 :         proj_destroy(otherGeodCRS);
    9337           0 :         return FALSE;
    9338             :     }
    9339             : 
    9340        5305 :     int ret = proj_is_equivalent_to(
    9341             :         geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
    9342             : 
    9343        5305 :     proj_destroy(geodCRS);
    9344        5305 :     proj_destroy(otherGeodCRS);
    9345        5305 :     return ret;
    9346             : }
    9347             : 
    9348             : /************************************************************************/
    9349             : /*                          OSRIsSameGeogCS()                           */
    9350             : /************************************************************************/
    9351             : 
    9352             : /**
    9353             :  * \brief Do the GeogCS'es match?
    9354             :  *
    9355             :  * This function is the same as OGRSpatialReference::IsSameGeogCS().
    9356             :  */
    9357           0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
    9358             : 
    9359             : {
    9360           0 :     VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
    9361           0 :     VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
    9362             : 
    9363           0 :     return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
    9364             : }
    9365             : 
    9366             : /************************************************************************/
    9367             : /*                            IsSameVertCS()                            */
    9368             : /************************************************************************/
    9369             : 
    9370             : /**
    9371             :  * \brief Do the VertCS'es match?
    9372             :  *
    9373             :  * This method is the same as the C function OSRIsSameVertCS().
    9374             :  *
    9375             :  * @param poOther the SRS being compared against.
    9376             :  *
    9377             :  * @return TRUE if they are the same or FALSE otherwise.
    9378             :  */
    9379             : 
    9380           2 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
    9381             : 
    9382             : {
    9383             :     /* -------------------------------------------------------------------- */
    9384             :     /*      Does the datum name match?                                      */
    9385             :     /* -------------------------------------------------------------------- */
    9386           2 :     const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
    9387           2 :     const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
    9388             : 
    9389           2 :     if (pszThisValue == nullptr || pszOtherValue == nullptr ||
    9390           2 :         !EQUAL(pszThisValue, pszOtherValue))
    9391           1 :         return FALSE;
    9392             : 
    9393             :     /* -------------------------------------------------------------------- */
    9394             :     /*      Do the units match?                                             */
    9395             :     /* -------------------------------------------------------------------- */
    9396           1 :     pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
    9397           1 :     if (pszThisValue == nullptr)
    9398           0 :         pszThisValue = "1.0";
    9399             : 
    9400           1 :     pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
    9401           1 :     if (pszOtherValue == nullptr)
    9402           0 :         pszOtherValue = "1.0";
    9403             : 
    9404           1 :     if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
    9405           0 :         return FALSE;
    9406             : 
    9407           1 :     return TRUE;
    9408             : }
    9409             : 
    9410             : /************************************************************************/
    9411             : /*                          OSRIsSameVertCS()                           */
    9412             : /************************************************************************/
    9413             : 
    9414             : /**
    9415             :  * \brief Do the VertCS'es match?
    9416             :  *
    9417             :  * This function is the same as OGRSpatialReference::IsSameVertCS().
    9418             :  */
    9419           0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
    9420             : 
    9421             : {
    9422           0 :     VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
    9423           0 :     VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
    9424             : 
    9425           0 :     return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
    9426             : }
    9427             : 
    9428             : /************************************************************************/
    9429             : /*                               IsSame()                               */
    9430             : /************************************************************************/
    9431             : 
    9432             : /**
    9433             :  * \brief Do these two spatial references describe the same system ?
    9434             :  *
    9435             :  * @param poOtherSRS the SRS being compared to.
    9436             :  *
    9437             :  * @return TRUE if equivalent or FALSE otherwise.
    9438             :  */
    9439             : 
    9440       24146 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
    9441             : 
    9442             : {
    9443       24146 :     return IsSame(poOtherSRS, nullptr);
    9444             : }
    9445             : 
    9446             : /**
    9447             :  * \brief Do these two spatial references describe the same system ?
    9448             :  *
    9449             :  * This also takes into account the data axis to CRS axis mapping by default
    9450             :  *
    9451             :  * @param poOtherSRS the SRS being compared to.
    9452             :  * @param papszOptions options. NULL or NULL terminated list of options.
    9453             :  * Currently supported options are:
    9454             :  * <ul>
    9455             :  * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
    9456             :  * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
    9457             :  *     Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
    9458             :  * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
    9459             :  * </ul>
    9460             :  *
    9461             :  * @return TRUE if equivalent or FALSE otherwise.
    9462             :  */
    9463             : 
    9464       32132 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
    9465             :                                 const char *const *papszOptions) const
    9466             : 
    9467             : {
    9468       32132 :     d->refreshProjObj();
    9469       32132 :     poOtherSRS->d->refreshProjObj();
    9470       32132 :     if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
    9471          50 :         return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
    9472       32082 :     if (!CPLTestBool(CSLFetchNameValueDef(
    9473             :             papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
    9474             :     {
    9475       31596 :         if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
    9476        1997 :             return false;
    9477             :     }
    9478             : 
    9479       30085 :     if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
    9480             :                                           "IGNORE_COORDINATE_EPOCH", "NO")))
    9481             :     {
    9482       29803 :         if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
    9483          25 :             return false;
    9484             :     }
    9485             : 
    9486       30060 :     bool reboundSelf = false;
    9487       30060 :     bool reboundOther = false;
    9488       30112 :     if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
    9489          52 :         poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
    9490             :     {
    9491          17 :         d->demoteFromBoundCRS();
    9492          17 :         reboundSelf = true;
    9493             :     }
    9494       60051 :     else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
    9495       30008 :              poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
    9496             :     {
    9497          68 :         poOtherSRS->d->demoteFromBoundCRS();
    9498          68 :         reboundOther = true;
    9499             :     }
    9500             : 
    9501       30060 :     PJ_COMPARISON_CRITERION criterion =
    9502             :         PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
    9503       30060 :     const char *pszCriterion = CSLFetchNameValueDef(
    9504             :         papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
    9505       30060 :     if (EQUAL(pszCriterion, "STRICT"))
    9506           0 :         criterion = PJ_COMP_STRICT;
    9507       30060 :     else if (EQUAL(pszCriterion, "EQUIVALENT"))
    9508        5765 :         criterion = PJ_COMP_EQUIVALENT;
    9509       24295 :     else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
    9510             :     {
    9511           0 :         CPLError(CE_Warning, CPLE_NotSupported,
    9512             :                  "Unsupported value for CRITERION: %s", pszCriterion);
    9513             :     }
    9514             :     int ret =
    9515       30060 :         proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
    9516       30060 :     if (reboundSelf)
    9517          17 :         d->undoDemoteFromBoundCRS();
    9518       30060 :     if (reboundOther)
    9519          68 :         poOtherSRS->d->undoDemoteFromBoundCRS();
    9520             : 
    9521       30060 :     return ret;
    9522             : }
    9523             : 
    9524             : /************************************************************************/
    9525             : /*                             OSRIsSame()                              */
    9526             : /************************************************************************/
    9527             : 
    9528             : /**
    9529             :  * \brief Do these two spatial references describe the same system ?
    9530             :  *
    9531             :  * This function is the same as OGRSpatialReference::IsSame().
    9532             :  */
    9533          25 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
    9534             : 
    9535             : {
    9536          25 :     VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
    9537          25 :     VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
    9538             : 
    9539          25 :     return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
    9540             : }
    9541             : 
    9542             : /************************************************************************/
    9543             : /*                             OSRIsSameEx()                            */
    9544             : /************************************************************************/
    9545             : 
    9546             : /**
    9547             :  * \brief Do these two spatial references describe the same system ?
    9548             :  *
    9549             :  * This function is the same as OGRSpatialReference::IsSame().
    9550             :  */
    9551         589 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
    9552             :                 const char *const *papszOptions)
    9553             : {
    9554         589 :     VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
    9555         589 :     VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
    9556             : 
    9557         589 :     return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
    9558             : }
    9559             : 
    9560             : /************************************************************************/
    9561             : /*                    convertToOtherProjection()                        */
    9562             : /************************************************************************/
    9563             : 
    9564             : /**
    9565             :  * \brief Convert to another equivalent projection
    9566             :  *
    9567             :  * Currently implemented:
    9568             :  * <ul>
    9569             :  * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
    9570             :  * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
    9571             :  * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
    9572             :  * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
    9573             :  * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
    9574             :  * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
    9575             :  * </ul>
    9576             :  *
    9577             :  * @param pszTargetProjection target projection.
    9578             :  * @param papszOptions lists of options. None supported currently.
    9579             :  * @return a new SRS, or NULL in case of error.
    9580             :  *
    9581             :  * @since GDAL 2.3
    9582             :  */
    9583          87 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
    9584             :     const char *pszTargetProjection,
    9585             :     CPL_UNUSED const char *const *papszOptions) const
    9586             : {
    9587          87 :     if (pszTargetProjection == nullptr)
    9588           1 :         return nullptr;
    9589             :     int new_code;
    9590          86 :     if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
    9591             :     {
    9592           6 :         new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
    9593             :     }
    9594          80 :     else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
    9595             :     {
    9596           5 :         new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
    9597             :     }
    9598          75 :     else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
    9599             :     {
    9600          63 :         new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
    9601             :     }
    9602          12 :     else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
    9603             :     {
    9604          11 :         new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
    9605             :     }
    9606             :     else
    9607             :     {
    9608           1 :         return nullptr;
    9609             :     }
    9610             : 
    9611          85 :     d->refreshProjObj();
    9612          85 :     d->demoteFromBoundCRS();
    9613          85 :     OGRSpatialReference *poNewSRS = nullptr;
    9614          85 :     if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
    9615             :     {
    9616             :         auto conv =
    9617          84 :             proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
    9618          84 :         auto new_conv = proj_convert_conversion_to_other_method(
    9619             :             d->getPROJContext(), conv, new_code, nullptr);
    9620          84 :         proj_destroy(conv);
    9621          84 :         if (new_conv)
    9622             :         {
    9623             :             auto geodCRS =
    9624          70 :                 proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
    9625          70 :             auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
    9626          70 :                                                      d->m_pj_crs);
    9627          70 :             if (geodCRS && cs)
    9628             :             {
    9629          70 :                 auto new_proj_crs = proj_create_projected_crs(
    9630          70 :                     d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
    9631             :                     new_conv, cs);
    9632          70 :                 proj_destroy(new_conv);
    9633          70 :                 if (new_proj_crs)
    9634             :                 {
    9635          70 :                     poNewSRS = new OGRSpatialReference();
    9636             : 
    9637          70 :                     if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
    9638             :                     {
    9639           9 :                         auto boundCRS = proj_crs_create_bound_crs(
    9640             :                             d->getPROJContext(), new_proj_crs,
    9641           9 :                             d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
    9642           9 :                         if (boundCRS)
    9643             :                         {
    9644           9 :                             proj_destroy(new_proj_crs);
    9645           9 :                             new_proj_crs = boundCRS;
    9646             :                         }
    9647             :                     }
    9648             : 
    9649          70 :                     poNewSRS->d->setPjCRS(new_proj_crs);
    9650             :                 }
    9651             :             }
    9652          70 :             proj_destroy(geodCRS);
    9653          70 :             proj_destroy(cs);
    9654             :         }
    9655             :     }
    9656          85 :     d->undoDemoteFromBoundCRS();
    9657          85 :     return poNewSRS;
    9658             : }
    9659             : 
    9660             : /************************************************************************/
    9661             : /*                    OSRConvertToOtherProjection()                     */
    9662             : /************************************************************************/
    9663             : 
    9664             : /**
    9665             :  * \brief Convert to another equivalent projection
    9666             :  *
    9667             :  * Currently implemented:
    9668             :  * <ul>
    9669             :  * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
    9670             :  * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
    9671             :  * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
    9672             :  * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
    9673             :  * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
    9674             :  * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
    9675             :  * </ul>
    9676             :  *
    9677             :  * @param hSRS source SRS
    9678             :  * @param pszTargetProjection target projection.
    9679             :  * @param papszOptions lists of options. None supported currently.
    9680             :  * @return a new SRS, or NULL in case of error.
    9681             :  *
    9682             :  * @since GDAL 2.3
    9683             :  */
    9684             : OGRSpatialReferenceH
    9685          28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
    9686             :                             const char *pszTargetProjection,
    9687             :                             const char *const *papszOptions)
    9688             : {
    9689          28 :     VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
    9690          28 :     return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
    9691          28 :         pszTargetProjection, papszOptions));
    9692             : }
    9693             : 
    9694             : /************************************************************************/
    9695             : /*                           OSRFindMatches()                           */
    9696             : /************************************************************************/
    9697             : 
    9698             : /**
    9699             :  * \brief Try to identify a match between the passed SRS and a related SRS
    9700             :  * in a catalog.
    9701             :  *
    9702             :  * Matching may be partial, or may fail.
    9703             :  * Returned entries will be sorted by decreasing match confidence (first
    9704             :  * entry has the highest match confidence).
    9705             :  *
    9706             :  * The exact way matching is done may change in future versions. Starting with
    9707             :  * GDAL 3.0, it relies on PROJ' proj_identify() function.
    9708             :  *
    9709             :  * This function is the same as OGRSpatialReference::FindMatches().
    9710             :  *
    9711             :  * @param hSRS SRS to match
    9712             :  * @param papszOptions NULL terminated list of options or NULL
    9713             :  * @param pnEntries Output parameter. Number of values in the returned array.
    9714             :  * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
    9715             :  * will be allocated to an array of *pnEntries whose values between 0 and 100
    9716             :  * indicate the confidence in the match. 100 is the highest confidence level.
    9717             :  * The array must be freed with CPLFree().
    9718             :  *
    9719             :  * @return an array of SRS that match the passed SRS, or NULL. Must be freed
    9720             :  * with OSRFreeSRSArray()
    9721             :  *
    9722             :  * @since GDAL 2.3
    9723             :  */
    9724           8 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
    9725             :                                      char **papszOptions, int *pnEntries,
    9726             :                                      int **ppanMatchConfidence)
    9727             : {
    9728           8 :     if (pnEntries)
    9729           8 :         *pnEntries = 0;
    9730           8 :     if (ppanMatchConfidence)
    9731           8 :         *ppanMatchConfidence = nullptr;
    9732           8 :     VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
    9733             : 
    9734           8 :     OGRSpatialReference *poSRS = ToPointer(hSRS);
    9735           8 :     return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
    9736             : }
    9737             : 
    9738             : /************************************************************************/
    9739             : /*                           OSRFreeSRSArray()                          */
    9740             : /************************************************************************/
    9741             : 
    9742             : /**
    9743             :  * \brief Free return of OSRIdentifyMatches()
    9744             :  *
    9745             :  * @param pahSRS array of SRS (must be NULL terminated)
    9746             :  * @since GDAL 2.3
    9747             :  */
    9748         130 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
    9749             : {
    9750         130 :     if (pahSRS != nullptr)
    9751             :     {
    9752        2109 :         for (int i = 0; pahSRS[i] != nullptr; ++i)
    9753             :         {
    9754        1997 :             OSRRelease(pahSRS[i]);
    9755             :         }
    9756         112 :         CPLFree(pahSRS);
    9757             :     }
    9758         130 : }
    9759             : 
    9760             : /************************************************************************/
    9761             : /*                         FindBestMatch()                              */
    9762             : /************************************************************************/
    9763             : 
    9764             : /**
    9765             :  * \brief Try to identify the best match between the passed SRS and a related
    9766             :  * SRS in a catalog.
    9767             :  *
    9768             :  * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
    9769             :  * of filtering its output.
    9770             :  * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
    9771             :  * will be considered. If there is a single match, it is returned.
    9772             :  * If there are several matches, only return the one under the
    9773             :  * pszPreferredAuthority, if there is a single one under that authority.
    9774             :  *
    9775             :  * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
    9776             :  * 100). If set to 0, 90 is used.
    9777             :  * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
    9778             :  * "EPSG" is used.
    9779             :  * @param papszOptions NULL terminated list of options or NULL. No option is
    9780             :  * defined at time of writing.
    9781             :  *
    9782             :  * @return a new OGRSpatialReference* object to free with Release(), or nullptr
    9783             :  *
    9784             :  * @since GDAL 3.6
    9785             :  * @see OGRSpatialReference::FindMatches()
    9786             :  */
    9787             : OGRSpatialReference *
    9788         971 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
    9789             :                                    const char *pszPreferredAuthority,
    9790             :                                    CSLConstList papszOptions) const
    9791             : {
    9792         971 :     CPL_IGNORE_RET_VAL(papszOptions);  // ignored for now.
    9793             : 
    9794         971 :     if (nMinimumMatchConfidence == 0)
    9795           0 :         nMinimumMatchConfidence = 90;
    9796         971 :     if (pszPreferredAuthority == nullptr)
    9797           0 :         pszPreferredAuthority = "EPSG";
    9798             : 
    9799             :     // Try to identify the CRS with the database
    9800         971 :     int nEntries = 0;
    9801         971 :     int *panConfidence = nullptr;
    9802             :     OGRSpatialReferenceH *pahSRS =
    9803         971 :         FindMatches(nullptr, &nEntries, &panConfidence);
    9804         971 :     if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
    9805             :     {
    9806        1776 :         std::vector<double> adfTOWGS84(7);
    9807         888 :         if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
    9808             :         {
    9809         887 :             adfTOWGS84.clear();
    9810             :         }
    9811             : 
    9812         888 :         auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
    9813             : 
    9814             :         auto poBaseGeogCRS =
    9815         888 :             std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
    9816             : 
    9817             :         // If the base geographic SRS of the SRS is EPSG:4326
    9818             :         // with TOWGS84[0,0,0,0,0,0], then just use the official
    9819             :         // SRS code
    9820             :         // Same with EPSG:4258 (ETRS89), since it's the only known
    9821             :         // TOWGS84[] style transformation to WGS 84, and given the
    9822             :         // "fuzzy" nature of both ETRS89 and WGS 84, there's little
    9823             :         // chance that a non-NULL TOWGS84[] will emerge.
    9824         888 :         const char *pszAuthorityName = nullptr;
    9825         888 :         const char *pszAuthorityCode = nullptr;
    9826         888 :         const char *pszBaseAuthorityName = nullptr;
    9827         888 :         const char *pszBaseAuthorityCode = nullptr;
    9828        1776 :         if (adfTOWGS84 == std::vector<double>(7) &&
    9829           1 :             (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
    9830           1 :             EQUAL(pszAuthorityName, "EPSG") &&
    9831           1 :             (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
    9832           1 :             (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
    9833           1 :                 nullptr &&
    9834           1 :             EQUAL(pszBaseAuthorityName, "EPSG") &&
    9835           1 :             (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
    9836        1777 :                 nullptr &&
    9837           1 :             (EQUAL(pszBaseAuthorityCode, "4326") ||
    9838           1 :              EQUAL(pszBaseAuthorityCode, "4258")))
    9839             :         {
    9840           1 :             poSRS->importFromEPSG(atoi(pszAuthorityCode));
    9841             :         }
    9842             : 
    9843         888 :         CPLFree(pahSRS);
    9844         888 :         CPLFree(panConfidence);
    9845             : 
    9846         888 :         return poSRS;
    9847             :     }
    9848             :     else
    9849             :     {
    9850             :         // If there are several matches >= nMinimumMatchConfidence, take the
    9851             :         // only one that is under pszPreferredAuthority
    9852          83 :         int iBestEntry = -1;
    9853        1299 :         for (int i = 0; i < nEntries; i++)
    9854             :         {
    9855        1216 :             if (panConfidence[i] >= nMinimumMatchConfidence)
    9856             :             {
    9857             :                 const char *pszAuthName =
    9858           0 :                     OGRSpatialReference::FromHandle(pahSRS[i])
    9859           0 :                         ->GetAuthorityName(nullptr);
    9860           0 :                 if (pszAuthName != nullptr &&
    9861           0 :                     EQUAL(pszAuthName, pszPreferredAuthority))
    9862             :                 {
    9863           0 :                     if (iBestEntry < 0)
    9864           0 :                         iBestEntry = i;
    9865             :                     else
    9866             :                     {
    9867           0 :                         iBestEntry = -1;
    9868           0 :                         break;
    9869             :                     }
    9870             :                 }
    9871             :             }
    9872             :         }
    9873          83 :         if (iBestEntry >= 0)
    9874             :         {
    9875           0 :             auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
    9876           0 :             OSRFreeSRSArray(pahSRS);
    9877           0 :             CPLFree(panConfidence);
    9878           0 :             return poRet;
    9879             :         }
    9880             :     }
    9881          83 :     OSRFreeSRSArray(pahSRS);
    9882          83 :     CPLFree(panConfidence);
    9883          83 :     return nullptr;
    9884             : }
    9885             : 
    9886             : /************************************************************************/
    9887             : /*                             SetTOWGS84()                             */
    9888             : /************************************************************************/
    9889             : 
    9890             : /**
    9891             :  * \brief Set the Bursa-Wolf conversion to WGS84.
    9892             :  *
    9893             :  * This will create the TOWGS84 node as a child of the DATUM.  It will fail
    9894             :  * if there is no existing DATUM node. It will replace
    9895             :  * an existing TOWGS84 node if there is one.
    9896             :  *
    9897             :  * The parameters have the same meaning as EPSG transformation 9606
    9898             :  * (Position Vector 7-param. transformation).
    9899             :  *
    9900             :  * This method is the same as the C function OSRSetTOWGS84().
    9901             :  *
    9902             :  * @param dfDX X child in meters.
    9903             :  * @param dfDY Y child in meters.
    9904             :  * @param dfDZ Z child in meters.
    9905             :  * @param dfEX X rotation in arc seconds (optional, defaults to zero).
    9906             :  * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
    9907             :  * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
    9908             :  * @param dfPPM scaling factor (parts per million).
    9909             :  *
    9910             :  * @return OGRERR_NONE on success.
    9911             :  */
    9912             : 
    9913         114 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
    9914             :                                        double dfEX, double dfEY, double dfEZ,
    9915             :                                        double dfPPM)
    9916             : 
    9917             : {
    9918         114 :     d->refreshProjObj();
    9919         114 :     if (d->m_pj_crs == nullptr)
    9920             :     {
    9921           0 :         return OGRERR_FAILURE;
    9922             :     }
    9923             : 
    9924             :     // Remove existing BoundCRS
    9925         114 :     if (d->m_pjType == PJ_TYPE_BOUND_CRS)
    9926             :     {
    9927           0 :         auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
    9928           0 :         if (!baseCRS)
    9929           0 :             return OGRERR_FAILURE;
    9930           0 :         d->setPjCRS(baseCRS);
    9931             :     }
    9932             : 
    9933             :     PJ_PARAM_DESCRIPTION params[7];
    9934             : 
    9935         114 :     params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
    9936         114 :     params[0].auth_name = "EPSG";
    9937         114 :     params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
    9938         114 :     params[0].value = dfDX;
    9939         114 :     params[0].unit_name = "metre";
    9940         114 :     params[0].unit_conv_factor = 1.0;
    9941         114 :     params[0].unit_type = PJ_UT_LINEAR;
    9942             : 
    9943         114 :     params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
    9944         114 :     params[1].auth_name = "EPSG";
    9945         114 :     params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
    9946         114 :     params[1].value = dfDY;
    9947         114 :     params[1].unit_name = "metre";
    9948         114 :     params[1].unit_conv_factor = 1.0;
    9949         114 :     params[1].unit_type = PJ_UT_LINEAR;
    9950             : 
    9951         114 :     params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
    9952         114 :     params[2].auth_name = "EPSG";
    9953         114 :     params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
    9954         114 :     params[2].value = dfDZ;
    9955         114 :     params[2].unit_name = "metre";
    9956         114 :     params[2].unit_conv_factor = 1.0;
    9957         114 :     params[2].unit_type = PJ_UT_LINEAR;
    9958             : 
    9959         114 :     params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
    9960         114 :     params[3].auth_name = "EPSG";
    9961         114 :     params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
    9962         114 :     params[3].value = dfEX;
    9963         114 :     params[3].unit_name = "arc-second";
    9964         114 :     params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
    9965         114 :     params[3].unit_type = PJ_UT_ANGULAR;
    9966             : 
    9967         114 :     params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
    9968         114 :     params[4].auth_name = "EPSG";
    9969         114 :     params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
    9970         114 :     params[4].value = dfEY;
    9971         114 :     params[4].unit_name = "arc-second";
    9972         114 :     params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
    9973         114 :     params[4].unit_type = PJ_UT_ANGULAR;
    9974             : 
    9975         114 :     params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
    9976         114 :     params[5].auth_name = "EPSG";
    9977         114 :     params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
    9978         114 :     params[5].value = dfEZ;
    9979         114 :     params[5].unit_name = "arc-second";
    9980         114 :     params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
    9981         114 :     params[5].unit_type = PJ_UT_ANGULAR;
    9982             : 
    9983         114 :     params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
    9984         114 :     params[6].auth_name = "EPSG";
    9985         114 :     params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
    9986         114 :     params[6].value = dfPPM;
    9987         114 :     params[6].unit_name = "parts per million";
    9988         114 :     params[6].unit_conv_factor = 1e-6;
    9989         114 :     params[6].unit_type = PJ_UT_SCALE;
    9990             : 
    9991             :     auto sourceCRS =
    9992         114 :         proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
    9993         114 :     if (!sourceCRS)
    9994             :     {
    9995           0 :         return OGRERR_FAILURE;
    9996             :     }
    9997             : 
    9998         114 :     const auto sourceType = proj_get_type(sourceCRS);
    9999             : 
   10000         114 :     auto targetCRS = proj_create_from_database(
   10001             :         d->getPROJContext(), "EPSG",
   10002             :         sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS   ? "4326"
   10003             :         : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
   10004             :                                                   : "4978",
   10005             :         PJ_CATEGORY_CRS, false, nullptr);
   10006         114 :     if (!targetCRS)
   10007             :     {
   10008           0 :         proj_destroy(sourceCRS);
   10009           0 :         return OGRERR_FAILURE;
   10010             :     }
   10011             : 
   10012         228 :     CPLString osMethodCode;
   10013             :     osMethodCode.Printf("%d",
   10014             :                         sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
   10015             :                             ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
   10016             :                         : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
   10017           0 :                             ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
   10018         114 :                             : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
   10019             : 
   10020         114 :     auto transf = proj_create_transformation(
   10021             :         d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
   10022             :         sourceCRS, targetCRS, nullptr,
   10023             :         sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
   10024             :             ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
   10025             :         : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
   10026           0 :             ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
   10027             :             : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
   10028             :         "EPSG", osMethodCode.c_str(), 7, params, -1);
   10029         114 :     proj_destroy(sourceCRS);
   10030         114 :     if (!transf)
   10031             :     {
   10032           0 :         proj_destroy(targetCRS);
   10033           0 :         return OGRERR_FAILURE;
   10034             :     }
   10035             : 
   10036         114 :     auto newBoundCRS = proj_crs_create_bound_crs(
   10037         114 :         d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
   10038         114 :     proj_destroy(transf);
   10039         114 :     proj_destroy(targetCRS);
   10040         114 :     if (!newBoundCRS)
   10041             :     {
   10042           0 :         return OGRERR_FAILURE;
   10043             :     }
   10044             : 
   10045         114 :     d->setPjCRS(newBoundCRS);
   10046         114 :     return OGRERR_NONE;
   10047             : }
   10048             : 
   10049             : /************************************************************************/
   10050             : /*                           OSRSetTOWGS84()                            */
   10051             : /************************************************************************/
   10052             : 
   10053             : /**
   10054             :  * \brief Set the Bursa-Wolf conversion to WGS84.
   10055             :  *
   10056             :  * This function is the same as OGRSpatialReference::SetTOWGS84().
   10057             :  */
   10058          10 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
   10059             :                      double dfDZ, double dfEX, double dfEY, double dfEZ,
   10060             :                      double dfPPM)
   10061             : 
   10062             : {
   10063          10 :     VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
   10064             : 
   10065          10 :     return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
   10066          10 :                                        dfPPM);
   10067             : }
   10068             : 
   10069             : /************************************************************************/
   10070             : /*                             GetTOWGS84()                             */
   10071             : /************************************************************************/
   10072             : 
   10073             : /**
   10074             :  * \brief Fetch TOWGS84 parameters, if available.
   10075             :  *
   10076             :  * The parameters have the same meaning as EPSG transformation 9606
   10077             :  * (Position Vector 7-param. transformation).
   10078             :  *
   10079             :  * @param padfCoeff array into which up to 7 coefficients are placed.
   10080             :  * @param nCoeffCount size of padfCoeff - defaults to 7.
   10081             :  *
   10082             :  * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
   10083             :  * TOWGS84 node available.
   10084             :  */
   10085             : 
   10086        3205 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
   10087             : 
   10088             : {
   10089        3205 :     d->refreshProjObj();
   10090        3205 :     if (d->m_pjType != PJ_TYPE_BOUND_CRS)
   10091        3145 :         return OGRERR_FAILURE;
   10092             : 
   10093          60 :     memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
   10094             : 
   10095          60 :     auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
   10096          60 :     int success = proj_coordoperation_get_towgs84_values(
   10097             :         d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
   10098          60 :     proj_destroy(transf);
   10099             : 
   10100          60 :     return success ? OGRERR_NONE : OGRERR_FAILURE;
   10101             : }
   10102             : 
   10103             : /************************************************************************/
   10104             : /*                           OSRGetTOWGS84()                            */
   10105             : /************************************************************************/
   10106             : 
   10107             : /**
   10108             :  * \brief Fetch TOWGS84 parameters, if available.
   10109             :  *
   10110             :  * This function is the same as OGRSpatialReference::GetTOWGS84().
   10111             :  */
   10112          11 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
   10113             :                      int nCoeffCount)
   10114             : 
   10115             : {
   10116          11 :     VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
   10117             : 
   10118          11 :     return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
   10119             : }
   10120             : 
   10121             : /************************************************************************/
   10122             : /*                         IsAngularParameter()                         */
   10123             : /************************************************************************/
   10124             : 
   10125             : /** Is the passed projection parameter an angular one?
   10126             :  *
   10127             :  * @return TRUE or FALSE
   10128             :  */
   10129             : 
   10130           4 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
   10131             : 
   10132             : {
   10133           4 :     if (STARTS_WITH_CI(pszParameterName, "long") ||
   10134           4 :         STARTS_WITH_CI(pszParameterName, "lati") ||
   10135           3 :         EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
   10136           2 :         STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
   10137           0 :         EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
   10138           0 :         EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
   10139           4 :         return TRUE;
   10140             : 
   10141           0 :     return FALSE;
   10142             : }
   10143             : 
   10144             : /************************************************************************/
   10145             : /*                        IsLongitudeParameter()                        */
   10146             : /************************************************************************/
   10147             : 
   10148             : /** Is the passed projection parameter an angular longitude
   10149             :  * (relative to a prime meridian)?
   10150             :  *
   10151             :  * @return TRUE or FALSE
   10152             :  */
   10153             : 
   10154           0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
   10155             : 
   10156             : {
   10157           0 :     if (STARTS_WITH_CI(pszParameterName, "long") ||
   10158           0 :         EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
   10159           0 :         return TRUE;
   10160             : 
   10161           0 :     return FALSE;
   10162             : }
   10163             : 
   10164             : /************************************************************************/
   10165             : /*                         IsLinearParameter()                          */
   10166             : /************************************************************************/
   10167             : 
   10168             : /** Is the passed projection parameter an linear one measured in meters or
   10169             :  * some similar linear measure.
   10170             :  *
   10171             :  * @return TRUE or FALSE
   10172             :  */
   10173          43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
   10174             : 
   10175             : {
   10176          43 :     if (STARTS_WITH_CI(pszParameterName, "false_") ||
   10177          34 :         EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
   10178           9 :         return TRUE;
   10179             : 
   10180          34 :     return FALSE;
   10181             : }
   10182             : 
   10183             : /************************************************************************/
   10184             : /*                            GetNormInfo()                             */
   10185             : /************************************************************************/
   10186             : 
   10187             : /**
   10188             :  * \brief Set the internal information for normalizing linear, and angular
   10189             :  * values.
   10190             :  */
   10191        3460 : void OGRSpatialReference::GetNormInfo() const
   10192             : 
   10193             : {
   10194        3460 :     if (d->bNormInfoSet)
   10195        2434 :         return;
   10196             : 
   10197             :     /* -------------------------------------------------------------------- */
   10198             :     /*      Initialize values.                                              */
   10199             :     /* -------------------------------------------------------------------- */
   10200        1026 :     d->bNormInfoSet = TRUE;
   10201             : 
   10202        1026 :     d->dfFromGreenwich = GetPrimeMeridian(nullptr);
   10203        1026 :     d->dfToMeter = GetLinearUnits(nullptr);
   10204        1026 :     d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
   10205        1026 :     if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
   10206        1025 :         d->dfToDegrees = 1.0;
   10207             : }
   10208             : 
   10209             : /************************************************************************/
   10210             : /*                            GetExtension()                            */
   10211             : /************************************************************************/
   10212             : 
   10213             : /**
   10214             :  * \brief Fetch extension value.
   10215             :  *
   10216             :  * Fetch the value of the named EXTENSION item for the identified
   10217             :  * target node.
   10218             :  *
   10219             :  * @param pszTargetKey the name or path to the parent node of the EXTENSION.
   10220             :  * @param pszName the name of the extension being fetched.
   10221             :  * @param pszDefault the value to return if the extension is not found.
   10222             :  *
   10223             :  * @return node value if successful or pszDefault on failure.
   10224             :  */
   10225             : 
   10226        7914 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
   10227             :                                               const char *pszName,
   10228             :                                               const char *pszDefault) const
   10229             : 
   10230             : {
   10231             :     /* -------------------------------------------------------------------- */
   10232             :     /*      Find the target node.                                           */
   10233             :     /* -------------------------------------------------------------------- */
   10234             :     const OGR_SRSNode *poNode =
   10235        7914 :         pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
   10236             : 
   10237        7914 :     if (poNode == nullptr)
   10238        1360 :         return nullptr;
   10239             : 
   10240             :     /* -------------------------------------------------------------------- */
   10241             :     /*      Fetch matching EXTENSION if there is one.                       */
   10242             :     /* -------------------------------------------------------------------- */
   10243       48446 :     for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
   10244             :     {
   10245       41914 :         const OGR_SRSNode *poChild = poNode->GetChild(i);
   10246             : 
   10247       41938 :         if (EQUAL(poChild->GetValue(), "EXTENSION") &&
   10248          24 :             poChild->GetChildCount() >= 2)
   10249             :         {
   10250          24 :             if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
   10251          22 :                 return poChild->GetChild(1)->GetValue();
   10252             :         }
   10253             :     }
   10254             : 
   10255        6532 :     return pszDefault;
   10256             : }
   10257             : 
   10258             : /************************************************************************/
   10259             : /*                            SetExtension()                            */
   10260             : /************************************************************************/
   10261             : /**
   10262             :  * \brief Set extension value.
   10263             :  *
   10264             :  * Set the value of the named EXTENSION item for the identified
   10265             :  * target node.
   10266             :  *
   10267             :  * @param pszTargetKey the name or path to the parent node of the EXTENSION.
   10268             :  * @param pszName the name of the extension being fetched.
   10269             :  * @param pszValue the value to set
   10270             :  *
   10271             :  * @return OGRERR_NONE on success
   10272             :  */
   10273             : 
   10274          18 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
   10275             :                                          const char *pszName,
   10276             :                                          const char *pszValue)
   10277             : 
   10278             : {
   10279             :     /* -------------------------------------------------------------------- */
   10280             :     /*      Find the target node.                                           */
   10281             :     /* -------------------------------------------------------------------- */
   10282          18 :     OGR_SRSNode *poNode = nullptr;
   10283             : 
   10284          18 :     if (pszTargetKey == nullptr)
   10285           0 :         poNode = GetRoot();
   10286             :     else
   10287          18 :         poNode = GetAttrNode(pszTargetKey);
   10288             : 
   10289          18 :     if (poNode == nullptr)
   10290           0 :         return OGRERR_FAILURE;
   10291             : 
   10292             :     /* -------------------------------------------------------------------- */
   10293             :     /*      Fetch matching EXTENSION if there is one.                       */
   10294             :     /* -------------------------------------------------------------------- */
   10295         139 :     for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
   10296             :     {
   10297         126 :         OGR_SRSNode *poChild = poNode->GetChild(i);
   10298             : 
   10299         131 :         if (EQUAL(poChild->GetValue(), "EXTENSION") &&
   10300           5 :             poChild->GetChildCount() >= 2)
   10301             :         {
   10302           5 :             if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
   10303             :             {
   10304           5 :                 poChild->GetChild(1)->SetValue(pszValue);
   10305           5 :                 return OGRERR_NONE;
   10306             :             }
   10307             :         }
   10308             :     }
   10309             : 
   10310             :     /* -------------------------------------------------------------------- */
   10311             :     /*      Create a new EXTENSION node.                                    */
   10312             :     /* -------------------------------------------------------------------- */
   10313          13 :     OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
   10314          13 :     poAuthNode->AddChild(new OGR_SRSNode(pszName));
   10315          13 :     poAuthNode->AddChild(new OGR_SRSNode(pszValue));
   10316             : 
   10317          13 :     poNode->AddChild(poAuthNode);
   10318             : 
   10319          13 :     return OGRERR_NONE;
   10320             : }
   10321             : 
   10322             : /************************************************************************/
   10323             : /*                             OSRCleanup()                             */
   10324             : /************************************************************************/
   10325             : 
   10326             : static void CleanupSRSWGS84Mutex();
   10327             : 
   10328             : /**
   10329             :  * \brief Cleanup cached SRS related memory.
   10330             :  *
   10331             :  * This function will attempt to cleanup any cache spatial reference
   10332             :  * related information, such as cached tables of coordinate systems.
   10333             :  *
   10334             :  * This function should not be called concurrently with any other GDAL/OGR
   10335             :  * function. It is meant at being called once before process termination
   10336             :  * (typically from the main thread). CPLCleanupTLS() might be used to clean
   10337             :  * thread-specific resources before thread termination.
   10338             :  */
   10339         857 : void OSRCleanup(void)
   10340             : 
   10341             : {
   10342         857 :     OGRCTDumpStatistics();
   10343         857 :     CSVDeaccess(nullptr);
   10344         857 :     CleanupSRSWGS84Mutex();
   10345         857 :     OSRCTCleanCache();
   10346         857 :     OSRCleanupTLSContext();
   10347         857 : }
   10348             : 
   10349             : /************************************************************************/
   10350             : /*                              GetAxesCount()                          */
   10351             : /************************************************************************/
   10352             : 
   10353             : /**
   10354             :  * \brief Return the number of axis of the coordinate system of the CRS.
   10355             :  *
   10356             :  * @since GDAL 3.0
   10357             :  */
   10358       27920 : int OGRSpatialReference::GetAxesCount() const
   10359             : {
   10360       27920 :     int axisCount = 0;
   10361       27920 :     d->refreshProjObj();
   10362       27920 :     if (d->m_pj_crs == nullptr)
   10363             :     {
   10364           0 :         return 0;
   10365             :     }
   10366       27920 :     d->demoteFromBoundCRS();
   10367       27920 :     auto ctxt = d->getPROJContext();
   10368       27920 :     if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
   10369             :     {
   10370          29 :         for (int i = 0;; i++)
   10371             :         {
   10372          87 :             auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
   10373          87 :             if (!subCRS)
   10374          29 :                 break;
   10375          58 :             if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
   10376             :             {
   10377          17 :                 auto baseCRS = proj_get_source_crs(ctxt, subCRS);
   10378          17 :                 if (baseCRS)
   10379             :                 {
   10380          17 :                     proj_destroy(subCRS);
   10381          17 :                     subCRS = baseCRS;
   10382             :                 }
   10383             :             }
   10384          58 :             auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
   10385          58 :             if (cs)
   10386             :             {
   10387          58 :                 axisCount += proj_cs_get_axis_count(ctxt, cs);
   10388          58 :                 proj_destroy(cs);
   10389             :             }
   10390          58 :             proj_destroy(subCRS);
   10391          58 :         }
   10392             :     }
   10393             :     else
   10394             :     {
   10395       27891 :         auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
   10396       27891 :         if (cs)
   10397             :         {
   10398       27891 :             axisCount = proj_cs_get_axis_count(ctxt, cs);
   10399       27891 :             proj_destroy(cs);
   10400             :         }
   10401             :     }
   10402       27920 :     d->undoDemoteFromBoundCRS();
   10403       27920 :     return axisCount;
   10404             : }
   10405             : 
   10406             : /************************************************************************/
   10407             : /*                           OSRGetAxesCount()                          */
   10408             : /************************************************************************/
   10409             : 
   10410             : /**
   10411             :  * \brief Return the number of axis of the coordinate system of the CRS.
   10412             :  *
   10413             :  * This method is the equivalent of the C++ method
   10414             :  * OGRSpatialReference::GetAxesCount()
   10415             :  *
   10416             :  * @since GDAL 3.1
   10417             :  */
   10418           6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
   10419             : 
   10420             : {
   10421           6 :     VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
   10422             : 
   10423           6 :     return ToPointer(hSRS)->GetAxesCount();
   10424             : }
   10425             : 
   10426             : /************************************************************************/
   10427             : /*                              GetAxis()                               */
   10428             : /************************************************************************/
   10429             : 
   10430             : /**
   10431             :  * \brief Fetch the orientation of one axis.
   10432             :  *
   10433             :  * Fetches the request axis (iAxis - zero based) from the
   10434             :  * indicated portion of the coordinate system (pszTargetKey) which
   10435             :  * should be either "GEOGCS" or "PROJCS".
   10436             :  *
   10437             :  * No CPLError is issued on routine failures (such as not finding the AXIS).
   10438             :  *
   10439             :  * This method is equivalent to the C function OSRGetAxis().
   10440             :  *
   10441             :  * @param pszTargetKey the coordinate system part to query ("PROJCS" or
   10442             :  * "GEOGCS").
   10443             :  * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
   10444             :  * @param peOrientation location into which to place the fetch orientation, may
   10445             :  * be NULL.
   10446             :  * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
   10447             :  * factor. May be NULL. Only set if pszTargetKey == NULL
   10448             :  *
   10449             :  * @return the name of the axis or NULL on failure.
   10450             :  */
   10451             : 
   10452        5121 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
   10453             :                                          OGRAxisOrientation *peOrientation,
   10454             :                                          double *pdfConvUnit) const
   10455             : 
   10456             : {
   10457        5121 :     if (peOrientation != nullptr)
   10458        5028 :         *peOrientation = OAO_Other;
   10459        5121 :     if (pdfConvUnit != nullptr)
   10460          85 :         *pdfConvUnit = 0;
   10461             : 
   10462        5121 :     d->refreshProjObj();
   10463        5121 :     if (d->m_pj_crs == nullptr)
   10464             :     {
   10465           1 :         return nullptr;
   10466             :     }
   10467             : 
   10468        5120 :     pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
   10469        5120 :     if (pszTargetKey == nullptr && iAxis <= 2)
   10470             :     {
   10471        5120 :         auto ctxt = d->getPROJContext();
   10472             : 
   10473        5120 :         int iAxisModified = iAxis;
   10474             : 
   10475        5120 :         d->demoteFromBoundCRS();
   10476             : 
   10477        5120 :         PJ *cs = nullptr;
   10478        5120 :         if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
   10479             :         {
   10480         134 :             auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
   10481         134 :             if (horizCRS)
   10482             :             {
   10483         134 :                 if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
   10484             :                 {
   10485           6 :                     auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
   10486           6 :                     if (baseCRS)
   10487             :                     {
   10488           6 :                         proj_destroy(horizCRS);
   10489           6 :                         horizCRS = baseCRS;
   10490             :                     }
   10491             :                 }
   10492         134 :                 cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
   10493         134 :                 proj_destroy(horizCRS);
   10494         134 :                 if (cs)
   10495             :                 {
   10496         134 :                     if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
   10497             :                     {
   10498          44 :                         iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
   10499          44 :                         proj_destroy(cs);
   10500          44 :                         cs = nullptr;
   10501             :                     }
   10502             :                 }
   10503             :             }
   10504             : 
   10505         134 :             if (cs == nullptr)
   10506             :             {
   10507          44 :                 auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
   10508          44 :                 if (vertCRS)
   10509             :                 {
   10510          44 :                     if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
   10511             :                     {
   10512          30 :                         auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
   10513          30 :                         if (baseCRS)
   10514             :                         {
   10515          30 :                             proj_destroy(vertCRS);
   10516          30 :                             vertCRS = baseCRS;
   10517             :                         }
   10518             :                     }
   10519             : 
   10520          44 :                     cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
   10521          44 :                     proj_destroy(vertCRS);
   10522             :                 }
   10523             :             }
   10524             :         }
   10525             :         else
   10526             :         {
   10527        4986 :             cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
   10528             :         }
   10529             : 
   10530        5120 :         if (cs)
   10531             :         {
   10532        5120 :             const char *pszName = nullptr;
   10533        5120 :             const char *pszOrientation = nullptr;
   10534        5120 :             double dfConvFactor = 0.0;
   10535        5120 :             proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
   10536             :                                   &pszOrientation, &dfConvFactor, nullptr,
   10537             :                                   nullptr, nullptr);
   10538             : 
   10539        5120 :             if (pdfConvUnit != nullptr)
   10540             :             {
   10541          85 :                 *pdfConvUnit = dfConvFactor;
   10542             :             }
   10543             : 
   10544        5120 :             if (pszName && pszOrientation)
   10545             :             {
   10546        5120 :                 d->m_osAxisName[iAxis] = pszName;
   10547        5120 :                 if (peOrientation)
   10548             :                 {
   10549        5027 :                     if (EQUAL(pszOrientation, "NORTH"))
   10550        3155 :                         *peOrientation = OAO_North;
   10551        1872 :                     else if (EQUAL(pszOrientation, "EAST"))
   10552        1834 :                         *peOrientation = OAO_East;
   10553          38 :                     else if (EQUAL(pszOrientation, "SOUTH"))
   10554          31 :                         *peOrientation = OAO_South;
   10555           7 :                     else if (EQUAL(pszOrientation, "WEST"))
   10556           0 :                         *peOrientation = OAO_West;
   10557           7 :                     else if (EQUAL(pszOrientation, "UP"))
   10558           1 :                         *peOrientation = OAO_Up;
   10559           6 :                     else if (EQUAL(pszOrientation, "DOWN"))
   10560           0 :                         *peOrientation = OAO_Down;
   10561             :                 }
   10562        5120 :                 proj_destroy(cs);
   10563        5120 :                 d->undoDemoteFromBoundCRS();
   10564        5120 :                 return d->m_osAxisName[iAxis].c_str();
   10565             :             }
   10566           0 :             proj_destroy(cs);
   10567             :         }
   10568           0 :         d->undoDemoteFromBoundCRS();
   10569             :     }
   10570             : 
   10571             :     /* -------------------------------------------------------------------- */
   10572             :     /*      Find the target node.                                           */
   10573             :     /* -------------------------------------------------------------------- */
   10574           0 :     const OGR_SRSNode *poNode = nullptr;
   10575             : 
   10576           0 :     if (pszTargetKey == nullptr)
   10577           0 :         poNode = GetRoot();
   10578             :     else
   10579           0 :         poNode = GetAttrNode(pszTargetKey);
   10580             : 
   10581           0 :     if (poNode == nullptr)
   10582           0 :         return nullptr;
   10583             : 
   10584             :     /* -------------------------------------------------------------------- */
   10585             :     /*      Find desired child AXIS.                                        */
   10586             :     /* -------------------------------------------------------------------- */
   10587           0 :     const OGR_SRSNode *poAxis = nullptr;
   10588           0 :     const int nChildCount = poNode->GetChildCount();
   10589             : 
   10590           0 :     for (int iChild = 0; iChild < nChildCount; iChild++)
   10591             :     {
   10592           0 :         const OGR_SRSNode *poChild = poNode->GetChild(iChild);
   10593             : 
   10594           0 :         if (!EQUAL(poChild->GetValue(), "AXIS"))
   10595           0 :             continue;
   10596             : 
   10597           0 :         if (iAxis == 0)
   10598             :         {
   10599           0 :             poAxis = poChild;
   10600           0 :             break;
   10601             :         }
   10602           0 :         iAxis--;
   10603             :     }
   10604             : 
   10605           0 :     if (poAxis == nullptr)
   10606           0 :         return nullptr;
   10607             : 
   10608           0 :     if (poAxis->GetChildCount() < 2)
   10609           0 :         return nullptr;
   10610             : 
   10611             :     /* -------------------------------------------------------------------- */
   10612             :     /*      Extract name and orientation if possible.                       */
   10613             :     /* -------------------------------------------------------------------- */
   10614           0 :     if (peOrientation != nullptr)
   10615             :     {
   10616           0 :         const char *pszOrientation = poAxis->GetChild(1)->GetValue();
   10617             : 
   10618           0 :         if (EQUAL(pszOrientation, "NORTH"))
   10619           0 :             *peOrientation = OAO_North;
   10620           0 :         else if (EQUAL(pszOrientation, "EAST"))
   10621           0 :             *peOrientation = OAO_East;
   10622           0 :         else if (EQUAL(pszOrientation, "SOUTH"))
   10623           0 :             *peOrientation = OAO_South;
   10624           0 :         else if (EQUAL(pszOrientation, "WEST"))
   10625           0 :             *peOrientation = OAO_West;
   10626           0 :         else if (EQUAL(pszOrientation, "UP"))
   10627           0 :             *peOrientation = OAO_Up;
   10628           0 :         else if (EQUAL(pszOrientation, "DOWN"))
   10629           0 :             *peOrientation = OAO_Down;
   10630           0 :         else if (EQUAL(pszOrientation, "OTHER"))
   10631           0 :             *peOrientation = OAO_Other;
   10632             :         else
   10633             :         {
   10634           0 :             CPLDebug("OSR", "Unrecognized orientation value '%s'.",
   10635             :                      pszOrientation);
   10636             :         }
   10637             :     }
   10638             : 
   10639           0 :     return poAxis->GetChild(0)->GetValue();
   10640             : }
   10641             : 
   10642             : /************************************************************************/
   10643             : /*                             OSRGetAxis()                             */
   10644             : /************************************************************************/
   10645             : 
   10646             : /**
   10647             :  * \brief Fetch the orientation of one axis.
   10648             :  *
   10649             :  * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
   10650             :  */
   10651          13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
   10652             :                        int iAxis, OGRAxisOrientation *peOrientation)
   10653             : 
   10654             : {
   10655          13 :     VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
   10656             : 
   10657          13 :     return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
   10658             : }
   10659             : 
   10660             : /************************************************************************/
   10661             : /*                         OSRAxisEnumToName()                          */
   10662             : /************************************************************************/
   10663             : 
   10664             : /**
   10665             :  * \brief Return the string representation for the OGRAxisOrientation
   10666             :  * enumeration.
   10667             :  *
   10668             :  * For example "NORTH" for OAO_North.
   10669             :  *
   10670             :  * @return an internal string
   10671             :  */
   10672         276 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
   10673             : 
   10674             : {
   10675         276 :     if (eOrientation == OAO_North)
   10676         138 :         return "NORTH";
   10677         138 :     if (eOrientation == OAO_East)
   10678         138 :         return "EAST";
   10679           0 :     if (eOrientation == OAO_South)
   10680           0 :         return "SOUTH";
   10681           0 :     if (eOrientation == OAO_West)
   10682           0 :         return "WEST";
   10683           0 :     if (eOrientation == OAO_Up)
   10684           0 :         return "UP";
   10685           0 :     if (eOrientation == OAO_Down)
   10686           0 :         return "DOWN";
   10687           0 :     if (eOrientation == OAO_Other)
   10688           0 :         return "OTHER";
   10689             : 
   10690           0 :     return "UNKNOWN";
   10691             : }
   10692             : 
   10693             : /************************************************************************/
   10694             : /*                              SetAxes()                               */
   10695             : /************************************************************************/
   10696             : 
   10697             : /**
   10698             :  * \brief Set the axes for a coordinate system.
   10699             :  *
   10700             :  * Set the names, and orientations of the axes for either a projected
   10701             :  * (PROJCS) or geographic (GEOGCS) coordinate system.
   10702             :  *
   10703             :  * This method is equivalent to the C function OSRSetAxes().
   10704             :  *
   10705             :  * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
   10706             :  * @param pszXAxisName name of first axis, normally "Long" or "Easting".
   10707             :  * @param eXAxisOrientation normally OAO_East.
   10708             :  * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
   10709             :  * @param eYAxisOrientation normally OAO_North.
   10710             :  *
   10711             :  * @return OGRERR_NONE on success or an error code.
   10712             :  */
   10713             : 
   10714         138 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
   10715             :                                     const char *pszXAxisName,
   10716             :                                     OGRAxisOrientation eXAxisOrientation,
   10717             :                                     const char *pszYAxisName,
   10718             :                                     OGRAxisOrientation eYAxisOrientation)
   10719             : 
   10720             : {
   10721             :     /* -------------------------------------------------------------------- */
   10722             :     /*      Find the target node.                                           */
   10723             :     /* -------------------------------------------------------------------- */
   10724         138 :     OGR_SRSNode *poNode = nullptr;
   10725             : 
   10726         138 :     if (pszTargetKey == nullptr)
   10727         138 :         poNode = GetRoot();
   10728             :     else
   10729           0 :         poNode = GetAttrNode(pszTargetKey);
   10730             : 
   10731         138 :     if (poNode == nullptr)
   10732           0 :         return OGRERR_FAILURE;
   10733             : 
   10734             :     /* -------------------------------------------------------------------- */
   10735             :     /*      Strip any existing AXIS children.                               */
   10736             :     /* -------------------------------------------------------------------- */
   10737         414 :     while (poNode->FindChild("AXIS") >= 0)
   10738         276 :         poNode->DestroyChild(poNode->FindChild("AXIS"));
   10739             : 
   10740             :     /* -------------------------------------------------------------------- */
   10741             :     /*      Insert desired axes                                             */
   10742             :     /* -------------------------------------------------------------------- */
   10743         138 :     OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
   10744             : 
   10745         138 :     poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
   10746         138 :     poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
   10747             : 
   10748         138 :     poNode->AddChild(poAxis);
   10749             : 
   10750         138 :     poAxis = new OGR_SRSNode("AXIS");
   10751             : 
   10752         138 :     poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
   10753         138 :     poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
   10754             : 
   10755         138 :     poNode->AddChild(poAxis);
   10756             : 
   10757         138 :     return OGRERR_NONE;
   10758             : }
   10759             : 
   10760             : /************************************************************************/
   10761             : /*                             OSRSetAxes()                             */
   10762             : /************************************************************************/
   10763             : /**
   10764             :  * \brief Set the axes for a coordinate system.
   10765             :  *
   10766             :  * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
   10767             :  */
   10768           0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
   10769             :                   const char *pszXAxisName,
   10770             :                   OGRAxisOrientation eXAxisOrientation,
   10771             :                   const char *pszYAxisName,
   10772             :                   OGRAxisOrientation eYAxisOrientation)
   10773             : {
   10774           0 :     VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
   10775             : 
   10776           0 :     return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
   10777             :                                     eXAxisOrientation, pszYAxisName,
   10778           0 :                                     eYAxisOrientation);
   10779             : }
   10780             : 
   10781             : #ifdef HAVE_MITAB
   10782             : char CPL_DLL *MITABSpatialRef2CoordSys(const OGRSpatialReference *);
   10783             : OGRSpatialReference CPL_DLL *MITABCoordSys2SpatialRef(const char *);
   10784             : #endif
   10785             : 
   10786             : /************************************************************************/
   10787             : /*                       OSRExportToMICoordSys()                        */
   10788             : /************************************************************************/
   10789             : /**
   10790             :  * \brief Export coordinate system in Mapinfo style CoordSys format.
   10791             :  *
   10792             :  * This method is the equivalent of the C++ method
   10793             :  * OGRSpatialReference::exportToMICoordSys
   10794             :  */
   10795           5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
   10796             : 
   10797             : {
   10798           5 :     VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
   10799             : 
   10800           5 :     *ppszReturn = nullptr;
   10801             : 
   10802           5 :     return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
   10803             : }
   10804             : 
   10805             : /************************************************************************/
   10806             : /*                         exportToMICoordSys()                         */
   10807             : /************************************************************************/
   10808             : 
   10809             : /**
   10810             :  * \brief Export coordinate system in Mapinfo style CoordSys format.
   10811             :  *
   10812             :  * Note that the returned WKT string should be freed with
   10813             :  * CPLFree() when no longer needed.  It is the responsibility of the caller.
   10814             :  *
   10815             :  * This method is the same as the C function OSRExportToMICoordSys().
   10816             :  *
   10817             :  * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
   10818             :  * definition will be assigned.
   10819             :  *
   10820             :  * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
   10821             :  * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
   10822             :  */
   10823             : 
   10824           7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
   10825             : 
   10826             : {
   10827             : #ifdef HAVE_MITAB
   10828           7 :     *ppszResult = MITABSpatialRef2CoordSys(this);
   10829           7 :     if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
   10830           7 :         return OGRERR_NONE;
   10831             : 
   10832           0 :     return OGRERR_FAILURE;
   10833             : #else
   10834             :     CPLError(CE_Failure, CPLE_NotSupported,
   10835             :              "MITAB not available, CoordSys support disabled.");
   10836             : 
   10837             :     return OGRERR_UNSUPPORTED_OPERATION;
   10838             : #endif
   10839             : }
   10840             : 
   10841             : /************************************************************************/
   10842             : /*                       OSRImportFromMICoordSys()                      */
   10843             : /************************************************************************/
   10844             : /**
   10845             :  * \brief Import Mapinfo style CoordSys definition.
   10846             :  *
   10847             :  * This method is the equivalent of the C++ method
   10848             :  * OGRSpatialReference::importFromMICoordSys
   10849             :  */
   10850             : 
   10851           3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
   10852             :                                const char *pszCoordSys)
   10853             : 
   10854             : {
   10855           3 :     VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
   10856             : 
   10857           3 :     return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
   10858             : }
   10859             : 
   10860             : /************************************************************************/
   10861             : /*                        importFromMICoordSys()                        */
   10862             : /************************************************************************/
   10863             : 
   10864             : /**
   10865             :  * \brief Import Mapinfo style CoordSys definition.
   10866             :  *
   10867             :  * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
   10868             :  * definition string.
   10869             :  *
   10870             :  * This method is the equivalent of the C function OSRImportFromMICoordSys().
   10871             :  *
   10872             :  * @param pszCoordSys Mapinfo style CoordSys definition string.
   10873             :  *
   10874             :  * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
   10875             :  * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
   10876             :  */
   10877             : 
   10878          17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
   10879             : 
   10880             : {
   10881             : #ifdef HAVE_MITAB
   10882          17 :     OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
   10883             : 
   10884          17 :     if (poResult == nullptr)
   10885           0 :         return OGRERR_FAILURE;
   10886             : 
   10887          17 :     *this = *poResult;
   10888          17 :     delete poResult;
   10889             : 
   10890          17 :     return OGRERR_NONE;
   10891             : #else
   10892             :     CPLError(CE_Failure, CPLE_NotSupported,
   10893             :              "MITAB not available, CoordSys support disabled.");
   10894             : 
   10895             :     return OGRERR_UNSUPPORTED_OPERATION;
   10896             : #endif
   10897             : }
   10898             : 
   10899             : /************************************************************************/
   10900             : /*                        OSRCalcInvFlattening()                        */
   10901             : /************************************************************************/
   10902             : 
   10903             : /**
   10904             :  * \brief Compute inverse flattening from semi-major and semi-minor axis
   10905             :  *
   10906             :  * @param dfSemiMajor Semi-major axis length.
   10907             :  * @param dfSemiMinor Semi-minor axis length.
   10908             :  *
   10909             :  * @return inverse flattening, or 0 if both axis are equal.
   10910             :  * @since GDAL 2.0
   10911             :  */
   10912             : 
   10913        5311 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
   10914             : {
   10915        5311 :     if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
   10916          26 :         return 0;
   10917        5285 :     if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
   10918             :     {
   10919           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   10920             :                  "OSRCalcInvFlattening(): Wrong input values");
   10921           0 :         return 0;
   10922             :     }
   10923             : 
   10924        5285 :     return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
   10925             : }
   10926             : 
   10927             : /************************************************************************/
   10928             : /*                        OSRCalcInvFlattening()                        */
   10929             : /************************************************************************/
   10930             : 
   10931             : /**
   10932             :  * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
   10933             :  *
   10934             :  * @param dfSemiMajor Semi-major axis length.
   10935             :  * @param dfInvFlattening Inverse flattening or 0 for sphere.
   10936             :  *
   10937             :  * @return semi-minor axis
   10938             :  * @since GDAL 2.0
   10939             :  */
   10940             : 
   10941         634 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
   10942             :                                          double dfInvFlattening)
   10943             : {
   10944         634 :     if (fabs(dfInvFlattening) < 0.000000000001)
   10945         101 :         return dfSemiMajor;
   10946         533 :     if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
   10947             :     {
   10948           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   10949             :                  "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
   10950           0 :         return dfSemiMajor;
   10951             :     }
   10952             : 
   10953         533 :     return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
   10954             : }
   10955             : 
   10956             : /************************************************************************/
   10957             : /*                        GetWGS84SRS()                                 */
   10958             : /************************************************************************/
   10959             : 
   10960             : static OGRSpatialReference *poSRSWGS84 = nullptr;
   10961             : static CPLMutex *hMutex = nullptr;
   10962             : 
   10963             : /**
   10964             :  * \brief Returns an instance of a SRS object with WGS84 WKT.
   10965             :  *
   10966             :  * Note: the instance will have
   10967             :  * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
   10968             :  *
   10969             :  * The reference counter of the returned object is not increased by this
   10970             :  * operation.
   10971             :  *
   10972             :  * @return instance.
   10973             :  * @since GDAL 2.0
   10974             :  */
   10975             : 
   10976         816 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
   10977             : {
   10978         816 :     CPLMutexHolderD(&hMutex);
   10979         816 :     if (poSRSWGS84 == nullptr)
   10980             :     {
   10981           4 :         poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
   10982           4 :         poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   10983             :     }
   10984        1632 :     return poSRSWGS84;
   10985             : }
   10986             : 
   10987             : /************************************************************************/
   10988             : /*                        CleanupSRSWGS84Mutex()                       */
   10989             : /************************************************************************/
   10990             : 
   10991         857 : static void CleanupSRSWGS84Mutex()
   10992             : {
   10993         857 :     if (hMutex != nullptr)
   10994             :     {
   10995           2 :         poSRSWGS84->Release();
   10996           2 :         poSRSWGS84 = nullptr;
   10997           2 :         CPLDestroyMutex(hMutex);
   10998           2 :         hMutex = nullptr;
   10999             :     }
   11000         857 : }
   11001             : 
   11002             : /************************************************************************/
   11003             : /*                         OSRImportFromProj4()                         */
   11004             : /************************************************************************/
   11005             : /**
   11006             :  * \brief Import PROJ coordinate string.
   11007             :  *
   11008             :  * This function is the same as OGRSpatialReference::importFromProj4().
   11009             :  */
   11010         186 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
   11011             : 
   11012             : {
   11013         186 :     VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
   11014             : 
   11015         186 :     return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
   11016             : }
   11017             : 
   11018             : /************************************************************************/
   11019             : /*                          importFromProj4()                           */
   11020             : /************************************************************************/
   11021             : 
   11022             : /**
   11023             :  * \brief Import PROJ coordinate string.
   11024             :  *
   11025             :  * The OGRSpatialReference is initialized from the passed PROJs style
   11026             :  * coordinate system string.
   11027             :  *
   11028             :  * Example:
   11029             :  *   pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
   11030             :  *
   11031             :  * It is also possible to import "+init=epsg:n" style definitions. Those are
   11032             :  * a legacy syntax that should be avoided in the future. In particular they will
   11033             :  * result in CRS objects whose axis order might not correspond to the official
   11034             :  * EPSG axis order.
   11035             :  *
   11036             :  * This method is the equivalent of the C function OSRImportFromProj4().
   11037             :  *
   11038             :  * @param pszProj4 the PROJ style string.
   11039             :  *
   11040             :  * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
   11041             :  */
   11042             : 
   11043         542 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
   11044             : 
   11045             : {
   11046         542 :     if (strlen(pszProj4) >= 10000)
   11047             :     {
   11048           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
   11049           1 :         return OGRERR_CORRUPT_DATA;
   11050             :     }
   11051             : 
   11052             :     /* -------------------------------------------------------------------- */
   11053             :     /*      Clear any existing definition.                                  */
   11054             :     /* -------------------------------------------------------------------- */
   11055         541 :     Clear();
   11056             : 
   11057         541 :     CPLString osProj4(pszProj4);
   11058         541 :     if (osProj4.find("type=crs") == std::string::npos)
   11059             :     {
   11060         532 :         osProj4 += " +type=crs";
   11061             :     }
   11062             : 
   11063         543 :     if (osProj4.find("+init=epsg:") != std::string::npos &&
   11064           2 :         getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
   11065             :     {
   11066             :         static bool bHasWarned = false;
   11067           2 :         if (!bHasWarned)
   11068             :         {
   11069           1 :             CPLError(CE_Warning, CPLE_AppDefined,
   11070             :                      "+init=epsg:XXXX syntax is deprecated. It might return "
   11071             :                      "a CRS with a non-EPSG compliant axis order.");
   11072           1 :             bHasWarned = true;
   11073             :         }
   11074             :     }
   11075         541 :     proj_context_use_proj4_init_rules(d->getPROJContext(), true);
   11076         541 :     d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
   11077         541 :     proj_context_use_proj4_init_rules(d->getPROJContext(), false);
   11078         541 :     return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
   11079             : }
   11080             : 
   11081             : /************************************************************************/
   11082             : /*                          OSRExportToProj4()                          */
   11083             : /************************************************************************/
   11084             : /**
   11085             :  * \brief Export coordinate system in PROJ.4 legacy format.
   11086             :  *
   11087             :  * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
   11088             :  * PROJ &gt;= 6 is significantly different from earlier versions. In particular
   11089             :  * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
   11090             :  * will be missing most of the time. PROJ strings to encode CRS should be
   11091             :  * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
   11092             :  * is the recommended way.
   11093             :  *
   11094             :  * This function is the same as OGRSpatialReference::exportToProj4().
   11095             :  */
   11096         442 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
   11097             :                                     char **ppszReturn)
   11098             : 
   11099             : {
   11100         442 :     VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
   11101             : 
   11102         442 :     *ppszReturn = nullptr;
   11103             : 
   11104         442 :     return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
   11105             : }
   11106             : 
   11107             : /************************************************************************/
   11108             : /*                           exportToProj4()                            */
   11109             : /************************************************************************/
   11110             : 
   11111             : /**
   11112             :  * \brief Export coordinate system in PROJ.4 legacy format.
   11113             :  *
   11114             :  * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
   11115             :  * PROJ &gt;= 6 is significantly different from earlier versions. In particular
   11116             :  * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
   11117             :  * will be missing most of the time. PROJ strings to encode CRS should be
   11118             :  * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
   11119             :  * representation is the recommended way.
   11120             :  *
   11121             :  * Converts the loaded coordinate reference system into PROJ format
   11122             :  * to the extent possible.  The string returned in ppszProj4 should be
   11123             :  * deallocated by the caller with CPLFree() when no longer needed.
   11124             :  *
   11125             :  * LOCAL_CS coordinate systems are not translatable.  An empty string
   11126             :  * will be returned along with OGRERR_NONE.
   11127             :  *
   11128             :  * Special processing for Transverse Mercator:
   11129             :  * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
   11130             :  * set to YES, the PROJ definition built from the SRS will use the +approx flag
   11131             :  * for the tmerc and utm projection methods, rather than the more accurate
   11132             :  * method.
   11133             :  *
   11134             :  * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
   11135             :  * if there's none attached yet to the SRS and if the SRS has a EPSG code.
   11136             :  * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
   11137             :  * added. This automatic addition may be disabled by setting the
   11138             :  * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
   11139             :  *
   11140             :  * This method is the equivalent of the C function OSRExportToProj4().
   11141             :  *
   11142             :  * @param ppszProj4 pointer to which dynamically allocated PROJ definition
   11143             :  * will be assigned.
   11144             :  *
   11145             :  * @return OGRERR_NONE on success or an error code on failure.
   11146             :  */
   11147             : 
   11148        1386 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
   11149             : 
   11150             : {
   11151             :     // In the past calling this method was thread-safe, even if we never
   11152             :     // guaranteed it. Now proj_as_proj_string() will cache the result
   11153             :     // internally, so this is no longer thread-safe.
   11154        2772 :     std::lock_guard<std::mutex> oLock(d->m_mutex);
   11155             : 
   11156        1386 :     d->refreshProjObj();
   11157        1386 :     if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
   11158             :     {
   11159          24 :         *ppszProj4 = CPLStrdup("");
   11160          24 :         return OGRERR_FAILURE;
   11161             :     }
   11162             : 
   11163             :     // OSR_USE_ETMERC is here just for legacy
   11164        1362 :     bool bForceApproxTMerc = false;
   11165        1362 :     const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
   11166        1362 :     if (pszUseETMERC && pszUseETMERC[0])
   11167             :     {
   11168             :         static bool bHasWarned = false;
   11169           0 :         if (!bHasWarned)
   11170             :         {
   11171           0 :             CPLError(CE_Warning, CPLE_AppDefined,
   11172             :                      "OSR_USE_ETMERC is a legacy configuration option, which "
   11173             :                      "now has only effect when set to NO (YES is the default). "
   11174             :                      "Use OSR_USE_APPROX_TMERC=YES instead");
   11175           0 :             bHasWarned = true;
   11176             :         }
   11177           0 :         bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
   11178             :     }
   11179             :     else
   11180             :     {
   11181             :         const char *pszUseApproxTMERC =
   11182        1362 :             CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
   11183        1362 :         if (pszUseApproxTMERC && pszUseApproxTMERC[0])
   11184             :         {
   11185           2 :             bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
   11186             :         }
   11187             :     }
   11188        1362 :     const char *options[] = {
   11189        1362 :         bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
   11190             : 
   11191        1362 :     const char *projString = proj_as_proj_string(
   11192        1362 :         d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
   11193             : 
   11194        1362 :     PJ *boundCRS = nullptr;
   11195        2720 :     if (projString &&
   11196        1358 :         (strstr(projString, "+datum=") == nullptr ||
   11197        2730 :          d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
   11198         521 :         CPLTestBool(
   11199             :             CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
   11200             :     {
   11201         521 :         boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
   11202         521 :             d->getPROJContext(), d->m_pj_crs, true,
   11203         521 :             strstr(projString, "+datum=") == nullptr);
   11204         521 :         if (boundCRS)
   11205             :         {
   11206         209 :             projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
   11207             :                                              PJ_PROJ_4, options);
   11208             :         }
   11209             :     }
   11210             : 
   11211        1362 :     if (projString == nullptr)
   11212             :     {
   11213           4 :         *ppszProj4 = CPLStrdup("");
   11214           4 :         proj_destroy(boundCRS);
   11215           4 :         return OGRERR_FAILURE;
   11216             :     }
   11217        1358 :     *ppszProj4 = CPLStrdup(projString);
   11218        1358 :     proj_destroy(boundCRS);
   11219        1358 :     char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
   11220        1358 :     if (pszTypeCrs)
   11221        1358 :         *pszTypeCrs = '\0';
   11222        1358 :     return OGRERR_NONE;
   11223             : }
   11224             : 
   11225             : /************************************************************************/
   11226             : /*                            morphToESRI()                             */
   11227             : /************************************************************************/
   11228             : /**
   11229             :  * \brief Convert in place to ESRI WKT format.
   11230             :  *
   11231             :  * The value nodes of this coordinate system are modified in various manners
   11232             :  * more closely map onto the ESRI concept of WKT format.  This includes
   11233             :  * renaming a variety of projections and arguments, and stripping out
   11234             :  * nodes note recognised by ESRI (like AUTHORITY and AXIS).
   11235             :  *
   11236             :  * \note Since GDAL 3.0, this function has only user-visible effects at
   11237             :  * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
   11238             :  * const char* const char*) const with options having FORMAT=WKT1_ESRI.
   11239             :  *
   11240             :  * This does the same as the C function OSRMorphToESRI().
   11241             :  *
   11242             :  * @return OGRERR_NONE unless something goes badly wrong.
   11243             :  * @deprecated
   11244             :  */
   11245             : 
   11246         268 : OGRErr OGRSpatialReference::morphToESRI()
   11247             : 
   11248             : {
   11249         268 :     d->refreshProjObj();
   11250         268 :     d->setMorphToESRI(true);
   11251             : 
   11252         268 :     return OGRERR_NONE;
   11253             : }
   11254             : 
   11255             : /************************************************************************/
   11256             : /*                           OSRMorphToESRI()                           */
   11257             : /************************************************************************/
   11258             : 
   11259             : /**
   11260             :  * \brief Convert in place to ESRI WKT format.
   11261             :  *
   11262             :  * This function is the same as the C++ method
   11263             :  * OGRSpatialReference::morphToESRI().
   11264             :  */
   11265         103 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
   11266             : 
   11267             : {
   11268         103 :     VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
   11269             : 
   11270         103 :     return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
   11271             : }
   11272             : 
   11273             : /************************************************************************/
   11274             : /*                           morphFromESRI()                            */
   11275             : /************************************************************************/
   11276             : 
   11277             : /**
   11278             :  * \brief Convert in place from ESRI WKT format.
   11279             :  *
   11280             :  * The value notes of this coordinate system are modified in various manners
   11281             :  * to adhere more closely to the WKT standard.  This mostly involves
   11282             :  * translating a variety of ESRI names for projections, arguments and
   11283             :  * datums to "standard" names, as defined by Adam Gawne-Cain's reference
   11284             :  * translation of EPSG to WKT for the CT specification.
   11285             :  *
   11286             :  * \note Since GDAL 3.0, this function is essentially a no-operation, since
   11287             :  * morphing from ESRI is automatically done by importFromWkt(). Its only
   11288             :  * effect is to undo the effect of a potential prior call to morphToESRI().
   11289             :  *
   11290             :  * This does the same as the C function OSRMorphFromESRI().
   11291             :  *
   11292             :  * @return OGRERR_NONE unless something goes badly wrong.
   11293             :  * @deprecated
   11294             :  */
   11295             : 
   11296          27 : OGRErr OGRSpatialReference::morphFromESRI()
   11297             : 
   11298             : {
   11299          27 :     d->refreshProjObj();
   11300          27 :     d->setMorphToESRI(false);
   11301             : 
   11302          27 :     return OGRERR_NONE;
   11303             : }
   11304             : 
   11305             : /************************************************************************/
   11306             : /*                          OSRMorphFromESRI()                          */
   11307             : /************************************************************************/
   11308             : 
   11309             : /**
   11310             :  * \brief Convert in place from ESRI WKT format.
   11311             :  *
   11312             :  * This function is the same as the C++ method
   11313             :  * OGRSpatialReference::morphFromESRI().
   11314             :  */
   11315          20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
   11316             : 
   11317             : {
   11318          20 :     VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
   11319             : 
   11320          20 :     return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
   11321             : }
   11322             : 
   11323             : /************************************************************************/
   11324             : /*                            FindMatches()                             */
   11325             : /************************************************************************/
   11326             : 
   11327             : /**
   11328             :  * \brief Try to identify a match between the passed SRS and a related SRS
   11329             :  * in a catalog.
   11330             :  *
   11331             :  * Matching may be partial, or may fail.
   11332             :  * Returned entries will be sorted by decreasing match confidence (first
   11333             :  * entry has the highest match confidence).
   11334             :  *
   11335             :  * The exact way matching is done may change in future versions. Starting with
   11336             :  * GDAL 3.0, it relies on PROJ' proj_identify() function.
   11337             :  *
   11338             :  * This method is the same as OSRFindMatches().
   11339             :  *
   11340             :  * @param papszOptions NULL terminated list of options or NULL
   11341             :  * @param pnEntries Output parameter. Number of values in the returned array.
   11342             :  * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
   11343             :  * will be allocated to an array of *pnEntries whose values between 0 and 100
   11344             :  * indicate the confidence in the match. 100 is the highest confidence level.
   11345             :  * The array must be freed with CPLFree().
   11346             :  *
   11347             :  * @return an array of SRS that match the passed SRS, or NULL. Must be freed
   11348             :  * with OSRFreeSRSArray()
   11349             :  *
   11350             :  * @since GDAL 2.3
   11351             :  *
   11352             :  * @see OGRSpatialReference::FindBestMatch()
   11353             :  */
   11354             : OGRSpatialReferenceH *
   11355         998 : OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
   11356             :                                  int **ppanMatchConfidence) const
   11357             : {
   11358         998 :     CPL_IGNORE_RET_VAL(papszOptions);
   11359             : 
   11360         998 :     if (pnEntries)
   11361         998 :         *pnEntries = 0;
   11362         998 :     if (ppanMatchConfidence)
   11363         998 :         *ppanMatchConfidence = nullptr;
   11364             : 
   11365         998 :     d->refreshProjObj();
   11366         998 :     if (!d->m_pj_crs)
   11367           0 :         return nullptr;
   11368             : 
   11369         998 :     int *panConfidence = nullptr;
   11370         998 :     auto list = proj_identify(d->getPROJContext(), d->m_pj_crs, nullptr,
   11371             :                               nullptr, &panConfidence);
   11372         998 :     if (!list)
   11373           0 :         return nullptr;
   11374             : 
   11375         998 :     const int nMatches = proj_list_get_count(list);
   11376             : 
   11377         998 :     if (pnEntries)
   11378         998 :         *pnEntries = static_cast<int>(nMatches);
   11379             :     OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
   11380         998 :         CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
   11381         998 :     if (ppanMatchConfidence)
   11382             :     {
   11383         998 :         *ppanMatchConfidence =
   11384         998 :             static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
   11385             :     }
   11386        3878 :     for (int i = 0; i < nMatches; i++)
   11387             :     {
   11388        2880 :         PJ *obj = proj_list_get(d->getPROJContext(), list, i);
   11389        2880 :         CPLAssert(obj);
   11390        2880 :         OGRSpatialReference *poSRS = new OGRSpatialReference();
   11391        2880 :         poSRS->d->setPjCRS(obj);
   11392        2880 :         pahRet[i] = ToHandle(poSRS);
   11393        2880 :         if (ppanMatchConfidence)
   11394        2880 :             (*ppanMatchConfidence)[i] = panConfidence[i];
   11395             :     }
   11396         998 :     pahRet[nMatches] = nullptr;
   11397         998 :     proj_list_destroy(list);
   11398         998 :     proj_int_list_destroy(panConfidence);
   11399             : 
   11400         998 :     return pahRet;
   11401             : }
   11402             : 
   11403             : /************************************************************************/
   11404             : /*                          importFromEPSGA()                           */
   11405             : /************************************************************************/
   11406             : 
   11407             : /**
   11408             :  * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
   11409             :  * code.
   11410             :  *
   11411             :  * This method will initialize the spatial reference based on the
   11412             :  * passed in EPSG CRS code found in the PROJ database.
   11413             :  *
   11414             :  * Since GDAL 3.0, this method is identical to importFromEPSG().
   11415             :  *
   11416             :  * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
   11417             :  * 7-parameter Helmert transformation to WGS84 when there is one and only one
   11418             :  * such method available for the CRS. This behavior might not always be
   11419             :  * desirable, so starting with GDAL 3.0.3, this is no longer done unless
   11420             :  * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
   11421             :  * The AddGuessedTOWGS84() method can also be used for that purpose.
   11422             :  *
   11423             :  * The method will also by default substitute a deprecated EPSG code by its
   11424             :  * non-deprecated replacement. If this behavior is not desired, the
   11425             :  * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
   11426             :  *
   11427             :  * This method is the same as the C function OSRImportFromEPSGA().
   11428             :  *
   11429             :  * @param nCode a CRS code.
   11430             :  *
   11431             :  * @return OGRERR_NONE on success, or an error code on failure.
   11432             :  */
   11433             : 
   11434       30809 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
   11435             : 
   11436             : {
   11437       30809 :     Clear();
   11438             : 
   11439             :     const char *pszUseNonDeprecated =
   11440       30809 :         CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
   11441             :     const bool bUseNonDeprecated =
   11442       30809 :         CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
   11443       30809 :     const bool bAddTOWGS84 = CPLTestBool(
   11444             :         CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
   11445       30809 :     auto tlsCache = OSRGetProjTLSCache();
   11446       30809 :     if (tlsCache)
   11447             :     {
   11448             :         auto cachedObj =
   11449       30809 :             tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
   11450       30809 :         if (cachedObj)
   11451             :         {
   11452       22988 :             d->setPjCRS(cachedObj);
   11453       22988 :             return OGRERR_NONE;
   11454             :         }
   11455             :     }
   11456             : 
   11457       15641 :     CPLString osCode;
   11458        7820 :     osCode.Printf("%d", nCode);
   11459             :     auto obj =
   11460        7821 :         proj_create_from_database(d->getPROJContext(), "EPSG", osCode.c_str(),
   11461             :                                   PJ_CATEGORY_CRS, true, nullptr);
   11462        7821 :     if (!obj)
   11463             :     {
   11464          20 :         return OGRERR_FAILURE;
   11465             :     }
   11466             : 
   11467        7801 :     if (bUseNonDeprecated && proj_is_deprecated(obj))
   11468             :     {
   11469         411 :         auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
   11470         411 :         if (list)
   11471             :         {
   11472         411 :             const auto count = proj_list_get_count(list);
   11473         411 :             if (count == 1)
   11474             :             {
   11475             :                 auto nonDeprecated =
   11476         360 :                     proj_list_get(d->getPROJContext(), list, 0);
   11477         360 :                 if (nonDeprecated)
   11478             :                 {
   11479         360 :                     if (pszUseNonDeprecated == nullptr)
   11480             :                     {
   11481             :                         const char *pszNewAuth =
   11482         360 :                             proj_get_id_auth_name(nonDeprecated, 0);
   11483             :                         const char *pszNewCode =
   11484         360 :                             proj_get_id_code(nonDeprecated, 0);
   11485         360 :                         CPLError(CE_Warning, CPLE_AppDefined,
   11486             :                                  "CRS EPSG:%d is deprecated. "
   11487             :                                  "Its non-deprecated replacement %s:%s "
   11488             :                                  "will be used instead. "
   11489             :                                  "To use the original CRS, set the "
   11490             :                                  "OSR_USE_NON_DEPRECATED "
   11491             :                                  "configuration option to NO.",
   11492             :                                  nCode, pszNewAuth ? pszNewAuth : "(null)",
   11493             :                                  pszNewCode ? pszNewCode : "(null)");
   11494             :                     }
   11495         360 :                     proj_destroy(obj);
   11496         360 :                     obj = nonDeprecated;
   11497             :                 }
   11498             :             }
   11499             :         }
   11500         411 :         proj_list_destroy(list);
   11501             :     }
   11502             : 
   11503        7801 :     if (bAddTOWGS84)
   11504             :     {
   11505           1 :         auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
   11506             :                                                            obj, nullptr);
   11507           1 :         if (boundCRS)
   11508             :         {
   11509           1 :             proj_destroy(obj);
   11510           1 :             obj = boundCRS;
   11511             :         }
   11512             :     }
   11513             : 
   11514        7801 :     d->setPjCRS(obj);
   11515             : 
   11516        7800 :     if (tlsCache)
   11517             :     {
   11518        7800 :         tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
   11519             :                                      obj);
   11520             :     }
   11521             : 
   11522        7800 :     return OGRERR_NONE;
   11523             : }
   11524             : 
   11525             : /************************************************************************/
   11526             : /*                          AddGuessedTOWGS84()                         */
   11527             : /************************************************************************/
   11528             : 
   11529             : /**
   11530             :  * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
   11531             :  * to WGS84.
   11532             :  *
   11533             :  * This method try to attach a 3-parameter or 7-parameter Helmert transformation
   11534             :  * to WGS84 when there is one and only one such method available for the CRS.
   11535             :  * Note: this is more restrictive to how GDAL < 3 worked.
   11536             :  *
   11537             :  * This method is the same as the C function OSRAddGuessedTOWGS84().
   11538             :  *
   11539             :  * @return OGRERR_NONE on success, or an error code on failure (the CRS has
   11540             :  * already a transformation to WGS84 or none matching could be found).
   11541             :  *
   11542             :  * @since GDAL 3.0.3
   11543             :  */
   11544          18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
   11545             : {
   11546          18 :     d->refreshProjObj();
   11547          18 :     if (!d->m_pj_crs)
   11548           0 :         return OGRERR_FAILURE;
   11549          18 :     auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
   11550          18 :         d->getPROJContext(), d->m_pj_crs, false, true);
   11551          18 :     if (!boundCRS)
   11552             :     {
   11553           0 :         return OGRERR_FAILURE;
   11554             :     }
   11555          18 :     d->setPjCRS(boundCRS);
   11556          18 :     return OGRERR_NONE;
   11557             : }
   11558             : 
   11559             : /************************************************************************/
   11560             : /*                         OSRImportFromEPSGA()                         */
   11561             : /************************************************************************/
   11562             : 
   11563             : /**
   11564             :  * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
   11565             :  * to WGS84.
   11566             :  *
   11567             :  * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
   11568             :  *
   11569             :  * @since GDAL 3.0.3
   11570             :  */
   11571             : 
   11572           2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
   11573             : 
   11574             : {
   11575           2 :     VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
   11576             : 
   11577           2 :     return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
   11578             : }
   11579             : 
   11580             : /************************************************************************/
   11581             : /*                         OSRImportFromEPSGA()                         */
   11582             : /************************************************************************/
   11583             : 
   11584             : /**
   11585             :  * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
   11586             :  * code.
   11587             :  *
   11588             :  * This function is the same as OGRSpatialReference::importFromEPSGA().
   11589             :  */
   11590             : 
   11591           3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
   11592             : 
   11593             : {
   11594           3 :     VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
   11595             : 
   11596           3 :     return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
   11597             : }
   11598             : 
   11599             : /************************************************************************/
   11600             : /*                           importFromEPSG()                           */
   11601             : /************************************************************************/
   11602             : 
   11603             : /**
   11604             :  * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
   11605             :  * code.
   11606             :  *
   11607             :  * This method will initialize the spatial reference based on the
   11608             :  * passed in EPSG CRS code found in the PROJ database.
   11609             :  *
   11610             :  * This method is the same as the C function OSRImportFromEPSG().
   11611             :  *
   11612             :  * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
   11613             :  * 7-parameter Helmert transformation to WGS84 when there is one and only one
   11614             :  * such method available for the CRS. This behavior might not always be
   11615             :  * desirable, so starting with GDAL 3.0.3, this is no longer done unless
   11616             :  * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
   11617             :  *
   11618             :  * @param nCode a GCS or PCS code from the horizontal coordinate system table.
   11619             :  *
   11620             :  * @return OGRERR_NONE on success, or an error code on failure.
   11621             :  */
   11622             : 
   11623       29403 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
   11624             : 
   11625             : {
   11626       29403 :     return importFromEPSGA(nCode);
   11627             : }
   11628             : 
   11629             : /************************************************************************/
   11630             : /*                         OSRImportFromEPSG()                          */
   11631             : /************************************************************************/
   11632             : 
   11633             : /**
   11634             :  * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
   11635             :  * code.
   11636             :  *
   11637             :  * This function is the same as OGRSpatialReference::importFromEPSG().
   11638             :  */
   11639             : 
   11640        1327 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
   11641             : 
   11642             : {
   11643        1327 :     VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
   11644             : 
   11645        1327 :     return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
   11646             : }
   11647             : 
   11648             : /************************************************************************/
   11649             : /*                        EPSGTreatsAsLatLong()                         */
   11650             : /************************************************************************/
   11651             : 
   11652             : /**
   11653             :  * \brief This method returns TRUE if EPSG feels this geographic coordinate
   11654             :  * system should be treated as having lat/long coordinate ordering.
   11655             :  *
   11656             :  * Currently this returns TRUE for all geographic coordinate systems
   11657             :  * with an EPSG code set, and axes set defining it as lat, long.
   11658             :  *
   11659             :  * \note Important change of behavior since GDAL 3.0. In previous versions,
   11660             :  * geographic CRS imported with importFromEPSG() would cause this method to
   11661             :  * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
   11662             :  * is now equivalent to importFromEPSGA().
   11663             :  *
   11664             :  * FALSE will be returned for all coordinate systems that are not geographic,
   11665             :  * or that do not have an EPSG code set.
   11666             :  *
   11667             :  * This method is the same as the C function OSREPSGTreatsAsLatLong().
   11668             :  *
   11669             :  * @return TRUE or FALSE.
   11670             :  */
   11671             : 
   11672         677 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
   11673             : 
   11674             : {
   11675         677 :     if (!IsGeographic())
   11676         536 :         return FALSE;
   11677             : 
   11678         141 :     d->demoteFromBoundCRS();
   11679         141 :     const char *pszAuth = proj_get_id_auth_name(d->m_pj_crs, 0);
   11680         141 :     if (pszAuth == nullptr || !EQUAL(pszAuth, "EPSG"))
   11681             :     {
   11682          25 :         d->undoDemoteFromBoundCRS();
   11683          25 :         return FALSE;
   11684             :     }
   11685             : 
   11686         116 :     bool ret = false;
   11687         116 :     if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
   11688             :     {
   11689             :         auto horizCRS =
   11690           3 :             proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
   11691           3 :         if (horizCRS)
   11692             :         {
   11693             :             auto cs =
   11694           3 :                 proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
   11695           3 :             if (cs)
   11696             :             {
   11697           3 :                 const char *pszDirection = nullptr;
   11698           3 :                 if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
   11699             :                                           nullptr, &pszDirection, nullptr,
   11700           3 :                                           nullptr, nullptr, nullptr))
   11701             :                 {
   11702           3 :                     if (EQUAL(pszDirection, "north"))
   11703             :                     {
   11704           3 :                         ret = true;
   11705             :                     }
   11706             :                 }
   11707             : 
   11708           3 :                 proj_destroy(cs);
   11709             :             }
   11710             : 
   11711           3 :             proj_destroy(horizCRS);
   11712             :         }
   11713             :     }
   11714             :     else
   11715             :     {
   11716             :         auto cs =
   11717         113 :             proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
   11718         113 :         if (cs)
   11719             :         {
   11720         113 :             const char *pszDirection = nullptr;
   11721         113 :             if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
   11722             :                                       nullptr, &pszDirection, nullptr, nullptr,
   11723         113 :                                       nullptr, nullptr))
   11724             :             {
   11725         113 :                 if (EQUAL(pszDirection, "north"))
   11726             :                 {
   11727         113 :                     ret = true;
   11728             :                 }
   11729             :             }
   11730             : 
   11731         113 :             proj_destroy(cs);
   11732             :         }
   11733             :     }
   11734         116 :     d->undoDemoteFromBoundCRS();
   11735             : 
   11736         116 :     return ret;
   11737             : }
   11738             : 
   11739             : /************************************************************************/
   11740             : /*                       OSREPSGTreatsAsLatLong()                       */
   11741             : /************************************************************************/
   11742             : 
   11743             : /**
   11744             :  * \brief This function returns TRUE if EPSG feels this geographic coordinate
   11745             :  * system should be treated as having lat/long coordinate ordering.
   11746             :  *
   11747             :  * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
   11748             :  */
   11749             : 
   11750         155 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
   11751             : 
   11752             : {
   11753         155 :     VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
   11754             : 
   11755         155 :     return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
   11756             : }
   11757             : 
   11758             : /************************************************************************/
   11759             : /*                     EPSGTreatsAsNorthingEasting()                    */
   11760             : /************************************************************************/
   11761             : 
   11762             : /**
   11763             :  * \brief This method returns TRUE if EPSG feels this projected coordinate
   11764             :  * system should be treated as having northing/easting coordinate ordering.
   11765             :  *
   11766             :  * Currently this returns TRUE for all projected coordinate systems
   11767             :  * with an EPSG code set, and axes set defining it as northing, easting.
   11768             :  *
   11769             :  * \note Important change of behavior since GDAL 3.0. In previous versions,
   11770             :  * projected CRS with northing, easting axis order imported with
   11771             :  * importFromEPSG() would cause this method to
   11772             :  * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
   11773             :  * is now equivalent to importFromEPSGA().
   11774             :  *
   11775             :  * FALSE will be returned for all coordinate systems that are not projected,
   11776             :  * or that do not have an EPSG code set.
   11777             :  *
   11778             :  * This method is the same as the C function EPSGTreatsAsNorthingEasting().
   11779             :  *
   11780             :  * @return TRUE or FALSE.
   11781             :  *
   11782             :  * @since OGR 1.10.0
   11783             :  */
   11784             : 
   11785         565 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
   11786             : 
   11787             : {
   11788         565 :     if (!IsProjected())
   11789          21 :         return FALSE;
   11790             : 
   11791         544 :     d->demoteFromBoundCRS();
   11792             :     PJ *projCRS;
   11793         544 :     const auto ctxt = d->getPROJContext();
   11794         544 :     if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
   11795             :     {
   11796           2 :         projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
   11797           2 :         if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
   11798             :         {
   11799           2 :             d->undoDemoteFromBoundCRS();
   11800           2 :             proj_destroy(projCRS);
   11801           2 :             return FALSE;
   11802             :         }
   11803             :     }
   11804             :     else
   11805             :     {
   11806         542 :         projCRS = proj_clone(ctxt, d->m_pj_crs);
   11807             :     }
   11808         542 :     const char *pszAuth = proj_get_id_auth_name(projCRS, 0);
   11809         542 :     if (pszAuth == nullptr || !EQUAL(pszAuth, "EPSG"))
   11810             :     {
   11811           0 :         d->undoDemoteFromBoundCRS();
   11812           0 :         proj_destroy(projCRS);
   11813           0 :         return FALSE;
   11814             :     }
   11815             : 
   11816         542 :     bool ret = false;
   11817         542 :     auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
   11818         542 :     proj_destroy(projCRS);
   11819         542 :     d->undoDemoteFromBoundCRS();
   11820             : 
   11821         542 :     if (cs)
   11822             :     {
   11823         542 :         ret = isNorthEastAxisOrder(ctxt, cs);
   11824         542 :         proj_destroy(cs);
   11825             :     }
   11826             : 
   11827         542 :     return ret;
   11828             : }
   11829             : 
   11830             : /************************************************************************/
   11831             : /*                     OSREPSGTreatsAsNorthingEasting()                 */
   11832             : /************************************************************************/
   11833             : 
   11834             : /**
   11835             :  * \brief This function returns TRUE if EPSG feels this projected coordinate
   11836             :  * system should be treated as having northing/easting coordinate ordering.
   11837             :  *
   11838             :  * This function is the same as
   11839             :  * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
   11840             :  *
   11841             :  * @since OGR 1.10.0
   11842             :  */
   11843             : 
   11844         160 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
   11845             : 
   11846             : {
   11847         160 :     VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
   11848             : 
   11849         160 :     return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
   11850             : }
   11851             : 
   11852             : /************************************************************************/
   11853             : /*                     ImportFromESRIWisconsinWKT()                     */
   11854             : /*                                                                      */
   11855             : /*      Search a ESRI State Plane WKT and import it.                    */
   11856             : /************************************************************************/
   11857             : 
   11858             : // This is only used by the HFA driver and somewhat dubious we really need that
   11859             : // Coming from an old ESRI merge
   11860             : 
   11861           1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
   11862             :                                                        double centralMeridian,
   11863             :                                                        double latOfOrigin,
   11864             :                                                        const char *unitsName,
   11865             :                                                        const char *crsName)
   11866             : {
   11867           1 :     if (centralMeridian < -93 || centralMeridian > -87)
   11868           0 :         return OGRERR_FAILURE;
   11869           1 :     if (latOfOrigin < 40 || latOfOrigin > 47)
   11870           0 :         return OGRERR_FAILURE;
   11871             : 
   11872             :     // If the CS name is known.
   11873           1 :     if (!prjName && !unitsName && crsName)
   11874             :     {
   11875           0 :         const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
   11876           0 :         PJ_OBJ_LIST *list = proj_create_from_name(
   11877             :             d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
   11878           0 :         if (list)
   11879             :         {
   11880           0 :             if (proj_list_get_count(list) == 1)
   11881             :             {
   11882           0 :                 auto crs = proj_list_get(d->getPROJContext(), list, 0);
   11883           0 :                 if (crs)
   11884             :                 {
   11885           0 :                     Clear();
   11886           0 :                     d->setPjCRS(crs);
   11887           0 :                     proj_list_destroy(list);
   11888           0 :                     return OGRERR_NONE;
   11889             :                 }
   11890             :             }
   11891           0 :             proj_list_destroy(list);
   11892             :         }
   11893           0 :         return OGRERR_FAILURE;
   11894             :     }
   11895             : 
   11896           1 :     if (prjName == nullptr || unitsName == nullptr)
   11897             :     {
   11898           0 :         return OGRERR_FAILURE;
   11899             :     }
   11900             : 
   11901           1 :     const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
   11902           1 :     PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
   11903             :                                               "NAD_1983_HARN_WISCRS_", &type, 1,
   11904             :                                               true, 0, nullptr);
   11905           1 :     if (list)
   11906             :     {
   11907           1 :         const auto listSize = proj_list_get_count(list);
   11908           8 :         for (int i = 0; i < listSize; i++)
   11909             :         {
   11910           8 :             auto crs = proj_list_get(d->getPROJContext(), list, i);
   11911           8 :             if (!crs)
   11912             :             {
   11913           7 :                 continue;
   11914             :             }
   11915             : 
   11916           8 :             auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
   11917           8 :             if (!conv)
   11918             :             {
   11919           0 :                 proj_destroy(crs);
   11920           0 :                 continue;
   11921             :             }
   11922           8 :             const char *pszMethodCode = nullptr;
   11923           8 :             proj_coordoperation_get_method_info(
   11924             :                 d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
   11925           8 :             const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
   11926           8 :             if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
   11927             :                    nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
   11928           3 :                   (EQUAL(prjName, "Lambert_Conformal_Conic") &&
   11929             :                    nMethodCode ==
   11930             :                        EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
   11931             :             {
   11932           3 :                 proj_destroy(crs);
   11933           3 :                 proj_destroy(conv);
   11934           3 :                 continue;
   11935             :             }
   11936             : 
   11937             :             auto coordSys =
   11938           5 :                 proj_crs_get_coordinate_system(d->getPROJContext(), crs);
   11939           5 :             if (!coordSys)
   11940             :             {
   11941           0 :                 proj_destroy(crs);
   11942           0 :                 proj_destroy(conv);
   11943           0 :                 continue;
   11944             :             }
   11945             : 
   11946           5 :             double dfConvFactor = 0.0;
   11947           5 :             proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
   11948             :                                   nullptr, nullptr, &dfConvFactor, nullptr,
   11949             :                                   nullptr, nullptr);
   11950           5 :             proj_destroy(coordSys);
   11951             : 
   11952           6 :             if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
   11953           1 :                 (!EQUAL(unitsName, "meters") &&
   11954           0 :                  std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
   11955             :                      1e-10))
   11956             :             {
   11957           4 :                 proj_destroy(crs);
   11958           4 :                 proj_destroy(conv);
   11959           4 :                 continue;
   11960             :             }
   11961             : 
   11962           1 :             int idx_lat = proj_coordoperation_get_param_index(
   11963             :                 d->getPROJContext(), conv,
   11964             :                 EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
   11965           1 :             double valueLat = -1000;
   11966           1 :             proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
   11967             :                                           nullptr, nullptr, nullptr, &valueLat,
   11968             :                                           nullptr, nullptr, nullptr, nullptr,
   11969             :                                           nullptr, nullptr);
   11970           1 :             int idx_lon = proj_coordoperation_get_param_index(
   11971             :                 d->getPROJContext(), conv,
   11972             :                 EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
   11973           1 :             double valueLong = -1000;
   11974           1 :             proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
   11975             :                                           nullptr, nullptr, nullptr, &valueLong,
   11976             :                                           nullptr, nullptr, nullptr, nullptr,
   11977             :                                           nullptr, nullptr);
   11978           1 :             if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
   11979           1 :                 std::fabs(latOfOrigin - valueLat) <= 1e-10)
   11980             :             {
   11981           1 :                 Clear();
   11982           1 :                 d->setPjCRS(crs);
   11983           1 :                 proj_list_destroy(list);
   11984           1 :                 proj_destroy(conv);
   11985           1 :                 return OGRERR_NONE;
   11986             :             }
   11987             : 
   11988           0 :             proj_destroy(crs);
   11989           0 :             proj_destroy(conv);
   11990             :         }
   11991           0 :         proj_list_destroy(list);
   11992             :     }
   11993             : 
   11994           0 :     return OGRERR_FAILURE;
   11995             : }
   11996             : 
   11997             : /************************************************************************/
   11998             : /*                      GetAxisMappingStrategy()                        */
   11999             : /************************************************************************/
   12000             : 
   12001             : /** \brief Return the data axis to CRS axis mapping strategy.
   12002             :  *
   12003             :  * <ul>
   12004             :  * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
   12005             :  *     lat/long order, the data will still be long/lat ordered. Similarly for
   12006             :  *     a projected CRS with northing/easting order, the data will still be
   12007             :  *     easting/northing ordered.
   12008             :  * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
   12009             :  *     the CRS axis.
   12010             :  * <li>OAMS_CUSTOM means that the data axis are customly defined with
   12011             :  *     SetDataAxisToSRSAxisMapping()
   12012             :  * </ul>
   12013             :  * @return the data axis to CRS axis mapping strategy.
   12014             :  * @since GDAL 3.0
   12015             :  */
   12016          37 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
   12017             : {
   12018          37 :     return d->m_axisMappingStrategy;
   12019             : }
   12020             : 
   12021             : /************************************************************************/
   12022             : /*                      OSRGetAxisMappingStrategy()                     */
   12023             : /************************************************************************/
   12024             : 
   12025             : /** \brief Return the data axis to CRS axis mapping strategy.
   12026             :  *
   12027             :  * See OGRSpatialReference::GetAxisMappingStrategy()
   12028             :  * @since GDAL 3.0
   12029             :  */
   12030          37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
   12031             : {
   12032          37 :     VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
   12033             : 
   12034          37 :     return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
   12035             : }
   12036             : 
   12037             : /************************************************************************/
   12038             : /*                      SetAxisMappingStrategy()                        */
   12039             : /************************************************************************/
   12040             : 
   12041             : /** \brief Set the data axis to CRS axis mapping strategy.
   12042             :  *
   12043             :  * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
   12044             :  * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
   12045             :  * later being the default value when the option is not set) to control the
   12046             :  * value of the data axis to CRS axis mapping strategy when a
   12047             :  * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
   12048             :  * override this default value.
   12049             :  *
   12050             :  * See OGRSpatialReference::GetAxisMappingStrategy()
   12051             :  * @since GDAL 3.0
   12052             :  */
   12053       79354 : void OGRSpatialReference::SetAxisMappingStrategy(
   12054             :     OSRAxisMappingStrategy strategy)
   12055             : {
   12056       79354 :     d->m_axisMappingStrategy = strategy;
   12057       79339 :     d->refreshAxisMapping();
   12058       79355 : }
   12059             : 
   12060             : /************************************************************************/
   12061             : /*                      OSRSetAxisMappingStrategy()                     */
   12062             : /************************************************************************/
   12063             : 
   12064             : /** \brief Set the data axis to CRS axis mapping strategy.
   12065             :  *
   12066             :  * See OGRSpatialReference::SetAxisMappingStrategy()
   12067             :  * @since GDAL 3.0
   12068             :  */
   12069         818 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
   12070             :                                OSRAxisMappingStrategy strategy)
   12071             : {
   12072         818 :     VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
   12073             : 
   12074         818 :     OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
   12075             : }
   12076             : 
   12077             : /************************************************************************/
   12078             : /*                      GetDataAxisToSRSAxisMapping()                   */
   12079             : /************************************************************************/
   12080             : 
   12081             : /** \brief Return the data axis to SRS axis mapping.
   12082             :  *
   12083             :  * The number of elements of the vector will be the number of axis of the CRS.
   12084             :  * Values start at 1.
   12085             :  *
   12086             :  * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
   12087             :  * for the first axis of the CRS.
   12088             :  *
   12089             :  * @since GDAL 3.0
   12090             :  */
   12091     3806020 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
   12092             : {
   12093     3806020 :     return d->m_axisMapping;
   12094             : }
   12095             : 
   12096             : /************************************************************************/
   12097             : /*                     OSRGetDataAxisToSRSAxisMapping()                 */
   12098             : /************************************************************************/
   12099             : 
   12100             : /** \brief Return the data axis to SRS axis mapping.
   12101             :  *
   12102             :  * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
   12103             :  *
   12104             :  * @since GDAL 3.0
   12105             :  */
   12106         175 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
   12107             :                                           int *pnCount)
   12108             : {
   12109         175 :     VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
   12110         175 :     VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
   12111             : 
   12112             :     const auto &v =
   12113         175 :         OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
   12114         175 :     *pnCount = static_cast<int>(v.size());
   12115         175 :     return v.data();
   12116             : }
   12117             : 
   12118             : /************************************************************************/
   12119             : /*                      SetDataAxisToSRSAxisMapping()                   */
   12120             : /************************************************************************/
   12121             : 
   12122             : /** \brief Set a custom data axis to CRS axis mapping.
   12123             :  *
   12124             :  * The number of elements of the mapping vector should be the number of axis
   12125             :  * of the CRS (as returned by GetAxesCount()) (although this method does not
   12126             :  * check that, beyond checking there are at least 2 elements, so that this
   12127             :  * method and setting the CRS can be done in any order).
   12128             :  * This is taken into account by OGRCoordinateTransformation to transform the
   12129             :  * order of coordinates to the order expected by the CRS before
   12130             :  * transformation, and back to the data order after transformation.
   12131             :  *
   12132             :  * The mapping[i] value (one based) represents the data axis number for the i(th)
   12133             :  * axis of the CRS. A negative value can also be used to ask for a sign
   12134             :  * reversal during coordinate transformation (to deal with northing vs southing,
   12135             :  * easting vs westing, heights vs depths).
   12136             :  *
   12137             :  * When used with OGRCoordinateTransformation,
   12138             :  * - the only valid values for mapping[0] (data axis number for the first axis
   12139             :  *   of the CRS) are 1, 2, -1, -2.
   12140             :  * - the only valid values for mapping[1] (data axis number for the second axis
   12141             :  *   of the CRS) are 1, 2, -1, -2.
   12142             :  *  - the only valid values mapping[2] are 3 or -3.
   12143             :  * Note: this method does not validate the values of mapping[].
   12144             :  *
   12145             :  * mapping=[2,1] typically expresses the inversion of axis between the data
   12146             :  * axis and the CRS axis for a 2D CRS.
   12147             :  *
   12148             :  * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
   12149             :  *
   12150             :  * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
   12151             :  *
   12152             :  * @param mapping The new data axis to CRS axis mapping.
   12153             :  *
   12154             :  * @since GDAL 3.0
   12155             :  * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
   12156             :  */
   12157        6246 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
   12158             :     const std::vector<int> &mapping)
   12159             : {
   12160        6246 :     if (mapping.size() < 2)
   12161           0 :         return OGRERR_FAILURE;
   12162        6246 :     d->m_axisMappingStrategy = OAMS_CUSTOM;
   12163        6246 :     d->m_axisMapping = mapping;
   12164        6246 :     return OGRERR_NONE;
   12165             : }
   12166             : 
   12167             : /************************************************************************/
   12168             : /*                     OSRSetDataAxisToSRSAxisMapping()                 */
   12169             : /************************************************************************/
   12170             : 
   12171             : /** \brief Set a custom data axis to CRS axis mapping.
   12172             :  *
   12173             :  * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
   12174             :  *
   12175             :  * This is the same as the C++ method
   12176             :  * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
   12177             :  *
   12178             :  * @since GDAL 3.1
   12179             :  */
   12180          12 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
   12181             :                                       int nMappingSize, const int *panMapping)
   12182             : {
   12183          12 :     VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
   12184          12 :     VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
   12185             :                       OGRERR_FAILURE);
   12186             : 
   12187          12 :     if (nMappingSize < 0)
   12188           0 :         return OGRERR_FAILURE;
   12189             : 
   12190          24 :     std::vector<int> mapping(nMappingSize);
   12191          12 :     if (nMappingSize)
   12192          12 :         memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
   12193          12 :     return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
   12194          12 :         mapping);
   12195             : }
   12196             : 
   12197             : /************************************************************************/
   12198             : /*                               GetAreaOfUse()                         */
   12199             : /************************************************************************/
   12200             : 
   12201             : /** \brief Return the area of use of the CRS.
   12202             :  *
   12203             :  * This method is the same as the OSRGetAreaOfUse() function.
   12204             :  *
   12205             :  * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
   12206             :  * longitude, expressed in degree. Might be NULL. If the returned value is
   12207             :  * -1000, the bounding box is unknown.
   12208             :  * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
   12209             :  * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
   12210             :  * the bounding box is unknown.
   12211             :  * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
   12212             :  * longitude, expressed in degree. Might be NULL. If the returned value is
   12213             :  * -1000, the bounding box is unknown.
   12214             :  * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
   12215             :  * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
   12216             :  * the bounding box is unknown.
   12217             :  * @param ppszAreaName Pointer to a string to receive the name of the area of
   12218             :  * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
   12219             :  * invalidated by further calls.
   12220             :  * @return true in case of success
   12221             :  * @since GDAL 3.0
   12222             :  */
   12223           1 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
   12224             :                                        double *pdfSouthLatitudeDeg,
   12225             :                                        double *pdfEastLongitudeDeg,
   12226             :                                        double *pdfNorthLatitudeDeg,
   12227             :                                        const char **ppszAreaName) const
   12228             : {
   12229           1 :     d->refreshProjObj();
   12230           1 :     if (!d->m_pj_crs)
   12231             :     {
   12232           0 :         return false;
   12233             :     }
   12234           1 :     d->demoteFromBoundCRS();
   12235           1 :     const char *pszAreaName = nullptr;
   12236           1 :     int bSuccess = proj_get_area_of_use(
   12237           1 :         d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
   12238             :         pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
   12239             :         &pszAreaName);
   12240           1 :     d->undoDemoteFromBoundCRS();
   12241           1 :     d->m_osAreaName = pszAreaName ? pszAreaName : "";
   12242           1 :     if (ppszAreaName)
   12243           1 :         *ppszAreaName = d->m_osAreaName.c_str();
   12244           1 :     return CPL_TO_BOOL(bSuccess);
   12245             : }
   12246             : 
   12247             : /************************************************************************/
   12248             : /*                               GetAreaOfUse()                         */
   12249             : /************************************************************************/
   12250             : 
   12251             : /** \brief Return the area of use of the CRS.
   12252             :  *
   12253             :  * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
   12254             :  *
   12255             :  * @since GDAL 3.0
   12256             :  */
   12257           1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
   12258             :                     double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
   12259             :                     double *pdfNorthLatitudeDeg, const char **ppszAreaName)
   12260             : {
   12261           1 :     VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
   12262             : 
   12263           1 :     return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
   12264             :         pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
   12265           1 :         pdfNorthLatitudeDeg, ppszAreaName);
   12266             : }
   12267             : 
   12268             : /************************************************************************/
   12269             : /*                     OSRGetCRSInfoListFromDatabase()                  */
   12270             : /************************************************************************/
   12271             : 
   12272             : /** \brief Enumerate CRS objects from the database.
   12273             :  *
   12274             :  * The returned object is an array of OSRCRSInfo* pointers, whose last
   12275             :  * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
   12276             :  *
   12277             :  * @param pszAuthName Authority name, used to restrict the search.
   12278             :  * Or NULL for all authorities.
   12279             :  * @param params Additional criteria. Must be set to NULL for now.
   12280             :  * @param pnOutResultCount Output parameter pointing to an integer to receive
   12281             :  * the size of the result list. Might be NULL
   12282             :  * @return an array of OSRCRSInfo* pointers to be freed with
   12283             :  * OSRDestroyCRSInfoList(), or NULL in case of error.
   12284             :  *
   12285             :  * @since GDAL 3.0
   12286             :  */
   12287             : OSRCRSInfo **
   12288           2 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
   12289             :                               CPL_UNUSED const OSRCRSListParameters *params,
   12290             :                               int *pnOutResultCount)
   12291             : {
   12292           2 :     int nResultCount = 0;
   12293           2 :     auto projList = proj_get_crs_info_list_from_database(
   12294             :         OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
   12295           2 :     if (pnOutResultCount)
   12296           2 :         *pnOutResultCount = nResultCount;
   12297           2 :     if (!projList)
   12298             :     {
   12299           0 :         return nullptr;
   12300             :     }
   12301           2 :     auto res = new OSRCRSInfo *[nResultCount + 1];
   12302       13220 :     for (int i = 0; i < nResultCount; i++)
   12303             :     {
   12304       13218 :         res[i] = new OSRCRSInfo;
   12305       26436 :         res[i]->pszAuthName = projList[i]->auth_name
   12306       13218 :                                   ? CPLStrdup(projList[i]->auth_name)
   12307             :                                   : nullptr;
   12308       13218 :         res[i]->pszCode =
   12309       13218 :             projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
   12310       13218 :         res[i]->pszName =
   12311       13218 :             projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
   12312       13218 :         res[i]->eType = OSR_CRS_TYPE_OTHER;
   12313       13218 :         switch (projList[i]->type)
   12314             :         {
   12315        1182 :             case PJ_TYPE_GEOGRAPHIC_2D_CRS:
   12316        1182 :                 res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
   12317        1182 :                 break;
   12318         442 :             case PJ_TYPE_GEOGRAPHIC_3D_CRS:
   12319         442 :                 res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
   12320         442 :                 break;
   12321         444 :             case PJ_TYPE_GEOCENTRIC_CRS:
   12322         444 :                 res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
   12323         444 :                 break;
   12324       10154 :             case PJ_TYPE_PROJECTED_CRS:
   12325       10154 :                 res[i]->eType = OSR_CRS_TYPE_PROJECTED;
   12326       10154 :                 break;
   12327         442 :             case PJ_TYPE_VERTICAL_CRS:
   12328         442 :                 res[i]->eType = OSR_CRS_TYPE_VERTICAL;
   12329         442 :                 break;
   12330         554 :             case PJ_TYPE_COMPOUND_CRS:
   12331         554 :                 res[i]->eType = OSR_CRS_TYPE_COMPOUND;
   12332         554 :                 break;
   12333           0 :             default:
   12334           0 :                 break;
   12335             :         }
   12336       13218 :         res[i]->bDeprecated = projList[i]->deprecated;
   12337       13218 :         res[i]->bBboxValid = projList[i]->bbox_valid;
   12338       13218 :         res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
   12339       13218 :         res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
   12340       13218 :         res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
   12341       13218 :         res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
   12342       26436 :         res[i]->pszAreaName = projList[i]->area_name
   12343       13218 :                                   ? CPLStrdup(projList[i]->area_name)
   12344             :                                   : nullptr;
   12345       13218 :         res[i]->pszProjectionMethod =
   12346       13218 :             projList[i]->projection_method_name
   12347       13218 :                 ? CPLStrdup(projList[i]->projection_method_name)
   12348             :                 : nullptr;
   12349             :     }
   12350           2 :     res[nResultCount] = nullptr;
   12351           2 :     proj_crs_info_list_destroy(projList);
   12352           2 :     return res;
   12353             : }
   12354             : 
   12355             : /************************************************************************/
   12356             : /*                        OSRDestroyCRSInfoList()                       */
   12357             : /************************************************************************/
   12358             : 
   12359             : /** \brief Destroy the result returned by
   12360             :  * OSRGetCRSInfoListFromDatabase().
   12361             :  *
   12362             :  * @since GDAL 3.0
   12363             :  */
   12364           2 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
   12365             : {
   12366           2 :     if (list)
   12367             :     {
   12368       13220 :         for (int i = 0; list[i] != nullptr; i++)
   12369             :         {
   12370       13218 :             CPLFree(list[i]->pszAuthName);
   12371       13218 :             CPLFree(list[i]->pszCode);
   12372       13218 :             CPLFree(list[i]->pszName);
   12373       13218 :             CPLFree(list[i]->pszAreaName);
   12374       13218 :             CPLFree(list[i]->pszProjectionMethod);
   12375       13218 :             delete list[i];
   12376             :         }
   12377           2 :         delete[] list;
   12378             :     }
   12379           2 : }
   12380             : 
   12381             : /************************************************************************/
   12382             : /*                    UpdateCoordinateSystemFromGeogCRS()               */
   12383             : /************************************************************************/
   12384             : 
   12385             : /*! @cond Doxygen_Suppress */
   12386             : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
   12387             :  *
   12388             :  * @since GDAL 3.1
   12389             :  */
   12390           1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
   12391             : {
   12392           1 :     d->refreshProjObj();
   12393           1 :     if (!d->m_pj_crs)
   12394           0 :         return;
   12395           1 :     if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
   12396           0 :         return;
   12397           1 :     if (GetAxesCount() == 3)
   12398           0 :         return;
   12399           1 :     auto ctxt = d->getPROJContext();
   12400           1 :     auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
   12401           1 :     if (!baseCRS)
   12402           0 :         return;
   12403           1 :     auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
   12404           1 :     if (!baseCRSCS)
   12405             :     {
   12406           0 :         proj_destroy(baseCRS);
   12407           0 :         return;
   12408             :     }
   12409           1 :     if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
   12410             :     {
   12411           0 :         proj_destroy(baseCRSCS);
   12412           0 :         proj_destroy(baseCRS);
   12413           0 :         return;
   12414             :     }
   12415           1 :     auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
   12416           1 :     if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
   12417             :     {
   12418           0 :         proj_destroy(baseCRSCS);
   12419           0 :         proj_destroy(baseCRS);
   12420           0 :         proj_destroy(projCS);
   12421           0 :         return;
   12422             :     }
   12423             : 
   12424             :     PJ_AXIS_DESCRIPTION axis[3];
   12425           4 :     for (int i = 0; i < 3; i++)
   12426             :     {
   12427           3 :         const char *name = nullptr;
   12428           3 :         const char *abbreviation = nullptr;
   12429           3 :         const char *direction = nullptr;
   12430           3 :         double unit_conv_factor = 0;
   12431           3 :         const char *unit_name = nullptr;
   12432           3 :         proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
   12433             :                               &abbreviation, &direction, &unit_conv_factor,
   12434             :                               &unit_name, nullptr, nullptr);
   12435           3 :         axis[i].name = CPLStrdup(name);
   12436           3 :         axis[i].abbreviation = CPLStrdup(abbreviation);
   12437           3 :         axis[i].direction = CPLStrdup(direction);
   12438           3 :         axis[i].unit_name = CPLStrdup(unit_name);
   12439           3 :         axis[i].unit_conv_factor = unit_conv_factor;
   12440           3 :         axis[i].unit_type = PJ_UT_LINEAR;
   12441             :     }
   12442           1 :     proj_destroy(baseCRSCS);
   12443           1 :     proj_destroy(projCS);
   12444           1 :     auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
   12445           4 :     for (int i = 0; i < 3; i++)
   12446             :     {
   12447           3 :         CPLFree(axis[i].name);
   12448           3 :         CPLFree(axis[i].abbreviation);
   12449           3 :         CPLFree(axis[i].direction);
   12450           3 :         CPLFree(axis[i].unit_name);
   12451             :     }
   12452           1 :     if (!cs)
   12453             :     {
   12454           0 :         proj_destroy(baseCRS);
   12455           0 :         return;
   12456             :     }
   12457           1 :     auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
   12458           1 :     auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
   12459             :                                          conversion, cs);
   12460           1 :     proj_destroy(baseCRS);
   12461           1 :     proj_destroy(conversion);
   12462           1 :     proj_destroy(cs);
   12463           1 :     d->setPjCRS(crs);
   12464             : }
   12465             : 
   12466             : /*! @endcond */
   12467             : 
   12468             : /************************************************************************/
   12469             : /*                             PromoteTo3D()                            */
   12470             : /************************************************************************/
   12471             : 
   12472             : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
   12473             :  *
   12474             :  * The new axis will be ellipsoidal height, oriented upwards, and with metre
   12475             :  * units.
   12476             :  *
   12477             :  * @param pszName New name for the CRS. If set to NULL, the previous name will
   12478             :  * be used.
   12479             :  * @return OGRERR_NONE if no error occurred.
   12480             :  * @since GDAL 3.1 and PROJ 6.3
   12481             :  */
   12482          41 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
   12483             : {
   12484          41 :     d->refreshProjObj();
   12485          41 :     if (!d->m_pj_crs)
   12486           0 :         return OGRERR_FAILURE;
   12487             :     auto newPj =
   12488          41 :         proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
   12489          41 :     if (!newPj)
   12490           0 :         return OGRERR_FAILURE;
   12491          41 :     d->setPjCRS(newPj);
   12492          41 :     return OGRERR_NONE;
   12493             : }
   12494             : 
   12495             : /************************************************************************/
   12496             : /*                             OSRPromoteTo3D()                         */
   12497             : /************************************************************************/
   12498             : 
   12499             : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
   12500             :  *
   12501             :  * See OGRSpatialReference::PromoteTo3D()
   12502             :  *
   12503             :  * @since GDAL 3.1 and PROJ 6.3
   12504             :  */
   12505           3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
   12506             : {
   12507           3 :     VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
   12508             : 
   12509           3 :     return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
   12510             : }
   12511             : 
   12512             : /************************************************************************/
   12513             : /*                             DemoteTo2D()                             */
   12514             : /************************************************************************/
   12515             : 
   12516             : /** \brief "Demote" a 3D CRS to a 2D CRS one.
   12517             :  *
   12518             :  * @param pszName New name for the CRS. If set to NULL, the previous name will
   12519             :  * be used.
   12520             :  * @return OGRERR_NONE if no error occurred.
   12521             :  * @since GDAL 3.2 and PROJ 6.3
   12522             :  */
   12523          45 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
   12524             : {
   12525          45 :     d->refreshProjObj();
   12526          45 :     if (!d->m_pj_crs)
   12527           0 :         return OGRERR_FAILURE;
   12528             :     auto newPj =
   12529          45 :         proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
   12530          45 :     if (!newPj)
   12531           0 :         return OGRERR_FAILURE;
   12532          45 :     d->setPjCRS(newPj);
   12533          45 :     return OGRERR_NONE;
   12534             : }
   12535             : 
   12536             : /************************************************************************/
   12537             : /*                             OSRDemoteTo2D()                          */
   12538             : /************************************************************************/
   12539             : 
   12540             : /** \brief "Demote" a 3D CRS to a 2D CRS one.
   12541             :  *
   12542             :  * See OGRSpatialReference::DemoteTo2D()
   12543             :  *
   12544             :  * @since GDAL 3.2 and PROJ 6.3
   12545             :  */
   12546           1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
   12547             : {
   12548           1 :     VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
   12549             : 
   12550           1 :     return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
   12551             : }
   12552             : 
   12553             : /************************************************************************/
   12554             : /*                           GetEPSGGeogCS()                            */
   12555             : /************************************************************************/
   12556             : 
   12557             : /** Try to establish what the EPSG code for this coordinate systems
   12558             :  * GEOGCS might be.  Returns -1 if no reasonable guess can be made.
   12559             :  *
   12560             :  * @return EPSG code
   12561             :  */
   12562             : 
   12563         331 : int OGRSpatialReference::GetEPSGGeogCS() const
   12564             : 
   12565             : {
   12566             :     /* -------------------------------------------------------------------- */
   12567             :     /*      Check axis order.                                               */
   12568             :     /* -------------------------------------------------------------------- */
   12569         662 :     auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
   12570         331 :     if (!poGeogCRS)
   12571           0 :         return -1;
   12572             : 
   12573         331 :     bool ret = false;
   12574         331 :     poGeogCRS->d->demoteFromBoundCRS();
   12575         331 :     auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
   12576         331 :                                              poGeogCRS->d->m_pj_crs);
   12577         331 :     poGeogCRS->d->undoDemoteFromBoundCRS();
   12578         331 :     if (cs)
   12579             :     {
   12580         331 :         const char *pszDirection = nullptr;
   12581         331 :         if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
   12582             :                                   &pszDirection, nullptr, nullptr, nullptr,
   12583         331 :                                   nullptr))
   12584             :         {
   12585         331 :             if (EQUAL(pszDirection, "north"))
   12586             :             {
   12587         133 :                 ret = true;
   12588             :             }
   12589             :         }
   12590             : 
   12591         331 :         proj_destroy(cs);
   12592             :     }
   12593         331 :     if (!ret)
   12594         198 :         return -1;
   12595             : 
   12596             :     /* -------------------------------------------------------------------- */
   12597             :     /*      Do we already have it?                                          */
   12598             :     /* -------------------------------------------------------------------- */
   12599         133 :     const char *pszAuthName = GetAuthorityName("GEOGCS");
   12600         133 :     if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
   12601          59 :         return atoi(GetAuthorityCode("GEOGCS"));
   12602             : 
   12603             :     /* -------------------------------------------------------------------- */
   12604             :     /*      Get the datum and geogcs names.                                 */
   12605             :     /* -------------------------------------------------------------------- */
   12606             : 
   12607          74 :     const char *pszGEOGCS = GetAttrValue("GEOGCS");
   12608          74 :     const char *pszDatum = GetAttrValue("DATUM");
   12609             : 
   12610             :     // We can only operate on coordinate systems with a geogcs.
   12611         148 :     OGRSpatialReference oSRSTmp;
   12612          74 :     if (pszGEOGCS == nullptr || pszDatum == nullptr)
   12613             :     {
   12614             :         // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
   12615             :         // export to WKT1, so try to extract the geographic CRS through PROJ
   12616             :         // API with CopyGeogCSFrom() and get the nodes' values from it.
   12617           1 :         oSRSTmp.CopyGeogCSFrom(this);
   12618           1 :         pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
   12619           1 :         pszDatum = oSRSTmp.GetAttrValue("DATUM");
   12620           1 :         if (pszGEOGCS == nullptr || pszDatum == nullptr)
   12621             :         {
   12622           0 :             return -1;
   12623             :         }
   12624             :     }
   12625             : 
   12626             :     // Lookup geographic CRS name
   12627          74 :     const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
   12628          74 :     PJ_OBJ_LIST *list = proj_create_from_name(
   12629             :         d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
   12630          74 :     if (list)
   12631             :     {
   12632          74 :         const auto listSize = proj_list_get_count(list);
   12633          74 :         if (listSize == 1)
   12634             :         {
   12635          49 :             auto crs = proj_list_get(d->getPROJContext(), list, 0);
   12636          49 :             if (crs)
   12637             :             {
   12638          49 :                 pszAuthName = proj_get_id_auth_name(crs, 0);
   12639          49 :                 const char *pszCode = proj_get_id_code(crs, 0);
   12640          49 :                 if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
   12641             :                 {
   12642          47 :                     const int nCode = atoi(pszCode);
   12643          47 :                     proj_destroy(crs);
   12644          47 :                     proj_list_destroy(list);
   12645          47 :                     return nCode;
   12646             :                 }
   12647           2 :                 proj_destroy(crs);
   12648             :             }
   12649             :         }
   12650          27 :         proj_list_destroy(list);
   12651             :     }
   12652             : 
   12653             :     /* -------------------------------------------------------------------- */
   12654             :     /*      Is this a "well known" geographic coordinate system?            */
   12655             :     /* -------------------------------------------------------------------- */
   12656          81 :     const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
   12657          27 :                       strstr(pszDatum, "WGS") ||
   12658          27 :                       strstr(pszGEOGCS, "World Geodetic System") ||
   12659          27 :                       strstr(pszGEOGCS, "World_Geodetic_System") ||
   12660          81 :                       strstr(pszDatum, "World Geodetic System") ||
   12661          27 :                       strstr(pszDatum, "World_Geodetic_System");
   12662             : 
   12663          81 :     const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
   12664          27 :                       strstr(pszDatum, "NAD") ||
   12665          27 :                       strstr(pszGEOGCS, "North American") ||
   12666          27 :                       strstr(pszGEOGCS, "North_American") ||
   12667          81 :                       strstr(pszDatum, "North American") ||
   12668          27 :                       strstr(pszDatum, "North_American");
   12669             : 
   12670          27 :     if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
   12671           0 :         return 4326;
   12672             : 
   12673          27 :     if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
   12674           0 :         return 4322;
   12675             : 
   12676             :     // This is questionable as there are several 'flavors' of NAD83 that
   12677             :     // are not the same as 4269
   12678          27 :     if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
   12679           0 :         return 4269;
   12680             : 
   12681          27 :     if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
   12682           0 :         return 4267;
   12683             : 
   12684             :     /* -------------------------------------------------------------------- */
   12685             :     /*      If we know the datum, associate the most likely GCS with        */
   12686             :     /*      it.                                                             */
   12687             :     /* -------------------------------------------------------------------- */
   12688          27 :     const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
   12689          27 :     pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
   12690          27 :     if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
   12691           0 :         GetPrimeMeridian() == 0.0)
   12692             :     {
   12693           0 :         const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
   12694             : 
   12695           0 :         if (nDatum >= 6000 && nDatum <= 6999)
   12696           0 :             return nDatum - 2000;
   12697             :     }
   12698             : 
   12699          27 :     return -1;
   12700             : }
   12701             : 
   12702             : /************************************************************************/
   12703             : /*                          SetCoordinateEpoch()                        */
   12704             : /************************************************************************/
   12705             : 
   12706             : /** Set the coordinate epoch, as decimal year.
   12707             :  *
   12708             :  * In a dynamic CRS, coordinates of a point on the surface of the Earth may
   12709             :  * change with time. To be unambiguous the coordinates must always be qualified
   12710             :  * with the epoch at which they are valid. The coordinate epoch is not
   12711             :  * necessarily the epoch at which the observation was collected.
   12712             :  *
   12713             :  * Pedantically the coordinate epoch of an observation belongs to the
   12714             :  * observation, and not to the CRS, however it is often more practical to
   12715             :  * bind it to the CRS. The coordinate epoch should be specified for dynamic
   12716             :  * CRS (see IsDynamic())
   12717             :  *
   12718             :  * This method is the same as the OSRSetCoordinateEpoch() function.
   12719             :  *
   12720             :  * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
   12721             :  * @since OGR 3.4
   12722             :  */
   12723             : 
   12724         702 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
   12725             : {
   12726         702 :     d->m_coordinateEpoch = dfCoordinateEpoch;
   12727         702 : }
   12728             : 
   12729             : /************************************************************************/
   12730             : /*                      OSRSetCoordinateEpoch()                         */
   12731             : /************************************************************************/
   12732             : 
   12733             : /** \brief Set the coordinate epoch, as decimal year.
   12734             :  *
   12735             :  * See OGRSpatialReference::SetCoordinateEpoch()
   12736             :  *
   12737             :  * @since OGR 3.4
   12738             :  */
   12739          29 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
   12740             : {
   12741          29 :     VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
   12742             : 
   12743          29 :     return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
   12744          29 :         dfCoordinateEpoch);
   12745             : }
   12746             : 
   12747             : /************************************************************************/
   12748             : /*                          GetCoordinateEpoch()                        */
   12749             : /************************************************************************/
   12750             : 
   12751             : /** Return the coordinate epoch, as decimal year.
   12752             :  *
   12753             :  * In a dynamic CRS, coordinates of a point on the surface of the Earth may
   12754             :  * change with time. To be unambiguous the coordinates must always be qualified
   12755             :  * with the epoch at which they are valid. The coordinate epoch is not
   12756             :  * necessarily the epoch at which the observation was collected.
   12757             :  *
   12758             :  * Pedantically the coordinate epoch of an observation belongs to the
   12759             :  * observation, and not to the CRS, however it is often more practical to
   12760             :  * bind it to the CRS. The coordinate epoch should be specified for dynamic
   12761             :  * CRS (see IsDynamic())
   12762             :  *
   12763             :  * This method is the same as the OSRGetCoordinateEpoch() function.
   12764             :  *
   12765             :  * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
   12766             :  *                         if not set, or relevant.
   12767             :  * @since OGR 3.4
   12768             :  */
   12769             : 
   12770        9251 : double OGRSpatialReference::GetCoordinateEpoch() const
   12771             : {
   12772        9251 :     return d->m_coordinateEpoch;
   12773             : }
   12774             : 
   12775             : /************************************************************************/
   12776             : /*                      OSRGetCoordinateEpoch()                        */
   12777             : /************************************************************************/
   12778             : 
   12779             : /** \brief Get the coordinate epoch, as decimal year.
   12780             :  *
   12781             :  * See OGRSpatialReference::GetCoordinateEpoch()
   12782             :  *
   12783             :  * @since OGR 3.4
   12784             :  */
   12785         582 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
   12786             : {
   12787         582 :     VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
   12788             : 
   12789         582 :     return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
   12790             : }

Generated by: LCOV version 1.14