Coverage Report

Created: 2025-11-16 06:25

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