1 : /******************************************************************************
2 : * $Id: ogr_srsnode.cpp 25340 2012-12-21 20:30:21Z 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 : *
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 25340 2012-12-21 20:30:21Z rouault $");
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 :
47 3974986 : OGR_SRSNode::OGR_SRSNode( const char * pszValueIn )
48 :
49 : {
50 3974986 : pszValue = CPLStrdup( pszValueIn );
51 :
52 3974986 : nChildren = 0;
53 3974986 : papoChildNodes = NULL;
54 :
55 3974986 : poParent = NULL;
56 3974986 : }
57 :
58 : /************************************************************************/
59 : /* ~OGR_SRSNode() */
60 : /************************************************************************/
61 :
62 3974986 : OGR_SRSNode::~OGR_SRSNode()
63 :
64 : {
65 3974986 : CPLFree( pszValue );
66 :
67 3974986 : ClearChildren();
68 3974986 : }
69 :
70 : /************************************************************************/
71 : /* ClearChildren() */
72 : /************************************************************************/
73 :
74 4765831 : void OGR_SRSNode::ClearChildren()
75 :
76 : {
77 8405705 : for( int i = 0; i < nChildren; i++ )
78 : {
79 3639874 : delete papoChildNodes[i];
80 : }
81 :
82 4765831 : CPLFree( papoChildNodes );
83 :
84 4765831 : papoChildNodes = NULL;
85 4765831 : nChildren = 0;
86 4765831 : }
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 :
114 16343801 : OGR_SRSNode *OGR_SRSNode::GetChild( int iChild )
115 :
116 : {
117 16343801 : if( iChild < 0 || iChild >= nChildren )
118 0 : return NULL;
119 : else
120 16343801 : return papoChildNodes[iChild];
121 : }
122 :
123 4415573 : const OGR_SRSNode *OGR_SRSNode::GetChild( int iChild ) const
124 :
125 : {
126 4415573 : if( iChild < 0 || iChild >= nChildren )
127 0 : return NULL;
128 : else
129 4415573 : 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 :
153 4773759 : OGR_SRSNode *OGR_SRSNode::GetNode( const char * pszName )
154 :
155 : {
156 : int i;
157 :
158 4773759 : if( this == NULL )
159 0 : return NULL;
160 :
161 4773759 : if( nChildren > 0 && EQUAL(pszName,pszValue) )
162 659536 : 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 10563851 : for( i = 0; i < nChildren; i++ )
169 : {
170 8250864 : if( EQUAL(papoChildNodes[i]->pszValue,pszName)
171 900618 : && papoChildNodes[i]->nChildren > 0 )
172 900618 : return papoChildNodes[i];
173 : }
174 :
175 : /* -------------------------------------------------------------------- */
176 : /* Then get each child to check their children. */
177 : /* -------------------------------------------------------------------- */
178 5872805 : for( i = 0; i < nChildren; i++ )
179 : {
180 : OGR_SRSNode *poNode;
181 :
182 3105465 : poNode = papoChildNodes[i]->GetNode( pszName );
183 3105465 : if( poNode != NULL )
184 446265 : return poNode;
185 : }
186 :
187 2767340 : return NULL;
188 : }
189 :
190 1461 : const OGR_SRSNode *OGR_SRSNode::GetNode( const char * pszName ) const
191 :
192 : {
193 1461 : 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 :
210 3866888 : void OGR_SRSNode::AddChild( OGR_SRSNode * poNew )
211 :
212 : {
213 3866888 : InsertChild( poNew, nChildren );
214 3866888 : }
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 :
233 3887510 : void OGR_SRSNode::InsertChild( OGR_SRSNode * poNew, int iChild )
234 :
235 : {
236 3887510 : if( iChild > nChildren )
237 0 : iChild = nChildren;
238 :
239 3887510 : nChildren++;
240 : papoChildNodes = (OGR_SRSNode **)
241 3887510 : CPLRealloc( papoChildNodes, sizeof(void*) * nChildren );
242 :
243 : memmove( papoChildNodes + iChild + 1, papoChildNodes + iChild,
244 3887510 : sizeof(void*) * (nChildren - iChild - 1) );
245 :
246 3887510 : papoChildNodes[iChild] = poNew;
247 3887510 : poNew->poParent = this;
248 3887510 : }
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 :
263 247659 : void OGR_SRSNode::DestroyChild( int iChild )
264 :
265 : {
266 247659 : if( iChild < 0 || iChild >= nChildren )
267 23 : return;
268 :
269 247636 : delete papoChildNodes[iChild];
270 586163 : while( iChild < nChildren-1 )
271 : {
272 90891 : papoChildNodes[iChild] = papoChildNodes[iChild+1];
273 90891 : iChild++;
274 : }
275 :
276 247636 : 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 :
294 6449527 : int OGR_SRSNode::FindChild( const char * pszValue ) const
295 :
296 : {
297 15465380 : for( int i = 0; i < nChildren; i++ )
298 : {
299 9608680 : if( EQUAL(papoChildNodes[i]->pszValue,pszValue) )
300 592827 : return i;
301 : }
302 :
303 5856700 : 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 :
330 950003 : void OGR_SRSNode::SetValue( const char * pszNewValue )
331 :
332 : {
333 950003 : CPLFree( pszValue );
334 950003 : pszValue = CPLStrdup( pszNewValue );
335 950003 : }
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 :
347 1621595 : OGR_SRSNode *OGR_SRSNode::Clone() const
348 :
349 : {
350 : OGR_SRSNode *poNew;
351 :
352 1621595 : poNew = new OGR_SRSNode( pszValue );
353 :
354 3201749 : for( int i = 0; i < nChildren; i++ )
355 : {
356 1580154 : poNew->AddChild( papoChildNodes[i]->Clone() );
357 : }
358 :
359 1621595 : return poNew;
360 : }
361 :
362 : /************************************************************************/
363 : /* NeedsQuoting() */
364 : /* */
365 : /* Does this node need to be quoted when it is exported to Wkt? */
366 : /************************************************************************/
367 :
368 3187439 : int OGR_SRSNode::NeedsQuoting() const
369 :
370 : {
371 : // non-terminals are never quoted.
372 3187439 : if( GetChildCount() != 0 )
373 1103098 : 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 2084341 : if( poParent != NULL && EQUAL(poParent->GetValue(),"AUTHORITY") )
378 275690 : 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 1808651 : if( poParent != NULL && EQUAL(poParent->GetValue(),"AXIS")
383 : && this != poParent->GetChild(0) )
384 31193 : return FALSE;
385 :
386 : // Strings starting with e or E are not valid numeric values, so they
387 : // need quoting, like in AXIS["E",EAST]
388 1777458 : if( (pszValue[0] == 'e' || pszValue[0] == 'E') )
389 14754 : return TRUE;
390 :
391 : // Non-numeric tokens are generally quoted while clean numeric values
392 : // are generally not.
393 7214587 : for( int i = 0; pszValue[i] != '\0'; i++ )
394 : {
395 11575110 : if( (pszValue[i] < '0' || pszValue[i] > '9')
396 1391986 : && pszValue[i] != '.'
397 1930371 : && pszValue[i] != '-' && pszValue[i] != '+'
398 1867264 : && pszValue[i] != 'e' && pszValue[i] != 'E' )
399 933606 : return TRUE;
400 : }
401 :
402 829098 : return FALSE;
403 : }
404 :
405 : /************************************************************************/
406 : /* exportToWkt() */
407 : /************************************************************************/
408 :
409 : /**
410 : * Convert this tree of nodes into WKT format.
411 : *
412 : * Note that the returned WKT string should be freed with OGRFree() or
413 : * CPLFree() when no longer needed. It is the responsibility of the caller.
414 : *
415 : * @param ppszResult the resulting string is returned in this pointer.
416 : *
417 : * @return currently OGRERR_NONE is always returned, but the future it
418 : * is possible error conditions will develop.
419 : */
420 :
421 :
422 3184388 : OGRErr OGR_SRSNode::exportToWkt( char ** ppszResult ) const
423 :
424 : {
425 3184388 : char **papszChildrenWkt = NULL;
426 3184388 : int nLength = strlen(pszValue)+4;
427 : int i;
428 :
429 : /* -------------------------------------------------------------------- */
430 : /* Build a list of the WKT format for the children. */
431 : /* -------------------------------------------------------------------- */
432 3184388 : papszChildrenWkt = (char **) CPLCalloc(sizeof(char*),(nChildren+1));
433 :
434 6291496 : for( i = 0; i < nChildren; i++ )
435 : {
436 3107108 : papoChildNodes[i]->exportToWkt( papszChildrenWkt + i );
437 3107108 : nLength += strlen(papszChildrenWkt[i]) + 1;
438 : }
439 :
440 : /* -------------------------------------------------------------------- */
441 : /* Allocate the result string. */
442 : /* -------------------------------------------------------------------- */
443 3184388 : *ppszResult = (char *) CPLMalloc(nLength);
444 3184388 : *ppszResult[0] = '\0';
445 :
446 : /* -------------------------------------------------------------------- */
447 : /* Capture this nodes value. We put it in double quotes if */
448 : /* this is a leaf node, otherwise we assume it is a well formed */
449 : /* node name. */
450 : /* -------------------------------------------------------------------- */
451 3184388 : if( NeedsQuoting() )
452 : {
453 1222790 : strcat( *ppszResult, "\"" );
454 1222790 : strcat( *ppszResult, pszValue ); /* should we do quoting? */
455 1222790 : strcat( *ppszResult, "\"" );
456 : }
457 : else
458 1961598 : strcat( *ppszResult, pszValue );
459 :
460 : /* -------------------------------------------------------------------- */
461 : /* Add the children strings with appropriate brackets and commas. */
462 : /* -------------------------------------------------------------------- */
463 3184388 : if( nChildren > 0 )
464 1102059 : strcat( *ppszResult, "[" );
465 :
466 6291496 : for( i = 0; i < nChildren; i++ )
467 : {
468 3107108 : strcat( *ppszResult, papszChildrenWkt[i] );
469 3107108 : if( i == nChildren-1 )
470 1102059 : strcat( *ppszResult, "]" );
471 : else
472 2005049 : strcat( *ppszResult, "," );
473 : }
474 :
475 3184388 : CSLDestroy( papszChildrenWkt );
476 :
477 3184388 : return OGRERR_NONE;
478 : }
479 :
480 : /************************************************************************/
481 : /* exportToPrettyWkt() */
482 : /************************************************************************/
483 :
484 3051 : OGRErr OGR_SRSNode::exportToPrettyWkt( char ** ppszResult, int nDepth ) const
485 :
486 : {
487 3051 : char **papszChildrenWkt = NULL;
488 3051 : int nLength = strlen(pszValue)+4;
489 : int i;
490 :
491 : /* -------------------------------------------------------------------- */
492 : /* Build a list of the WKT format for the children. */
493 : /* -------------------------------------------------------------------- */
494 3051 : papszChildrenWkt = (char **) CPLCalloc(sizeof(char*),(nChildren+1));
495 :
496 6033 : for( i = 0; i < nChildren; i++ )
497 : {
498 2982 : papoChildNodes[i]->exportToPrettyWkt( papszChildrenWkt + i,
499 5964 : nDepth + 1);
500 2982 : nLength += strlen(papszChildrenWkt[i]) + 2 + nDepth*4;
501 : }
502 :
503 : /* -------------------------------------------------------------------- */
504 : /* Allocate the result string. */
505 : /* -------------------------------------------------------------------- */
506 3051 : *ppszResult = (char *) CPLMalloc(nLength);
507 3051 : *ppszResult[0] = '\0';
508 :
509 : /* -------------------------------------------------------------------- */
510 : /* Capture this nodes value. We put it in double quotes if */
511 : /* this is a leaf node, otherwise we assume it is a well formed */
512 : /* node name. */
513 : /* -------------------------------------------------------------------- */
514 3051 : if( NeedsQuoting() )
515 : {
516 1260 : strcat( *ppszResult, "\"" );
517 1260 : strcat( *ppszResult, pszValue ); /* should we do quoting? */
518 1260 : strcat( *ppszResult, "\"" );
519 : }
520 : else
521 1791 : strcat( *ppszResult, pszValue );
522 :
523 : /* -------------------------------------------------------------------- */
524 : /* Add the children strings with appropriate brackets and commas. */
525 : /* -------------------------------------------------------------------- */
526 3051 : if( nChildren > 0 )
527 1039 : strcat( *ppszResult, "[" );
528 :
529 6033 : for( i = 0; i < nChildren; i++ )
530 : {
531 2982 : if( papoChildNodes[i]->GetChildCount() > 0 )
532 : {
533 : int j;
534 :
535 970 : strcat( *ppszResult, "\n" );
536 7870 : for( j = 0; j < 4*nDepth; j++ )
537 6900 : strcat( *ppszResult, " " );
538 : }
539 2982 : strcat( *ppszResult, papszChildrenWkt[i] );
540 2982 : if( i < nChildren-1 )
541 1943 : strcat( *ppszResult, "," );
542 : }
543 :
544 3051 : if( nChildren > 0 )
545 : {
546 1039 : if( (*ppszResult)[strlen(*ppszResult)-1] == ',' )
547 0 : (*ppszResult)[strlen(*ppszResult)-1] = '\0';
548 :
549 1039 : strcat( *ppszResult, "]" );
550 : }
551 :
552 3051 : CSLDestroy( papszChildrenWkt );
553 :
554 3051 : return OGRERR_NONE;
555 : }
556 :
557 : /************************************************************************/
558 : /* importFromWkt() */
559 : /************************************************************************/
560 :
561 : /**
562 : * Import from WKT string.
563 : *
564 : * This method will wipe the existing children and value of this node, and
565 : * reassign them based on the contents of the passed WKT string. Only as
566 : * much of the input string as needed to construct this node, and it's
567 : * children is consumed from the input string, and the input string pointer
568 : * is then updated to point to the remaining (unused) input.
569 : *
570 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
571 : * point to remaining unused input text.
572 : *
573 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
574 : * fails for any reason.
575 : */
576 :
577 790845 : OGRErr OGR_SRSNode::importFromWkt( char ** ppszInput )
578 :
579 : {
580 790845 : const char *pszInput = *ppszInput;
581 790845 : int bInQuotedString = FALSE;
582 :
583 : /* -------------------------------------------------------------------- */
584 : /* Clear any existing children of this node. */
585 : /* -------------------------------------------------------------------- */
586 790845 : ClearChildren();
587 :
588 : /* -------------------------------------------------------------------- */
589 : /* Read the ``value'' for this node. */
590 : /* -------------------------------------------------------------------- */
591 : char szToken[512];
592 790845 : int nTokenLen = 0;
593 :
594 9611984 : while( *pszInput != '\0' && nTokenLen < (int) sizeof(szToken)-1 )
595 : {
596 8821117 : if( *pszInput == '"' )
597 : {
598 603066 : bInQuotedString = !bInQuotedString;
599 : }
600 8218051 : else if( !bInQuotedString
601 : && (*pszInput == '[' || *pszInput == ']' || *pszInput == ','
602 : || *pszInput == '(' || *pszInput == ')' ) )
603 : {
604 790823 : break;
605 : }
606 7427228 : else if( !bInQuotedString
607 : && (*pszInput == ' ' || *pszInput == '\t'
608 : || *pszInput == 10 || *pszInput == 13) )
609 : {
610 : /* just skip over whitespace */
611 : }
612 : else
613 : {
614 7412406 : szToken[nTokenLen++] = *pszInput;
615 : }
616 :
617 8030294 : pszInput++;
618 : }
619 :
620 790845 : if( *pszInput == '\0' || nTokenLen == sizeof(szToken) - 1 )
621 22 : return OGRERR_CORRUPT_DATA;
622 :
623 790823 : szToken[nTokenLen++] = '\0';
624 790823 : SetValue( szToken );
625 :
626 : /* -------------------------------------------------------------------- */
627 : /* Read children, if we have a sublist. */
628 : /* -------------------------------------------------------------------- */
629 790823 : if( *pszInput == '[' || *pszInput == '(' )
630 : {
631 766098 : do
632 : {
633 : OGR_SRSNode *poNewChild;
634 : OGRErr eErr;
635 :
636 766098 : pszInput++; // Skip bracket or comma.
637 :
638 766098 : poNewChild = new OGR_SRSNode();
639 :
640 766098 : eErr = poNewChild->importFromWkt( (char **) &pszInput );
641 766098 : if( eErr != OGRERR_NONE )
642 : {
643 0 : delete poNewChild;
644 0 : return eErr;
645 : }
646 :
647 766098 : AddChild( poNewChild );
648 :
649 : // swallow whitespace
650 1532196 : while( isspace(*pszInput) )
651 0 : pszInput++;
652 :
653 : } while( *pszInput == ',' );
654 :
655 278800 : if( *pszInput != ')' && *pszInput != ']' )
656 0 : return OGRERR_CORRUPT_DATA;
657 :
658 278800 : pszInput++;
659 : }
660 :
661 790823 : *ppszInput = (char *) pszInput;
662 :
663 790823 : return OGRERR_NONE;
664 : }
665 :
666 : /************************************************************************/
667 : /* MakeValueSafe() */
668 : /************************************************************************/
669 :
670 : /**
671 : * Massage value string, stripping special characters so it will be a
672 : * database safe string.
673 : *
674 : * The operation is also applies to all subnodes of the current node.
675 : */
676 :
677 :
678 0 : void OGR_SRSNode::MakeValueSafe()
679 :
680 : {
681 : int i, j;
682 :
683 : /* -------------------------------------------------------------------- */
684 : /* First process subnodes. */
685 : /* -------------------------------------------------------------------- */
686 0 : for( int iChild = 0; iChild < GetChildCount(); iChild++ )
687 : {
688 0 : GetChild(iChild)->MakeValueSafe();
689 : }
690 :
691 : /* -------------------------------------------------------------------- */
692 : /* Skip numeric nodes. */
693 : /* -------------------------------------------------------------------- */
694 0 : if( (pszValue[0] >= '0' && pszValue[0] <= '9') || pszValue[0] != '.' )
695 0 : return;
696 :
697 : /* -------------------------------------------------------------------- */
698 : /* Translate non-alphanumeric values to underscores. */
699 : /* -------------------------------------------------------------------- */
700 0 : for( i = 0; pszValue[i] != '\0'; i++ )
701 : {
702 0 : if( !(pszValue[i] >= 'A' && pszValue[i] <= 'Z')
703 0 : && !(pszValue[i] >= 'a' && pszValue[i] <= 'z')
704 0 : && !(pszValue[i] >= '0' && pszValue[i] <= '9') )
705 : {
706 0 : pszValue[i] = '_';
707 : }
708 : }
709 :
710 : /* -------------------------------------------------------------------- */
711 : /* Remove repeated and trailing underscores. */
712 : /* -------------------------------------------------------------------- */
713 0 : for( i = 1, j = 0; pszValue[i] != '\0'; i++ )
714 : {
715 0 : if( pszValue[j] == '_' && pszValue[i] == '_' )
716 0 : continue;
717 :
718 0 : pszValue[++j] = pszValue[i];
719 : }
720 :
721 0 : if( pszValue[j] == '_' )
722 0 : pszValue[j] = '\0';
723 : else
724 0 : pszValue[j+1] = '\0';
725 : }
726 :
727 : /************************************************************************/
728 : /* applyRemapper() */
729 : /************************************************************************/
730 :
731 : /**
732 : * Remap node values matching list.
733 : *
734 : * Remap the value of this node or any of it's children if it matches
735 : * one of the values in the source list to the corresponding value from
736 : * the destination list. If the pszNode value is set, only do so if the
737 : * parent node matches that value. Even if a replacement occurs, searching
738 : * continues.
739 : *
740 : * @param pszNode Restrict remapping to children of this type of node
741 : * (eg. "PROJECTION")
742 : * @param papszSrcValues a NULL terminated array of source string. If the
743 : * node value matches one of these (case insensitive) then replacement occurs.
744 : * @param papszDstValues an array of destination strings. On a match, the
745 : * one corresponding to a source value will be used to replace a node.
746 : * @param nStepSize increment when stepping through source and destination
747 : * arrays, allowing source and destination arrays to be one interleaved array
748 : * for instances. Defaults to 1.
749 : * @param bChildOfHit Only TRUE if we the current node is the child of a match,
750 : * and so needs to be set. Application code would normally pass FALSE for this
751 : * argument.
752 : *
753 : * @return returns OGRERR_NONE unless something bad happens. There is no
754 : * indication returned about whether any replacement occured.
755 : */
756 :
757 6313187 : OGRErr OGR_SRSNode::applyRemapper( const char *pszNode,
758 : char **papszSrcValues,
759 : char **papszDstValues,
760 : int nStepSize, int bChildOfHit )
761 :
762 : {
763 : int i;
764 :
765 : /* -------------------------------------------------------------------- */
766 : /* Scan for value, and replace if our parent was a "hit". */
767 : /* -------------------------------------------------------------------- */
768 6313187 : if( bChildOfHit || pszNode == NULL )
769 : {
770 52349456 : for( i = 0; papszSrcValues[i] != NULL; i += nStepSize )
771 : {
772 51971701 : if( EQUAL(papszSrcValues[i],pszValue) &&
773 80501 : ! EQUAL(papszDstValues[i],"") )
774 : {
775 79076 : SetValue( papszDstValues[i] );
776 79076 : break;
777 : }
778 : }
779 : }
780 :
781 : /* -------------------------------------------------------------------- */
782 : /* Are the the target node? */
783 : /* -------------------------------------------------------------------- */
784 6313187 : if( pszNode != NULL )
785 6313187 : bChildOfHit = EQUAL(pszValue,pszNode);
786 :
787 : /* -------------------------------------------------------------------- */
788 : /* Recurse */
789 : /* -------------------------------------------------------------------- */
790 12432480 : for( i = 0; i < GetChildCount(); i++ )
791 : {
792 : GetChild(i)->applyRemapper( pszNode, papszSrcValues,
793 6119293 : papszDstValues, nStepSize, bChildOfHit );
794 : }
795 :
796 6313187 : return OGRERR_NONE;
797 : }
798 :
799 : /************************************************************************/
800 : /* StripNodes() */
801 : /************************************************************************/
802 :
803 : /**
804 : * Strip child nodes matching name.
805 : *
806 : * Removes any decendent nodes of this node that match the given name.
807 : * Of course children of removed nodes are also discarded.
808 : *
809 : * @param pszName the name for nodes that should be removed.
810 : */
811 :
812 5630111 : void OGR_SRSNode::StripNodes( const char * pszName )
813 :
814 : {
815 : /* -------------------------------------------------------------------- */
816 : /* Strip any children matching this name. */
817 : /* -------------------------------------------------------------------- */
818 11504278 : while( FindChild( pszName ) >= 0 )
819 244056 : DestroyChild( FindChild( pszName ) );
820 :
821 : /* -------------------------------------------------------------------- */
822 : /* Recurse */
823 : /* -------------------------------------------------------------------- */
824 11093007 : for( int i = 0; i < GetChildCount(); i++ )
825 5462896 : GetChild(i)->StripNodes( pszName );
826 5630111 : }
827 :
828 : /************************************************************************/
829 : /* FixupOrdering() */
830 : /************************************************************************/
831 :
832 : /* EXTENSION ... being a OSR extension... is arbitrary placed before the AUTHORITY */
833 : static const char * const apszPROJCSRule[] =
834 : { "PROJCS", "GEOGCS", "PROJECTION", "PARAMETER", "UNIT", "AXIS", "EXTENSION", "AUTHORITY",
835 : NULL };
836 :
837 : static const char * const apszDATUMRule[] =
838 : { "DATUM", "SPHEROID", "TOWGS84", "AUTHORITY", NULL };
839 :
840 : static const char * const apszGEOGCSRule[] =
841 : { "GEOGCS", "DATUM", "PRIMEM", "UNIT", "AXIS", "AUTHORITY", NULL };
842 :
843 : static const char * const apszGEOCCSRule[] =
844 : { "GEOCCS", "DATUM", "PRIMEM", "UNIT", "AXIS", "AUTHORITY", NULL };
845 :
846 : static const char * const *apszOrderingRules[] = {
847 : apszPROJCSRule, apszGEOGCSRule, apszDATUMRule, apszGEOCCSRule, NULL };
848 :
849 : /**
850 : * Correct parameter ordering to match CT Specification.
851 : *
852 : * Some mechanisms to create WKT using OGRSpatialReference, and some
853 : * imported WKT fail to maintain the order of parameters required according
854 : * to the BNF definitions in the OpenGIS SF-SQL and CT Specifications. This
855 : * method attempts to massage things back into the required order.
856 : *
857 : * This method will reorder the children of the node it is invoked on and
858 : * then recurse to all children to fix up their children.
859 : *
860 : * @return OGRERR_NONE on success or an error code if something goes
861 : * wrong.
862 : */
863 :
864 2315700 : OGRErr OGR_SRSNode::FixupOrdering()
865 :
866 : {
867 : int i;
868 :
869 : /* -------------------------------------------------------------------- */
870 : /* Recurse ordering children. */
871 : /* -------------------------------------------------------------------- */
872 4592072 : for( i = 0; i < GetChildCount(); i++ )
873 2276372 : GetChild(i)->FixupOrdering();
874 :
875 2315700 : if( GetChildCount() < 3 )
876 2061829 : return OGRERR_NONE;
877 :
878 : /* -------------------------------------------------------------------- */
879 : /* Is this a node for which an ordering rule exists? */
880 : /* -------------------------------------------------------------------- */
881 253871 : const char * const * papszRule = NULL;
882 :
883 969298 : for( i = 0; apszOrderingRules[i] != NULL; i++ )
884 : {
885 816972 : if( EQUAL(apszOrderingRules[i][0],pszValue) )
886 : {
887 101545 : papszRule = apszOrderingRules[i] + 1;
888 101545 : break;
889 : }
890 : }
891 :
892 253871 : if( papszRule == NULL )
893 152326 : return OGRERR_NONE;
894 :
895 : /* -------------------------------------------------------------------- */
896 : /* If we have a rule, apply it. We create an array */
897 : /* (panChildPr) with the priority code for each child (derived */
898 : /* from the rule) and we then bubble sort based on this. */
899 : /* -------------------------------------------------------------------- */
900 101545 : int *panChildKey = (int *) CPLCalloc(sizeof(int),GetChildCount());
901 :
902 697777 : for( i = 1; i < GetChildCount(); i++ )
903 : {
904 596232 : panChildKey[i] = CSLFindString( (char**) papszRule,
905 596232 : GetChild(i)->GetValue() );
906 596232 : if( panChildKey[i] == -1 )
907 : {
908 : CPLDebug( "OGRSpatialReference",
909 : "Found unexpected key %s when trying to order SRS nodes.",
910 0 : GetChild(i)->GetValue() );
911 : }
912 : }
913 :
914 : /* -------------------------------------------------------------------- */
915 : /* Sort - Note we don't try to do anything with the first child */
916 : /* which we assume is a name string. */
917 : /* -------------------------------------------------------------------- */
918 101545 : int j, bChange = TRUE;
919 :
920 242810 : for( i = 1; bChange && i < GetChildCount()-1; i++ )
921 : {
922 141265 : bChange = FALSE;
923 879443 : for( j = 1; j < GetChildCount()-i; j++ )
924 : {
925 738178 : if( panChildKey[j] == -1 || panChildKey[j+1] == -1 )
926 0 : continue;
927 :
928 738178 : if( panChildKey[j] > panChildKey[j+1] )
929 : {
930 180591 : OGR_SRSNode *poTemp = papoChildNodes[j];
931 180591 : int nKeyTemp = panChildKey[j];
932 :
933 180591 : papoChildNodes[j] = papoChildNodes[j+1];
934 180591 : papoChildNodes[j+1] = poTemp;
935 :
936 180591 : nKeyTemp = panChildKey[j];
937 180591 : panChildKey[j] = panChildKey[j+1];
938 180591 : panChildKey[j+1] = nKeyTemp;
939 :
940 180591 : bChange = TRUE;
941 : }
942 : }
943 : }
944 :
945 101545 : CPLFree( panChildKey );
946 :
947 101545 : return OGRERR_NONE;
948 : }
949 :
950 :
|