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