LCOV - code coverage report
Current view: top level - ogr - ogr_srs_xml.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 338 408 82.8 %
Date: 2025-12-02 17:42:13 Functions: 21 21 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  OGRSpatialReference interface to OGC XML (014r4).
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2001, Frank Warmerdam (warmerdam@pobox.com)
       9             :  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogr_srs_api.h"
      16             : 
      17             : #include <cstdio>
      18             : #include <cstdlib>
      19             : #include <cstring>
      20             : 
      21             : #include "cpl_conv.h"
      22             : #include "cpl_error.h"
      23             : #include "cpl_minixml.h"
      24             : #include "cpl_multiproc.h"
      25             : #include "cpl_string.h"
      26             : #include "ogr_core.h"
      27             : #include "ogr_p.h"
      28             : #include "ogr_spatialref.h"
      29             : 
      30             : /************************************************************************/
      31             : /*                              parseURN()                              */
      32             : /*                                                                      */
      33             : /*      Parses requested sections out of URN.  The passed in URN        */
      34             : /*      *is* altered but the returned values point into the             */
      35             : /*      original string.                                                */
      36             : /************************************************************************/
      37             : 
      38           6 : static bool parseURN(char *pszURN, const char **ppszObjectType,
      39             :                      const char **ppszAuthority, const char **ppszCode,
      40             :                      const char **ppszVersion = nullptr)
      41             : 
      42             : {
      43           6 :     if (ppszObjectType != nullptr)
      44           0 :         *ppszObjectType = "";
      45           6 :     if (ppszAuthority != nullptr)
      46           6 :         *ppszAuthority = "";
      47           6 :     if (ppszCode != nullptr)
      48           6 :         *ppszCode = "";
      49           6 :     if (ppszVersion != nullptr)
      50           0 :         *ppszVersion = "";
      51             : 
      52             :     /* -------------------------------------------------------------------- */
      53             :     /*      Verify prefix.                                                  */
      54             :     /* -------------------------------------------------------------------- */
      55           6 :     if (!STARTS_WITH_CI(pszURN, "urn:ogc:def:"))
      56           0 :         return false;
      57             : 
      58             :     /* -------------------------------------------------------------------- */
      59             :     /*      Extract object type                                             */
      60             :     /* -------------------------------------------------------------------- */
      61           6 :     if (ppszObjectType != nullptr)
      62           0 :         *ppszObjectType = pszURN + 12;
      63             : 
      64           6 :     int i = 12;
      65          37 :     while (pszURN[i] != ':' && pszURN[i] != '\0')
      66          31 :         i++;
      67             : 
      68           6 :     if (pszURN[i] == '\0')
      69           0 :         return false;
      70             : 
      71           6 :     pszURN[i] = '\0';
      72           6 :     i++;
      73             : 
      74             :     /* -------------------------------------------------------------------- */
      75             :     /*      Extract authority                                               */
      76             :     /* -------------------------------------------------------------------- */
      77           6 :     if (ppszAuthority != nullptr)
      78           6 :         *ppszAuthority = pszURN + i;
      79             : 
      80          30 :     while (pszURN[i] != ':' && pszURN[i] != '\0')
      81          24 :         i++;
      82             : 
      83           6 :     if (pszURN[i] == '\0')
      84           0 :         return false;
      85             : 
      86           6 :     pszURN[i] = '\0';
      87           6 :     i++;
      88             : 
      89             :     /* -------------------------------------------------------------------- */
      90             :     /*      Extract version                                                 */
      91             :     /* -------------------------------------------------------------------- */
      92           6 :     if (ppszVersion != nullptr)
      93           0 :         *ppszVersion = pszURN + i;
      94             : 
      95           6 :     while (pszURN[i] != ':' && pszURN[i] != '\0')
      96           0 :         i++;
      97             : 
      98           6 :     if (pszURN[i] == '\0')
      99           0 :         return false;
     100             : 
     101           6 :     pszURN[i] = '\0';
     102           6 :     i++;
     103             : 
     104             :     /* -------------------------------------------------------------------- */
     105             :     /*      Extract code.                                                   */
     106             :     /* -------------------------------------------------------------------- */
     107           6 :     if (ppszCode != nullptr)
     108           6 :         *ppszCode = pszURN + i;
     109             : 
     110           6 :     return true;
     111             : }
     112             : 
     113             : /************************************************************************/
     114             : /*                               addURN()                               */
     115             : /************************************************************************/
     116             : 
     117          24 : static void addURN(CPLXMLNode *psTarget, const char *pszAuthority,
     118             :                    const char *pszObjectType, int nCode,
     119             :                    const char *pszVersion = "")
     120             : 
     121             : {
     122          24 :     if (pszVersion == nullptr)
     123           0 :         pszVersion = "";
     124             : 
     125          48 :     std::string osURN("urn:ogc:def:");
     126          24 :     osURN += pszObjectType;
     127          24 :     osURN += ':';
     128          24 :     osURN += pszAuthority;
     129          24 :     osURN += ':';
     130          24 :     osURN += pszVersion;
     131          24 :     osURN += ':';
     132             : 
     133          24 :     if (nCode != 0)
     134          24 :         osURN.append(std::to_string(nCode));
     135             : 
     136          24 :     CPLCreateXMLNode(CPLCreateXMLNode(psTarget, CXT_Attribute, "xlink:href"),
     137             :                      CXT_Text, osURN.c_str());
     138          24 : }
     139             : 
     140             : /************************************************************************/
     141             : /*                         AddValueIDWithURN()                          */
     142             : /*                                                                      */
     143             : /*      Adds element of the form <ElementName                           */
     144             : /*      xlink:href="urn_without_id">id</ElementName>"                   */
     145             : /************************************************************************/
     146             : 
     147          24 : static CPLXMLNode *AddValueIDWithURN(CPLXMLNode *psTarget,
     148             :                                      const char *pszElement,
     149             :                                      const char *pszAuthority,
     150             :                                      const char *pszObjectType, int nCode,
     151             :                                      const char *pszVersion = "")
     152             : 
     153             : {
     154          24 :     CPLXMLNode *psElement = CPLCreateXMLNode(psTarget, CXT_Element, pszElement);
     155          24 :     addURN(psElement, pszAuthority, pszObjectType, nCode, pszVersion);
     156             : 
     157          24 :     return psElement;
     158             : }
     159             : 
     160             : /************************************************************************/
     161             : /*                          addAuthorityIDBlock()                          */
     162             : /*                                                                      */
     163             : /*      Creates a structure like:                                       */
     164             : /*      <srsId>                                                         */
     165             : /*        <name codeSpace="urn">code</name>                             */
     166             : /*      </srsId>                                                        */
     167             : /************************************************************************/
     168         118 : static CPLXMLNode *addAuthorityIDBlock(CPLXMLNode *psTarget,
     169             :                                        const char *pszElement,
     170             :                                        const char *pszAuthority,
     171             :                                        const char *pszObjectType, int nCode,
     172             :                                        const char *pszVersion = "")
     173             : 
     174             : {
     175             :     /* -------------------------------------------------------------------- */
     176             :     /*      Prepare partial URN without the actual code.                    */
     177             :     /* -------------------------------------------------------------------- */
     178         118 :     if (pszVersion == nullptr)
     179          13 :         pszVersion = "";
     180             : 
     181         118 :     std::string osURN("urn:ogc:def:");
     182         118 :     osURN += pszObjectType;
     183         118 :     osURN += ':';
     184         118 :     osURN += pszAuthority;
     185         118 :     osURN += ':';
     186         118 :     osURN += pszVersion;
     187         118 :     osURN += ':';
     188             : 
     189             :     /* -------------------------------------------------------------------- */
     190             :     /*      Prepare the base name, eg. <srsID>.                             */
     191             :     /* -------------------------------------------------------------------- */
     192         118 :     CPLXMLNode *psElement = CPLCreateXMLNode(psTarget, CXT_Element, pszElement);
     193             : 
     194             :     /* -------------------------------------------------------------------- */
     195             :     /*      Prepare the name element.                                       */
     196             :     /* -------------------------------------------------------------------- */
     197         118 :     CPLXMLNode *psName = CPLCreateXMLNode(psElement, CXT_Element, "gml:name");
     198             : 
     199             :     /* -------------------------------------------------------------------- */
     200             :     /*      Prepare the codespace attribute.                                */
     201             :     /* -------------------------------------------------------------------- */
     202         118 :     CPLCreateXMLNode(CPLCreateXMLNode(psName, CXT_Attribute, "codeSpace"),
     203             :                      CXT_Text, osURN.c_str());
     204             : 
     205             :     /* -------------------------------------------------------------------- */
     206             :     /*      Attach code value to name node.                                 */
     207             :     /* -------------------------------------------------------------------- */
     208         118 :     char szCode[32] = {};
     209         118 :     snprintf(szCode, sizeof(szCode), "%d", nCode);
     210             : 
     211         118 :     CPLCreateXMLNode(psName, CXT_Text, szCode);
     212             : 
     213         236 :     return psElement;
     214             : }
     215             : 
     216             : /************************************************************************/
     217             : /*                              addGMLId()                              */
     218             : /************************************************************************/
     219             : 
     220         243 : static void addGMLId(CPLXMLNode *psParent)
     221             : {
     222             :     static CPLMutex *hGMLIdMutex = nullptr;
     223         486 :     CPLMutexHolderD(&hGMLIdMutex);
     224             : 
     225             :     static int nNextGMLId = 1;
     226         243 :     char szIdText[40] = {};
     227             : 
     228         243 :     snprintf(szIdText, sizeof(szIdText), "ogrcrs%d", nNextGMLId++);
     229             : 
     230         243 :     CPLCreateXMLNode(CPLCreateXMLNode(psParent, CXT_Attribute, "gml:id"),
     231             :                      CXT_Text, szIdText);
     232         243 : }
     233             : 
     234             : /************************************************************************/
     235             : /*                        exportAuthorityToXML()                        */
     236             : /************************************************************************/
     237             : 
     238         131 : static CPLXMLNode *exportAuthorityToXML(const OGR_SRSNode *poAuthParent,
     239             :                                         const char *pszTagName,
     240             :                                         CPLXMLNode *psXMLParent,
     241             :                                         const char *pszObjectType,
     242             :                                         int bUseSubName = TRUE)
     243             : 
     244             : {
     245             :     /* -------------------------------------------------------------------- */
     246             :     /*      Get authority node from parent.                                 */
     247             :     /* -------------------------------------------------------------------- */
     248         131 :     const int nAuthority = poAuthParent->FindChild("AUTHORITY");
     249         131 :     if (nAuthority == -1)
     250         118 :         return nullptr;
     251             : 
     252          13 :     const OGR_SRSNode *poAuthority = poAuthParent->GetChild(nAuthority);
     253             : 
     254             :     /* -------------------------------------------------------------------- */
     255             :     /*      Create identification.                                          */
     256             :     /* -------------------------------------------------------------------- */
     257          13 :     if (poAuthority->GetChildCount() < 2)
     258           0 :         return nullptr;
     259             : 
     260          13 :     const char *pszCodeSpace = poAuthority->GetChild(0)->GetValue();
     261          13 :     const char *pszCode = poAuthority->GetChild(1)->GetValue();
     262          13 :     const char *pszEdition = nullptr;
     263             : 
     264          13 :     if (bUseSubName)
     265          13 :         return addAuthorityIDBlock(psXMLParent, pszTagName, pszCodeSpace,
     266          13 :                                    pszObjectType, atoi(pszCode), pszEdition);
     267             : 
     268           0 :     return AddValueIDWithURN(psXMLParent, pszTagName, pszCodeSpace,
     269           0 :                              pszObjectType, atoi(pszCode), pszEdition);
     270             : }
     271             : 
     272             : /************************************************************************/
     273             : /*                             addProjArg()                             */
     274             : /************************************************************************/
     275             : 
     276          20 : static void addProjArg(const OGRSpatialReference *poSRS, CPLXMLNode *psBase,
     277             :                        const char *pszMeasureType, double dfDefault,
     278             :                        int nParameterID, const char *pszWKTName)
     279             : 
     280             : {
     281          20 :     CPLXMLNode *psNode = CPLCreateXMLNode(psBase, CXT_Element, "gml:usesValue");
     282             : 
     283             :     /* -------------------------------------------------------------------- */
     284             :     /*      Handle the UOM.                                                 */
     285             :     /* -------------------------------------------------------------------- */
     286          20 :     const char *pszUOMValue = EQUAL(pszMeasureType, "Angular")
     287             :                                   ? "urn:ogc:def:uom:EPSG::9102"
     288             :                                   : "urn:ogc:def:uom:EPSG::9001";
     289             : 
     290          20 :     CPLXMLNode *psValue = CPLCreateXMLNode(psNode, CXT_Element, "gml:value");
     291             : 
     292          20 :     CPLCreateXMLNode(CPLCreateXMLNode(psValue, CXT_Attribute, "uom"), CXT_Text,
     293             :                      pszUOMValue);
     294             : 
     295             :     /* -------------------------------------------------------------------- */
     296             :     /*      Add the parameter value itself.                                 */
     297             :     /* -------------------------------------------------------------------- */
     298             :     double dfParamValue =
     299          20 :         poSRS->GetNormProjParm(pszWKTName, dfDefault, nullptr);
     300             : 
     301          20 :     CPLCreateXMLNode(psValue, CXT_Text,
     302          40 :                      CPLString().Printf("%.16g", dfParamValue));
     303             : 
     304             :     /* -------------------------------------------------------------------- */
     305             :     /*      Add the valueOfParameter.                                       */
     306             :     /* -------------------------------------------------------------------- */
     307          20 :     AddValueIDWithURN(psNode, "gml:valueOfParameter", "EPSG", "parameter",
     308             :                       nParameterID);
     309          20 : }
     310             : 
     311             : /************************************************************************/
     312             : /*                              addAxis()                               */
     313             : /*                                                                      */
     314             : /*      Added the <usesAxis> element and down.                          */
     315             : /************************************************************************/
     316             : 
     317          70 : static CPLXMLNode *addAxis(CPLXMLNode *psXMLParent,
     318             :                            const char *pszAxis,  // "Lat", "Long", "E" or "N"
     319             :                            const OGR_SRSNode * /* poUnitsSrc */)
     320             : 
     321             : {
     322          70 :     CPLXMLNode *psAxisXML = CPLCreateXMLNode(
     323             :         CPLCreateXMLNode(psXMLParent, CXT_Element, "gml:usesAxis"), CXT_Element,
     324             :         "gml:CoordinateSystemAxis");
     325          70 :     if (!psAxisXML)
     326             :     {
     327           0 :         CPLError(CE_Failure, CPLE_AppDefined, "addAxis failed.");
     328           0 :         return nullptr;
     329             :     }
     330          70 :     addGMLId(psAxisXML);
     331             : 
     332          70 :     if (EQUAL(pszAxis, "Lat"))
     333             :     {
     334          31 :         CPLCreateXMLNode(CPLCreateXMLNode(psAxisXML, CXT_Attribute, "gml:uom"),
     335             :                          CXT_Text, "urn:ogc:def:uom:EPSG::9102");
     336             : 
     337          31 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:name", "Geodetic latitude");
     338          31 :         addAuthorityIDBlock(psAxisXML, "gml:axisID", "EPSG", "axis", 9901);
     339          31 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:axisAbbrev", "Lat");
     340          31 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:axisDirection", "north");
     341             :     }
     342          39 :     else if (EQUAL(pszAxis, "Long"))
     343             :     {
     344          31 :         CPLCreateXMLNode(CPLCreateXMLNode(psAxisXML, CXT_Attribute, "gml:uom"),
     345             :                          CXT_Text, "urn:ogc:def:uom:EPSG::9102");
     346             : 
     347          31 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:name",
     348             :                                     "Geodetic longitude");
     349          31 :         addAuthorityIDBlock(psAxisXML, "gml:axisID", "EPSG", "axis", 9902);
     350          31 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:axisAbbrev", "Lon");
     351          31 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:axisDirection", "east");
     352             :     }
     353           8 :     else if (EQUAL(pszAxis, "E"))
     354             :     {
     355           4 :         CPLCreateXMLNode(CPLCreateXMLNode(psAxisXML, CXT_Attribute, "gml:uom"),
     356             :                          CXT_Text, "urn:ogc:def:uom:EPSG::9001");
     357             : 
     358           4 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:name", "Easting");
     359           4 :         addAuthorityIDBlock(psAxisXML, "gml:axisID", "EPSG", "axis", 9906);
     360           4 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:axisAbbrev", "E");
     361           4 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:axisDirection", "east");
     362             :     }
     363           4 :     else if (EQUAL(pszAxis, "N"))
     364             :     {
     365           4 :         CPLCreateXMLNode(CPLCreateXMLNode(psAxisXML, CXT_Attribute, "gml:uom"),
     366             :                          CXT_Text, "urn:ogc:def:uom:EPSG::9001");
     367             : 
     368           4 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:name", "Northing");
     369           4 :         addAuthorityIDBlock(psAxisXML, "gml:axisID", "EPSG", "axis", 9907);
     370           4 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:axisAbbrev", "N");
     371           4 :         CPLCreateXMLElementAndValue(psAxisXML, "gml:axisDirection", "north");
     372             :     }
     373             :     else
     374             :     {
     375           0 :         CPLAssert(false);
     376             :     }
     377             : 
     378          70 :     return psAxisXML;
     379             : }
     380             : 
     381             : /************************************************************************/
     382             : /*                         exportGeogCSToXML()                          */
     383             : /************************************************************************/
     384             : 
     385          31 : static CPLXMLNode *exportGeogCSToXML(const OGRSpatialReference *poSRS)
     386             : 
     387             : {
     388          31 :     const OGR_SRSNode *poGeogCS = poSRS->GetAttrNode("GEOGCS");
     389             : 
     390          31 :     if (poGeogCS == nullptr)
     391           0 :         return nullptr;
     392             : 
     393             :     /* -------------------------------------------------------------------- */
     394             :     /*      Establish initial infrastructure.                               */
     395             :     /* -------------------------------------------------------------------- */
     396             :     CPLXMLNode *psGCS_XML =
     397          31 :         CPLCreateXMLNode(nullptr, CXT_Element, "gml:GeographicCRS");
     398          31 :     addGMLId(psGCS_XML);
     399             : 
     400             :     /* -------------------------------------------------------------------- */
     401             :     /*      Attach symbolic name (srsName).                                 */
     402             :     /* -------------------------------------------------------------------- */
     403          31 :     CPLCreateXMLElementAndValue(psGCS_XML, "gml:srsName",
     404             :                                 poGeogCS->GetChild(0)->GetValue());
     405             : 
     406             :     /* -------------------------------------------------------------------- */
     407             :     /*      Does the overall coordinate system have an authority?  If so    */
     408             :     /*      attach as an identification section.                            */
     409             :     /* -------------------------------------------------------------------- */
     410          31 :     exportAuthorityToXML(poGeogCS, "gml:srsID", psGCS_XML, "crs");
     411             : 
     412             :     /* -------------------------------------------------------------------- */
     413             :     /*      Insert a big whack of fixed stuff defining the                  */
     414             :     /*      ellipsoidalCS.  Basically this defines the axes and their       */
     415             :     /*      units.                                                          */
     416             :     /* -------------------------------------------------------------------- */
     417          31 :     CPLXMLNode *psECS = CPLCreateXMLNode(
     418             :         CPLCreateXMLNode(psGCS_XML, CXT_Element, "gml:usesEllipsoidalCS"),
     419             :         CXT_Element, "gml:EllipsoidalCS");
     420             : 
     421          31 :     addGMLId(psECS);
     422             : 
     423          31 :     CPLCreateXMLElementAndValue(psECS, "gml:csName", "ellipsoidal");
     424             : 
     425          31 :     addAuthorityIDBlock(psECS, "gml:csID", "EPSG", "cs", 6402);
     426             : 
     427          31 :     addAxis(psECS, "Lat", nullptr);
     428          31 :     addAxis(psECS, "Long", nullptr);
     429             : 
     430             :     /* -------------------------------------------------------------------- */
     431             :     /*      Start with the datum.                                           */
     432             :     /* -------------------------------------------------------------------- */
     433          31 :     const OGR_SRSNode *poDatum = poGeogCS->GetNode("DATUM");
     434             : 
     435          31 :     if (poDatum == nullptr)
     436             :     {
     437           0 :         CPLDestroyXMLNode(psGCS_XML);
     438           0 :         return nullptr;
     439             :     }
     440             : 
     441          31 :     CPLXMLNode *psDatumXML = CPLCreateXMLNode(
     442             :         CPLCreateXMLNode(psGCS_XML, CXT_Element, "gml:usesGeodeticDatum"),
     443             :         CXT_Element, "gml:GeodeticDatum");
     444             : 
     445          31 :     addGMLId(psDatumXML);
     446             : 
     447             :     /* -------------------------------------------------------------------- */
     448             :     /*      Set the datumName.                                              */
     449             :     /* -------------------------------------------------------------------- */
     450          31 :     CPLCreateXMLElementAndValue(psDatumXML, "gml:datumName",
     451             :                                 poDatum->GetChild(0)->GetValue());
     452             : 
     453             :     /* -------------------------------------------------------------------- */
     454             :     /*      Set authority id info if available.                             */
     455             :     /* -------------------------------------------------------------------- */
     456          31 :     exportAuthorityToXML(poDatum, "gml:datumID", psDatumXML, "datum");
     457             : 
     458             :     /* -------------------------------------------------------------------- */
     459             :     /*      Setup prime meridian information.                               */
     460             :     /* -------------------------------------------------------------------- */
     461          31 :     const OGR_SRSNode *poPMNode = poGeogCS->GetNode("PRIMEM");
     462          31 :     const char *pszPMName = "Greenwich";
     463          31 :     double dfPMOffset = poSRS->GetPrimeMeridian(&pszPMName);
     464             : 
     465          31 :     CPLXMLNode *psPM = CPLCreateXMLNode(
     466             :         CPLCreateXMLNode(psDatumXML, CXT_Element, "gml:usesPrimeMeridian"),
     467             :         CXT_Element, "gml:PrimeMeridian");
     468             : 
     469          31 :     addGMLId(psPM);
     470             : 
     471          31 :     CPLCreateXMLElementAndValue(psPM, "gml:meridianName", pszPMName);
     472             : 
     473          31 :     if (poPMNode)
     474          31 :         exportAuthorityToXML(poPMNode, "gml:meridianID", psPM, "meridian");
     475             : 
     476          31 :     CPLXMLNode *psAngle = CPLCreateXMLNode(
     477             :         CPLCreateXMLNode(psPM, CXT_Element, "gml:greenwichLongitude"),
     478             :         CXT_Element, "gml:angle");
     479             : 
     480          31 :     CPLCreateXMLNode(CPLCreateXMLNode(psAngle, CXT_Attribute, "uom"), CXT_Text,
     481             :                      "urn:ogc:def:uom:EPSG::9102");
     482             : 
     483          31 :     CPLCreateXMLNode(psAngle, CXT_Text,
     484          62 :                      CPLString().Printf("%.16g", dfPMOffset));
     485             : 
     486             :     /* -------------------------------------------------------------------- */
     487             :     /*      Translate the ellipsoid.                                        */
     488             :     /* -------------------------------------------------------------------- */
     489          31 :     const OGR_SRSNode *poEllipsoid = poDatum->GetNode("SPHEROID");
     490             : 
     491          31 :     if (poEllipsoid != nullptr)
     492             :     {
     493          31 :         CPLXMLNode *psEllipseXML = CPLCreateXMLNode(
     494             :             CPLCreateXMLNode(psDatumXML, CXT_Element, "gml:usesEllipsoid"),
     495             :             CXT_Element, "gml:Ellipsoid");
     496             : 
     497          31 :         addGMLId(psEllipseXML);
     498             : 
     499          31 :         CPLCreateXMLElementAndValue(psEllipseXML, "gml:ellipsoidName",
     500             :                                     poEllipsoid->GetChild(0)->GetValue());
     501             : 
     502          31 :         exportAuthorityToXML(poEllipsoid, "gml:ellipsoidID", psEllipseXML,
     503             :                              "ellipsoid");
     504             : 
     505             :         CPLXMLNode *psParamXML =
     506          31 :             CPLCreateXMLNode(psEllipseXML, CXT_Element, "gml:semiMajorAxis");
     507             : 
     508          31 :         CPLCreateXMLNode(CPLCreateXMLNode(psParamXML, CXT_Attribute, "uom"),
     509             :                          CXT_Text, "urn:ogc:def:uom:EPSG::9001");
     510             : 
     511          31 :         CPLCreateXMLNode(psParamXML, CXT_Text,
     512             :                          poEllipsoid->GetChild(1)->GetValue());
     513             : 
     514             :         psParamXML =
     515          31 :             CPLCreateXMLNode(CPLCreateXMLNode(psEllipseXML, CXT_Element,
     516             :                                               "gml:secondDefiningParameter"),
     517             :                              CXT_Element, "gml:inverseFlattening");
     518             : 
     519          31 :         CPLCreateXMLNode(CPLCreateXMLNode(psParamXML, CXT_Attribute, "uom"),
     520             :                          CXT_Text, "urn:ogc:def:uom:EPSG::9201");
     521             : 
     522          31 :         CPLCreateXMLNode(psParamXML, CXT_Text,
     523             :                          poEllipsoid->GetChild(2)->GetValue());
     524             :     }
     525             : 
     526          31 :     return psGCS_XML;
     527             : }
     528             : 
     529             : /************************************************************************/
     530             : /*                         exportProjCSToXML()                          */
     531             : /************************************************************************/
     532             : 
     533           7 : static CPLXMLNode *exportProjCSToXML(const OGRSpatialReference *poSRS)
     534             : 
     535             : {
     536           7 :     const OGR_SRSNode *poProjCS = poSRS->GetAttrNode("PROJCS");
     537             : 
     538           7 :     if (poProjCS == nullptr)
     539           0 :         return nullptr;
     540             : 
     541             :     /* -------------------------------------------------------------------- */
     542             :     /*      Establish initial infrastructure.                               */
     543             :     /* -------------------------------------------------------------------- */
     544             :     CPLXMLNode *psCRS_XML =
     545           7 :         CPLCreateXMLNode(nullptr, CXT_Element, "gml:ProjectedCRS");
     546           7 :     addGMLId(psCRS_XML);
     547             : 
     548             :     /* -------------------------------------------------------------------- */
     549             :     /*      Attach symbolic name (a name in a nameset).                     */
     550             :     /* -------------------------------------------------------------------- */
     551           7 :     CPLCreateXMLElementAndValue(psCRS_XML, "gml:srsName",
     552             :                                 poProjCS->GetChild(0)->GetValue());
     553             : 
     554             :     /* -------------------------------------------------------------------- */
     555             :     /*      Add authority info if we have it.                               */
     556             :     /* -------------------------------------------------------------------- */
     557           7 :     exportAuthorityToXML(poProjCS, "gml:srsID", psCRS_XML, "crs");
     558             : 
     559             :     /* -------------------------------------------------------------------- */
     560             :     /*      Use the GEOGCS as a <baseCRS>                                   */
     561             :     /* -------------------------------------------------------------------- */
     562             :     CPLXMLNode *psBaseCRSXML =
     563           7 :         CPLCreateXMLNode(psCRS_XML, CXT_Element, "gml:baseCRS");
     564             : 
     565           7 :     CPLAddXMLChild(psBaseCRSXML, exportGeogCSToXML(poSRS));
     566             : 
     567             :     /* -------------------------------------------------------------------- */
     568             :     /*      Our projected coordinate system is "defined by Conversion".     */
     569             :     /* -------------------------------------------------------------------- */
     570             :     CPLXMLNode *psDefinedBy =
     571           7 :         CPLCreateXMLNode(psCRS_XML, CXT_Element, "gml:definedByConversion");
     572             : 
     573             :     /* -------------------------------------------------------------------- */
     574             :     /*      Projections are handled as ParameterizedTransformations.        */
     575             :     /* -------------------------------------------------------------------- */
     576           7 :     const char *pszProjection = poSRS->GetAttrValue("PROJECTION");
     577             :     CPLXMLNode *psConv =
     578           7 :         CPLCreateXMLNode(psDefinedBy, CXT_Element, "gml:Conversion");
     579           7 :     addGMLId(psConv);
     580             : 
     581           7 :     CPLCreateXMLNode(
     582             :         CPLCreateXMLNode(psConv, CXT_Element, "gml:coordinateOperationName"),
     583             :         CXT_Text, pszProjection);
     584             : 
     585             :     /* -------------------------------------------------------------------- */
     586             :     /*      Transverse Mercator                                             */
     587             :     /* -------------------------------------------------------------------- */
     588           7 :     if (pszProjection == nullptr)
     589             :     {
     590           0 :         CPLError(CE_Failure, CPLE_NotSupported, "No projection method");
     591             :     }
     592           7 :     else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
     593             :     {
     594           4 :         AddValueIDWithURN(psConv, "gml:usesMethod", "EPSG", "method", 9807);
     595             : 
     596           4 :         addProjArg(poSRS, psConv, "Angular", 0.0, 8801,
     597             :                    SRS_PP_LATITUDE_OF_ORIGIN);
     598           4 :         addProjArg(poSRS, psConv, "Angular", 0.0, 8802,
     599             :                    SRS_PP_CENTRAL_MERIDIAN);
     600           4 :         addProjArg(poSRS, psConv, "Unitless", 1.0, 8805, SRS_PP_SCALE_FACTOR);
     601           4 :         addProjArg(poSRS, psConv, "Linear", 0.0, 8806, SRS_PP_FALSE_EASTING);
     602           4 :         addProjArg(poSRS, psConv, "Linear", 0.0, 8807, SRS_PP_FALSE_NORTHING);
     603             :     }
     604             :     /* -------------------------------------------------------------------- */
     605             :     /*      Lambert Conformal Conic                                         */
     606             :     /* -------------------------------------------------------------------- */
     607           3 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
     608             :     {
     609           0 :         AddValueIDWithURN(psConv, "gml:usesMethod", "EPSG", "method", 9801);
     610             : 
     611           0 :         addProjArg(poSRS, psConv, "Angular", 0.0, 8801,
     612             :                    SRS_PP_LATITUDE_OF_ORIGIN);
     613           0 :         addProjArg(poSRS, psConv, "Angular", 0.0, 8802,
     614             :                    SRS_PP_CENTRAL_MERIDIAN);
     615           0 :         addProjArg(poSRS, psConv, "Unitless", 1.0, 8805, SRS_PP_SCALE_FACTOR);
     616           0 :         addProjArg(poSRS, psConv, "Linear", 0.0, 8806, SRS_PP_FALSE_EASTING);
     617           0 :         addProjArg(poSRS, psConv, "Linear", 0.0, 8807, SRS_PP_FALSE_NORTHING);
     618             :     }
     619             :     else
     620             :     {
     621           3 :         CPLError(CE_Failure, CPLE_NotSupported,
     622             :                  "Unhandled projection method %s", pszProjection);
     623           3 :         CPLDestroyXMLNode(psCRS_XML);
     624           3 :         return nullptr;
     625             :     }
     626             : 
     627             :     /* -------------------------------------------------------------------- */
     628             :     /*      Define the cartesian coordinate system.                         */
     629             :     /* -------------------------------------------------------------------- */
     630           4 :     CPLXMLNode *psCCS = CPLCreateXMLNode(
     631             :         CPLCreateXMLNode(psCRS_XML, CXT_Element, "gml:usesCartesianCS"),
     632             :         CXT_Element, "gml:CartesianCS");
     633             : 
     634           4 :     addGMLId(psCCS);
     635             : 
     636           4 :     CPLCreateXMLElementAndValue(psCCS, "gml:csName", "Cartesian");
     637           4 :     addAuthorityIDBlock(psCCS, "gml:csID", "EPSG", "cs", 4400);
     638           4 :     addAxis(psCCS, "E", nullptr);
     639           4 :     addAxis(psCCS, "N", nullptr);
     640             : 
     641           4 :     return psCRS_XML;
     642             : }
     643             : 
     644             : /************************************************************************/
     645             : /*                            exportToXML()                             */
     646             : /************************************************************************/
     647             : 
     648             : /**
     649             :  * \brief Export coordinate system in XML format.
     650             :  *
     651             :  * Converts the loaded coordinate reference system into XML format
     652             :  * to the extent possible.  The string returned in ppszRawXML should be
     653             :  * deallocated by the caller with CPLFree() when no longer needed.
     654             :  *
     655             :  * LOCAL_CS coordinate systems are not translatable.  An empty string
     656             :  * will be returned along with OGRERR_NONE.
     657             :  *
     658             :  * This method is the equivalent of the C function OSRExportToXML().
     659             :  *
     660             :  * @param ppszRawXML pointer to which dynamically allocated XML definition
     661             :  * will be assigned.
     662             :  * @param pszDialect currently ignored. The dialect used is GML based.
     663             :  *
     664             :  * @return OGRERR_NONE on success or an error code on failure.
     665             :  */
     666             : 
     667          41 : OGRErr OGRSpatialReference::exportToXML(char **ppszRawXML,
     668             :                                         CPL_UNUSED const char *pszDialect) const
     669             : {
     670          41 :     CPLXMLNode *psXMLTree = nullptr;
     671             : 
     672          41 :     if (IsGeographic())
     673             :     {
     674          24 :         psXMLTree = exportGeogCSToXML(this);
     675             :     }
     676          17 :     else if (IsProjected())
     677             :     {
     678           7 :         psXMLTree = exportProjCSToXML(this);
     679             :     }
     680             :     else
     681          10 :         return OGRERR_UNSUPPORTED_SRS;
     682             : 
     683          31 :     if (!psXMLTree)
     684           3 :         return OGRERR_FAILURE;
     685             : 
     686          28 :     *ppszRawXML = CPLSerializeXMLTree(psXMLTree);
     687          28 :     CPLDestroyXMLNode(psXMLTree);
     688             : 
     689          28 :     return OGRERR_NONE;
     690             : }
     691             : 
     692             : /************************************************************************/
     693             : /*                           OSRExportToXML()                           */
     694             : /************************************************************************/
     695             : /**
     696             :  * \brief Export coordinate system in XML format.
     697             :  *
     698             :  * This function is the same as OGRSpatialReference::exportToXML().
     699             :  */
     700             : 
     701           2 : OGRErr OSRExportToXML(OGRSpatialReferenceH hSRS, char **ppszRawXML,
     702             :                       const char *pszDialect)
     703             : 
     704             : {
     705           2 :     VALIDATE_POINTER1(hSRS, "OSRExportToXML", OGRERR_FAILURE);
     706             : 
     707           2 :     return OGRSpatialReference::FromHandle(hSRS)->exportToXML(ppszRawXML,
     708           2 :                                                               pszDialect);
     709             : }
     710             : 
     711             : #ifdef notdef
     712             : /************************************************************************/
     713             : /*                           importXMLUnits()                           */
     714             : /************************************************************************/
     715             : 
     716             : static void importXMLUnits(CPLXMLNode *psSrcXML, const char *pszClass,
     717             :                            OGRSpatialReference *poSRS, const char *pszTarget)
     718             : 
     719             : {
     720             :     OGR_SRSNode *poNode = poSRS->GetAttrNode(pszTarget);
     721             : 
     722             :     CPLAssert(EQUAL(pszClass, "AngularUnit") || EQUAL(pszClass, "LinearUnit"));
     723             : 
     724             :     psSrcXML = CPLGetXMLNode(psSrcXML, pszClass);
     725             : 
     726             :     OGR_SRSNode *poUnits = NULL;
     727             :     const char *pszUnitName = NULL;
     728             :     const char *pszUnitsPer = NULL;
     729             : 
     730             :     // TODO(schwehr): Remove the goto.
     731             :     if (psSrcXML == NULL)
     732             :         goto DefaultTarget;
     733             : 
     734             :     pszUnitName = CPLGetXMLValue(psSrcXML, "NameSet.name", "unnamed");
     735             : 
     736             :     pszUnitsPer = EQUAL(pszClass, "AngularUnit")
     737             :                       ? CPLGetXMLValue(psSrcXML, "radiansPerUnit", NULL)
     738             :                       : CPLGetXMLValue(psSrcXML, "metresPerUnit", NULL);
     739             : 
     740             :     if (pszUnitsPer == NULL)
     741             :     {
     742             :         CPLDebug("OGR_SRS_XML", "Missing PerUnit value for %s.", pszClass);
     743             :         goto DefaultTarget;
     744             :     }
     745             : 
     746             :     if (poNode == NULL)
     747             :     {
     748             :         CPLDebug("OGR_SRS_XML", "Can't find %s in importXMLUnits.", pszTarget);
     749             :         goto DefaultTarget;
     750             :     }
     751             : 
     752             :     if (poNode->FindChild("UNIT") != -1)
     753             :     {
     754             :         poUnits = poNode->GetChild(poNode->FindChild("UNIT"));
     755             :         poUnits->GetChild(0)->SetValue(pszUnitName);
     756             :         poUnits->GetChild(1)->SetValue(pszUnitsPer);
     757             :     }
     758             :     else
     759             :     {
     760             :         poUnits = new OGR_SRSNode("UNIT");
     761             :         poUnits->AddChild(new OGR_SRSNode(pszUnitName));
     762             :         poUnits->AddChild(new OGR_SRSNode(pszUnitsPer));
     763             : 
     764             :         poNode->AddChild(poUnits);
     765             :     }
     766             :     return;
     767             : 
     768             : DefaultTarget:
     769             :     poUnits = new OGR_SRSNode("UNIT");
     770             :     if (EQUAL(pszClass, "AngularUnit"))
     771             :     {
     772             :         poUnits->AddChild(new OGR_SRSNode(SRS_UA_DEGREE));
     773             :         poUnits->AddChild(new OGR_SRSNode(SRS_UA_DEGREE_CONV));
     774             :     }
     775             :     else
     776             :     {
     777             :         poUnits->AddChild(new OGR_SRSNode(SRS_UL_METER));
     778             :         poUnits->AddChild(new OGR_SRSNode("1.0"));
     779             :     }
     780             : 
     781             :     poNode->AddChild(poUnits);
     782             : }
     783             : #endif
     784             : 
     785             : /************************************************************************/
     786             : /*                         importXMLAuthority()                         */
     787             : /************************************************************************/
     788             : 
     789           6 : static void importXMLAuthority(CPLXMLNode *psSrcXML, OGRSpatialReference *poSRS,
     790             :                                const char *pszSourceKey,
     791             :                                const char *pszTargetKey)
     792             : 
     793             : {
     794           6 :     CPLXMLNode *psIDNode = CPLGetXMLNode(psSrcXML, pszSourceKey);
     795           6 :     CPLXMLNode *psNameNode = CPLGetXMLNode(psIDNode, "name");
     796           6 :     CPLXMLNode *psCodeSpace = CPLGetXMLNode(psNameNode, "codeSpace");
     797             : 
     798           6 :     if (psIDNode == nullptr || psNameNode == nullptr || psCodeSpace == nullptr)
     799           0 :         return;
     800             : 
     801           6 :     char *pszURN = CPLStrdup(CPLGetXMLValue(psCodeSpace, "", ""));
     802             : 
     803             :     const char *pszAuthority;
     804             :     const char *pszCode;
     805           6 :     if (!parseURN(pszURN, nullptr, &pszAuthority, &pszCode))
     806             :     {
     807           0 :         CPLFree(pszURN);
     808           0 :         return;
     809             :     }
     810             : 
     811           6 :     if (strlen(pszCode) == 0)
     812           6 :         pszCode = CPLGetXMLValue(psNameNode, "", "");
     813             : 
     814           6 :     const int nCode = pszCode != nullptr ? atoi(pszCode) : 0;
     815             : 
     816           6 :     if (nCode != 0)
     817           6 :         poSRS->SetAuthority(pszTargetKey, pszAuthority, nCode);
     818             : 
     819           6 :     CPLFree(pszURN);
     820             : }
     821             : 
     822             : /************************************************************************/
     823             : /*                           ParseOGCDefURN()                           */
     824             : /*                                                                      */
     825             : /*      Parse out fields from a URN of the form:                        */
     826             : /*        urn:ogc:def:parameter:EPSG:6.3:9707                           */
     827             : /************************************************************************/
     828             : 
     829          16 : static bool ParseOGCDefURN(const char *pszURN, CPLString *poObjectType,
     830             :                            CPLString *poAuthority, CPLString *poVersion,
     831             :                            CPLString *poValue)
     832             : 
     833             : {
     834          16 :     if (poObjectType != nullptr)
     835          16 :         *poObjectType = "";
     836             : 
     837          16 :     if (poAuthority != nullptr)
     838          16 :         *poAuthority = "";
     839             : 
     840          16 :     if (poVersion != nullptr)
     841           0 :         *poVersion = "";
     842             : 
     843          16 :     if (poValue != nullptr)
     844          16 :         *poValue = "";
     845             : 
     846          16 :     if (pszURN == nullptr || !STARTS_WITH_CI(pszURN, "urn:ogc:def:"))
     847           0 :         return false;
     848             : 
     849             :     char **papszTokens =
     850          16 :         CSLTokenizeStringComplex(pszURN + 12, ":", FALSE, TRUE);
     851             : 
     852          16 :     if (CSLCount(papszTokens) != 4)
     853             :     {
     854           0 :         CSLDestroy(papszTokens);
     855           0 :         return false;
     856             :     }
     857             : 
     858          16 :     if (poObjectType != nullptr)
     859          16 :         *poObjectType = papszTokens[0];
     860             : 
     861          16 :     if (poAuthority != nullptr)
     862          16 :         *poAuthority = papszTokens[1];
     863             : 
     864          16 :     if (poVersion != nullptr)
     865           0 :         *poVersion = papszTokens[2];
     866             : 
     867          16 :     if (poValue != nullptr)
     868          16 :         *poValue = papszTokens[3];
     869             : 
     870          16 :     CSLDestroy(papszTokens);
     871          16 :     return true;
     872             : }
     873             : 
     874             : /************************************************************************/
     875             : /*                       getEPSGObjectCodeValue()                       */
     876             : /*                                                                      */
     877             : /*      Fetch a code value from the indicated node.  Should work on     */
     878             : /*      something of the form <elem xlink:href="urn:...:n" /> or        */
     879             : /*      something of the form <elem xlink:href="urn:...:">n</a>.        */
     880             : /************************************************************************/
     881             : 
     882          16 : static int getEPSGObjectCodeValue(CPLXMLNode *psNode,
     883             :                                   const char *pszEPSGObjectType, /*"method" */
     884             :                                   int nDefault)
     885             : 
     886             : {
     887          16 :     if (psNode == nullptr)
     888           0 :         return nDefault;
     889             : 
     890          16 :     const char *pszHrefVal = CPLGetXMLValue(psNode, "xlink:href", nullptr);
     891          16 :     if (pszHrefVal == nullptr)
     892           0 :         pszHrefVal = CPLGetXMLValue(psNode, "href", nullptr);
     893             : 
     894          32 :     CPLString osObjectType;
     895          32 :     CPLString osAuthority;
     896          32 :     CPLString osValue;
     897          16 :     if (!ParseOGCDefURN(pszHrefVal, &osObjectType, &osAuthority, nullptr,
     898             :                         &osValue))
     899           0 :         return nDefault;
     900             : 
     901          16 :     if (!EQUAL(osAuthority, "EPSG") || !EQUAL(osObjectType, pszEPSGObjectType))
     902           0 :         return nDefault;
     903             : 
     904          16 :     if (!osValue.empty())
     905          16 :         return atoi(osValue);
     906             : 
     907           0 :     const char *pszValue = CPLGetXMLValue(psNode, "", nullptr);
     908           0 :     if (pszValue != nullptr)
     909           0 :         return atoi(pszValue);
     910             : 
     911           0 :     return nDefault;
     912             : }
     913             : 
     914             : /************************************************************************/
     915             : /*                         getProjectionParam()                          */
     916             : /************************************************************************/
     917             : 
     918           5 : static double getProjectionParam(CPLXMLNode *psRootNode, int nParameterCode,
     919             :                                  const char * /*pszMeasureType */,
     920             :                                  double dfDefault)
     921             : 
     922             : {
     923           5 :     for (CPLXMLNode *psUsesParameter = psRootNode->psChild;
     924          25 :          psUsesParameter != nullptr; psUsesParameter = psUsesParameter->psNext)
     925             :     {
     926          25 :         if (psUsesParameter->eType != CXT_Element)
     927           0 :             continue;
     928             : 
     929          25 :         if (!EQUAL(psUsesParameter->pszValue, "usesParameterValue") &&
     930          25 :             !EQUAL(psUsesParameter->pszValue, "usesValue"))
     931          10 :             continue;
     932             : 
     933          15 :         if (getEPSGObjectCodeValue(
     934             :                 CPLGetXMLNode(psUsesParameter, "valueOfParameter"), "parameter",
     935          15 :                 0) == nParameterCode)
     936             :         {
     937             :             const char *pszValue =
     938           5 :                 CPLGetXMLValue(psUsesParameter, "value", nullptr);
     939             : 
     940           5 :             if (pszValue == nullptr)
     941           0 :                 return dfDefault;
     942             : 
     943           5 :             return CPLAtof(pszValue);
     944             :         }
     945             :     }
     946             : 
     947           0 :     return dfDefault;
     948             : }
     949             : 
     950             : /************************************************************************/
     951             : /*                         getNormalizedValue()                         */
     952             : /*                                                                      */
     953             : /*      Parse a node to get its numerical value, and then normalize     */
     954             : /*      into meters of degrees depending on the measure type.           */
     955             : /************************************************************************/
     956             : 
     957           3 : static double getNormalizedValue(CPLXMLNode *psNode, const char *pszPath,
     958             :                                  const char * /*pszMeasure*/, double dfDefault)
     959             : 
     960             : {
     961           3 :     CPLXMLNode *psTargetNode = pszPath == nullptr || strlen(pszPath) == 0
     962           3 :                                    ? psNode
     963           3 :                                    : CPLGetXMLNode(psNode, pszPath);
     964             : 
     965           3 :     if (psTargetNode == nullptr)
     966           0 :         return dfDefault;
     967             : 
     968           3 :     CPLXMLNode *psValueNode = psTargetNode->psChild;  // Used after for.
     969           6 :     for (; psValueNode != nullptr && psValueNode->eType != CXT_Text;
     970           3 :          psValueNode = psValueNode->psNext)
     971             :     {
     972             :     }
     973             : 
     974           3 :     if (psValueNode == nullptr)
     975           0 :         return dfDefault;
     976             : 
     977             :     // Add normalization later.
     978             : 
     979           3 :     return CPLAtof(psValueNode->pszValue);
     980             : }
     981             : 
     982             : /************************************************************************/
     983             : /*                        importGeogCSFromXML()                         */
     984             : /************************************************************************/
     985             : 
     986           1 : static OGRErr importGeogCSFromXML(OGRSpatialReference *poSRS, CPLXMLNode *psCRS)
     987             : 
     988             : {
     989             :     /* -------------------------------------------------------------------- */
     990             :     /*      Set the GEOGCS name from the srsName.                           */
     991             :     /* -------------------------------------------------------------------- */
     992             :     const char *pszGeogName =
     993           1 :         CPLGetXMLValue(psCRS, "srsName", "Unnamed GeogCS");
     994             : 
     995             :     /* -------------------------------------------------------------------- */
     996             :     /*      If we don't seem to have a detailed coordinate system           */
     997             :     /*      definition, check if we can define based on an EPSG code.       */
     998             :     /* -------------------------------------------------------------------- */
     999             :     CPLXMLNode *psDatum =
    1000           1 :         CPLGetXMLNode(psCRS, "usesGeodeticDatum.GeodeticDatum");
    1001             : 
    1002           1 :     if (psDatum == nullptr)
    1003             :     {
    1004           0 :         OGRSpatialReference oIdSRS;
    1005             : 
    1006           0 :         oIdSRS.SetLocalCS("dummy");
    1007           0 :         importXMLAuthority(psCRS, &oIdSRS, "srsID", "LOCAL_CS");
    1008             : 
    1009           0 :         if (oIdSRS.GetAuthorityCode("LOCAL_CS") != nullptr &&
    1010           0 :             oIdSRS.GetAuthorityName("LOCAL_CS") != nullptr &&
    1011           0 :             EQUAL(oIdSRS.GetAuthorityName("LOCAL_CS"), "EPSG"))
    1012             :         {
    1013           0 :             return poSRS->importFromEPSG(
    1014           0 :                 atoi(oIdSRS.GetAuthorityCode("LOCAL_CS")));
    1015             :         }
    1016             :     }
    1017             : 
    1018             :     /* -------------------------------------------------------------------- */
    1019             :     /*      Get datum name.                                                 */
    1020             :     /* -------------------------------------------------------------------- */
    1021             :     const char *pszDatumName =
    1022           1 :         CPLGetXMLValue(psDatum, "datumName", "Unnamed Datum");
    1023             : 
    1024             :     /* -------------------------------------------------------------------- */
    1025             :     /*      Get ellipsoid information.                                      */
    1026             :     /* -------------------------------------------------------------------- */
    1027           1 :     CPLXMLNode *psE = CPLGetXMLNode(psDatum, "usesEllipsoid.Ellipsoid");
    1028             :     const char *pszEllipsoidName =
    1029           1 :         CPLGetXMLValue(psE, "ellipsoidName", "Unnamed Ellipsoid");
    1030             : 
    1031             :     const double dfSemiMajor =
    1032           1 :         getNormalizedValue(psE, "semiMajorAxis", "Linear", SRS_WGS84_SEMIMAJOR);
    1033             : 
    1034           1 :     const double dfInvFlattening = getNormalizedValue(
    1035             :         psE, "secondDefiningParameter.inverseFlattening", "Unitless", 0.0);
    1036             : 
    1037           1 :     if (dfInvFlattening == 0.0)
    1038             :     {
    1039           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1040             :                  "Ellipsoid inverseFlattening corrupt or missing.");
    1041           0 :         return OGRERR_CORRUPT_DATA;
    1042             :     }
    1043             : 
    1044             :     /* -------------------------------------------------------------------- */
    1045             :     /*      Get the prime meridian.                                         */
    1046             :     /* -------------------------------------------------------------------- */
    1047           1 :     const char *pszPMName = nullptr;
    1048           1 :     double dfPMOffset = 0.0;
    1049             : 
    1050             :     CPLXMLNode *psPM =
    1051           1 :         CPLGetXMLNode(psDatum, "usesPrimeMeridian.PrimeMeridian");
    1052           1 :     if (psPM == nullptr)
    1053             :     {
    1054           0 :         pszPMName = "Greenwich";
    1055           0 :         dfPMOffset = 0.0;
    1056             :     }
    1057             :     else
    1058             :     {
    1059             :         pszPMName =
    1060           1 :             CPLGetXMLValue(psPM, "meridianName", "Unnamed Prime Meridian");
    1061           1 :         dfPMOffset = getNormalizedValue(psPM, "greenwichLongitude.angle",
    1062             :                                         "Angular", 0.0);
    1063             :     }
    1064             : 
    1065             :     /* -------------------------------------------------------------------- */
    1066             :     /*      Set the geographic definition.                                  */
    1067             :     /* -------------------------------------------------------------------- */
    1068           1 :     poSRS->SetGeogCS(pszGeogName, pszDatumName, pszEllipsoidName, dfSemiMajor,
    1069             :                      dfInvFlattening, pszPMName, dfPMOffset);
    1070             : 
    1071             : /* -------------------------------------------------------------------- */
    1072             : /*      Look for angular units.  We don't check that all axes match     */
    1073             : /*      at this time.                                                   */
    1074             : /* -------------------------------------------------------------------- */
    1075             : #if 0  // Does not compile.
    1076             :     CPLXMLNode *psAxis =
    1077             :         CPLGetXMLNode( psGeo2DCRS,
    1078             :                        "EllipsoidalCoordinateSystem.CoordinateAxis" );
    1079             :     importXMLUnits( psAxis, "AngularUnit", poSRS, "GEOGCS" );
    1080             : #endif
    1081             : 
    1082             :     /* -------------------------------------------------------------------- */
    1083             :     /*      Can we set authorities for any of the levels?                   */
    1084             :     /* -------------------------------------------------------------------- */
    1085           1 :     importXMLAuthority(psCRS, poSRS, "srsID", "GEOGCS");
    1086           1 :     importXMLAuthority(psDatum, poSRS, "datumID", "GEOGCS|DATUM");
    1087           1 :     importXMLAuthority(psE, poSRS, "ellipsoidID", "GEOGCS|DATUM|SPHEROID");
    1088           1 :     importXMLAuthority(psDatum, poSRS,
    1089             :                        "usesPrimeMeridian.PrimeMeridian.meridianID",
    1090             :                        "GEOGCS|PRIMEM");
    1091             : 
    1092           1 :     return OGRERR_NONE;
    1093             : }
    1094             : 
    1095             : /************************************************************************/
    1096             : /*                        importProjCSFromXML()                         */
    1097             : /************************************************************************/
    1098             : 
    1099           1 : static OGRErr importProjCSFromXML(OGRSpatialReference *poSRS, CPLXMLNode *psCRS)
    1100             : 
    1101             : {
    1102             :     /* -------------------------------------------------------------------- */
    1103             :     /*      Setup the PROJCS node with a name.                              */
    1104             :     /* -------------------------------------------------------------------- */
    1105           1 :     poSRS->SetProjCS(CPLGetXMLValue(psCRS, "srsName", "Unnamed"));
    1106             : 
    1107             :     /* -------------------------------------------------------------------- */
    1108             :     /*      Get authority information if available.  If we got it, and      */
    1109             :     /*      we seem to be lacking inline definition values, try and         */
    1110             :     /*      define according to the EPSG code for the PCS.                  */
    1111             :     /* -------------------------------------------------------------------- */
    1112           1 :     importXMLAuthority(psCRS, poSRS, "srsID", "PROJCS");
    1113             : 
    1114           1 :     if (poSRS->GetAuthorityCode("PROJCS") != nullptr &&
    1115           1 :         poSRS->GetAuthorityName("PROJCS") != nullptr &&
    1116           3 :         EQUAL(poSRS->GetAuthorityName("PROJCS"), "EPSG") &&
    1117           1 :         (CPLGetXMLNode(psCRS, "definedByConversion.Conversion") == nullptr ||
    1118           1 :          CPLGetXMLNode(psCRS, "baseCRS.GeographicCRS") == nullptr))
    1119             :     {
    1120           0 :         return poSRS->importFromEPSG(atoi(poSRS->GetAuthorityCode("PROJCS")));
    1121             :     }
    1122             : 
    1123             :     /* -------------------------------------------------------------------- */
    1124             :     /*      Try to set the GEOGCS info.                                     */
    1125             :     /* -------------------------------------------------------------------- */
    1126             : 
    1127           1 :     CPLXMLNode *psSubXML = CPLGetXMLNode(psCRS, "baseCRS.GeographicCRS");
    1128           1 :     if (psSubXML != nullptr)
    1129             :     {
    1130           1 :         const OGRErr eErr = importGeogCSFromXML(poSRS, psSubXML);
    1131           1 :         if (eErr != OGRERR_NONE)
    1132           0 :             return eErr;
    1133             :     }
    1134             : 
    1135             :     /* -------------------------------------------------------------------- */
    1136             :     /*      Get the conversion node.  It should be the only child of the    */
    1137             :     /*      definedByConversion node.                                       */
    1138             :     /* -------------------------------------------------------------------- */
    1139           1 :     CPLXMLNode *psConv = CPLGetXMLNode(psCRS, "definedByConversion.Conversion");
    1140           1 :     if (psConv == nullptr || psConv->eType != CXT_Element)
    1141             :     {
    1142           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1143             :                  "Unable to find a conversion node under the "
    1144             :                  "definedByConversion node of the ProjectedCRS.");
    1145           0 :         return OGRERR_CORRUPT_DATA;
    1146             :     }
    1147             : 
    1148             :     /* -------------------------------------------------------------------- */
    1149             :     /*      Determine the conversion method in effect.                      */
    1150             :     /* -------------------------------------------------------------------- */
    1151           1 :     const int nMethod = getEPSGObjectCodeValue(
    1152             :         CPLGetXMLNode(psConv, "usesMethod"), "method", 0);
    1153             : 
    1154             :     /* -------------------------------------------------------------------- */
    1155             :     /*      Transverse Mercator.                                            */
    1156             :     /* -------------------------------------------------------------------- */
    1157           1 :     if (nMethod == 9807)
    1158             :     {
    1159           1 :         poSRS->SetTM(getProjectionParam(psConv, 8801, "Angular", 0.0),
    1160             :                      getProjectionParam(psConv, 8802, "Angular", 0.0),
    1161             :                      getProjectionParam(psConv, 8805, "Unitless", 1.0),
    1162             :                      getProjectionParam(psConv, 8806, "Linear", 0.0),
    1163             :                      getProjectionParam(psConv, 8807, "Linear", 0.0));
    1164             :     }
    1165             : 
    1166             :     /* -------------------------------------------------------------------- */
    1167             :     /*      Didn't recognise?                                               */
    1168             :     /* -------------------------------------------------------------------- */
    1169             :     else
    1170             :     {
    1171           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1172             :                  "Conversion method %d not recognised.", nMethod);
    1173           0 :         return OGRERR_CORRUPT_DATA;
    1174             :     }
    1175             : 
    1176             :     // Re-set authority as all editions above will have removed it
    1177           1 :     importXMLAuthority(psCRS, poSRS, "srsID", "PROJCS");
    1178             : 
    1179             :     // Need to get linear units here!
    1180             : 
    1181           1 :     return OGRERR_NONE;
    1182             : }
    1183             : 
    1184             : /************************************************************************/
    1185             : /*                           importFromXML()                            */
    1186             : /************************************************************************/
    1187             : 
    1188             : /**
    1189             :  * \brief Import coordinate system from XML format (GML only currently).
    1190             :  *
    1191             :  * This method is the same as the C function OSRImportFromXML()
    1192             :  * @param pszXML XML string to import
    1193             :  * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
    1194             :  */
    1195           1 : OGRErr OGRSpatialReference::importFromXML(const char *pszXML)
    1196             : 
    1197             : {
    1198           1 :     Clear();
    1199             : 
    1200             :     /* -------------------------------------------------------------------- */
    1201             :     /*      Parse the XML.                                                  */
    1202             :     /* -------------------------------------------------------------------- */
    1203           1 :     CPLXMLNode *psTree = CPLParseXMLString(pszXML);
    1204             : 
    1205           1 :     if (psTree == nullptr)
    1206           0 :         return OGRERR_CORRUPT_DATA;
    1207             : 
    1208           1 :     CPLStripXMLNamespace(psTree, "gml", TRUE);
    1209             : 
    1210             :     /* -------------------------------------------------------------------- */
    1211             :     /*      Import according to the root node type.  We walk through        */
    1212             :     /*      root elements as there is sometimes prefix stuff like           */
    1213             :     /*      <?xml>.                                                         */
    1214             :     /* -------------------------------------------------------------------- */
    1215           1 :     OGRErr eErr = OGRERR_UNSUPPORTED_SRS;
    1216             : 
    1217           1 :     for (CPLXMLNode *psNode = psTree; psNode != nullptr;
    1218           0 :          psNode = psNode->psNext)
    1219             :     {
    1220           1 :         if (EQUAL(psNode->pszValue, "GeographicCRS"))
    1221             :         {
    1222           0 :             eErr = importGeogCSFromXML(this, psNode);
    1223           0 :             break;
    1224             :         }
    1225             : 
    1226           1 :         else if (EQUAL(psNode->pszValue, "ProjectedCRS"))
    1227             :         {
    1228           1 :             eErr = importProjCSFromXML(this, psNode);
    1229           1 :             break;
    1230             :         }
    1231             :     }
    1232             : 
    1233           1 :     CPLDestroyXMLNode(psTree);
    1234             : 
    1235           1 :     return eErr;
    1236             : }
    1237             : 
    1238             : /************************************************************************/
    1239             : /*                          OSRImportFromXML()                          */
    1240             : /************************************************************************/
    1241             : 
    1242             : /**
    1243             :  * \brief Import coordinate system from XML format (GML only currently).
    1244             :  *
    1245             :  * This function is the same as OGRSpatialReference::importFromXML().
    1246             :  */
    1247           1 : OGRErr OSRImportFromXML(OGRSpatialReferenceH hSRS, const char *pszXML)
    1248             : 
    1249             : {
    1250           1 :     VALIDATE_POINTER1(hSRS, "OSRImportFromXML", OGRERR_FAILURE);
    1251           1 :     VALIDATE_POINTER1(pszXML, "OSRImportFromXML", OGRERR_FAILURE);
    1252             : 
    1253           1 :     return OGRSpatialReference::FromHandle(hSRS)->importFromXML(pszXML);
    1254             : }

Generated by: LCOV version 1.14