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