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

Generated by: LCOV version 1.14