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 48 : std::string osURN("urn:ogc:def:");
126 24 : osURN += pszObjectType;
127 24 : osURN += ':';
128 24 : osURN += pszAuthority;
129 24 : osURN += ':';
130 24 : osURN += pszVersion;
131 24 : osURN += ':';
132 :
133 24 : if (nCode != 0)
134 24 : osURN.append(std::to_string(nCode));
135 :
136 24 : CPLCreateXMLNode(CPLCreateXMLNode(psTarget, CXT_Attribute, "xlink:href"),
137 : CXT_Text, osURN.c_str());
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 : std::string osURN("urn:ogc:def:");
182 118 : osURN += pszObjectType;
183 118 : osURN += ':';
184 118 : osURN += pszAuthority;
185 118 : osURN += ':';
186 118 : osURN += pszVersion;
187 118 : osURN += ':';
188 :
189 : /* -------------------------------------------------------------------- */
190 : /* Prepare the base name, eg. <srsID>. */
191 : /* -------------------------------------------------------------------- */
192 118 : CPLXMLNode *psElement = CPLCreateXMLNode(psTarget, CXT_Element, pszElement);
193 :
194 : /* -------------------------------------------------------------------- */
195 : /* Prepare the name element. */
196 : /* -------------------------------------------------------------------- */
197 118 : CPLXMLNode *psName = CPLCreateXMLNode(psElement, CXT_Element, "gml:name");
198 :
199 : /* -------------------------------------------------------------------- */
200 : /* Prepare the codespace attribute. */
201 : /* -------------------------------------------------------------------- */
202 118 : CPLCreateXMLNode(CPLCreateXMLNode(psName, CXT_Attribute, "codeSpace"),
203 : CXT_Text, osURN.c_str());
204 :
205 : /* -------------------------------------------------------------------- */
206 : /* Attach code value to name node. */
207 : /* -------------------------------------------------------------------- */
208 118 : char szCode[32] = {};
209 118 : snprintf(szCode, sizeof(szCode), "%d", nCode);
210 :
211 118 : CPLCreateXMLNode(psName, CXT_Text, szCode);
212 :
213 236 : return psElement;
214 : }
215 :
216 : /************************************************************************/
217 : /* addGMLId() */
218 : /************************************************************************/
219 :
220 243 : static void addGMLId(CPLXMLNode *psParent)
221 : {
222 : static CPLMutex *hGMLIdMutex = nullptr;
223 486 : CPLMutexHolderD(&hGMLIdMutex);
224 :
225 : static int nNextGMLId = 1;
226 243 : char szIdText[40] = {};
227 :
228 243 : snprintf(szIdText, sizeof(szIdText), "ogrcrs%d", nNextGMLId++);
229 :
230 243 : CPLCreateXMLNode(CPLCreateXMLNode(psParent, CXT_Attribute, "gml:id"),
231 : CXT_Text, szIdText);
232 243 : }
233 :
234 : /************************************************************************/
235 : /* exportAuthorityToXML() */
236 : /************************************************************************/
237 :
238 131 : static CPLXMLNode *exportAuthorityToXML(const OGR_SRSNode *poAuthParent,
239 : const char *pszTagName,
240 : CPLXMLNode *psXMLParent,
241 : const char *pszObjectType,
242 : int bUseSubName = TRUE)
243 :
244 : {
245 : /* -------------------------------------------------------------------- */
246 : /* Get authority node from parent. */
247 : /* -------------------------------------------------------------------- */
248 131 : const int nAuthority = poAuthParent->FindChild("AUTHORITY");
249 131 : if (nAuthority == -1)
250 118 : return nullptr;
251 :
252 13 : const OGR_SRSNode *poAuthority = poAuthParent->GetChild(nAuthority);
253 :
254 : /* -------------------------------------------------------------------- */
255 : /* Create identification. */
256 : /* -------------------------------------------------------------------- */
257 13 : if (poAuthority->GetChildCount() < 2)
258 0 : return nullptr;
259 :
260 13 : const char *pszCodeSpace = poAuthority->GetChild(0)->GetValue();
261 13 : const char *pszCode = poAuthority->GetChild(1)->GetValue();
262 13 : const char *pszEdition = nullptr;
263 :
264 13 : if (bUseSubName)
265 13 : return addAuthorityIDBlock(psXMLParent, pszTagName, pszCodeSpace,
266 13 : pszObjectType, atoi(pszCode), pszEdition);
267 :
268 0 : return AddValueIDWithURN(psXMLParent, pszTagName, pszCodeSpace,
269 0 : pszObjectType, atoi(pszCode), pszEdition);
270 : }
271 :
272 : /************************************************************************/
273 : /* addProjArg() */
274 : /************************************************************************/
275 :
276 20 : static void addProjArg(const OGRSpatialReference *poSRS, CPLXMLNode *psBase,
277 : const char *pszMeasureType, double dfDefault,
278 : int nParameterID, const char *pszWKTName)
279 :
280 : {
281 20 : CPLXMLNode *psNode = CPLCreateXMLNode(psBase, CXT_Element, "gml:usesValue");
282 :
283 : /* -------------------------------------------------------------------- */
284 : /* Handle the UOM. */
285 : /* -------------------------------------------------------------------- */
286 20 : const char *pszUOMValue = EQUAL(pszMeasureType, "Angular")
287 : ? "urn:ogc:def:uom:EPSG::9102"
288 : : "urn:ogc:def:uom:EPSG::9001";
289 :
290 20 : CPLXMLNode *psValue = CPLCreateXMLNode(psNode, CXT_Element, "gml:value");
291 :
292 20 : CPLCreateXMLNode(CPLCreateXMLNode(psValue, CXT_Attribute, "uom"), CXT_Text,
293 : pszUOMValue);
294 :
295 : /* -------------------------------------------------------------------- */
296 : /* Add the parameter value itself. */
297 : /* -------------------------------------------------------------------- */
298 : double dfParamValue =
299 20 : poSRS->GetNormProjParm(pszWKTName, dfDefault, nullptr);
300 :
301 20 : CPLCreateXMLNode(psValue, CXT_Text,
302 40 : CPLString().Printf("%.16g", dfParamValue));
303 :
304 : /* -------------------------------------------------------------------- */
305 : /* Add the valueOfParameter. */
306 : /* -------------------------------------------------------------------- */
307 20 : AddValueIDWithURN(psNode, "gml:valueOfParameter", "EPSG", "parameter",
308 : nParameterID);
309 20 : }
310 :
311 : /************************************************************************/
312 : /* addAxis() */
313 : /* */
314 : /* Added the <usesAxis> element and down. */
315 : /************************************************************************/
316 :
317 70 : static CPLXMLNode *addAxis(CPLXMLNode *psXMLParent,
318 : const char *pszAxis, // "Lat", "Long", "E" or "N"
319 : const OGR_SRSNode * /* poUnitsSrc */)
320 :
321 : {
322 70 : CPLXMLNode *psAxisXML = CPLCreateXMLNode(
323 : CPLCreateXMLNode(psXMLParent, CXT_Element, "gml:usesAxis"), CXT_Element,
324 : "gml:CoordinateSystemAxis");
325 70 : if (!psAxisXML)
326 : {
327 0 : CPLError(CE_Failure, CPLE_AppDefined, "addAxis failed.");
328 0 : return nullptr;
329 : }
330 70 : addGMLId(psAxisXML);
331 :
332 70 : if (EQUAL(pszAxis, "Lat"))
333 : {
334 31 : CPLCreateXMLNode(CPLCreateXMLNode(psAxisXML, CXT_Attribute, "gml:uom"),
335 : CXT_Text, "urn:ogc:def:uom:EPSG::9102");
336 :
337 31 : CPLCreateXMLElementAndValue(psAxisXML, "gml:name", "Geodetic latitude");
338 31 : addAuthorityIDBlock(psAxisXML, "gml:axisID", "EPSG", "axis", 9901);
339 31 : CPLCreateXMLElementAndValue(psAxisXML, "gml:axisAbbrev", "Lat");
340 31 : CPLCreateXMLElementAndValue(psAxisXML, "gml:axisDirection", "north");
341 : }
342 39 : else if (EQUAL(pszAxis, "Long"))
343 : {
344 31 : CPLCreateXMLNode(CPLCreateXMLNode(psAxisXML, CXT_Attribute, "gml:uom"),
345 : CXT_Text, "urn:ogc:def:uom:EPSG::9102");
346 :
347 31 : CPLCreateXMLElementAndValue(psAxisXML, "gml:name",
348 : "Geodetic longitude");
349 31 : addAuthorityIDBlock(psAxisXML, "gml:axisID", "EPSG", "axis", 9902);
350 31 : CPLCreateXMLElementAndValue(psAxisXML, "gml:axisAbbrev", "Lon");
351 31 : CPLCreateXMLElementAndValue(psAxisXML, "gml:axisDirection", "east");
352 : }
353 8 : else if (EQUAL(pszAxis, "E"))
354 : {
355 4 : CPLCreateXMLNode(CPLCreateXMLNode(psAxisXML, CXT_Attribute, "gml:uom"),
356 : CXT_Text, "urn:ogc:def:uom:EPSG::9001");
357 :
358 4 : CPLCreateXMLElementAndValue(psAxisXML, "gml:name", "Easting");
359 4 : addAuthorityIDBlock(psAxisXML, "gml:axisID", "EPSG", "axis", 9906);
360 4 : CPLCreateXMLElementAndValue(psAxisXML, "gml:axisAbbrev", "E");
361 4 : CPLCreateXMLElementAndValue(psAxisXML, "gml:axisDirection", "east");
362 : }
363 4 : else if (EQUAL(pszAxis, "N"))
364 : {
365 4 : CPLCreateXMLNode(CPLCreateXMLNode(psAxisXML, CXT_Attribute, "gml:uom"),
366 : CXT_Text, "urn:ogc:def:uom:EPSG::9001");
367 :
368 4 : CPLCreateXMLElementAndValue(psAxisXML, "gml:name", "Northing");
369 4 : addAuthorityIDBlock(psAxisXML, "gml:axisID", "EPSG", "axis", 9907);
370 4 : CPLCreateXMLElementAndValue(psAxisXML, "gml:axisAbbrev", "N");
371 4 : CPLCreateXMLElementAndValue(psAxisXML, "gml:axisDirection", "north");
372 : }
373 : else
374 : {
375 0 : CPLAssert(false);
376 : }
377 :
378 70 : return psAxisXML;
379 : }
380 :
381 : /************************************************************************/
382 : /* exportGeogCSToXML() */
383 : /************************************************************************/
384 :
385 31 : static CPLXMLNode *exportGeogCSToXML(const OGRSpatialReference *poSRS)
386 :
387 : {
388 31 : const OGR_SRSNode *poGeogCS = poSRS->GetAttrNode("GEOGCS");
389 :
390 31 : if (poGeogCS == nullptr)
391 0 : return nullptr;
392 :
393 : /* -------------------------------------------------------------------- */
394 : /* Establish initial infrastructure. */
395 : /* -------------------------------------------------------------------- */
396 : CPLXMLNode *psGCS_XML =
397 31 : CPLCreateXMLNode(nullptr, CXT_Element, "gml:GeographicCRS");
398 31 : addGMLId(psGCS_XML);
399 :
400 : /* -------------------------------------------------------------------- */
401 : /* Attach symbolic name (srsName). */
402 : /* -------------------------------------------------------------------- */
403 31 : CPLCreateXMLElementAndValue(psGCS_XML, "gml:srsName",
404 : 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 31 : 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 31 : CPLXMLNode *psECS = CPLCreateXMLNode(
418 : CPLCreateXMLNode(psGCS_XML, CXT_Element, "gml:usesEllipsoidalCS"),
419 : CXT_Element, "gml:EllipsoidalCS");
420 :
421 31 : addGMLId(psECS);
422 :
423 31 : CPLCreateXMLElementAndValue(psECS, "gml:csName", "ellipsoidal");
424 :
425 31 : addAuthorityIDBlock(psECS, "gml:csID", "EPSG", "cs", 6402);
426 :
427 31 : addAxis(psECS, "Lat", nullptr);
428 31 : addAxis(psECS, "Long", nullptr);
429 :
430 : /* -------------------------------------------------------------------- */
431 : /* Start with the datum. */
432 : /* -------------------------------------------------------------------- */
433 31 : const OGR_SRSNode *poDatum = poGeogCS->GetNode("DATUM");
434 :
435 31 : if (poDatum == nullptr)
436 : {
437 0 : CPLDestroyXMLNode(psGCS_XML);
438 0 : return nullptr;
439 : }
440 :
441 31 : CPLXMLNode *psDatumXML = CPLCreateXMLNode(
442 : CPLCreateXMLNode(psGCS_XML, CXT_Element, "gml:usesGeodeticDatum"),
443 : CXT_Element, "gml:GeodeticDatum");
444 :
445 31 : addGMLId(psDatumXML);
446 :
447 : /* -------------------------------------------------------------------- */
448 : /* Set the datumName. */
449 : /* -------------------------------------------------------------------- */
450 31 : CPLCreateXMLElementAndValue(psDatumXML, "gml:datumName",
451 : poDatum->GetChild(0)->GetValue());
452 :
453 : /* -------------------------------------------------------------------- */
454 : /* Set authority id info if available. */
455 : /* -------------------------------------------------------------------- */
456 31 : exportAuthorityToXML(poDatum, "gml:datumID", psDatumXML, "datum");
457 :
458 : /* -------------------------------------------------------------------- */
459 : /* Setup prime meridian information. */
460 : /* -------------------------------------------------------------------- */
461 31 : const OGR_SRSNode *poPMNode = poGeogCS->GetNode("PRIMEM");
462 31 : const char *pszPMName = "Greenwich";
463 31 : double dfPMOffset = poSRS->GetPrimeMeridian(&pszPMName);
464 :
465 31 : CPLXMLNode *psPM = CPLCreateXMLNode(
466 : CPLCreateXMLNode(psDatumXML, CXT_Element, "gml:usesPrimeMeridian"),
467 : CXT_Element, "gml:PrimeMeridian");
468 :
469 31 : addGMLId(psPM);
470 :
471 31 : CPLCreateXMLElementAndValue(psPM, "gml:meridianName", pszPMName);
472 :
473 31 : if (poPMNode)
474 31 : exportAuthorityToXML(poPMNode, "gml:meridianID", psPM, "meridian");
475 :
476 31 : CPLXMLNode *psAngle = CPLCreateXMLNode(
477 : CPLCreateXMLNode(psPM, CXT_Element, "gml:greenwichLongitude"),
478 : CXT_Element, "gml:angle");
479 :
480 31 : CPLCreateXMLNode(CPLCreateXMLNode(psAngle, CXT_Attribute, "uom"), CXT_Text,
481 : "urn:ogc:def:uom:EPSG::9102");
482 :
483 31 : CPLCreateXMLNode(psAngle, CXT_Text,
484 62 : CPLString().Printf("%.16g", dfPMOffset));
485 :
486 : /* -------------------------------------------------------------------- */
487 : /* Translate the ellipsoid. */
488 : /* -------------------------------------------------------------------- */
489 31 : const OGR_SRSNode *poEllipsoid = poDatum->GetNode("SPHEROID");
490 :
491 31 : if (poEllipsoid != nullptr)
492 : {
493 31 : CPLXMLNode *psEllipseXML = CPLCreateXMLNode(
494 : CPLCreateXMLNode(psDatumXML, CXT_Element, "gml:usesEllipsoid"),
495 : CXT_Element, "gml:Ellipsoid");
496 :
497 31 : addGMLId(psEllipseXML);
498 :
499 31 : CPLCreateXMLElementAndValue(psEllipseXML, "gml:ellipsoidName",
500 : poEllipsoid->GetChild(0)->GetValue());
501 :
502 31 : exportAuthorityToXML(poEllipsoid, "gml:ellipsoidID", psEllipseXML,
503 : "ellipsoid");
504 :
505 : CPLXMLNode *psParamXML =
506 31 : CPLCreateXMLNode(psEllipseXML, CXT_Element, "gml:semiMajorAxis");
507 :
508 31 : CPLCreateXMLNode(CPLCreateXMLNode(psParamXML, CXT_Attribute, "uom"),
509 : CXT_Text, "urn:ogc:def:uom:EPSG::9001");
510 :
511 31 : CPLCreateXMLNode(psParamXML, CXT_Text,
512 : poEllipsoid->GetChild(1)->GetValue());
513 :
514 : psParamXML =
515 31 : CPLCreateXMLNode(CPLCreateXMLNode(psEllipseXML, CXT_Element,
516 : "gml:secondDefiningParameter"),
517 : CXT_Element, "gml:inverseFlattening");
518 :
519 31 : CPLCreateXMLNode(CPLCreateXMLNode(psParamXML, CXT_Attribute, "uom"),
520 : CXT_Text, "urn:ogc:def:uom:EPSG::9201");
521 :
522 31 : CPLCreateXMLNode(psParamXML, CXT_Text,
523 : poEllipsoid->GetChild(2)->GetValue());
524 : }
525 :
526 31 : return psGCS_XML;
527 : }
528 :
529 : /************************************************************************/
530 : /* exportProjCSToXML() */
531 : /************************************************************************/
532 :
533 7 : static CPLXMLNode *exportProjCSToXML(const OGRSpatialReference *poSRS)
534 :
535 : {
536 7 : const OGR_SRSNode *poProjCS = poSRS->GetAttrNode("PROJCS");
537 :
538 7 : if (poProjCS == nullptr)
539 0 : return nullptr;
540 :
541 : /* -------------------------------------------------------------------- */
542 : /* Establish initial infrastructure. */
543 : /* -------------------------------------------------------------------- */
544 : CPLXMLNode *psCRS_XML =
545 7 : CPLCreateXMLNode(nullptr, CXT_Element, "gml:ProjectedCRS");
546 7 : addGMLId(psCRS_XML);
547 :
548 : /* -------------------------------------------------------------------- */
549 : /* Attach symbolic name (a name in a nameset). */
550 : /* -------------------------------------------------------------------- */
551 7 : CPLCreateXMLElementAndValue(psCRS_XML, "gml:srsName",
552 : poProjCS->GetChild(0)->GetValue());
553 :
554 : /* -------------------------------------------------------------------- */
555 : /* Add authority info if we have it. */
556 : /* -------------------------------------------------------------------- */
557 7 : exportAuthorityToXML(poProjCS, "gml:srsID", psCRS_XML, "crs");
558 :
559 : /* -------------------------------------------------------------------- */
560 : /* Use the GEOGCS as a <baseCRS> */
561 : /* -------------------------------------------------------------------- */
562 : CPLXMLNode *psBaseCRSXML =
563 7 : CPLCreateXMLNode(psCRS_XML, CXT_Element, "gml:baseCRS");
564 :
565 7 : CPLAddXMLChild(psBaseCRSXML, exportGeogCSToXML(poSRS));
566 :
567 : /* -------------------------------------------------------------------- */
568 : /* Our projected coordinate system is "defined by Conversion". */
569 : /* -------------------------------------------------------------------- */
570 : CPLXMLNode *psDefinedBy =
571 7 : CPLCreateXMLNode(psCRS_XML, CXT_Element, "gml:definedByConversion");
572 :
573 : /* -------------------------------------------------------------------- */
574 : /* Projections are handled as ParameterizedTransformations. */
575 : /* -------------------------------------------------------------------- */
576 7 : const char *pszProjection = poSRS->GetAttrValue("PROJECTION");
577 : CPLXMLNode *psConv =
578 7 : CPLCreateXMLNode(psDefinedBy, CXT_Element, "gml:Conversion");
579 7 : addGMLId(psConv);
580 :
581 7 : CPLCreateXMLNode(
582 : CPLCreateXMLNode(psConv, CXT_Element, "gml:coordinateOperationName"),
583 : CXT_Text, pszProjection);
584 :
585 : /* -------------------------------------------------------------------- */
586 : /* Transverse Mercator */
587 : /* -------------------------------------------------------------------- */
588 7 : if (pszProjection == nullptr)
589 : {
590 0 : CPLError(CE_Failure, CPLE_NotSupported, "No projection method");
591 : }
592 7 : else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
593 : {
594 4 : AddValueIDWithURN(psConv, "gml:usesMethod", "EPSG", "method", 9807);
595 :
596 4 : addProjArg(poSRS, psConv, "Angular", 0.0, 8801,
597 : SRS_PP_LATITUDE_OF_ORIGIN);
598 4 : addProjArg(poSRS, psConv, "Angular", 0.0, 8802,
599 : SRS_PP_CENTRAL_MERIDIAN);
600 4 : addProjArg(poSRS, psConv, "Unitless", 1.0, 8805, SRS_PP_SCALE_FACTOR);
601 4 : addProjArg(poSRS, psConv, "Linear", 0.0, 8806, SRS_PP_FALSE_EASTING);
602 4 : addProjArg(poSRS, psConv, "Linear", 0.0, 8807, SRS_PP_FALSE_NORTHING);
603 : }
604 : /* -------------------------------------------------------------------- */
605 : /* Lambert Conformal Conic */
606 : /* -------------------------------------------------------------------- */
607 3 : else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
608 : {
609 0 : AddValueIDWithURN(psConv, "gml:usesMethod", "EPSG", "method", 9801);
610 :
611 0 : addProjArg(poSRS, psConv, "Angular", 0.0, 8801,
612 : SRS_PP_LATITUDE_OF_ORIGIN);
613 0 : addProjArg(poSRS, psConv, "Angular", 0.0, 8802,
614 : 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 : }
619 : else
620 : {
621 3 : CPLError(CE_Failure, CPLE_NotSupported,
622 : "Unhandled projection method %s", pszProjection);
623 3 : CPLDestroyXMLNode(psCRS_XML);
624 3 : return nullptr;
625 : }
626 :
627 : /* -------------------------------------------------------------------- */
628 : /* Define the cartesian coordinate system. */
629 : /* -------------------------------------------------------------------- */
630 4 : CPLXMLNode *psCCS = CPLCreateXMLNode(
631 : CPLCreateXMLNode(psCRS_XML, CXT_Element, "gml:usesCartesianCS"),
632 : CXT_Element, "gml:CartesianCS");
633 :
634 4 : addGMLId(psCCS);
635 :
636 4 : CPLCreateXMLElementAndValue(psCCS, "gml:csName", "Cartesian");
637 4 : addAuthorityIDBlock(psCCS, "gml:csID", "EPSG", "cs", 4400);
638 4 : addAxis(psCCS, "E", nullptr);
639 4 : addAxis(psCCS, "N", nullptr);
640 :
641 4 : return psCRS_XML;
642 : }
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 41 : OGRErr OGRSpatialReference::exportToXML(char **ppszRawXML,
668 : CPL_UNUSED const char *pszDialect) const
669 : {
670 41 : CPLXMLNode *psXMLTree = nullptr;
671 :
672 41 : if (IsGeographic())
673 : {
674 24 : psXMLTree = exportGeogCSToXML(this);
675 : }
676 17 : else if (IsProjected())
677 : {
678 7 : psXMLTree = exportProjCSToXML(this);
679 : }
680 : else
681 10 : return OGRERR_UNSUPPORTED_SRS;
682 :
683 31 : if (!psXMLTree)
684 3 : return OGRERR_FAILURE;
685 :
686 28 : *ppszRawXML = CPLSerializeXMLTree(psXMLTree);
687 28 : CPLDestroyXMLNode(psXMLTree);
688 :
689 28 : return OGRERR_NONE;
690 : }
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 2 : OGRErr OSRExportToXML(OGRSpatialReferenceH hSRS, char **ppszRawXML,
702 : const char *pszDialect)
703 :
704 : {
705 2 : VALIDATE_POINTER1(hSRS, "OSRExportToXML", OGRERR_FAILURE);
706 :
707 2 : return OGRSpatialReference::FromHandle(hSRS)->exportToXML(ppszRawXML,
708 2 : pszDialect);
709 : }
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 6 : static void importXMLAuthority(CPLXMLNode *psSrcXML, OGRSpatialReference *poSRS,
790 : const char *pszSourceKey,
791 : const char *pszTargetKey)
792 :
793 : {
794 6 : CPLXMLNode *psIDNode = CPLGetXMLNode(psSrcXML, pszSourceKey);
795 6 : CPLXMLNode *psNameNode = CPLGetXMLNode(psIDNode, "name");
796 6 : CPLXMLNode *psCodeSpace = CPLGetXMLNode(psNameNode, "codeSpace");
797 :
798 6 : if (psIDNode == nullptr || psNameNode == nullptr || psCodeSpace == nullptr)
799 0 : return;
800 :
801 6 : char *pszURN = CPLStrdup(CPLGetXMLValue(psCodeSpace, "", ""));
802 :
803 : const char *pszAuthority;
804 : const char *pszCode;
805 6 : if (!parseURN(pszURN, nullptr, &pszAuthority, &pszCode))
806 : {
807 0 : CPLFree(pszURN);
808 0 : return;
809 : }
810 :
811 6 : if (strlen(pszCode) == 0)
812 6 : pszCode = CPLGetXMLValue(psNameNode, "", "");
813 :
814 6 : const int nCode = pszCode != nullptr ? atoi(pszCode) : 0;
815 :
816 6 : if (nCode != 0)
817 6 : poSRS->SetAuthority(pszTargetKey, pszAuthority, nCode);
818 :
819 6 : CPLFree(pszURN);
820 : }
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 16 : static bool ParseOGCDefURN(const char *pszURN, CPLString *poObjectType,
830 : CPLString *poAuthority, CPLString *poVersion,
831 : CPLString *poValue)
832 :
833 : {
834 16 : if (poObjectType != nullptr)
835 16 : *poObjectType = "";
836 :
837 16 : if (poAuthority != nullptr)
838 16 : *poAuthority = "";
839 :
840 16 : if (poVersion != nullptr)
841 0 : *poVersion = "";
842 :
843 16 : if (poValue != nullptr)
844 16 : *poValue = "";
845 :
846 16 : if (pszURN == nullptr || !STARTS_WITH_CI(pszURN, "urn:ogc:def:"))
847 0 : return false;
848 :
849 : char **papszTokens =
850 16 : CSLTokenizeStringComplex(pszURN + 12, ":", FALSE, TRUE);
851 :
852 16 : if (CSLCount(papszTokens) != 4)
853 : {
854 0 : CSLDestroy(papszTokens);
855 0 : return false;
856 : }
857 :
858 16 : if (poObjectType != nullptr)
859 16 : *poObjectType = papszTokens[0];
860 :
861 16 : if (poAuthority != nullptr)
862 16 : *poAuthority = papszTokens[1];
863 :
864 16 : if (poVersion != nullptr)
865 0 : *poVersion = papszTokens[2];
866 :
867 16 : if (poValue != nullptr)
868 16 : *poValue = papszTokens[3];
869 :
870 16 : CSLDestroy(papszTokens);
871 16 : return true;
872 : }
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 16 : static int getEPSGObjectCodeValue(CPLXMLNode *psNode,
883 : const char *pszEPSGObjectType, /*"method" */
884 : int nDefault)
885 :
886 : {
887 16 : if (psNode == nullptr)
888 0 : return nDefault;
889 :
890 16 : const char *pszHrefVal = CPLGetXMLValue(psNode, "xlink:href", nullptr);
891 16 : if (pszHrefVal == nullptr)
892 0 : pszHrefVal = CPLGetXMLValue(psNode, "href", nullptr);
893 :
894 32 : CPLString osObjectType;
895 32 : CPLString osAuthority;
896 32 : CPLString osValue;
897 16 : if (!ParseOGCDefURN(pszHrefVal, &osObjectType, &osAuthority, nullptr,
898 : &osValue))
899 0 : return nDefault;
900 :
901 16 : if (!EQUAL(osAuthority, "EPSG") || !EQUAL(osObjectType, pszEPSGObjectType))
902 0 : return nDefault;
903 :
904 16 : if (!osValue.empty())
905 16 : 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 : }
913 :
914 : /************************************************************************/
915 : /* getProjectionParam() */
916 : /************************************************************************/
917 :
918 5 : static double getProjectionParam(CPLXMLNode *psRootNode, int nParameterCode,
919 : const char * /*pszMeasureType */,
920 : double dfDefault)
921 :
922 : {
923 5 : for (CPLXMLNode *psUsesParameter = psRootNode->psChild;
924 25 : psUsesParameter != nullptr; psUsesParameter = psUsesParameter->psNext)
925 : {
926 25 : if (psUsesParameter->eType != CXT_Element)
927 0 : continue;
928 :
929 25 : if (!EQUAL(psUsesParameter->pszValue, "usesParameterValue") &&
930 25 : !EQUAL(psUsesParameter->pszValue, "usesValue"))
931 10 : continue;
932 :
933 15 : if (getEPSGObjectCodeValue(
934 : CPLGetXMLNode(psUsesParameter, "valueOfParameter"), "parameter",
935 15 : 0) == nParameterCode)
936 : {
937 : const char *pszValue =
938 5 : CPLGetXMLValue(psUsesParameter, "value", nullptr);
939 :
940 5 : if (pszValue == nullptr)
941 0 : return dfDefault;
942 :
943 5 : return CPLAtof(pszValue);
944 : }
945 : }
946 :
947 0 : return dfDefault;
948 : }
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 3 : static double getNormalizedValue(CPLXMLNode *psNode, const char *pszPath,
958 : const char * /*pszMeasure*/, double dfDefault)
959 :
960 : {
961 3 : CPLXMLNode *psTargetNode = pszPath == nullptr || strlen(pszPath) == 0
962 3 : ? psNode
963 3 : : CPLGetXMLNode(psNode, pszPath);
964 :
965 3 : if (psTargetNode == nullptr)
966 0 : return dfDefault;
967 :
968 3 : CPLXMLNode *psValueNode = psTargetNode->psChild; // Used after for.
969 6 : for (; psValueNode != nullptr && psValueNode->eType != CXT_Text;
970 3 : psValueNode = psValueNode->psNext)
971 : {
972 : }
973 :
974 3 : if (psValueNode == nullptr)
975 0 : return dfDefault;
976 :
977 : // Add normalization later.
978 :
979 3 : return CPLAtof(psValueNode->pszValue);
980 : }
981 :
982 : /************************************************************************/
983 : /* importGeogCSFromXML() */
984 : /************************************************************************/
985 :
986 1 : static OGRErr importGeogCSFromXML(OGRSpatialReference *poSRS, CPLXMLNode *psCRS)
987 :
988 : {
989 : /* -------------------------------------------------------------------- */
990 : /* Set the GEOGCS name from the srsName. */
991 : /* -------------------------------------------------------------------- */
992 : const char *pszGeogName =
993 1 : 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 : CPLXMLNode *psDatum =
1000 1 : CPLGetXMLNode(psCRS, "usesGeodeticDatum.GeodeticDatum");
1001 :
1002 1 : if (psDatum == nullptr)
1003 : {
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 : {
1013 0 : return poSRS->importFromEPSG(
1014 0 : atoi(oIdSRS.GetAuthorityCode("LOCAL_CS")));
1015 : }
1016 : }
1017 :
1018 : /* -------------------------------------------------------------------- */
1019 : /* Get datum name. */
1020 : /* -------------------------------------------------------------------- */
1021 : const char *pszDatumName =
1022 1 : CPLGetXMLValue(psDatum, "datumName", "Unnamed Datum");
1023 :
1024 : /* -------------------------------------------------------------------- */
1025 : /* Get ellipsoid information. */
1026 : /* -------------------------------------------------------------------- */
1027 1 : CPLXMLNode *psE = CPLGetXMLNode(psDatum, "usesEllipsoid.Ellipsoid");
1028 : const char *pszEllipsoidName =
1029 1 : CPLGetXMLValue(psE, "ellipsoidName", "Unnamed Ellipsoid");
1030 :
1031 : const double dfSemiMajor =
1032 1 : getNormalizedValue(psE, "semiMajorAxis", "Linear", SRS_WGS84_SEMIMAJOR);
1033 :
1034 1 : const double dfInvFlattening = getNormalizedValue(
1035 : psE, "secondDefiningParameter.inverseFlattening", "Unitless", 0.0);
1036 :
1037 1 : if (dfInvFlattening == 0.0)
1038 : {
1039 0 : CPLError(CE_Failure, CPLE_AppDefined,
1040 : "Ellipsoid inverseFlattening corrupt or missing.");
1041 0 : return OGRERR_CORRUPT_DATA;
1042 : }
1043 :
1044 : /* -------------------------------------------------------------------- */
1045 : /* Get the prime meridian. */
1046 : /* -------------------------------------------------------------------- */
1047 1 : const char *pszPMName = nullptr;
1048 1 : double dfPMOffset = 0.0;
1049 :
1050 : CPLXMLNode *psPM =
1051 1 : CPLGetXMLNode(psDatum, "usesPrimeMeridian.PrimeMeridian");
1052 1 : if (psPM == nullptr)
1053 : {
1054 0 : pszPMName = "Greenwich";
1055 0 : dfPMOffset = 0.0;
1056 : }
1057 : else
1058 : {
1059 : pszPMName =
1060 1 : CPLGetXMLValue(psPM, "meridianName", "Unnamed Prime Meridian");
1061 1 : dfPMOffset = getNormalizedValue(psPM, "greenwichLongitude.angle",
1062 : "Angular", 0.0);
1063 : }
1064 :
1065 : /* -------------------------------------------------------------------- */
1066 : /* Set the geographic definition. */
1067 : /* -------------------------------------------------------------------- */
1068 1 : poSRS->SetGeogCS(pszGeogName, pszDatumName, pszEllipsoidName, dfSemiMajor,
1069 : 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 1 : importXMLAuthority(psCRS, poSRS, "srsID", "GEOGCS");
1086 1 : importXMLAuthority(psDatum, poSRS, "datumID", "GEOGCS|DATUM");
1087 1 : importXMLAuthority(psE, poSRS, "ellipsoidID", "GEOGCS|DATUM|SPHEROID");
1088 1 : importXMLAuthority(psDatum, poSRS,
1089 : "usesPrimeMeridian.PrimeMeridian.meridianID",
1090 : "GEOGCS|PRIMEM");
1091 :
1092 1 : return OGRERR_NONE;
1093 : }
1094 :
1095 : /************************************************************************/
1096 : /* importProjCSFromXML() */
1097 : /************************************************************************/
1098 :
1099 1 : static OGRErr importProjCSFromXML(OGRSpatialReference *poSRS, CPLXMLNode *psCRS)
1100 :
1101 : {
1102 : /* -------------------------------------------------------------------- */
1103 : /* Setup the PROJCS node with a name. */
1104 : /* -------------------------------------------------------------------- */
1105 1 : 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 1 : importXMLAuthority(psCRS, poSRS, "srsID", "PROJCS");
1113 :
1114 1 : if (poSRS->GetAuthorityCode("PROJCS") != nullptr &&
1115 1 : poSRS->GetAuthorityName("PROJCS") != nullptr &&
1116 3 : EQUAL(poSRS->GetAuthorityName("PROJCS"), "EPSG") &&
1117 1 : (CPLGetXMLNode(psCRS, "definedByConversion.Conversion") == nullptr ||
1118 1 : CPLGetXMLNode(psCRS, "baseCRS.GeographicCRS") == nullptr))
1119 : {
1120 0 : return poSRS->importFromEPSG(atoi(poSRS->GetAuthorityCode("PROJCS")));
1121 : }
1122 :
1123 : /* -------------------------------------------------------------------- */
1124 : /* Try to set the GEOGCS info. */
1125 : /* -------------------------------------------------------------------- */
1126 :
1127 1 : CPLXMLNode *psSubXML = CPLGetXMLNode(psCRS, "baseCRS.GeographicCRS");
1128 1 : if (psSubXML != nullptr)
1129 : {
1130 1 : const OGRErr eErr = importGeogCSFromXML(poSRS, psSubXML);
1131 1 : if (eErr != OGRERR_NONE)
1132 0 : return eErr;
1133 : }
1134 :
1135 : /* -------------------------------------------------------------------- */
1136 : /* Get the conversion node. It should be the only child of the */
1137 : /* definedByConversion node. */
1138 : /* -------------------------------------------------------------------- */
1139 1 : CPLXMLNode *psConv = CPLGetXMLNode(psCRS, "definedByConversion.Conversion");
1140 1 : if (psConv == nullptr || psConv->eType != CXT_Element)
1141 : {
1142 0 : CPLError(CE_Failure, CPLE_AppDefined,
1143 : "Unable to find a conversion node under the "
1144 : "definedByConversion node of the ProjectedCRS.");
1145 0 : return OGRERR_CORRUPT_DATA;
1146 : }
1147 :
1148 : /* -------------------------------------------------------------------- */
1149 : /* Determine the conversion method in effect. */
1150 : /* -------------------------------------------------------------------- */
1151 1 : const int nMethod = getEPSGObjectCodeValue(
1152 : CPLGetXMLNode(psConv, "usesMethod"), "method", 0);
1153 :
1154 : /* -------------------------------------------------------------------- */
1155 : /* Transverse Mercator. */
1156 : /* -------------------------------------------------------------------- */
1157 1 : if (nMethod == 9807)
1158 : {
1159 1 : poSRS->SetTM(getProjectionParam(psConv, 8801, "Angular", 0.0),
1160 : getProjectionParam(psConv, 8802, "Angular", 0.0),
1161 : getProjectionParam(psConv, 8805, "Unitless", 1.0),
1162 : getProjectionParam(psConv, 8806, "Linear", 0.0),
1163 : getProjectionParam(psConv, 8807, "Linear", 0.0));
1164 : }
1165 :
1166 : /* -------------------------------------------------------------------- */
1167 : /* Didn't recognise? */
1168 : /* -------------------------------------------------------------------- */
1169 : else
1170 : {
1171 0 : CPLError(CE_Failure, CPLE_AppDefined,
1172 : "Conversion method %d not recognised.", nMethod);
1173 0 : return OGRERR_CORRUPT_DATA;
1174 : }
1175 :
1176 : // Re-set authority as all editions above will have removed it
1177 1 : importXMLAuthority(psCRS, poSRS, "srsID", "PROJCS");
1178 :
1179 : // Need to get linear units here!
1180 :
1181 1 : return OGRERR_NONE;
1182 : }
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 1 : OGRErr OGRSpatialReference::importFromXML(const char *pszXML)
1196 :
1197 : {
1198 1 : Clear();
1199 :
1200 : /* -------------------------------------------------------------------- */
1201 : /* Parse the XML. */
1202 : /* -------------------------------------------------------------------- */
1203 1 : CPLXMLNode *psTree = CPLParseXMLString(pszXML);
1204 :
1205 1 : if (psTree == nullptr)
1206 0 : return OGRERR_CORRUPT_DATA;
1207 :
1208 1 : 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 1 : OGRErr eErr = OGRERR_UNSUPPORTED_SRS;
1216 :
1217 1 : for (CPLXMLNode *psNode = psTree; psNode != nullptr;
1218 0 : psNode = psNode->psNext)
1219 : {
1220 1 : if (EQUAL(psNode->pszValue, "GeographicCRS"))
1221 : {
1222 0 : eErr = importGeogCSFromXML(this, psNode);
1223 0 : break;
1224 : }
1225 :
1226 1 : else if (EQUAL(psNode->pszValue, "ProjectedCRS"))
1227 : {
1228 1 : eErr = importProjCSFromXML(this, psNode);
1229 1 : break;
1230 : }
1231 : }
1232 :
1233 1 : CPLDestroyXMLNode(psTree);
1234 :
1235 1 : return eErr;
1236 : }
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 1 : OGRErr OSRImportFromXML(OGRSpatialReferenceH hSRS, const char *pszXML)
1248 :
1249 : {
1250 1 : VALIDATE_POINTER1(hSRS, "OSRImportFromXML", OGRERR_FAILURE);
1251 1 : VALIDATE_POINTER1(pszXML, "OSRImportFromXML", OGRERR_FAILURE);
1252 :
1253 1 : return OGRSpatialReference::FromHandle(hSRS)->importFromXML(pszXML);
1254 : }
|