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