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