root/tags/gdal_1_3_2/gcore/gdalpamdataset.cpp

Revision 9578, 36.1 kB (checked in by fwarmerdam, 3 years ago)

keep track if an object is PAM enabled, bug 1135

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  GDAL Core
5  * Purpose:  Implementation of GDALPamDataset, a dataset base class that
6  *           knows how to persist auxilary metadata into a support XML file.
7  * Author:   Frank Warmerdam, warmerdam@pobox.com
8  *
9  ******************************************************************************
10  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ******************************************************************************
30  *
31  * $Log$
32  * Revision 1.19  2006/04/13 03:16:01  fwarmerdam
33  * keep track if an object is PAM enabled, bug 1135
34  *
35  * Revision 1.18  2006/04/07 13:12:16  fwarmerdam
36  * Fix merging of band metadata.
37  *
38  * Revision 1.17  2006/03/31 14:25:10  fwarmerdam
39  * avoiding cloning from missing source bands
40  *
41  * Revision 1.16  2005/10/14 21:10:16  fwarmerdam
42  * avoid error if .aux.xml file does not exist
43  *
44  * Revision 1.15  2005/10/13 01:19:57  fwarmerdam
45  * moved GDALMultiDomainMetadata into GDALMajorObject
46  *
47  * Revision 1.14  2005/10/12 15:35:23  fwarmerdam
48  * mark pam info clean at end of TryLoadAux
49  *
50  * Revision 1.13  2005/09/27 22:12:19  fwarmerdam
51  * added RAT support when cloning
52  *
53  * Revision 1.12  2005/09/26 15:52:03  fwarmerdam
54  * centralized .aux opening logic
55  *
56  * Revision 1.11  2005/09/23 20:55:19  fwarmerdam
57  * avoid unimplemented errors if PAM disabled
58  *
59  * Revision 1.10  2005/09/11 18:04:24  fwarmerdam
60  * clearup multidomain metadata
61  *
62  * Revision 1.9  2005/08/31 03:34:23  fwarmerdam
63  * use CPLString.Printf instead of CSPrintf()
64  *
65  * Revision 1.8  2005/07/22 13:50:13  fwarmerdam
66  * Default PAM support to off.
67  *
68  * Revision 1.7  2005/06/09 15:43:02  fwarmerdam
69  * Clear dirty flag in TryLoadXML() so we don't end up writing out
70  * a pam file for metadata set within the callers Open() method.
71  *
72  * Revision 1.6  2005/06/08 14:04:38  fwarmerdam
73  * use .aux.xml as the default extension instead of .pam
74  *
75  * Revision 1.5  2005/05/22 08:13:40  fwarmerdam
76  * added multidomain metadata support
77  *
78  * Revision 1.4  2005/05/17 15:13:28  fwarmerdam
79  * ensure pam info initialize on any set operation
80  *
81  * Revision 1.3  2005/05/16 21:36:23  fwarmerdam
82  * prototype support for reading aux file
83  *
84  * Revision 1.2  2005/05/11 14:03:08  fwarmerdam
85  * added option to disable pam, and quiet failed .pam opens
86  *
87  * Revision 1.1  2005/04/27 16:27:44  fwarmerdam
88  * New
89  *
90  */
91
92 #include "gdal_pam.h"
93 #include "cpl_string.h"
94 #include "ogr_spatialref.h"
95
96 CPL_CVSID("$Id$");
97
98 /************************************************************************/
99 /*                           GDALPamDataset()                           */
100 /************************************************************************/
101
102 GDALPamDataset::GDALPamDataset()
103
104 {
105     nPamFlags = 0;
106     psPam = NULL;
107     SetMOFlags( GetMOFlags() | GMO_PAM_CLASS );
108 }
109
110 /************************************************************************/
111 /*                          ~GDALPamDataset()                           */
112 /************************************************************************/
113
114 GDALPamDataset::~GDALPamDataset()
115
116 {
117     if( nPamFlags & GPF_DIRTY )
118     {
119         CPLDebug( "GDALPamDataset", "In destructor with dirty metadata." );
120         FlushCache();
121     }
122
123     PamClear();
124 }
125
126 /************************************************************************/
127 /*                             FlushCache()                             */
128 /************************************************************************/
129
130 void GDALPamDataset::FlushCache()
131
132 {
133     GDALDataset::FlushCache();
134     if( nPamFlags & GPF_DIRTY )
135         TrySaveXML();
136 }
137
138 /************************************************************************/
139 /*                           SerializeToXML()                           */
140 /************************************************************************/
141
142 CPLXMLNode *GDALPamDataset::SerializeToXML( const char *pszVRTPath )
143
144 {
145     CPLString oFmt;
146
147     if( psPam == NULL )
148         return NULL;
149
150 /* -------------------------------------------------------------------- */
151 /*      Setup root node and attributes.                                 */
152 /* -------------------------------------------------------------------- */
153     CPLXMLNode *psDSTree;
154
155     psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" );
156
157 /* -------------------------------------------------------------------- */
158 /*      SRS                                                             */
159 /* -------------------------------------------------------------------- */
160     if( psPam->pszProjection != NULL && strlen(psPam->pszProjection) > 0 )
161         CPLSetXMLValue( psDSTree, "SRS", psPam->pszProjection );
162
163 /* -------------------------------------------------------------------- */
164 /*      GeoTransform.                                                   */
165 /* -------------------------------------------------------------------- */
166     if( psPam->bHaveGeoTransform )
167     {
168         CPLSetXMLValue( psDSTree, "GeoTransform",
169                         oFmt.Printf( "%24.16e,%24.16e,%24.16e,%24.16e,%24.16e,%24.16e",
170                                      psPam->adfGeoTransform[0],
171                                      psPam->adfGeoTransform[1],
172                                      psPam->adfGeoTransform[2],
173                                      psPam->adfGeoTransform[3],
174                                      psPam->adfGeoTransform[4],
175                                      psPam->adfGeoTransform[5] ) );
176     }
177
178 /* -------------------------------------------------------------------- */
179 /*      Metadata.                                                       */
180 /* -------------------------------------------------------------------- */
181     CPLXMLNode *psMD;
182
183     psMD = oMDMD.Serialize();
184     if( psMD != NULL )
185         CPLAddXMLChild( psDSTree, psMD );
186
187 /* -------------------------------------------------------------------- */
188 /*      GCPs                                                            */
189 /* -------------------------------------------------------------------- */
190     if( psPam->nGCPCount > 0 )
191     {
192         CPLXMLNode *psPamGCPList = CPLCreateXMLNode( psDSTree, CXT_Element,
193                                                      "GCPList" );
194
195         if( psPam->pszGCPProjection != NULL
196             && strlen(psPam->pszGCPProjection) > 0 )
197             CPLSetXMLValue( psPamGCPList, "#Projection",
198                             psPam->pszGCPProjection );
199
200         for( int iGCP = 0; iGCP < psPam->nGCPCount; iGCP++ )
201         {
202             CPLXMLNode *psXMLGCP;
203             GDAL_GCP *psGCP = psPam->pasGCPList + iGCP;
204
205             psXMLGCP = CPLCreateXMLNode( psPamGCPList, CXT_Element, "GCP" );
206
207             CPLSetXMLValue( psXMLGCP, "#Id", psGCP->pszId );
208
209             if( psGCP->pszInfo != NULL && strlen(psGCP->pszInfo) > 0 )
210                 CPLSetXMLValue( psXMLGCP, "Info", psGCP->pszInfo );
211
212             CPLSetXMLValue( psXMLGCP, "#Pixel",
213                             oFmt.Printf( "%.4f", psGCP->dfGCPPixel ) );
214
215             CPLSetXMLValue( psXMLGCP, "#Line",
216                             oFmt.Printf( "%.4f", psGCP->dfGCPLine ) );
217
218             CPLSetXMLValue( psXMLGCP, "#X",
219                             oFmt.Printf( "%.12E", psGCP->dfGCPX ) );
220
221             CPLSetXMLValue( psXMLGCP, "#Y",
222                             oFmt.Printf( "%.12E", psGCP->dfGCPY ) );
223
224             if( psGCP->dfGCPZ != 0.0 )
225                 CPLSetXMLValue( psXMLGCP, "#GCPZ",
226                                 oFmt.Printf( "%.12E", psGCP->dfGCPZ ) );
227         }
228     }
229
230 /* -------------------------------------------------------------------- */
231 /*      Process bands.                                                  */
232 /* -------------------------------------------------------------------- */
233     int iBand;
234
235     for( iBand = 0; iBand < GetRasterCount(); iBand++ )
236     {
237         CPLXMLNode *psBandTree;
238
239         GDALPamRasterBand *poBand = (GDALPamRasterBand *)
240             GetRasterBand(iBand+1);
241
242         // we really should be testing if poBand is a really derived
243         // from PamRasterBand or not.
244
245         psBandTree = poBand->SerializeToXML( pszVRTPath );
246
247         if( psBandTree != NULL )
248             CPLAddXMLChild( psDSTree, psBandTree );
249     }
250
251 /* -------------------------------------------------------------------- */
252 /*      We don't want to return anything if we had no metadata to       */
253 /*      attach.                                                         */
254 /* -------------------------------------------------------------------- */
255     if( psDSTree->psChild == NULL )
256     {
257         CPLDestroyXMLNode( psDSTree );
258         psDSTree = NULL;
259     }
260
261     return psDSTree;
262 }
263
264 /************************************************************************/
265 /*                           PamInitialize()                            */
266 /************************************************************************/
267
268 void GDALPamDataset::PamInitialize()
269
270 {
271     if( psPam || (nPamFlags & GPF_DISABLED) )
272         return;
273
274     if( !CSLTestBoolean( CPLGetConfigOption( "GDAL_PAM_ENABLED", "NO" ) ) )
275     {
276         nPamFlags |= GPF_DISABLED;
277         return;
278     }
279
280     if( EQUAL( CPLGetConfigOption( "GDAL_PAM_MODE", "PAM" ), "AUX") )
281         nPamFlags |= GPF_AUXMODE;
282
283     psPam = (GDALDatasetPamInfo *) CPLCalloc(sizeof(GDALDatasetPamInfo),1);
284
285     int iBand;
286    
287     for( iBand = 0; iBand < GetRasterCount(); iBand++ )
288     {
289         GDALPamRasterBand *poBand = (GDALPamRasterBand *)
290             GetRasterBand(iBand+1);
291        
292         // we really should be testing if poBand is a really derived
293         // from PamRasterBand or not.
294         
295         poBand->PamInitialize();
296     }
297 }
298
299 /************************************************************************/
300 /*                              PamClear()                              */
301 /************************************************************************/
302
303 void GDALPamDataset::PamClear()
304
305 {
306     if( psPam )
307     {
308         CPLFree( psPam->pszPamFilename );
309
310         CPLFree( psPam->pszProjection );
311
312         CPLFree( psPam->pszGCPProjection );
313         if( psPam->nGCPCount > 0 )
314         {
315             GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList );
316             CPLFree( psPam->pasGCPList );
317         }
318
319         CPLFree( psPam );
320         psPam = NULL;
321     }
322 }
323
324 /************************************************************************/
325 /*                              XMLInit()                               */
326 /************************************************************************/
327
328 CPLErr GDALPamDataset::XMLInit( CPLXMLNode *psTree, const char *pszVRTPath )
329
330 {
331 /* -------------------------------------------------------------------- */
332 /*      Check for an SRS node.                                          */
333 /* -------------------------------------------------------------------- */
334     if( strlen(CPLGetXMLValue(psTree, "SRS", "")) > 0 )
335     {
336         OGRSpatialReference oSRS;
337
338         CPLFree( psPam->pszProjection );
339         psPam->pszProjection = NULL;
340
341         if( oSRS.SetFromUserInput( CPLGetXMLValue(psTree, "SRS", "") )
342             == OGRERR_NONE )
343             oSRS.exportToWkt( &(psPam->pszProjection) );
344     }
345
346 /* -------------------------------------------------------------------- */
347 /*      Check for a GeoTransform node.                                  */
348 /* -------------------------------------------------------------------- */
349     if( strlen(CPLGetXMLValue(psTree, "GeoTransform", "")) > 0 )
350     {
351         const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", "");
352         char    **papszTokens;
353
354         papszTokens = CSLTokenizeStringComplex( pszGT, ",", FALSE, FALSE );
355         if( CSLCount(papszTokens) != 6 )
356         {
357             CPLError( CE_Warning, CPLE_AppDefined,
358                       "GeoTransform node does not have expected six values.");
359         }
360         else
361         {
362             for( int iTA = 0; iTA < 6; iTA++ )
363                 psPam->adfGeoTransform[iTA] = atof(papszTokens[iTA]);
364             psPam->bHaveGeoTransform = TRUE;
365         }
366
367         CSLDestroy( papszTokens );
368     }
369
370 /* -------------------------------------------------------------------- */
371 /*      Check for GCPs.                                                 */
372 /* -------------------------------------------------------------------- */
373     CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" );
374
375     if( psGCPList != NULL )
376     {
377         CPLXMLNode *psXMLGCP;
378         OGRSpatialReference oSRS;
379         const char *pszRawProj = CPLGetXMLValue(psGCPList, "Projection", "");
380
381         CPLFree( psPam->pszGCPProjection );
382
383         if( strlen(pszRawProj) > 0
384             && oSRS.SetFromUserInput( pszRawProj ) == OGRERR_NONE )
385             oSRS.exportToWkt( &(psPam->pszGCPProjection) );
386         else
387             psPam->pszGCPProjection = CPLStrdup("");
388
389         // Count GCPs.
390         int  nGCPMax = 0;
391          
392         for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL;
393              psXMLGCP = psXMLGCP->psNext )
394             nGCPMax++;
395          
396         psPam->pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPMax);
397          
398         for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL;
399              psXMLGCP = psXMLGCP->psNext )
400         {
401             GDAL_GCP *psGCP = psPam->pasGCPList + psPam->nGCPCount;
402
403             if( !EQUAL(psXMLGCP->pszValue,"GCP") ||
404                 psXMLGCP->eType != CXT_Element )
405                 continue;
406              
407             GDALInitGCPs( 1, psGCP );
408              
409             CPLFree( psGCP->pszId );
410             psGCP->pszId = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Id",""));
411              
412             CPLFree( psGCP->pszInfo );
413             psGCP->pszInfo = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Info",""));
414              
415             psGCP->dfGCPPixel = atof(CPLGetXMLValue(psXMLGCP,"Pixel","0.0"));
416             psGCP->dfGCPLine = atof(CPLGetXMLValue(psXMLGCP,"Line","0.0"));
417              
418             psGCP->dfGCPX = atof(CPLGetXMLValue(psXMLGCP,"X","0.0"));
419             psGCP->dfGCPY = atof(CPLGetXMLValue(psXMLGCP,"Y","0.0"));
420             psGCP->dfGCPZ = atof(CPLGetXMLValue(psXMLGCP,"Z","0.0"));
421
422             psPam->nGCPCount++;
423         }
424     }
425
426 /* -------------------------------------------------------------------- */
427 /*      Apply any dataset level metadata.                               */
428 /* -------------------------------------------------------------------- */
429     oMDMD.XMLInit( psTree, TRUE );
430
431 /* -------------------------------------------------------------------- */
432 /*      Process bands.                                                  */
433 /* -------------------------------------------------------------------- */
434     CPLXMLNode *psBandTree;
435
436     for( psBandTree = psTree->psChild;
437          psBandTree != NULL; psBandTree = psBandTree->psNext )
438     {
439         if( psBandTree->eType != CXT_Element
440             || !EQUAL(psBandTree->pszValue,"PAMRasterBand") )
441             continue;
442
443         int nBand = atoi(CPLGetXMLValue( psBandTree, "band", "0"));
444
445         if( nBand < 1 || nBand > GetRasterCount() )
446             continue;
447
448         GDALPamRasterBand *poBand = (GDALPamRasterBand *)
449             GetRasterBand(nBand);
450
451         if( poBand == NULL )
452             continue;
453
454         // we really should be testing if poBand is a really derived
455         // from PamRasterBand or not.
456
457         poBand->XMLInit( psBandTree, pszVRTPath );
458     }
459
460 /* -------------------------------------------------------------------- */
461 /*      Clear dirty flag.                                               */
462 /* -------------------------------------------------------------------- */
463     nPamFlags &= ~GPF_DIRTY;
464
465     return CE_None;
466 }
467
468 /************************************************************************/
469 /*                          BuildPamFilename()                          */
470 /************************************************************************/
471
472 const char *GDALPamDataset::BuildPamFilename()
473
474 {
475     if( psPam == NULL )
476         return NULL;
477
478     if( psPam->pszPamFilename != NULL )
479         return psPam->pszPamFilename;
480    
481     if( GetDescription() == NULL || strlen(GetDescription()) == 0 )
482         return NULL;
483
484     psPam->pszPamFilename = (char *) CPLMalloc(strlen(GetDescription()) + 10 );
485     strcpy( psPam->pszPamFilename, GetDescription() );
486     strcat( psPam->pszPamFilename, ".aux.xml" );
487
488     return psPam->pszPamFilename;
489 }
490
491 /************************************************************************/
492 /*                             TryLoadXML()                             */
493 /************************************************************************/
494
495 CPLErr GDALPamDataset::TryLoadXML()
496
497 {
498     char *pszVRTPath = NULL;
499     CPLXMLNode *psTree = NULL;
500
501     PamInitialize();
502
503 /* -------------------------------------------------------------------- */
504 /*      Clear dirty flag.  Generally when we get to this point is       */
505 /*      from a call at the end of the Open() method, and some calls     */
506 /*      may have already marked the PAM info as dirty (for instance     */
507 /*      setting metadata), but really everything to this point is       */
508 /*      reproducable, and so the PAM info shouldn't really be           */
509 /*      thought of as dirty.                                            */
510 /* -------------------------------------------------------------------- */
511     nPamFlags &= ~GPF_DIRTY;
512
513 /* -------------------------------------------------------------------- */
514 /*      Try reading the file.                                           */
515 /* -------------------------------------------------------------------- */
516     if( !BuildPamFilename() )
517         return CE_None;
518
519     VSIStatBufL sStatBuf;
520
521     if( VSIStatL( psPam->pszPamFilename, &sStatBuf ) == 0
522         && VSI_ISREG( sStatBuf.st_mode ) )
523     {
524         CPLErrorReset();
525         CPLPushErrorHandler( CPLQuietErrorHandler );
526         psTree = CPLParseXMLFile( psPam->pszPamFilename );
527         CPLPopErrorHandler();
528     }
529
530 /* -------------------------------------------------------------------- */
531 /*      If we fail, try .aux.                                           */
532 /* -------------------------------------------------------------------- */
533     if( psTree == NULL )
534         return TryLoadAux();
535
536 /* -------------------------------------------------------------------- */
537 /*      Initialize ourselves from this XML tree.                        */
538 /* -------------------------------------------------------------------- */
539     CPLErr eErr;
540
541     pszVRTPath = CPLStrdup(CPLGetPath(psPam->pszPamFilename));
542     eErr = XMLInit( psTree, pszVRTPath );
543     CPLFree( pszVRTPath );
544
545     CPLDestroyXMLNode( psTree );
546
547     if( eErr != CE_None )
548         PamClear();
549
550     return eErr;
551 }
552
553 /************************************************************************/
554 /*                             TrySaveXML()                             */
555 /************************************************************************/
556
557 CPLErr GDALPamDataset::TrySaveXML()
558
559 {
560     CPLXMLNode *psTree;
561     char *pszVRTPath;
562     CPLErr eErr = CE_None;
563
564     nPamFlags &= ~GPF_DIRTY;
565
566     if( psPam == NULL )
567         return CE_None;
568
569     if( !BuildPamFilename() )
570         return CE_None;
571
572     pszVRTPath = CPLStrdup(CPLGetPath(psPam->pszPamFilename));
573     psTree = SerializeToXML( pszVRTPath );
574     CPLFree( pszVRTPath );
575
576     if( psTree != NULL )
577     {
578         if( CPLSerializeXMLTreeToFile( psTree, psPam->pszPamFilename ) )
579             eErr = CE_None;
580         else
581             eErr = CE_Failure;
582     }
583    
584     CPLDestroyXMLNode( psTree );
585
586     return eErr;
587 }
588
589 /************************************************************************/
590 /*                             CloneInfo()                              */
591 /************************************************************************/
592
593 CPLErr GDALPamDataset::CloneInfo( GDALDataset *poSrcDS, int nCloneFlags )
594
595 {
596     int bOnlyIfMissing = nCloneFlags & GCIF_ONLY_IF_MISSING;
597     int nSavedMOFlags = GetMOFlags();
598
599     PamInitialize();
600
601 /* -------------------------------------------------------------------- */
602 /*      Supress NotImplemented error messages - mainly needed if PAM    */
603 /*      disabled.                                                       */
604 /* -------------------------------------------------------------------- */
605     SetMOFlags( nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED );
606
607 /* -------------------------------------------------------------------- */
608 /*      GeoTransform                                                    */
609 /* -------------------------------------------------------------------- */
610     if( nCloneFlags & GCIF_GEOTRANSFORM )
611     {
612         double adfGeoTransform[6];
613
614         if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None )
615         {
616             double adfOldGT[6];
617
618             if( !bOnlyIfMissing || GetGeoTransform( adfOldGT ) != CE_None )
619                 SetGeoTransform( adfGeoTransform );
620         }
621     }
622
623 /* -------------------------------------------------------------------- */
624 /*      Projection                                                      */
625 /* -------------------------------------------------------------------- */
626     if( nCloneFlags & GCIF_PROJECTION )
627     {
628         const char *pszWKT = poSrcDS->GetProjectionRef();
629
630         if( pszWKT != NULL && strlen(pszWKT) > 0 )
631         {
632             if( !bOnlyIfMissing
633                 || GetProjectionRef() == NULL
634                 || strlen(GetProjectionRef()) == 0 )
635                 SetProjection( pszWKT );
636         }
637     }
638
639 /* -------------------------------------------------------------------- */
640 /*      GCPs                                                            */
641 /* -------------------------------------------------------------------- */
642     if( nCloneFlags & GCIF_GCPS )
643     {
644         if( poSrcDS->GetGCPCount() > 0 )
645         {
646             if( !bOnlyIfMissing || GetGCPCount() == 0 )
647             {
648                 SetGCPs( poSrcDS->GetGCPCount(),
649                          poSrcDS->GetGCPs(),
650                          poSrcDS->GetGCPProjection() );
651             }
652         }
653     }
654
655 /* -------------------------------------------------------------------- */
656 /*      Metadata                                                        */
657 /* -------------------------------------------------------------------- */
658     if( nCloneFlags & GCIF_METADATA )
659     {
660         if( poSrcDS->GetMetadata() != NULL )
661         {
662             if( !bOnlyIfMissing
663                 || CSLCount(GetMetadata()) != CSLCount(poSrcDS->GetMetadata()) )
664             {
665                 SetMetadata( poSrcDS->GetMetadata() );
666             }
667         }
668     }
669
670 /* -------------------------------------------------------------------- */
671 /*      Process bands.                                                  */
672 /* -------------------------------------------------------------------- */
673     if( nCloneFlags & GCIF_PROCESS_BANDS )
674     {
675         int iBand;
676
677         for( iBand = 0; iBand < GetRasterCount(); iBand++ )
678         {
679             GDALPamRasterBand *poBand = (GDALPamRasterBand *)
680                 GetRasterBand(iBand+1);
681
682             if( !(poBand->GetMOFlags() | GMO_PAM_CLASS) )
683                 continue;
684
685             if( poSrcDS->GetRasterCount() >= iBand+1 )
686                 poBand->CloneInfo( poSrcDS->GetRasterBand(iBand+1),
687                                    nCloneFlags );
688             else
689                 CPLDebug( "GDALPamDataset", "Skipping CloneInfo for band not in source, this is a bit unusual!" );
690         }
691     }
692
693 /* -------------------------------------------------------------------- */
694 /*      Restore MO flags.                                               */
695 /* -------------------------------------------------------------------- */
696     SetMOFlags( nSavedMOFlags );
697
698     return CE_None;
699 }
700
701 /************************************************************************/
702 /*                          GetProjectionRef()                          */
703 /************************************************************************/
704
705 const char *GDALPamDataset::GetProjectionRef()
706
707 {
708     if( psPam && psPam->pszProjection )
709         return psPam->pszProjection;
710     else
711         return GDALDataset::GetProjectionRef();
712 }
713
714 /************************************************************************/
715 /*                           SetProjection()                            */
716 /************************************************************************/
717
718 CPLErr GDALPamDataset::SetProjection( const char *pszProjectionIn )
719
720 {
721     PamInitialize();
722
723     if( psPam == NULL )
724         return GDALDataset::SetProjection( pszProjectionIn );
725     else
726     {
727         CPLFree( psPam->pszProjection );
728         psPam->pszProjection = CPLStrdup( pszProjectionIn );
729         MarkPamDirty();
730
731         return CE_None;
732     }
733 }
734
735 /************************************************************************/
736 /*                          GetGeoTransform()                           */
737 /************************************************************************/
738
739 CPLErr GDALPamDataset::GetGeoTransform( double * padfTransform )
740
741 {
742     if( psPam && psPam->bHaveGeoTransform )
743     {
744         memcpy( padfTransform, psPam->adfGeoTransform, sizeof(double) * 6 );
745         return CE_None;
746     }
747     else
748         return GDALDataset::GetGeoTransform( padfTransform );
749 }
750
751 /************************************************************************/
752 /*                          SetGeoTransform()                           */
753 /************************************************************************/
754
755 CPLErr GDALPamDataset::SetGeoTransform( double * padfTransform )
756
757 {
758     PamInitialize();
759
760     if( psPam )
761     {
762         MarkPamDirty();
763         psPam->bHaveGeoTransform = TRUE;
764         memcpy( psPam->adfGeoTransform, padfTransform, sizeof(double) * 6 );
765         return( CE_None );
766     }
767     else
768     {
769         return GDALDataset::SetGeoTransform( padfTransform );
770     }
771 }
772
773 /************************************************************************/
774 /*                            GetGCPCount()                             */
775 /************************************************************************/
776
777 int GDALPamDataset::GetGCPCount()
778
779 {
780     if( psPam && psPam->nGCPCount > 0 )
781         return psPam->nGCPCount;
782     else
783         return GDALDataset::GetGCPCount();
784 }
785
786 /************************************************************************/
787 /*                          GetGCPProjection()                          */
788 /************************************************************************/
789
790 const char *GDALPamDataset::GetGCPProjection()
791
792 {
793     if( psPam && psPam->pszGCPProjection != NULL )
794         return psPam->pszGCPProjection;
795     else
796         return GDALDataset::GetGCPProjection();
797 }
798
799 /************************************************************************/
800 /*                               GetGCPs()                              */
801 /************************************************************************/
802
803 const GDAL_GCP *GDALPamDataset::GetGCPs()
804
805 {
806     if( psPam && psPam->nGCPCount > 0 )
807         return psPam->pasGCPList;
808     else
809         return GDALDataset::GetGCPs();
810 }
811
812 /************************************************************************/
813 /*                              SetGCPs()                               */
814 /************************************************************************/
815
816 CPLErr GDALPamDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
817                                 const char *pszGCPProjection )
818
819 {
820     PamInitialize();
821
822     if( psPam )
823     {
824         CPLFree( psPam->pszGCPProjection );
825         if( psPam->nGCPCount > 0 )
826         {
827             GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList );
828             CPLFree( psPam->pasGCPList );
829         }
830
831         psPam->pszGCPProjection = CPLStrdup(pszGCPProjection);
832         psPam->nGCPCount = nGCPCount;
833         psPam->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList );
834
835         MarkPamDirty();
836
837         return CE_None;
838     }
839     else
840     {
841         return GDALDataset::SetGCPs( nGCPCount, pasGCPList, pszGCPProjection );
842     }
843 }
844
845 /************************************************************************/
846 /*                            SetMetadata()                             */
847 /************************************************************************/
848
849 CPLErr GDALPamDataset::SetMetadata( char **papszMetadata,
850                                     const char *pszDomain )
851
852 {
853     PamInitialize();
854
855     if( psPam )
856         MarkPamDirty();
857
858     return GDALDataset::SetMetadata( papszMetadata, pszDomain );
859 }
860
861 /************************************************************************/
862 /*                          SetMetadataItem()                           */
863 /************************************************************************/
864
865 CPLErr GDALPamDataset::SetMetadataItem( const char *pszName,
866                                         const char *pszValue,
867                                         const char *pszDomain )
868
869 {
870     PamInitialize();
871
872     if( psPam )
873         MarkPamDirty();
874
875     return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain );
876 }
877
878 /************************************************************************/
879 /*                          PAMApplyMetadata()                          */
880 /************************************************************************/
881 int PamApplyMetadata( CPLXMLNode *psTree, GDALMajorObject *poMO )
882
883 {
884     char **papszMD = NULL;
885     CPLXMLNode *psMetadata = CPLGetXMLNode( psTree, "Metadata" );
886     CPLXMLNode *psMDI;
887
888     if( psMetadata == NULL )
889         return FALSE;
890
891     /* Format is <MDI key="...">value_Text</MDI> */
892
893     for( psMDI = psMetadata->psChild; psMDI != NULL; psMDI = psMDI->psNext )
894     {
895         if( !EQUAL(psMDI->pszValue,"MDI") || psMDI->eType != CXT_Element
896             || psMDI->psChild == NULL || psMDI->psChild->psNext == NULL
897             || psMDI->psChild->eType != CXT_Attribute
898             || psMDI->psChild->psChild == NULL )
899             continue;
900
901         papszMD =
902             CSLSetNameValue( papszMD, psMDI->psChild->psChild->pszValue,
903                              psMDI->psChild->psNext->pszValue );
904     }
905
906     poMO->SetMetadata( papszMD );
907     CSLDestroy( papszMD );
908
909     return papszMD != NULL;
910 }
911
912 /************************************************************************/
913 /*                        PAMSerializeMetadata()                        */
914 /************************************************************************/
915
916 CPLXMLNode *PamSerializeMetadata( GDALMajorObject *poMO )</