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