1 /******************************************************************************
2  * $Id: gdalpamdataset.cpp 29038 2015-04-28 09:03:36Z rouault $
3  *
4  * Project:  GDAL Core
5  * Purpose:  Implementation of GDALPamDataset, a dataset base class that
6  *           knows how to persist auxiliary 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  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at mines-paris dot org>
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  ****************************************************************************/
31 
32 #include "gdal_pam.h"
33 #include "cpl_string.h"
34 #include "ogr_spatialref.h"
35 
36 CPL_CVSID("$Id: gdalpamdataset.cpp 29038 2015-04-28 09:03:36Z rouault $");
37 
38 /************************************************************************/
39 /*                           GDALPamDataset()                           */
40 /************************************************************************/
41 
42 /**
43  * \class GDALPamDataset "gdal_pam.h"
44  *
45  * A subclass of GDALDataset which introduces the ability to save and
46  * restore auxiliary information (coordinate system, gcps, metadata,
47  * etc) not supported by a file format via an "auxiliary metadata" file
48  * with the .aux.xml extension.
49  *
50  * <h3>Enabling PAM</h3>
51  *
52  * PAM support can be enabled (resp. disabled) in GDAL by setting the GDAL_PAM_ENABLED
53  * configuration option (via CPLSetConfigOption(), or the environment) to
54  * the value of YES (resp. NO). Note: The default value is build dependant and defaults
55  * to YES in Windows and Unix builds.
56  *
57  * <h3>PAM Proxy Files</h3>
58  *
59  * In order to be able to record auxiliary information about files on
60  * read-only media such as CDROMs or in directories where the user does not
61  * have write permissions, it is possible to enable the "PAM Proxy Database".
62  * When enabled the .aux.xml files are kept in a different directory, writable
63  * by the user. Overviews will also be stored in the PAM proxy directory.
64  *
65  * To enable this, set the GDAL_PAM_PROXY_DIR configuration option to be
66  * the name of the directory where the proxies should be kept. The configuration
67  * option must be set *before* the first access to PAM, because its value is cached
68  * for later access.
69  *
70  * <h3>Adding PAM to Drivers</h3>
71  *
72  * Drivers for physical file formats that wish to support persistent auxiliary
73  * metadata in addition to that for the format itself should derive their
74  * dataset class from GDALPamDataset instead of directly from GDALDataset.
75  * The raster band classes should also be derived from GDALPamRasterBand.
76  *
77  * They should also call something like this near the end of the Open()
78  * method:
79  *
80  * \code
81  *      poDS->SetDescription( poOpenInfo->pszFilename );
82  *      poDS->TryLoadXML();
83  * \endcode
84  *
85  * The SetDescription() is necessary so that the dataset will have a valid
86  * filename set as the description before TryLoadXML() is called.  TryLoadXML()
87  * will look for an .aux.xml file with the same basename as the dataset and
88  * in the same directory.  If found the contents will be loaded and kept
89  * track of in the GDALPamDataset and GDALPamRasterBand objects.  When a
90  * call like GetProjectionRef() is not implemented by the format specific
91  * class, it will fall through to the PAM implementation which will return
92  * information if it was in the .aux.xml file.
93  *
94  * Drivers should also try to call the GDALPamDataset/GDALPamRasterBand
95  * methods as a fallback if their implementation does not find information.
96  * This allows using the .aux.xml for variations that can't be stored in
97  * the format.  For instance, the GeoTIFF driver GetProjectionRef() looks
98  * like this:
99  *
100  * \code
101  *      if( EQUAL(pszProjection,"") )
102  *          return GDALPamDataset::GetProjectionRef();
103  *      else
104  *          return( pszProjection );
105  * \endcode
106  *
107  * So if the geotiff header is missing, the .aux.xml file will be
108  * consulted.
109  *
110  * Similarly, if SetProjection() were called with a coordinate system
111  * not supported by GeoTIFF, the SetProjection() method should pass it on
112  * to the GDALPamDataset::SetProjection() method after issuing a warning
113  * that the information can't be represented within the file itself.
114  *
115  * Drivers for subdataset based formats will also need to declare the
116  * name of the physical file they are related to, and the name of their
117  * subdataset before calling TryLoadXML().
118  *
119  * \code
120  *      poDS->SetDescription( poOpenInfo->pszFilename );
121  *      poDS->SetPhysicalFilename( poDS->pszFilename );
122  *      poDS->SetSubdatasetName( osSubdatasetName );
123  *
124  *      poDS->TryLoadXML();
125  * \endcode
126  */
127 
GDALPamDataset()128 GDALPamDataset::GDALPamDataset()
129 
130 {
131     nPamFlags = 0;
132     psPam = NULL;
133     SetMOFlags( GetMOFlags() | GMO_PAM_CLASS );
134 }
135 
136 /************************************************************************/
137 /*                          ~GDALPamDataset()                           */
138 /************************************************************************/
139 
~GDALPamDataset()140 GDALPamDataset::~GDALPamDataset()
141 
142 {
143     if( nPamFlags & GPF_DIRTY )
144     {
145         CPLDebug( "GDALPamDataset", "In destructor with dirty metadata." );
146         FlushCache();
147     }
148 
149     PamClear();
150 }
151 
152 /************************************************************************/
153 /*                             FlushCache()                             */
154 /************************************************************************/
155 
FlushCache()156 void GDALPamDataset::FlushCache()
157 
158 {
159     GDALDataset::FlushCache();
160     if( nPamFlags & GPF_DIRTY )
161         TrySaveXML();
162 }
163 
164 /************************************************************************/
165 /*                           SerializeToXML()                           */
166 /************************************************************************/
167 
SerializeToXML(const char * pszUnused)168 CPLXMLNode *GDALPamDataset::SerializeToXML( const char *pszUnused )
169 
170 {
171     CPLString oFmt;
172 
173     if( psPam == NULL )
174         return NULL;
175 
176 /* -------------------------------------------------------------------- */
177 /*      Setup root node and attributes.                                 */
178 /* -------------------------------------------------------------------- */
179     CPLXMLNode *psDSTree;
180 
181     psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" );
182 
183 /* -------------------------------------------------------------------- */
184 /*      SRS                                                             */
185 /* -------------------------------------------------------------------- */
186     if( psPam->pszProjection != NULL && strlen(psPam->pszProjection) > 0 )
187         CPLSetXMLValue( psDSTree, "SRS", psPam->pszProjection );
188 
189 /* -------------------------------------------------------------------- */
190 /*      GeoTransform.                                                   */
191 /* -------------------------------------------------------------------- */
192     if( psPam->bHaveGeoTransform )
193     {
194         CPLSetXMLValue( psDSTree, "GeoTransform",
195                         oFmt.Printf( "%24.16e,%24.16e,%24.16e,%24.16e,%24.16e,%24.16e",
196                                      psPam->adfGeoTransform[0],
197                                      psPam->adfGeoTransform[1],
198                                      psPam->adfGeoTransform[2],
199                                      psPam->adfGeoTransform[3],
200                                      psPam->adfGeoTransform[4],
201                                      psPam->adfGeoTransform[5] ) );
202     }
203 
204 /* -------------------------------------------------------------------- */
205 /*      Metadata.                                                       */
206 /* -------------------------------------------------------------------- */
207     if( psPam->bHasMetadata )
208     {
209         CPLXMLNode *psMD;
210 
211         psMD = oMDMD.Serialize();
212         if( psMD != NULL )
213         {
214             CPLAddXMLChild( psDSTree, psMD );
215         }
216     }
217 
218 /* -------------------------------------------------------------------- */
219 /*      GCPs                                                            */
220 /* -------------------------------------------------------------------- */
221     if( psPam->nGCPCount > 0 )
222     {
223         GDALSerializeGCPListToXML( psDSTree,
224                                    psPam->pasGCPList,
225                                    psPam->nGCPCount,
226                                    psPam->pszGCPProjection );
227     }
228 
229 /* -------------------------------------------------------------------- */
230 /*      Process bands.                                                  */
231 /* -------------------------------------------------------------------- */
232     int iBand;
233 
234     for( iBand = 0; iBand < GetRasterCount(); iBand++ )
235     {
236         CPLXMLNode *psBandTree;
237 
238         GDALPamRasterBand *poBand = (GDALPamRasterBand *)
239             GetRasterBand(iBand+1);
240 
241         if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
242             continue;
243 
244         psBandTree = poBand->SerializeToXML( pszUnused );
245 
246         if( psBandTree != NULL )
247             CPLAddXMLChild( psDSTree, psBandTree );
248     }
249 
250 /* -------------------------------------------------------------------- */
251 /*      We don't want to return anything if we had no metadata to       */
252 /*      attach.                                                         */
253 /* -------------------------------------------------------------------- */
254     if( psDSTree->psChild == NULL )
255     {
256         CPLDestroyXMLNode( psDSTree );
257         psDSTree = NULL;
258     }
259 
260     return psDSTree;
261 }
262 
263 /************************************************************************/
264 /*                           PamInitialize()                            */
265 /************************************************************************/
266 
PamInitialize()267 void GDALPamDataset::PamInitialize()
268 
269 {
270 #ifdef PAM_ENABLED
271     static const char *pszPamDefault = "YES";
272 #else
273     static const char *pszPamDefault = "NO";
274 #endif
275 
276     if( psPam || (nPamFlags & GPF_DISABLED) )
277         return;
278 
279     if( !CSLTestBoolean( CPLGetConfigOption( "GDAL_PAM_ENABLED",
280                                              pszPamDefault ) ) )
281     {
282         nPamFlags |= GPF_DISABLED;
283         return;
284     }
285 
286     /* ERO 2011/04/13 : GPF_AUXMODE seems to be unimplemented */
287     if( EQUAL( CPLGetConfigOption( "GDAL_PAM_MODE", "PAM" ), "AUX") )
288         nPamFlags |= GPF_AUXMODE;
289 
290     psPam = new GDALDatasetPamInfo;
291     psPam->pszPamFilename = NULL;
292     psPam->pszProjection = NULL;
293     psPam->bHaveGeoTransform = FALSE;
294     psPam->nGCPCount = 0;
295     psPam->pasGCPList = NULL;
296     psPam->pszGCPProjection = NULL;
297     psPam->bHasMetadata = FALSE;
298 
299     int iBand;
300 
301     for( iBand = 0; iBand < GetRasterCount(); iBand++ )
302     {
303         GDALPamRasterBand *poBand = (GDALPamRasterBand *)
304             GetRasterBand(iBand+1);
305 
306         if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
307             continue;
308 
309         poBand->PamInitialize();
310     }
311 }
312 
313 /************************************************************************/
314 /*                              PamClear()                              */
315 /************************************************************************/
316 
PamClear()317 void GDALPamDataset::PamClear()
318 
319 {
320     if( psPam )
321     {
322         CPLFree( psPam->pszPamFilename );
323         CPLFree( psPam->pszProjection );
324         CPLFree( psPam->pszGCPProjection );
325         if( psPam->nGCPCount > 0 )
326         {
327             GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList );
328             CPLFree( psPam->pasGCPList );
329         }
330 
331         delete psPam;
332         psPam = NULL;
333     }
334 }
335 
336 /************************************************************************/
337 /*                              XMLInit()                               */
338 /************************************************************************/
339 
XMLInit(CPLXMLNode * psTree,const char * pszUnused)340 CPLErr GDALPamDataset::XMLInit( CPLXMLNode *psTree, const char *pszUnused )
341 
342 {
343 /* -------------------------------------------------------------------- */
344 /*      Check for an SRS node.                                          */
345 /* -------------------------------------------------------------------- */
346     if( strlen(CPLGetXMLValue(psTree, "SRS", "")) > 0 )
347     {
348         OGRSpatialReference oSRS;
349 
350         CPLFree( psPam->pszProjection );
351         psPam->pszProjection = NULL;
352 
353         if( oSRS.SetFromUserInput( CPLGetXMLValue(psTree, "SRS", "") )
354             == OGRERR_NONE )
355             oSRS.exportToWkt( &(psPam->pszProjection) );
356     }
357 
358 /* -------------------------------------------------------------------- */
359 /*      Check for a GeoTransform node.                                  */
360 /* -------------------------------------------------------------------- */
361     if( strlen(CPLGetXMLValue(psTree, "GeoTransform", "")) > 0 )
362     {
363         const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", "");
364         char	**papszTokens;
365 
366         papszTokens = CSLTokenizeStringComplex( pszGT, ",", FALSE, FALSE );
367         if( CSLCount(papszTokens) != 6 )
368         {
369             CPLError( CE_Warning, CPLE_AppDefined,
370                       "GeoTransform node does not have expected six values.");
371         }
372         else
373         {
374             for( int iTA = 0; iTA < 6; iTA++ )
375                 psPam->adfGeoTransform[iTA] = CPLAtof(papszTokens[iTA]);
376             psPam->bHaveGeoTransform = TRUE;
377         }
378 
379         CSLDestroy( papszTokens );
380     }
381 
382 /* -------------------------------------------------------------------- */
383 /*      Check for GCPs.                                                 */
384 /* -------------------------------------------------------------------- */
385     CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" );
386 
387     if( psGCPList != NULL )
388     {
389         CPLFree( psPam->pszGCPProjection );
390         psPam->pszGCPProjection = NULL;
391 
392         // Make sure any previous GCPs, perhaps from an .aux file, are cleared
393         // if we have new ones.
394         if( psPam->nGCPCount > 0 )
395         {
396             GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList );
397             CPLFree( psPam->pasGCPList );
398             psPam->nGCPCount = 0;
399             psPam->pasGCPList = 0;
400         }
401 
402         GDALDeserializeGCPListFromXML( psGCPList,
403                                        &(psPam->pasGCPList),
404                                        &(psPam->nGCPCount),
405                                        &(psPam->pszGCPProjection) );
406     }
407 
408 /* -------------------------------------------------------------------- */
409 /*      Apply any dataset level metadata.                               */
410 /* -------------------------------------------------------------------- */
411     oMDMD.XMLInit( psTree, TRUE );
412 
413 /* -------------------------------------------------------------------- */
414 /*      Try loading ESRI xml encoded projection                         */
415 /* -------------------------------------------------------------------- */
416     if (psPam->pszProjection == NULL)
417     {
418         char** papszXML = oMDMD.GetMetadata( "xml:ESRI" );
419         if (CSLCount(papszXML) == 1)
420         {
421             CPLXMLNode *psValueAsXML = CPLParseXMLString( papszXML[0] );
422             if (psValueAsXML)
423             {
424                 const char* pszESRI_WKT = CPLGetXMLValue(psValueAsXML,
425                                   "=GeodataXform.SpatialReference.WKT", NULL);
426                 if (pszESRI_WKT)
427                 {
428                     OGRSpatialReference* poSRS = new OGRSpatialReference(NULL);
429                     char* pszTmp = (char*)pszESRI_WKT;
430                     if (poSRS->importFromWkt(&pszTmp) == OGRERR_NONE &&
431                         poSRS->morphFromESRI() == OGRERR_NONE)
432                     {
433                         char* pszWKT = NULL;
434                         if (poSRS->exportToWkt(&pszWKT) == OGRERR_NONE)
435                         {
436                             psPam->pszProjection = CPLStrdup(pszWKT);
437                         }
438                         CPLFree(pszWKT);
439                     }
440                     delete poSRS;
441                 }
442                 CPLDestroyXMLNode(psValueAsXML);
443             }
444         }
445     }
446 
447 /* -------------------------------------------------------------------- */
448 /*      Process bands.                                                  */
449 /* -------------------------------------------------------------------- */
450     CPLXMLNode *psBandTree;
451 
452     for( psBandTree = psTree->psChild;
453          psBandTree != NULL; psBandTree = psBandTree->psNext )
454     {
455         if( psBandTree->eType != CXT_Element
456             || !EQUAL(psBandTree->pszValue,"PAMRasterBand") )
457             continue;
458 
459         int nBand = atoi(CPLGetXMLValue( psBandTree, "band", "0"));
460 
461         if( nBand < 1 || nBand > GetRasterCount() )
462             continue;
463 
464         GDALPamRasterBand *poBand = (GDALPamRasterBand *)
465             GetRasterBand(nBand);
466 
467         if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
468             continue;
469 
470         poBand->XMLInit( psBandTree, pszUnused );
471     }
472 
473 /* -------------------------------------------------------------------- */
474 /*      Clear dirty flag.                                               */
475 /* -------------------------------------------------------------------- */
476     nPamFlags &= ~GPF_DIRTY;
477 
478     return CE_None;
479 }
480 
481 /************************************************************************/
482 /*                        SetPhysicalFilename()                         */
483 /************************************************************************/
484 
SetPhysicalFilename(const char * pszFilename)485 void GDALPamDataset::SetPhysicalFilename( const char *pszFilename )
486 
487 {
488     PamInitialize();
489 
490     if( psPam )
491         psPam->osPhysicalFilename = pszFilename;
492 }
493 
494 /************************************************************************/
495 /*                        GetPhysicalFilename()                         */
496 /************************************************************************/
497 
GetPhysicalFilename()498 const char *GDALPamDataset::GetPhysicalFilename()
499 
500 {
501     PamInitialize();
502 
503     if( psPam )
504         return psPam->osPhysicalFilename;
505     else
506         return "";
507 }
508 
509 /************************************************************************/
510 /*                         SetSubdatasetName()                          */
511 /************************************************************************/
512 
SetSubdatasetName(const char * pszSubdataset)513 void GDALPamDataset::SetSubdatasetName( const char *pszSubdataset )
514 
515 {
516     PamInitialize();
517 
518     if( psPam )
519         psPam->osSubdatasetName = pszSubdataset;
520 }
521 
522 /************************************************************************/
523 /*                         GetSubdatasetName()                          */
524 /************************************************************************/
525 
GetSubdatasetName()526 const char *GDALPamDataset::GetSubdatasetName()
527 
528 {
529     PamInitialize();
530 
531     if( psPam )
532         return psPam->osSubdatasetName;
533     else
534         return "";
535 }
536 
537 /************************************************************************/
538 /*                          BuildPamFilename()                          */
539 /************************************************************************/
540 
BuildPamFilename()541 const char *GDALPamDataset::BuildPamFilename()
542 
543 {
544     if( psPam == NULL )
545         return NULL;
546 
547 /* -------------------------------------------------------------------- */
548 /*      What is the name of the physical file we are referencing?       */
549 /*      We allow an override via the psPam->pszPhysicalFile item.       */
550 /* -------------------------------------------------------------------- */
551     if( psPam->pszPamFilename != NULL )
552         return psPam->pszPamFilename;
553 
554     const char *pszPhysicalFile = psPam->osPhysicalFilename;
555 
556     if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL )
557         pszPhysicalFile = GetDescription();
558 
559     if( strlen(pszPhysicalFile) == 0 )
560         return NULL;
561 
562 /* -------------------------------------------------------------------- */
563 /*      Try a proxy lookup, otherwise just add .aux.xml.                */
564 /* -------------------------------------------------------------------- */
565     const char *pszProxyPam = PamGetProxy( pszPhysicalFile );
566     if( pszProxyPam != NULL )
567         psPam->pszPamFilename = CPLStrdup(pszProxyPam);
568     else
569     {
570         if( !GDALCanFileAcceptSidecarFile(pszPhysicalFile) )
571             return NULL;
572         psPam->pszPamFilename = (char*) CPLMalloc(strlen(pszPhysicalFile)+10);
573         strcpy( psPam->pszPamFilename, pszPhysicalFile );
574         strcat( psPam->pszPamFilename, ".aux.xml" );
575     }
576 
577     return psPam->pszPamFilename;
578 }
579 
580 /************************************************************************/
581 /*                   IsPamFilenameAPotentialSiblingFile()               */
582 /************************************************************************/
583 
IsPamFilenameAPotentialSiblingFile()584 int GDALPamDataset::IsPamFilenameAPotentialSiblingFile()
585 {
586     if (psPam == NULL)
587         return FALSE;
588 
589 /* -------------------------------------------------------------------- */
590 /*      Determine if the PAM filename is a .aux.xml file next to the    */
591 /*      physical file, or if it comes from the ProxyDB                  */
592 /* -------------------------------------------------------------------- */
593     const char *pszPhysicalFile = psPam->osPhysicalFilename;
594 
595     if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL )
596         pszPhysicalFile = GetDescription();
597 
598     int nLenPhysicalFile = strlen(pszPhysicalFile);
599     int bIsSiblingPamFile = strncmp(psPam->pszPamFilename, pszPhysicalFile,
600                                     nLenPhysicalFile) == 0 &&
601                             strcmp(psPam->pszPamFilename + nLenPhysicalFile,
602                                    ".aux.xml") == 0;
603 
604     return bIsSiblingPamFile;
605 }
606 
607 /************************************************************************/
608 /*                             TryLoadXML()                             */
609 /************************************************************************/
610 
TryLoadXML(char ** papszSiblingFiles)611 CPLErr GDALPamDataset::TryLoadXML(char **papszSiblingFiles)
612 
613 {
614     CPLXMLNode *psTree = NULL;
615 
616     PamInitialize();
617 
618 /* -------------------------------------------------------------------- */
619 /*      Clear dirty flag.  Generally when we get to this point is       */
620 /*      from a call at the end of the Open() method, and some calls     */
621 /*      may have already marked the PAM info as dirty (for instance     */
622 /*      setting metadata), but really everything to this point is       */
623 /*      reproducable, and so the PAM info shouldn't really be           */
624 /*      thought of as dirty.                                            */
625 /* -------------------------------------------------------------------- */
626     nPamFlags &= ~GPF_DIRTY;
627 
628 /* -------------------------------------------------------------------- */
629 /*      Try reading the file.                                           */
630 /* -------------------------------------------------------------------- */
631     if( !BuildPamFilename() )
632         return CE_None;
633 
634     VSIStatBufL sStatBuf;
635 
636 /* -------------------------------------------------------------------- */
637 /*      In case the PAM filename is a .aux.xml file next to the         */
638 /*      physical file and we have a siblings list, then we can skip     */
639 /*      stat'ing the filesystem.                                        */
640 /* -------------------------------------------------------------------- */
641     if (papszSiblingFiles != NULL && IsPamFilenameAPotentialSiblingFile())
642     {
643         int iSibling = CSLFindString( papszSiblingFiles,
644                                       CPLGetFilename(psPam->pszPamFilename) );
645         if( iSibling >= 0 )
646         {
647             CPLErrorReset();
648             CPLPushErrorHandler( CPLQuietErrorHandler );
649             psTree = CPLParseXMLFile( psPam->pszPamFilename );
650             CPLPopErrorHandler();
651         }
652     }
653     else
654     if( VSIStatExL( psPam->pszPamFilename, &sStatBuf,
655                     VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0
656         && VSI_ISREG( sStatBuf.st_mode ) )
657     {
658         CPLErrorReset();
659         CPLPushErrorHandler( CPLQuietErrorHandler );
660         psTree = CPLParseXMLFile( psPam->pszPamFilename );
661         CPLPopErrorHandler();
662     }
663 
664 /* -------------------------------------------------------------------- */
665 /*      If we are looking for a subdataset, search for it's subtree     */
666 /*      now.                                                            */
667 /* -------------------------------------------------------------------- */
668     if( psTree && psPam->osSubdatasetName.size() )
669     {
670         CPLXMLNode *psSubTree;
671 
672         for( psSubTree = psTree->psChild;
673              psSubTree != NULL;
674              psSubTree = psSubTree->psNext )
675         {
676             if( psSubTree->eType != CXT_Element
677                 || !EQUAL(psSubTree->pszValue,"Subdataset") )
678                 continue;
679 
680             if( !EQUAL(CPLGetXMLValue( psSubTree, "name", "" ),
681                        psPam->osSubdatasetName) )
682                 continue;
683 
684             psSubTree = CPLGetXMLNode( psSubTree, "PAMDataset" );
685             break;
686         }
687 
688         if( psSubTree != NULL )
689             psSubTree = CPLCloneXMLTree( psSubTree );
690 
691         CPLDestroyXMLNode( psTree );
692         psTree = psSubTree;
693     }
694 
695 /* -------------------------------------------------------------------- */
696 /*      If we fail, try .aux.                                           */
697 /* -------------------------------------------------------------------- */
698     if( psTree == NULL )
699         return TryLoadAux(papszSiblingFiles);
700 
701 /* -------------------------------------------------------------------- */
702 /*      Initialize ourselves from this XML tree.                        */
703 /* -------------------------------------------------------------------- */
704     CPLErr eErr;
705 
706     CPLString osVRTPath(CPLGetPath(psPam->pszPamFilename));
707     eErr = XMLInit( psTree, osVRTPath );
708 
709     CPLDestroyXMLNode( psTree );
710 
711     if( eErr != CE_None )
712         PamClear();
713 
714     return eErr;
715 }
716 
717 /************************************************************************/
718 /*                             TrySaveXML()                             */
719 /************************************************************************/
720 
TrySaveXML()721 CPLErr GDALPamDataset::TrySaveXML()
722 
723 {
724     CPLXMLNode *psTree;
725     CPLErr eErr = CE_None;
726 
727     nPamFlags &= ~GPF_DIRTY;
728 
729     if( psPam == NULL || (nPamFlags & GPF_NOSAVE) )
730         return CE_None;
731 
732 /* -------------------------------------------------------------------- */
733 /*      Make sure we know the filename we want to store in.             */
734 /* -------------------------------------------------------------------- */
735     if( !BuildPamFilename() )
736         return CE_None;
737 
738 /* -------------------------------------------------------------------- */
739 /*      Build the XML representation of the auxiliary metadata.          */
740 /* -------------------------------------------------------------------- */
741     psTree = SerializeToXML( NULL );
742 
743     if( psTree == NULL )
744     {
745         /* If we have unset all metadata, we have to delete the PAM file */
746         CPLPushErrorHandler( CPLQuietErrorHandler );
747         VSIUnlink(psPam->pszPamFilename);
748         CPLPopErrorHandler();
749         return CE_None;
750     }
751 
752 /* -------------------------------------------------------------------- */
753 /*      If we are working with a subdataset, we need to integrate       */
754 /*      the subdataset tree within the whole existing pam tree,         */
755 /*      after removing any old version of the same subdataset.          */
756 /* -------------------------------------------------------------------- */
757     if( psPam->osSubdatasetName.size() != 0 )
758     {
759         CPLXMLNode *psOldTree, *psSubTree;
760 
761         CPLErrorReset();
762         CPLPushErrorHandler( CPLQuietErrorHandler );
763         psOldTree = CPLParseXMLFile( psPam->pszPamFilename );
764         CPLPopErrorHandler();
765 
766         if( psOldTree == NULL )
767             psOldTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" );
768 
769         for( psSubTree = psOldTree->psChild;
770              psSubTree != NULL;
771              psSubTree = psSubTree->psNext )
772         {
773             if( psSubTree->eType != CXT_Element
774                 || !EQUAL(psSubTree->pszValue,"Subdataset") )
775                 continue;
776 
777             if( !EQUAL(CPLGetXMLValue( psSubTree, "name", "" ),
778                        psPam->osSubdatasetName) )
779                 continue;
780 
781             break;
782         }
783 
784         if( psSubTree == NULL )
785         {
786             psSubTree = CPLCreateXMLNode( psOldTree, CXT_Element,
787                                           "Subdataset" );
788             CPLCreateXMLNode(
789                 CPLCreateXMLNode( psSubTree, CXT_Attribute, "name" ),
790                 CXT_Text, psPam->osSubdatasetName );
791         }
792 
793         CPLXMLNode *psOldPamDataset = CPLGetXMLNode( psSubTree, "PAMDataset");
794         if( psOldPamDataset != NULL )
795         {
796             CPLRemoveXMLChild( psSubTree, psOldPamDataset );
797             CPLDestroyXMLNode( psOldPamDataset );
798         }
799 
800         CPLAddXMLChild( psSubTree, psTree );
801         psTree = psOldTree;
802     }
803 
804 /* -------------------------------------------------------------------- */
805 /*      Try saving the auxiliary metadata.                               */
806 /* -------------------------------------------------------------------- */
807     int bSaved;
808 
809     CPLPushErrorHandler( CPLQuietErrorHandler );
810     bSaved = CPLSerializeXMLTreeToFile( psTree, psPam->pszPamFilename );
811     CPLPopErrorHandler();
812 
813 /* -------------------------------------------------------------------- */
814 /*      If it fails, check if we have a proxy directory for auxiliary    */
815 /*      metadata to be stored in, and try to save there.                */
816 /* -------------------------------------------------------------------- */
817     if( bSaved )
818         eErr = CE_None;
819     else
820     {
821         const char *pszNewPam;
822         const char *pszBasename = GetDescription();
823 
824         if( psPam && psPam->osPhysicalFilename.length() > 0 )
825             pszBasename = psPam->osPhysicalFilename;
826 
827         if( PamGetProxy(pszBasename) == NULL
828             && ((pszNewPam = PamAllocateProxy(pszBasename)) != NULL))
829         {
830             CPLErrorReset();
831             CPLFree( psPam->pszPamFilename );
832             psPam->pszPamFilename = CPLStrdup(pszNewPam);
833             eErr = TrySaveXML();
834         }
835         /* No way we can save into a /vsicurl resource */
836         else if( strncmp(psPam->pszPamFilename, "/vsicurl", strlen("/vsicurl")) != 0 )
837         {
838             CPLError( CE_Warning, CPLE_AppDefined,
839                       "Unable to save auxiliary information in %s.",
840                       psPam->pszPamFilename );
841             eErr = CE_Warning;
842         }
843     }
844 
845 /* -------------------------------------------------------------------- */
846 /*      Cleanup                                                         */
847 /* -------------------------------------------------------------------- */
848     CPLDestroyXMLNode( psTree );
849 
850     return eErr;
851 }
852 
853 /************************************************************************/
854 /*                             CloneInfo()                              */
855 /************************************************************************/
856 
CloneInfo(GDALDataset * poSrcDS,int nCloneFlags)857 CPLErr GDALPamDataset::CloneInfo( GDALDataset *poSrcDS, int nCloneFlags )
858 
859 {
860     int bOnlyIfMissing = nCloneFlags & GCIF_ONLY_IF_MISSING;
861     int nSavedMOFlags = GetMOFlags();
862 
863     PamInitialize();
864 
865 /* -------------------------------------------------------------------- */
866 /*      Suppress NotImplemented error messages - mainly needed if PAM   */
867 /*      disabled.                                                       */
868 /* -------------------------------------------------------------------- */
869     SetMOFlags( nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED );
870 
871 /* -------------------------------------------------------------------- */
872 /*      GeoTransform                                                    */
873 /* -------------------------------------------------------------------- */
874     if( nCloneFlags & GCIF_GEOTRANSFORM )
875     {
876         double adfGeoTransform[6];
877 
878         if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None )
879         {
880             double adfOldGT[6];
881 
882             if( !bOnlyIfMissing || GetGeoTransform( adfOldGT ) != CE_None )
883                 SetGeoTransform( adfGeoTransform );
884         }
885     }
886 
887 /* -------------------------------------------------------------------- */
888 /*      Projection                                                      */
889 /* -------------------------------------------------------------------- */
890     if( nCloneFlags & GCIF_PROJECTION )
891     {
892         const char *pszWKT = poSrcDS->GetProjectionRef();
893 
894         if( pszWKT != NULL && strlen(pszWKT) > 0 )
895         {
896             if( !bOnlyIfMissing
897                 || GetProjectionRef() == NULL
898                 || strlen(GetProjectionRef()) == 0 )
899                 SetProjection( pszWKT );
900         }
901     }
902 
903 /* -------------------------------------------------------------------- */
904 /*      GCPs                                                            */
905 /* -------------------------------------------------------------------- */
906     if( nCloneFlags & GCIF_GCPS )
907     {
908         if( poSrcDS->GetGCPCount() > 0 )
909         {
910             if( !bOnlyIfMissing || GetGCPCount() == 0 )
911             {
912                 SetGCPs( poSrcDS->GetGCPCount(),
913                          poSrcDS->GetGCPs(),
914                          poSrcDS->GetGCPProjection() );
915             }
916         }
917     }
918 
919 /* -------------------------------------------------------------------- */
920 /*      Metadata                                                        */
921 /* -------------------------------------------------------------------- */
922     if( nCloneFlags & GCIF_METADATA )
923     {
924         if( poSrcDS->GetMetadata() != NULL )
925         {
926             if( !bOnlyIfMissing
927                 || CSLCount(GetMetadata()) != CSLCount(poSrcDS->GetMetadata()) )
928             {
929                 SetMetadata( poSrcDS->GetMetadata() );
930             }
931         }
932         if( poSrcDS->GetMetadata("RPC") != NULL )
933         {
934             if( !bOnlyIfMissing
935                 || CSLCount(GetMetadata("RPC"))
936                    != CSLCount(poSrcDS->GetMetadata("RPC")) )
937             {
938                 SetMetadata( poSrcDS->GetMetadata("RPC"), "RPC" );
939             }
940         }
941     }
942 
943 /* -------------------------------------------------------------------- */
944 /*      Process bands.                                                  */
945 /* -------------------------------------------------------------------- */
946     if( nCloneFlags & GCIF_PROCESS_BANDS )
947     {
948         int iBand;
949 
950         for( iBand = 0; iBand < GetRasterCount(); iBand++ )
951         {
952             GDALPamRasterBand *poBand = (GDALPamRasterBand *)
953                 GetRasterBand(iBand+1);
954 
955             if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
956                 continue;
957 
958             if( poSrcDS->GetRasterCount() >= iBand+1 )
959                 poBand->CloneInfo( poSrcDS->GetRasterBand(iBand+1),
960                                    nCloneFlags );
961             else
962                 CPLDebug( "GDALPamDataset", "Skipping CloneInfo for band not in source, this is a bit unusual!" );
963         }
964     }
965 
966 /* -------------------------------------------------------------------- */
967 /*      Copy masks.  These are really copied at a lower level using     */
968 /*      GDALDefaultOverviews, for formats with no native mask           */
969 /*      support but this is a convenient central point to put this      */
970 /*      for most drivers.                                               */
971 /* -------------------------------------------------------------------- */
972     if( nCloneFlags & GCIF_MASK )
973     {
974         GDALDriver::DefaultCopyMasks( poSrcDS, this, FALSE );
975     }
976 
977 /* -------------------------------------------------------------------- */
978 /*      Restore MO flags.                                               */
979 /* -------------------------------------------------------------------- */
980     SetMOFlags( nSavedMOFlags );
981 
982     return CE_None;
983 }
984 
985 /************************************************************************/
986 /*                            GetFileList()                             */
987 /*                                                                      */
988 /*      Add .aux.xml or .aux file into file list as appropriate.        */
989 /************************************************************************/
990 
GetFileList()991 char **GDALPamDataset::GetFileList()
992 
993 {
994     VSIStatBufL sStatBuf;
995     char **papszFileList = GDALDataset::GetFileList();
996 
997     if( psPam && psPam->osPhysicalFilename.size() > 0
998         && CSLFindString( papszFileList, psPam->osPhysicalFilename ) == -1 )
999     {
1000         papszFileList = CSLInsertString( papszFileList, 0,
1001                                          psPam->osPhysicalFilename );
1002     }
1003 
1004     if( psPam && psPam->pszPamFilename )
1005     {
1006         int bAddPamFile = (nPamFlags & GPF_DIRTY);
1007         if (!bAddPamFile)
1008         {
1009             if (oOvManager.GetSiblingFiles() != NULL && IsPamFilenameAPotentialSiblingFile())
1010                 bAddPamFile = CSLFindString(oOvManager.GetSiblingFiles(),
1011                                   CPLGetFilename(psPam->pszPamFilename)) >= 0;
1012             else
1013                 bAddPamFile = VSIStatExL( psPam->pszPamFilename, &sStatBuf,
1014                                           VSI_STAT_EXISTS_FLAG ) == 0;
1015         }
1016         if (bAddPamFile)
1017         {
1018             papszFileList = CSLAddString( papszFileList, psPam->pszPamFilename );
1019         }
1020     }
1021 
1022     if( psPam && psPam->osAuxFilename.size() > 0 &&
1023         CSLFindString( papszFileList, psPam->osAuxFilename ) == -1 )
1024     {
1025         papszFileList = CSLAddString( papszFileList, psPam->osAuxFilename );
1026     }
1027     return papszFileList;
1028 }
1029 
1030 /************************************************************************/
1031 /*                          IBuildOverviews()                           */
1032 /************************************************************************/
1033 
IBuildOverviews(const char * pszResampling,int nOverviews,int * panOverviewList,int nListBands,int * panBandList,GDALProgressFunc pfnProgress,void * pProgressData)1034 CPLErr GDALPamDataset::IBuildOverviews( const char *pszResampling,
1035                                         int nOverviews, int *panOverviewList,
1036                                         int nListBands, int *panBandList,
1037                                         GDALProgressFunc pfnProgress,
1038                                         void * pProgressData )
1039 
1040 {
1041 /* -------------------------------------------------------------------- */
1042 /*      Initialize PAM.                                                 */
1043 /* -------------------------------------------------------------------- */
1044     PamInitialize();
1045     if( psPam == NULL )
1046         return GDALDataset::IBuildOverviews( pszResampling,
1047                                              nOverviews, panOverviewList,
1048                                              nListBands, panBandList,
1049                                              pfnProgress, pProgressData );
1050 
1051 /* -------------------------------------------------------------------- */
1052 /*      If we appear to have subdatasets and to have a physical         */
1053 /*      filename, use that physical filename to derive a name for a     */
1054 /*      new overview file.                                              */
1055 /* -------------------------------------------------------------------- */
1056     if( oOvManager.IsInitialized() && psPam->osPhysicalFilename.length() != 0 )
1057         return oOvManager.BuildOverviewsSubDataset(
1058             psPam->osPhysicalFilename, pszResampling,
1059             nOverviews, panOverviewList,
1060             nListBands, panBandList,
1061             pfnProgress, pProgressData );
1062     else
1063         return GDALDataset::IBuildOverviews( pszResampling,
1064                                              nOverviews, panOverviewList,
1065                                              nListBands, panBandList,
1066                                              pfnProgress, pProgressData );
1067 }
1068 
1069 
1070 /************************************************************************/
1071 /*                          GetProjectionRef()                          */
1072 /************************************************************************/
1073 
GetProjectionRef()1074 const char *GDALPamDataset::GetProjectionRef()
1075 
1076 {
1077     if( psPam && psPam->pszProjection )
1078         return psPam->pszProjection;
1079     else
1080         return GDALDataset::GetProjectionRef();
1081 }
1082 
1083 /************************************************************************/
1084 /*                           SetProjection()                            */
1085 /************************************************************************/
1086 
SetProjection(const char * pszProjectionIn)1087 CPLErr GDALPamDataset::SetProjection( const char *pszProjectionIn )
1088 
1089 {
1090     PamInitialize();
1091 
1092     if( psPam == NULL )
1093         return GDALDataset::SetProjection( pszProjectionIn );
1094     else
1095     {
1096         CPLFree( psPam->pszProjection );
1097         psPam->pszProjection = CPLStrdup( pszProjectionIn );
1098         MarkPamDirty();
1099 
1100         return CE_None;
1101     }
1102 }
1103 
1104 /************************************************************************/
1105 /*                          GetGeoTransform()                           */
1106 /************************************************************************/
1107 
GetGeoTransform(double * padfTransform)1108 CPLErr GDALPamDataset::GetGeoTransform( double * padfTransform )
1109 
1110 {
1111     if( psPam && psPam->bHaveGeoTransform )
1112     {
1113         memcpy( padfTransform, psPam->adfGeoTransform, sizeof(double) * 6 );
1114         return CE_None;
1115     }
1116     else
1117         return GDALDataset::GetGeoTransform( padfTransform );
1118 }
1119 
1120 /************************************************************************/
1121 /*                          SetGeoTransform()                           */
1122 /************************************************************************/
1123 
SetGeoTransform(double * padfTransform)1124 CPLErr GDALPamDataset::SetGeoTransform( double * padfTransform )
1125 
1126 {
1127     PamInitialize();
1128 
1129     if( psPam )
1130     {
1131         MarkPamDirty();
1132         psPam->bHaveGeoTransform = TRUE;
1133         memcpy( psPam->adfGeoTransform, padfTransform, sizeof(double) * 6 );
1134         return( CE_None );
1135     }
1136     else
1137     {
1138         return GDALDataset::SetGeoTransform( padfTransform );
1139     }
1140 }
1141 
1142 /************************************************************************/
1143 /*                            GetGCPCount()                             */
1144 /************************************************************************/
1145 
GetGCPCount()1146 int GDALPamDataset::GetGCPCount()
1147 
1148 {
1149     if( psPam && psPam->nGCPCount > 0 )
1150         return psPam->nGCPCount;
1151     else
1152         return GDALDataset::GetGCPCount();
1153 }
1154 
1155 /************************************************************************/
1156 /*                          GetGCPProjection()                          */
1157 /************************************************************************/
1158 
GetGCPProjection()1159 const char *GDALPamDataset::GetGCPProjection()
1160 
1161 {
1162     if( psPam && psPam->pszGCPProjection != NULL )
1163         return psPam->pszGCPProjection;
1164     else
1165         return GDALDataset::GetGCPProjection();
1166 }
1167 
1168 /************************************************************************/
1169 /*                               GetGCPs()                              */
1170 /************************************************************************/
1171 
GetGCPs()1172 const GDAL_GCP *GDALPamDataset::GetGCPs()
1173 
1174 {
1175     if( psPam && psPam->nGCPCount > 0 )
1176         return psPam->pasGCPList;
1177     else
1178         return GDALDataset::GetGCPs();
1179 }
1180 
1181 /************************************************************************/
1182 /*                              SetGCPs()                               */
1183 /************************************************************************/
1184 
SetGCPs(int nGCPCount,const GDAL_GCP * pasGCPList,const char * pszGCPProjection)1185 CPLErr GDALPamDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
1186                                 const char *pszGCPProjection )
1187 
1188 {
1189     PamInitialize();
1190 
1191     if( psPam )
1192     {
1193         CPLFree( psPam->pszGCPProjection );
1194         if( psPam->nGCPCount > 0 )
1195         {
1196             GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList );
1197             CPLFree( psPam->pasGCPList );
1198         }
1199 
1200         psPam->pszGCPProjection = CPLStrdup(pszGCPProjection);
1201         psPam->nGCPCount = nGCPCount;
1202         psPam->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList );
1203 
1204         MarkPamDirty();
1205 
1206         return CE_None;
1207     }
1208     else
1209     {
1210         return GDALDataset::SetGCPs( nGCPCount, pasGCPList, pszGCPProjection );
1211     }
1212 }
1213 
1214 /************************************************************************/
1215 /*                            SetMetadata()                             */
1216 /************************************************************************/
1217 
SetMetadata(char ** papszMetadata,const char * pszDomain)1218 CPLErr GDALPamDataset::SetMetadata( char **papszMetadata,
1219                                     const char *pszDomain )
1220 
1221 {
1222     PamInitialize();
1223 
1224     if( psPam )
1225     {
1226         psPam->bHasMetadata = TRUE;
1227         MarkPamDirty();
1228     }
1229 
1230     return GDALDataset::SetMetadata( papszMetadata, pszDomain );
1231 }
1232 
1233 /************************************************************************/
1234 /*                          SetMetadataItem()                           */
1235 /************************************************************************/
1236 
SetMetadataItem(const char * pszName,const char * pszValue,const char * pszDomain)1237 CPLErr GDALPamDataset::SetMetadataItem( const char *pszName,
1238                                         const char *pszValue,
1239                                         const char *pszDomain )
1240 
1241 {
1242     PamInitialize();
1243 
1244     if( psPam )
1245     {
1246         psPam->bHasMetadata = TRUE;
1247         MarkPamDirty();
1248     }
1249 
1250     return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain );
1251 }
1252 
1253 /************************************************************************/
1254 /*                          GetMetadataItem()                           */
1255 /************************************************************************/
1256 
GetMetadataItem(const char * pszName,const char * pszDomain)1257 const char *GDALPamDataset::GetMetadataItem( const char *pszName,
1258                                              const char *pszDomain )
1259 
1260 {
1261 /* -------------------------------------------------------------------- */
1262 /*      A request against the ProxyOverviewRequest is a special         */
1263 /*      mechanism to request an overview filename be allocated in       */
1264 /*      the proxy pool location.  The allocated name is saved as        */
1265 /*      metadata as well as being returned.                             */
1266 /* -------------------------------------------------------------------- */
1267     if( pszDomain != NULL && EQUAL(pszDomain,"ProxyOverviewRequest") )
1268     {
1269         CPLString osPrelimOvr = GetDescription();
1270         osPrelimOvr += ":::OVR";
1271 
1272         const char *pszProxyOvrFilename = PamAllocateProxy( osPrelimOvr );
1273         if( pszProxyOvrFilename == NULL )
1274             return NULL;
1275 
1276         SetMetadataItem( "OVERVIEW_FILE", pszProxyOvrFilename, "OVERVIEWS" );
1277 
1278         return pszProxyOvrFilename;
1279     }
1280 
1281 /* -------------------------------------------------------------------- */
1282 /*      If the OVERVIEW_FILE metadata is requested, we intercept the    */
1283 /*      request in order to replace ":::BASE:::" with the path to       */
1284 /*      the physical file - if available.  This is primarily for the    */
1285 /*      purpose of managing subdataset overview filenames as being      */
1286 /*      relative to the physical file the subdataset comes              */
1287 /*      from. (#3287).                                                  */
1288 /* -------------------------------------------------------------------- */
1289     else if( pszDomain != NULL
1290              && EQUAL(pszDomain,"OVERVIEWS")
1291              && EQUAL(pszName,"OVERVIEW_FILE") )
1292     {
1293         const char *pszOverviewFile =
1294             GDALDataset::GetMetadataItem( pszName, pszDomain );
1295 
1296         if( pszOverviewFile == NULL
1297             || !EQUALN(pszOverviewFile,":::BASE:::",10) )
1298             return pszOverviewFile;
1299 
1300         CPLString osPath;
1301 
1302         if( strlen(GetPhysicalFilename()) > 0 )
1303             osPath = CPLGetPath(GetPhysicalFilename());
1304         else
1305             osPath = CPLGetPath(GetDescription());
1306 
1307         return CPLFormFilename( osPath, pszOverviewFile + 10, NULL );
1308     }
1309 
1310 /* -------------------------------------------------------------------- */
1311 /*      Everything else is a pass through.                              */
1312 /* -------------------------------------------------------------------- */
1313     else
1314         return GDALDataset::GetMetadataItem( pszName, pszDomain );
1315 
1316 }
1317 
1318 /************************************************************************/
1319 /*                            GetMetadata()                             */
1320 /************************************************************************/
1321 
GetMetadata(const char * pszDomain)1322 char **GDALPamDataset::GetMetadata( const char *pszDomain )
1323 
1324 {
1325 //    if( pszDomain == NULL || !EQUAL(pszDomain,"ProxyOverviewRequest") )
1326         return GDALDataset::GetMetadata( pszDomain );
1327 }
1328 
1329 /************************************************************************/
1330 /*                             TryLoadAux()                             */
1331 /************************************************************************/
1332 
TryLoadAux(char ** papszSiblingFiles)1333 CPLErr GDALPamDataset::TryLoadAux(char **papszSiblingFiles)
1334 
1335 {
1336 /* -------------------------------------------------------------------- */
1337 /*      Initialize PAM.                                                 */
1338 /* -------------------------------------------------------------------- */
1339     PamInitialize();
1340     if( psPam == NULL )
1341         return CE_None;
1342 
1343 /* -------------------------------------------------------------------- */
1344 /*      What is the name of the physical file we are referencing?       */
1345 /*      We allow an override via the psPam->pszPhysicalFile item.       */
1346 /* -------------------------------------------------------------------- */
1347     const char *pszPhysicalFile = psPam->osPhysicalFilename;
1348 
1349     if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL )
1350         pszPhysicalFile = GetDescription();
1351 
1352     if( strlen(pszPhysicalFile) == 0 )
1353         return CE_None;
1354 
1355     if( papszSiblingFiles )
1356     {
1357         CPLString osAuxFilename = CPLResetExtension( pszPhysicalFile, "aux");
1358         int iSibling = CSLFindString( papszSiblingFiles,
1359                                       CPLGetFilename(osAuxFilename) );
1360         if( iSibling < 0 )
1361         {
1362             osAuxFilename = pszPhysicalFile;
1363             osAuxFilename += ".aux";
1364             iSibling = CSLFindString( papszSiblingFiles,
1365                                       CPLGetFilename(osAuxFilename) );
1366             if( iSibling < 0 )
1367                 return CE_None;
1368         }
1369     }
1370 
1371 /* -------------------------------------------------------------------- */
1372 /*      Try to open .aux file.                                          */
1373 /* -------------------------------------------------------------------- */
1374     GDALDataset *poAuxDS = GDALFindAssociatedAuxFile( pszPhysicalFile,
1375                                                       GA_ReadOnly, this );
1376 
1377     if( poAuxDS == NULL )
1378         return CE_None;
1379 
1380     psPam->osAuxFilename = poAuxDS->GetDescription();
1381 
1382 /* -------------------------------------------------------------------- */
1383 /*      Do we have an SRS on the aux file?                              */
1384 /* -------------------------------------------------------------------- */
1385     if( strlen(poAuxDS->GetProjectionRef()) > 0 )
1386         GDALPamDataset::SetProjection( poAuxDS->GetProjectionRef() );
1387 
1388 /* -------------------------------------------------------------------- */
1389 /*      Geotransform.                                                   */
1390 /* -------------------------------------------------------------------- */
1391     if( poAuxDS->GetGeoTransform( psPam->adfGeoTransform ) == CE_None )
1392         psPam->bHaveGeoTransform = TRUE;
1393 
1394 /* -------------------------------------------------------------------- */
1395 /*      GCPs                                                            */
1396 /* -------------------------------------------------------------------- */
1397     if( poAuxDS->GetGCPCount() > 0 )
1398     {
1399         psPam->nGCPCount = poAuxDS->GetGCPCount();
1400         psPam->pasGCPList = GDALDuplicateGCPs( psPam->nGCPCount,
1401                                                poAuxDS->GetGCPs() );
1402     }
1403 
1404 /* -------------------------------------------------------------------- */
1405 /*      Apply metadata. We likely ought to be merging this in rather    */
1406 /*      than overwriting everything that was there.                     */
1407 /* -------------------------------------------------------------------- */
1408     char **papszMD = poAuxDS->GetMetadata();
1409     if( CSLCount(papszMD) > 0 )
1410     {
1411         char **papszMerged =
1412             CSLMerge( CSLDuplicate(GetMetadata()), papszMD );
1413         GDALPamDataset::SetMetadata( papszMerged );
1414         CSLDestroy( papszMerged );
1415     }
1416 
1417     papszMD = poAuxDS->GetMetadata("XFORMS");
1418     if( CSLCount(papszMD) > 0 )
1419     {
1420         char **papszMerged =
1421             CSLMerge( CSLDuplicate(GetMetadata("XFORMS")), papszMD );
1422         GDALPamDataset::SetMetadata( papszMerged, "XFORMS" );
1423         CSLDestroy( papszMerged );
1424     }
1425 
1426 /* ==================================================================== */
1427 /*      Process bands.                                                  */
1428 /* ==================================================================== */
1429     int iBand;
1430 
1431     for( iBand = 0; iBand < poAuxDS->GetRasterCount(); iBand++ )
1432     {
1433         if( iBand >= GetRasterCount() )
1434             break;
1435 
1436         GDALRasterBand *poAuxBand = poAuxDS->GetRasterBand( iBand+1 );
1437         GDALRasterBand *poBand = GetRasterBand( iBand+1 );
1438 
1439         papszMD = poAuxBand->GetMetadata();
1440         if( CSLCount(papszMD) > 0 )
1441         {
1442             char **papszMerged =
1443                 CSLMerge( CSLDuplicate(poBand->GetMetadata()), papszMD );
1444             poBand->SetMetadata( papszMerged );
1445             CSLDestroy( papszMerged );
1446         }
1447 
1448         if( strlen(poAuxBand->GetDescription()) > 0 )
1449             poBand->SetDescription( poAuxBand->GetDescription() );
1450 
1451         if( poAuxBand->GetCategoryNames() != NULL )
1452             poBand->SetCategoryNames( poAuxBand->GetCategoryNames() );
1453 
1454         if( poAuxBand->GetColorTable() != NULL
1455             && poBand->GetColorTable() == NULL )
1456             poBand->SetColorTable( poAuxBand->GetColorTable() );
1457 
1458         // histograms?
1459         double dfMin, dfMax;
1460         int nBuckets;
1461         GUIntBig *panHistogram=NULL;
1462 
1463         if( poAuxBand->GetDefaultHistogram( &dfMin, &dfMax,
1464                                             &nBuckets, &panHistogram,
1465                                             FALSE, NULL, NULL ) == CE_None )
1466         {
1467             poBand->SetDefaultHistogram( dfMin, dfMax, nBuckets,
1468                                          panHistogram );
1469             CPLFree( panHistogram );
1470         }
1471 
1472         // RAT
1473         if( poAuxBand->GetDefaultRAT() != NULL )
1474             poBand->SetDefaultRAT( poAuxBand->GetDefaultRAT() );
1475 
1476         // NoData
1477         int bSuccess = FALSE;
1478         double dfNoDataValue = poAuxBand->GetNoDataValue( &bSuccess );
1479         if( bSuccess )
1480             poBand->SetNoDataValue( dfNoDataValue );
1481     }
1482 
1483     GDALClose( poAuxDS );
1484 
1485 /* -------------------------------------------------------------------- */
1486 /*      Mark PAM info as clean.                                         */
1487 /* -------------------------------------------------------------------- */
1488     nPamFlags &= ~GPF_DIRTY;
1489 
1490     return CE_Failure;
1491 }
1492