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