Coverage Report

Created: 2025-06-13 06:18

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