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