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