1 /******************************************************************************
2 * $Id: ogr_srsnode.cpp 27975 2014-11-17 12:37:48Z rouault $
3 *
4 * Project: OpenGIS Simple Features Reference Implementation
5 * Purpose: The OGR_SRSNode class.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 1999, Les Technologies SoftMap Inc.
10 * Copyright (c) 2010-2013, Even Rouault <even dot rouault at mines-paris dot org>
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included
20 * in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 ****************************************************************************/
30
31 #include "ogr_spatialref.h"
32 #include "ogr_p.h"
33
34 CPL_CVSID("$Id: ogr_srsnode.cpp 27975 2014-11-17 12:37:48Z rouault $");
35
36 /************************************************************************/
37 /* OGR_SRSNode() */
38 /************************************************************************/
39
40 /**
41 * Constructor.
42 *
43 * @param pszValueIn this optional parameter can be used to initialize
44 * the value of the node upon creation. If omitted the node will be created
45 * with a value of "". Newly created OGR_SRSNodes have no children.
46 */
47
OGR_SRSNode(const char * pszValueIn)48 OGR_SRSNode::OGR_SRSNode( const char * pszValueIn )
49
50 {
51 pszValue = CPLStrdup( pszValueIn );
52
53 nChildren = 0;
54 papoChildNodes = NULL;
55
56 poParent = NULL;
57 }
58
59 /************************************************************************/
60 /* ~OGR_SRSNode() */
61 /************************************************************************/
62
~OGR_SRSNode()63 OGR_SRSNode::~OGR_SRSNode()
64
65 {
66 CPLFree( pszValue );
67
68 ClearChildren();
69 }
70
71 /************************************************************************/
72 /* ClearChildren() */
73 /************************************************************************/
74
ClearChildren()75 void OGR_SRSNode::ClearChildren()
76
77 {
78 for( int i = 0; i < nChildren; i++ )
79 {
80 delete papoChildNodes[i];
81 }
82
83 CPLFree( papoChildNodes );
84
85 papoChildNodes = NULL;
86 nChildren = 0;
87 }
88
89 /************************************************************************/
90 /* GetChildCount() */
91 /************************************************************************/
92
93 /**
94 * \fn int OGR_SRSNode::GetChildCount() const;
95 *
96 * Get number of children nodes.
97 *
98 * @return 0 for leaf nodes, or the number of children nodes.
99 */
100
101 /************************************************************************/
102 /* GetChild() */
103 /************************************************************************/
104
105 /**
106 * Fetch requested child.
107 *
108 * @param iChild the index of the child to fetch, from 0 to
109 * GetChildCount() - 1.
110 *
111 * @return a pointer to the child OGR_SRSNode, or NULL if there is no such
112 * child.
113 */
114
GetChild(int iChild)115 OGR_SRSNode *OGR_SRSNode::GetChild( int iChild )
116
117 {
118 if( iChild < 0 || iChild >= nChildren )
119 return NULL;
120 else
121 return papoChildNodes[iChild];
122 }
123
GetChild(int iChild) const124 const OGR_SRSNode *OGR_SRSNode::GetChild( int iChild ) const
125
126 {
127 if( iChild < 0 || iChild >= nChildren )
128 return NULL;
129 else
130 return papoChildNodes[iChild];
131 }
132
133 /************************************************************************/
134 /* GetNode() */
135 /************************************************************************/
136
137 /**
138 * Find named node in tree.
139 *
140 * This method does a pre-order traversal of the node tree searching for
141 * a node with this exact value (case insensitive), and returns it. Leaf
142 * nodes are not considered, under the assumption that they are just
143 * attribute value nodes.
144 *
145 * If a node appears more than once in the tree (such as UNIT for instance),
146 * the first encountered will be returned. Use GetNode() on a subtree to be
147 * more specific.
148 *
149 * @param pszName the name of the node to search for.
150 *
151 * @return a pointer to the node found, or NULL if none.
152 */
153
GetNode(const char * pszName)154 OGR_SRSNode *OGR_SRSNode::GetNode( const char * pszName )
155
156 {
157 int i;
158
159 if( this == NULL )
160 return NULL;
161
162 if( nChildren > 0 && EQUAL(pszName,pszValue) )
163 return this;
164
165 /* -------------------------------------------------------------------- */
166 /* First we check the immediate children so we will get an */
167 /* immediate child in preference to a subchild. */
168 /* -------------------------------------------------------------------- */
169 for( i = 0; i < nChildren; i++ )
170 {
171 if( EQUAL(papoChildNodes[i]->pszValue,pszName)
172 && papoChildNodes[i]->nChildren > 0 )
173 return papoChildNodes[i];
174 }
175
176 /* -------------------------------------------------------------------- */
177 /* Then get each child to check their children. */
178 /* -------------------------------------------------------------------- */
179 for( i = 0; i < nChildren; i++ )
180 {
181 OGR_SRSNode *poNode;
182
183 poNode = papoChildNodes[i]->GetNode( pszName );
184 if( poNode != NULL )
185 return poNode;
186 }
187
188 return NULL;
189 }
190
GetNode(const char * pszName) const191 const OGR_SRSNode *OGR_SRSNode::GetNode( const char * pszName ) const
192
193 {
194 return ((OGR_SRSNode *) this)->GetNode( pszName );
195 }
196
197 /************************************************************************/
198 /* AddChild() */
199 /************************************************************************/
200
201 /**
202 * Add passed node as a child of target node.
203 *
204 * Note that ownership of the passed node is assumed by the node on which
205 * the method is invoked ... use the Clone() method if the original is to
206 * be preserved. New children are always added at the end of the list.
207 *
208 * @param poNew the node to add as a child.
209 */
210
AddChild(OGR_SRSNode * poNew)211 void OGR_SRSNode::AddChild( OGR_SRSNode * poNew )
212
213 {
214 InsertChild( poNew, nChildren );
215 }
216
217 /************************************************************************/
218 /* InsertChild() */
219 /************************************************************************/
220
221 /**
222 * Insert the passed node as a child of target node, at the indicated
223 * position.
224 *
225 * Note that ownership of the passed node is assumed by the node on which
226 * the method is invoked ... use the Clone() method if the original is to
227 * be preserved. All existing children at location iChild and beyond are
228 * push down one space to make space for the new child.
229 *
230 * @param poNew the node to add as a child.
231 * @param iChild position to insert, use 0 to insert at the beginning.
232 */
233
InsertChild(OGR_SRSNode * poNew,int iChild)234 void OGR_SRSNode::InsertChild( OGR_SRSNode * poNew, int iChild )
235
236 {
237 if( iChild > nChildren )
238 iChild = nChildren;
239
240 nChildren++;
241 papoChildNodes = (OGR_SRSNode **)
242 CPLRealloc( papoChildNodes, sizeof(void*) * nChildren );
243
244 memmove( papoChildNodes + iChild + 1, papoChildNodes + iChild,
245 sizeof(void*) * (nChildren - iChild - 1) );
246
247 papoChildNodes[iChild] = poNew;
248 poNew->poParent = this;
249 }
250
251 /************************************************************************/
252 /* DestroyChild() */
253 /************************************************************************/
254
255 /**
256 * Remove a child node, and it's subtree.
257 *
258 * Note that removing a child node will result in children after it
259 * being renumbered down one.
260 *
261 * @param iChild the index of the child.
262 */
263
DestroyChild(int iChild)264 void OGR_SRSNode::DestroyChild( int iChild )
265
266 {
267 if( iChild < 0 || iChild >= nChildren )
268 return;
269
270 delete papoChildNodes[iChild];
271 while( iChild < nChildren-1 )
272 {
273 papoChildNodes[iChild] = papoChildNodes[iChild+1];
274 iChild++;
275 }
276
277 nChildren--;
278 }
279
280 /************************************************************************/
281 /* FindChild() */
282 /************************************************************************/
283
284 /**
285 * Find the index of the child matching the given string.
286 *
287 * Note that the node value must match pszValue with the exception of
288 * case. The comparison is case insensitive.
289 *
290 * @param pszValue the node value being searched for.
291 *
292 * @return the child index, or -1 on failure.
293 */
294
FindChild(const char * pszValue) const295 int OGR_SRSNode::FindChild( const char * pszValue ) const
296
297 {
298 for( int i = 0; i < nChildren; i++ )
299 {
300 if( EQUAL(papoChildNodes[i]->pszValue,pszValue) )
301 return i;
302 }
303
304 return -1;
305 }
306
307 /************************************************************************/
308 /* GetValue() */
309 /************************************************************************/
310
311 /**
312 * \fn const char *OGR_SRSNode::GetValue() const;
313 *
314 * Fetch value string for this node.
315 *
316 * @return A non-NULL string is always returned. The returned pointer is to
317 * the internal value of this node, and should not be modified, or freed.
318 */
319
320 /************************************************************************/
321 /* SetValue() */
322 /************************************************************************/
323
324 /**
325 * Set the node value.
326 *
327 * @param pszNewValue the new value to assign to this node. The passed
328 * string is duplicated and remains the responsibility of the caller.
329 */
330
SetValue(const char * pszNewValue)331 void OGR_SRSNode::SetValue( const char * pszNewValue )
332
333 {
334 CPLFree( pszValue );
335 pszValue = CPLStrdup( pszNewValue );
336 }
337
338 /************************************************************************/
339 /* Clone() */
340 /************************************************************************/
341
342 /**
343 * Make a duplicate of this node, and it's children.
344 *
345 * @return a new node tree, which becomes the responsiblity of the caller.
346 */
347
Clone() const348 OGR_SRSNode *OGR_SRSNode::Clone() const
349
350 {
351 OGR_SRSNode *poNew;
352
353 poNew = new OGR_SRSNode( pszValue );
354
355 for( int i = 0; i < nChildren; i++ )
356 {
357 poNew->AddChild( papoChildNodes[i]->Clone() );
358 }
359
360 return poNew;
361 }
362
363 /************************************************************************/
364 /* NeedsQuoting() */
365 /* */
366 /* Does this node need to be quoted when it is exported to Wkt? */
367 /************************************************************************/
368
NeedsQuoting() const369 int OGR_SRSNode::NeedsQuoting() const
370
371 {
372 // non-terminals are never quoted.
373 if( GetChildCount() != 0 )
374 return FALSE;
375
376 // As per bugzilla bug 201, the OGC spec says the authority code
377 // needs to be quoted even though it appears well behaved.
378 if( poParent != NULL && EQUAL(poParent->GetValue(),"AUTHORITY") )
379 return TRUE;
380
381 // As per bugzilla bug 294, the OGC spec says the direction
382 // values for the AXIS keywords should *not* be quoted.
383 if( poParent != NULL && EQUAL(poParent->GetValue(),"AXIS")
384 && this != poParent->GetChild(0) )
385 return FALSE;
386
387 // Strings starting with e or E are not valid numeric values, so they
388 // need quoting, like in AXIS["E",EAST]
389 if( (pszValue[0] == 'e' || pszValue[0] == 'E') )
390 return TRUE;
391
392 // Non-numeric tokens are generally quoted while clean numeric values
393 // are generally not.
394 for( int i = 0; pszValue[i] != '\0'; i++ )
395 {
396 if( (pszValue[i] < '0' || pszValue[i] > '9')
397 && pszValue[i] != '.'
398 && pszValue[i] != '-' && pszValue[i] != '+'
399 && pszValue[i] != 'e' && pszValue[i] != 'E' )
400 return TRUE;
401 }
402
403 return FALSE;
404 }
405
406 /************************************************************************/
407 /* exportToWkt() */
408 /************************************************************************/
409
410 /**
411 * Convert this tree of nodes into WKT format.
412 *
413 * Note that the returned WKT string should be freed with OGRFree() or
414 * CPLFree() when no longer needed. It is the responsibility of the caller.
415 *
416 * @param ppszResult the resulting string is returned in this pointer.
417 *
418 * @return currently OGRERR_NONE is always returned, but the future it
419 * is possible error conditions will develop.
420 */
421
422
exportToWkt(char ** ppszResult) const423 OGRErr OGR_SRSNode::exportToWkt( char ** ppszResult ) const
424
425 {
426 char **papszChildrenWkt = NULL;
427 int nLength = strlen(pszValue)+4;
428 int i;
429
430 /* -------------------------------------------------------------------- */
431 /* Build a list of the WKT format for the children. */
432 /* -------------------------------------------------------------------- */
433 papszChildrenWkt = (char **) CPLCalloc(sizeof(char*),(nChildren+1));
434
435 for( i = 0; i < nChildren; i++ )
436 {
437 papoChildNodes[i]->exportToWkt( papszChildrenWkt + i );
438 nLength += strlen(papszChildrenWkt[i]) + 1;
439 }
440
441 /* -------------------------------------------------------------------- */
442 /* Allocate the result string. */
443 /* -------------------------------------------------------------------- */
444 *ppszResult = (char *) CPLMalloc(nLength);
445 *ppszResult[0] = '\0';
446
447 /* -------------------------------------------------------------------- */
448 /* Capture this nodes value. We put it in double quotes if */
449 /* this is a leaf node, otherwise we assume it is a well formed */
450 /* node name. */
451 /* -------------------------------------------------------------------- */
452 if( NeedsQuoting() )
453 {
454 strcat( *ppszResult, "\"" );
455 strcat( *ppszResult, pszValue ); /* should we do quoting? */
456 strcat( *ppszResult, "\"" );
457 }
458 else
459 strcat( *ppszResult, pszValue );
460
461 /* -------------------------------------------------------------------- */
462 /* Add the children strings with appropriate brackets and commas. */
463 /* -------------------------------------------------------------------- */
464 if( nChildren > 0 )
465 strcat( *ppszResult, "[" );
466
467 for( i = 0; i < nChildren; i++ )
468 {
469 strcat( *ppszResult, papszChildrenWkt[i] );
470 if( i == nChildren-1 )
471 strcat( *ppszResult, "]" );
472 else
473 strcat( *ppszResult, "," );
474 }
475
476 CSLDestroy( papszChildrenWkt );
477
478 return OGRERR_NONE;
479 }
480
481 /************************************************************************/
482 /* exportToPrettyWkt() */
483 /************************************************************************/
484
exportToPrettyWkt(char ** ppszResult,int nDepth) const485 OGRErr OGR_SRSNode::exportToPrettyWkt( char ** ppszResult, int nDepth ) const
486
487 {
488 char **papszChildrenWkt = NULL;
489 int nLength = strlen(pszValue)+4;
490 int i;
491
492 /* -------------------------------------------------------------------- */
493 /* Build a list of the WKT format for the children. */
494 /* -------------------------------------------------------------------- */
495 papszChildrenWkt = (char **) CPLCalloc(sizeof(char*),(nChildren+1));
496
497 for( i = 0; i < nChildren; i++ )
498 {
499 papoChildNodes[i]->exportToPrettyWkt( papszChildrenWkt + i,
500 nDepth + 1);
501 nLength += strlen(papszChildrenWkt[i]) + 2 + nDepth*4;
502 }
503
504 /* -------------------------------------------------------------------- */
505 /* Allocate the result string. */
506 /* -------------------------------------------------------------------- */
507 *ppszResult = (char *) CPLMalloc(nLength);
508 *ppszResult[0] = '\0';
509
510 /* -------------------------------------------------------------------- */
511 /* Capture this nodes value. We put it in double quotes if */
512 /* this is a leaf node, otherwise we assume it is a well formed */
513 /* node name. */
514 /* -------------------------------------------------------------------- */
515 if( NeedsQuoting() )
516 {
517 strcat( *ppszResult, "\"" );
518 strcat( *ppszResult, pszValue ); /* should we do quoting? */
519 strcat( *ppszResult, "\"" );
520 }
521 else
522 strcat( *ppszResult, pszValue );
523
524 /* -------------------------------------------------------------------- */
525 /* Add the children strings with appropriate brackets and commas. */
526 /* -------------------------------------------------------------------- */
527 if( nChildren > 0 )
528 strcat( *ppszResult, "[" );
529
530 for( i = 0; i < nChildren; i++ )
531 {
532 if( papoChildNodes[i]->GetChildCount() > 0 )
533 {
534 int j;
535
536 strcat( *ppszResult, "\n" );
537 for( j = 0; j < 4*nDepth; j++ )
538 strcat( *ppszResult, " " );
539 }
540 strcat( *ppszResult, papszChildrenWkt[i] );
541 if( i < nChildren-1 )
542 strcat( *ppszResult, "," );
543 }
544
545 if( nChildren > 0 )
546 {
547 if( (*ppszResult)[strlen(*ppszResult)-1] == ',' )
548 (*ppszResult)[strlen(*ppszResult)-1] = '\0';
549
550 strcat( *ppszResult, "]" );
551 }
552
553 CSLDestroy( papszChildrenWkt );
554
555 return OGRERR_NONE;
556 }
557
558 /************************************************************************/
559 /* importFromWkt() */
560 /************************************************************************/
561
562 /**
563 * Import from WKT string.
564 *
565 * This method will wipe the existing children and value of this node, and
566 * reassign them based on the contents of the passed WKT string. Only as
567 * much of the input string as needed to construct this node, and it's
568 * children is consumed from the input string, and the input string pointer
569 * is then updated to point to the remaining (unused) input.
570 *
571 * @param ppszInput Pointer to pointer to input. The pointer is updated to
572 * point to remaining unused input text.
573 *
574 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
575 * fails for any reason.
576 */
577
importFromWkt(char ** ppszInput)578 OGRErr OGR_SRSNode::importFromWkt( char ** ppszInput )
579
580 {
581 int nNodes = 0;
582 return importFromWkt( ppszInput, 0, &nNodes );
583 }
584
importFromWkt(char ** ppszInput,int nRecLevel,int * pnNodes)585 OGRErr OGR_SRSNode::importFromWkt( char ** ppszInput, int nRecLevel, int* pnNodes )
586
587 {
588 const char *pszInput = *ppszInput;
589 int bInQuotedString = FALSE;
590
591 /* Sanity checks */
592 if( nRecLevel == 10 )
593 {
594 return OGRERR_CORRUPT_DATA;
595 }
596 if( *pnNodes == 1000 )
597 {
598 return OGRERR_CORRUPT_DATA;
599 }
600
601 /* -------------------------------------------------------------------- */
602 /* Clear any existing children of this node. */
603 /* -------------------------------------------------------------------- */
604 ClearChildren();
605
606 /* -------------------------------------------------------------------- */
607 /* Read the ``value'' for this node. */
608 /* -------------------------------------------------------------------- */
609 char szToken[512];
610 int nTokenLen = 0;
611
612 while( *pszInput != '\0' && nTokenLen < (int) sizeof(szToken)-1 )
613 {
614 if( *pszInput == '"' )
615 {
616 bInQuotedString = !bInQuotedString;
617 }
618 else if( !bInQuotedString
619 && (*pszInput == '[' || *pszInput == ']' || *pszInput == ','
620 || *pszInput == '(' || *pszInput == ')' ) )
621 {
622 break;
623 }
624 else if( !bInQuotedString
625 && (*pszInput == ' ' || *pszInput == '\t'
626 || *pszInput == 10 || *pszInput == 13) )
627 {
628 /* just skip over whitespace */
629 }
630 else
631 {
632 szToken[nTokenLen++] = *pszInput;
633 }
634
635 pszInput++;
636 }
637
638 if( *pszInput == '\0' || nTokenLen == sizeof(szToken) - 1 )
639 return OGRERR_CORRUPT_DATA;
640
641 szToken[nTokenLen++] = '\0';
642 SetValue( szToken );
643
644 /* -------------------------------------------------------------------- */
645 /* Read children, if we have a sublist. */
646 /* -------------------------------------------------------------------- */
647 if( *pszInput == '[' || *pszInput == '(' )
648 {
649 do
650 {
651 OGR_SRSNode *poNewChild;
652 OGRErr eErr;
653
654 pszInput++; // Skip bracket or comma.
655
656 poNewChild = new OGR_SRSNode();
657
658 (*pnNodes) ++;
659 eErr = poNewChild->importFromWkt( (char **) &pszInput, nRecLevel + 1, pnNodes );
660 if( eErr != OGRERR_NONE )
661 {
662 delete poNewChild;
663 return eErr;
664 }
665
666 AddChild( poNewChild );
667
668 // swallow whitespace
669 while( isspace(*pszInput) )
670 pszInput++;
671
672 } while( *pszInput == ',' );
673
674 if( *pszInput != ')' && *pszInput != ']' )
675 return OGRERR_CORRUPT_DATA;
676
677 pszInput++;
678 }
679
680 *ppszInput = (char *) pszInput;
681
682 return OGRERR_NONE;
683 }
684
685 /************************************************************************/
686 /* MakeValueSafe() */
687 /************************************************************************/
688
689 /**
690 * Massage value string, stripping special characters so it will be a
691 * database safe string.
692 *
693 * The operation is also applies to all subnodes of the current node.
694 */
695
696
MakeValueSafe()697 void OGR_SRSNode::MakeValueSafe()
698
699 {
700 int i, j;
701
702 /* -------------------------------------------------------------------- */
703 /* First process subnodes. */
704 /* -------------------------------------------------------------------- */
705 for( int iChild = 0; iChild < GetChildCount(); iChild++ )
706 {
707 GetChild(iChild)->MakeValueSafe();
708 }
709
710 /* -------------------------------------------------------------------- */
711 /* Skip numeric nodes. */
712 /* -------------------------------------------------------------------- */
713 if( (pszValue[0] >= '0' && pszValue[0] <= '9') || pszValue[0] != '.' )
714 return;
715
716 /* -------------------------------------------------------------------- */
717 /* Translate non-alphanumeric values to underscores. */
718 /* -------------------------------------------------------------------- */
719 for( i = 0; pszValue[i] != '\0'; i++ )
720 {
721 if( !(pszValue[i] >= 'A' && pszValue[i] <= 'Z')
722 && !(pszValue[i] >= 'a' && pszValue[i] <= 'z')
723 && !(pszValue[i] >= '0' && pszValue[i] <= '9') )
724 {
725 pszValue[i] = '_';
726 }
727 }
728
729 /* -------------------------------------------------------------------- */
730 /* Remove repeated and trailing underscores. */
731 /* -------------------------------------------------------------------- */
732 for( i = 1, j = 0; pszValue[i] != '\0'; i++ )
733 {
734 if( pszValue[j] == '_' && pszValue[i] == '_' )
735 continue;
736
737 pszValue[++j] = pszValue[i];
738 }
739
740 if( pszValue[j] == '_' )
741 pszValue[j] = '\0';
742 else
743 pszValue[j+1] = '\0';
744 }
745
746 /************************************************************************/
747 /* applyRemapper() */
748 /************************************************************************/
749
750 /**
751 * Remap node values matching list.
752 *
753 * Remap the value of this node or any of it's children if it matches
754 * one of the values in the source list to the corresponding value from
755 * the destination list. If the pszNode value is set, only do so if the
756 * parent node matches that value. Even if a replacement occurs, searching
757 * continues.
758 *
759 * @param pszNode Restrict remapping to children of this type of node
760 * (eg. "PROJECTION")
761 * @param papszSrcValues a NULL terminated array of source string. If the
762 * node value matches one of these (case insensitive) then replacement occurs.
763 * @param papszDstValues an array of destination strings. On a match, the
764 * one corresponding to a source value will be used to replace a node.
765 * @param nStepSize increment when stepping through source and destination
766 * arrays, allowing source and destination arrays to be one interleaved array
767 * for instances. Defaults to 1.
768 * @param bChildOfHit Only TRUE if we the current node is the child of a match,
769 * and so needs to be set. Application code would normally pass FALSE for this
770 * argument.
771 *
772 * @return returns OGRERR_NONE unless something bad happens. There is no
773 * indication returned about whether any replacement occured.
774 */
775
applyRemapper(const char * pszNode,char ** papszSrcValues,char ** papszDstValues,int nStepSize,int bChildOfHit)776 OGRErr OGR_SRSNode::applyRemapper( const char *pszNode,
777 char **papszSrcValues,
778 char **papszDstValues,
779 int nStepSize, int bChildOfHit )
780
781 {
782 int i;
783
784 /* -------------------------------------------------------------------- */
785 /* Scan for value, and replace if our parent was a "hit". */
786 /* -------------------------------------------------------------------- */
787 if( bChildOfHit || pszNode == NULL )
788 {
789 for( i = 0; papszSrcValues[i] != NULL; i += nStepSize )
790 {
791 if( EQUAL(papszSrcValues[i],pszValue) &&
792 ! EQUAL(papszDstValues[i],"") )
793 {
794 SetValue( papszDstValues[i] );
795 break;
796 }
797 }
798 }
799
800 /* -------------------------------------------------------------------- */
801 /* Are the the target node? */
802 /* -------------------------------------------------------------------- */
803 if( pszNode != NULL )
804 bChildOfHit = EQUAL(pszValue,pszNode);
805
806 /* -------------------------------------------------------------------- */
807 /* Recurse */
808 /* -------------------------------------------------------------------- */
809 for( i = 0; i < GetChildCount(); i++ )
810 {
811 GetChild(i)->applyRemapper( pszNode, papszSrcValues,
812 papszDstValues, nStepSize, bChildOfHit );
813 }
814
815 return OGRERR_NONE;
816 }
817
818 /************************************************************************/
819 /* StripNodes() */
820 /************************************************************************/
821
822 /**
823 * Strip child nodes matching name.
824 *
825 * Removes any decendent nodes of this node that match the given name.
826 * Of course children of removed nodes are also discarded.
827 *
828 * @param pszName the name for nodes that should be removed.
829 */
830
StripNodes(const char * pszName)831 void OGR_SRSNode::StripNodes( const char * pszName )
832
833 {
834 /* -------------------------------------------------------------------- */
835 /* Strip any children matching this name. */
836 /* -------------------------------------------------------------------- */
837 while( FindChild( pszName ) >= 0 )
838 DestroyChild( FindChild( pszName ) );
839
840 /* -------------------------------------------------------------------- */
841 /* Recurse */
842 /* -------------------------------------------------------------------- */
843 for( int i = 0; i < GetChildCount(); i++ )
844 GetChild(i)->StripNodes( pszName );
845 }
846
847 /************************************************************************/
848 /* FixupOrdering() */
849 /************************************************************************/
850
851 /* EXTENSION ... being a OSR extension... is arbitrary placed before the AUTHORITY */
852 static const char * const apszPROJCSRule[] =
853 { "PROJCS", "GEOGCS", "PROJECTION", "PARAMETER", "UNIT", "AXIS", "EXTENSION", "AUTHORITY",
854 NULL };
855
856 static const char * const apszDATUMRule[] =
857 { "DATUM", "SPHEROID", "TOWGS84", "EXTENSION", "AUTHORITY", NULL };
858
859 static const char * const apszGEOGCSRule[] =
860 { "GEOGCS", "DATUM", "PRIMEM", "UNIT", "AXIS", "EXTENSION", "AUTHORITY", NULL };
861
862 static const char * const apszGEOCCSRule[] =
863 { "GEOCCS", "DATUM", "PRIMEM", "UNIT", "AXIS", "AUTHORITY", NULL };
864
865 static const char * const apszVERTCSRule[] =
866 { "VERT_CS", "VERT_DATUM", "UNIT", "AXIS", "EXTENSION", "AUTHORITY", NULL };
867
868 static const char * const *apszOrderingRules[] = {
869 apszPROJCSRule, apszGEOGCSRule, apszDATUMRule, apszGEOCCSRule, apszVERTCSRule, NULL };
870
871 /**
872 * Correct parameter ordering to match CT Specification.
873 *
874 * Some mechanisms to create WKT using OGRSpatialReference, and some
875 * imported WKT fail to maintain the order of parameters required according
876 * to the BNF definitions in the OpenGIS SF-SQL and CT Specifications. This
877 * method attempts to massage things back into the required order.
878 *
879 * This method will reorder the children of the node it is invoked on and
880 * then recurse to all children to fix up their children.
881 *
882 * @return OGRERR_NONE on success or an error code if something goes
883 * wrong.
884 */
885
FixupOrdering()886 OGRErr OGR_SRSNode::FixupOrdering()
887
888 {
889 int i;
890
891 /* -------------------------------------------------------------------- */
892 /* Recurse ordering children. */
893 /* -------------------------------------------------------------------- */
894 for( i = 0; i < GetChildCount(); i++ )
895 GetChild(i)->FixupOrdering();
896
897 if( GetChildCount() < 3 )
898 return OGRERR_NONE;
899
900 /* -------------------------------------------------------------------- */
901 /* Is this a node for which an ordering rule exists? */
902 /* -------------------------------------------------------------------- */
903 const char * const * papszRule = NULL;
904
905 for( i = 0; apszOrderingRules[i] != NULL; i++ )
906 {
907 if( EQUAL(apszOrderingRules[i][0],pszValue) )
908 {
909 papszRule = apszOrderingRules[i] + 1;
910 break;
911 }
912 }
913
914 if( papszRule == NULL )
915 return OGRERR_NONE;
916
917 /* -------------------------------------------------------------------- */
918 /* If we have a rule, apply it. We create an array */
919 /* (panChildPr) with the priority code for each child (derived */
920 /* from the rule) and we then bubble sort based on this. */
921 /* -------------------------------------------------------------------- */
922 int *panChildKey = (int *) CPLCalloc(sizeof(int),GetChildCount());
923
924 for( i = 1; i < GetChildCount(); i++ )
925 {
926 panChildKey[i] = CSLFindString( (char**) papszRule,
927 GetChild(i)->GetValue() );
928 if( panChildKey[i] == -1 )
929 {
930 CPLDebug( "OGRSpatialReference",
931 "Found unexpected key %s when trying to order SRS nodes.",
932 GetChild(i)->GetValue() );
933 }
934 }
935
936 /* -------------------------------------------------------------------- */
937 /* Sort - Note we don't try to do anything with the first child */
938 /* which we assume is a name string. */
939 /* -------------------------------------------------------------------- */
940 int j, bChange = TRUE;
941
942 for( i = 1; bChange && i < GetChildCount()-1; i++ )
943 {
944 bChange = FALSE;
945 for( j = 1; j < GetChildCount()-i; j++ )
946 {
947 if( panChildKey[j] == -1 || panChildKey[j+1] == -1 )
948 continue;
949
950 if( panChildKey[j] > panChildKey[j+1] )
951 {
952 OGR_SRSNode *poTemp = papoChildNodes[j];
953 int nKeyTemp = panChildKey[j];
954
955 papoChildNodes[j] = papoChildNodes[j+1];
956 papoChildNodes[j+1] = poTemp;
957
958 nKeyTemp = panChildKey[j];
959 panChildKey[j] = panChildKey[j+1];
960 panChildKey[j+1] = nKeyTemp;
961
962 bChange = TRUE;
963 }
964 }
965 }
966
967 CPLFree( panChildKey );
968
969 return OGRERR_NONE;
970 }
971
972
973