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

Generated by: LCOV version 1.14