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 9959 : GDALPamDataset::GDALPamDataset()
128 :
129 : {
130 9959 : nPamFlags = 0;
131 9959 : psPam = NULL;
132 9959 : SetMOFlags( GetMOFlags() | GMO_PAM_CLASS );
133 9959 : }
134 :
135 : /************************************************************************/
136 : /* ~GDALPamDataset() */
137 : /************************************************************************/
138 :
139 9959 : GDALPamDataset::~GDALPamDataset()
140 :
141 : {
142 9959 : if( nPamFlags & GPF_DIRTY )
143 : {
144 363 : CPLDebug( "GDALPamDataset", "In destructor with dirty metadata." );
145 363 : FlushCache();
146 : }
147 :
148 9959 : PamClear();
149 9959 : }
150 :
151 : /************************************************************************/
152 : /* FlushCache() */
153 : /************************************************************************/
154 :
155 15051 : void GDALPamDataset::FlushCache()
156 :
157 : {
158 15051 : GDALDataset::FlushCache();
159 15051 : if( nPamFlags & GPF_DIRTY )
160 1026 : TrySaveXML();
161 15051 : }
162 :
163 : /************************************************************************/
164 : /* SerializeToXML() */
165 : /************************************************************************/
166 :
167 742 : CPLXMLNode *GDALPamDataset::SerializeToXML( const char *pszUnused )
168 :
169 : {
170 742 : CPLString oFmt;
171 :
172 742 : if( psPam == NULL )
173 0 : return NULL;
174 :
175 : /* -------------------------------------------------------------------- */
176 : /* Setup root node and attributes. */
177 : /* -------------------------------------------------------------------- */
178 : CPLXMLNode *psDSTree;
179 :
180 742 : psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" );
181 :
182 : /* -------------------------------------------------------------------- */
183 : /* SRS */
184 : /* -------------------------------------------------------------------- */
185 742 : if( psPam->pszProjection != NULL && strlen(psPam->pszProjection) > 0 )
186 283 : CPLSetXMLValue( psDSTree, "SRS", psPam->pszProjection );
187 :
188 : /* -------------------------------------------------------------------- */
189 : /* GeoTransform. */
190 : /* -------------------------------------------------------------------- */
191 742 : 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 193 : psPam->adfGeoTransform[5] ) );
201 : }
202 :
203 : /* -------------------------------------------------------------------- */
204 : /* Metadata. */
205 : /* -------------------------------------------------------------------- */
206 : CPLXMLNode *psMD;
207 :
208 742 : psMD = oMDMD.Serialize();
209 742 : if( psMD != NULL )
210 : {
211 568 : if( psMD->psChild == NULL && psMD->psNext == NULL )
212 20 : CPLDestroyXMLNode( psMD );
213 : else
214 528 : CPLAddXMLChild( psDSTree, psMD );
215 : }
216 :
217 : /* -------------------------------------------------------------------- */
218 : /* GCPs */
219 : /* -------------------------------------------------------------------- */
220 742 : 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 1944 : for( iBand = 0; iBand < GetRasterCount(); iBand++ )
277 : {
278 : CPLXMLNode *psBandTree;
279 :
280 : GDALPamRasterBand *poBand = (GDALPamRasterBand *)
281 1202 : GetRasterBand(iBand+1);
282 :
283 1202 : if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
284 0 : continue;
285 :
286 1202 : psBandTree = poBand->SerializeToXML( pszUnused );
287 :
288 1202 : if( psBandTree != NULL )
289 651 : CPLAddXMLChild( psDSTree, psBandTree );
290 : }
291 :
292 : /* -------------------------------------------------------------------- */
293 : /* We don't want to return anything if we had no metadata to */
294 : /* attach. */
295 : /* -------------------------------------------------------------------- */
296 742 : if( psDSTree->psChild == NULL )
297 : {
298 6 : CPLDestroyXMLNode( psDSTree );
299 6 : psDSTree = NULL;
300 : }
301 :
302 742 : return psDSTree;
303 : }
304 :
305 : /************************************************************************/
306 : /* PamInitialize() */
307 : /************************************************************************/
308 :
309 508770 : 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 508770 : if( psPam || (nPamFlags & GPF_DISABLED) )
319 500794 : return;
320 :
321 7976 : 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 7971 : if( EQUAL( CPLGetConfigOption( "GDAL_PAM_MODE", "PAM" ), "AUX") )
330 0 : nPamFlags |= GPF_AUXMODE;
331 :
332 7971 : psPam = new GDALDatasetPamInfo;
333 7971 : psPam->pszPamFilename = NULL;
334 7971 : psPam->pszProjection = NULL;
335 7971 : psPam->bHaveGeoTransform = FALSE;
336 7971 : psPam->nGCPCount = 0;
337 7971 : psPam->pasGCPList = NULL;
338 7971 : psPam->pszGCPProjection = NULL;
339 :
340 : int iBand;
341 :
342 489072 : for( iBand = 0; iBand < GetRasterCount(); iBand++ )
343 : {
344 : GDALPamRasterBand *poBand = (GDALPamRasterBand *)
345 481101 : GetRasterBand(iBand+1);
346 :
347 481101 : if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
348 9 : continue;
349 :
350 481092 : poBand->PamInitialize();
351 : }
352 : }
353 :
354 : /************************************************************************/
355 : /* PamClear() */
356 : /************************************************************************/
357 :
358 9959 : void GDALPamDataset::PamClear()
359 :
360 : {
361 9959 : if( psPam )
362 : {
363 7971 : CPLFree( psPam->pszPamFilename );
364 7971 : CPLFree( psPam->pszProjection );
365 7971 : CPLFree( psPam->pszGCPProjection );
366 7971 : if( psPam->nGCPCount > 0 )
367 : {
368 6 : GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList );
369 6 : CPLFree( psPam->pasGCPList );
370 : }
371 :
372 7971 : delete psPam;
373 7971 : psPam = NULL;
374 : }
375 9959 : }
376 :
377 : /************************************************************************/
378 : /* XMLInit() */
379 : /************************************************************************/
380 :
381 508 : CPLErr GDALPamDataset::XMLInit( CPLXMLNode *psTree, const char *pszUnused )
382 :
383 : {
384 : /* -------------------------------------------------------------------- */
385 : /* Check for an SRS node. */
386 : /* -------------------------------------------------------------------- */
387 508 : if( strlen(CPLGetXMLValue(psTree, "SRS", "")) > 0 )
388 : {
389 181 : OGRSpatialReference oSRS;
390 :
391 181 : CPLFree( psPam->pszProjection );
392 181 : psPam->pszProjection = NULL;
393 :
394 181 : if( oSRS.SetFromUserInput( CPLGetXMLValue(psTree, "SRS", "") )
395 : == OGRERR_NONE )
396 181 : oSRS.exportToWkt( &(psPam->pszProjection) );
397 : }
398 :
399 : /* -------------------------------------------------------------------- */
400 : /* Check for a GeoTransform node. */
401 : /* -------------------------------------------------------------------- */
402 508 : 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 508 : CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" );
427 :
428 508 : 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 508 : oMDMD.XMLInit( psTree, TRUE );
493 :
494 : /* -------------------------------------------------------------------- */
495 : /* Try loading ESRI xml encoded projection */
496 : /* -------------------------------------------------------------------- */
497 508 : if (psPam->pszProjection == NULL)
498 : {
499 327 : char** papszXML = oMDMD.GetMetadata( "xml:ESRI" );
500 327 : 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 1609 : for( psBandTree = psTree->psChild;
534 : psBandTree != NULL; psBandTree = psBandTree->psNext )
535 : {
536 1101 : if( psBandTree->eType != CXT_Element
537 : || !EQUAL(psBandTree->pszValue,"PAMRasterBand") )
538 666 : continue;
539 :
540 435 : int nBand = atoi(CPLGetXMLValue( psBandTree, "band", "0"));
541 :
542 435 : if( nBand < 1 || nBand > GetRasterCount() )
543 1 : continue;
544 :
545 : GDALPamRasterBand *poBand = (GDALPamRasterBand *)
546 434 : GetRasterBand(nBand);
547 :
548 434 : if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
549 0 : continue;
550 :
551 434 : poBand->XMLInit( psBandTree, pszUnused );
552 : }
553 :
554 : /* -------------------------------------------------------------------- */
555 : /* Clear dirty flag. */
556 : /* -------------------------------------------------------------------- */
557 508 : nPamFlags &= ~GPF_DIRTY;
558 :
559 508 : return CE_None;
560 : }
561 :
562 : /************************************************************************/
563 : /* SetPhysicalFilename() */
564 : /************************************************************************/
565 :
566 340 : void GDALPamDataset::SetPhysicalFilename( const char *pszFilename )
567 :
568 : {
569 340 : PamInitialize();
570 :
571 340 : if( psPam )
572 340 : psPam->osPhysicalFilename = pszFilename;
573 340 : }
574 :
575 : /************************************************************************/
576 : /* GetPhysicalFilename() */
577 : /************************************************************************/
578 :
579 24 : const char *GDALPamDataset::GetPhysicalFilename()
580 :
581 : {
582 24 : PamInitialize();
583 :
584 24 : if( psPam )
585 24 : return psPam->osPhysicalFilename;
586 : else
587 0 : return "";
588 : }
589 :
590 : /************************************************************************/
591 : /* SetSubdatasetName() */
592 : /************************************************************************/
593 :
594 340 : void GDALPamDataset::SetSubdatasetName( const char *pszSubdataset )
595 :
596 : {
597 340 : PamInitialize();
598 :
599 340 : if( psPam )
600 340 : psPam->osSubdatasetName = pszSubdataset;
601 340 : }
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 8303 : const char *GDALPamDataset::BuildPamFilename()
623 :
624 : {
625 8303 : if( psPam == NULL )
626 20 : 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 8283 : if( psPam->pszPamFilename != NULL )
633 610 : return psPam->pszPamFilename;
634 :
635 7673 : const char *pszPhysicalFile = psPam->osPhysicalFilename;
636 :
637 7673 : if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL )
638 7339 : pszPhysicalFile = GetDescription();
639 :
640 7673 : if( strlen(pszPhysicalFile) == 0 )
641 7 : return NULL;
642 :
643 : /* -------------------------------------------------------------------- */
644 : /* Try a proxy lookup, otherwise just add .aux.xml. */
645 : /* -------------------------------------------------------------------- */
646 7666 : const char *pszProxyPam = PamGetProxy( pszPhysicalFile );
647 7666 : if( pszProxyPam != NULL )
648 4 : psPam->pszPamFilename = CPLStrdup(pszProxyPam);
649 : else
650 : {
651 7662 : psPam->pszPamFilename = (char*) CPLMalloc(strlen(pszPhysicalFile)+10);
652 7662 : strcpy( psPam->pszPamFilename, pszPhysicalFile );
653 7662 : strcat( psPam->pszPamFilename, ".aux.xml" );
654 : }
655 :
656 7666 : return psPam->pszPamFilename;
657 : }
658 :
659 : /************************************************************************/
660 : /* IsPamFilenameAPotentialSiblingFile() */
661 : /************************************************************************/
662 :
663 4834 : int GDALPamDataset::IsPamFilenameAPotentialSiblingFile()
664 : {
665 4834 : 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 4834 : const char *pszPhysicalFile = psPam->osPhysicalFilename;
673 :
674 4834 : if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL )
675 4834 : pszPhysicalFile = GetDescription();
676 :
677 4834 : int nLenPhysicalFile = strlen(pszPhysicalFile);
678 : int bIsSiblingPamFile = strncmp(psPam->pszPamFilename, pszPhysicalFile,
679 : nLenPhysicalFile) == 0 &&
680 : strcmp(psPam->pszPamFilename + nLenPhysicalFile,
681 4834 : ".aux.xml") == 0;
682 :
683 4834 : return bIsSiblingPamFile;
684 : }
685 :
686 : /************************************************************************/
687 : /* TryLoadXML() */
688 : /************************************************************************/
689 :
690 7552 : CPLErr GDALPamDataset::TryLoadXML(char **papszSiblingFiles)
691 :
692 : {
693 7552 : CPLXMLNode *psTree = NULL;
694 :
695 7552 : 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 7552 : nPamFlags &= ~GPF_DIRTY;
706 :
707 : /* -------------------------------------------------------------------- */
708 : /* Try reading the file. */
709 : /* -------------------------------------------------------------------- */
710 7552 : if( !BuildPamFilename() )
711 20 : 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 7532 : if (papszSiblingFiles != NULL && IsPamFilenameAPotentialSiblingFile())
721 : {
722 : int iSibling = CSLFindString( papszSiblingFiles,
723 3788 : CPLGetFilename(psPam->pszPamFilename) );
724 3788 : if( iSibling >= 0 )
725 : {
726 109 : CPLErrorReset();
727 109 : CPLPushErrorHandler( CPLQuietErrorHandler );
728 109 : psTree = CPLParseXMLFile( psPam->pszPamFilename );
729 109 : CPLPopErrorHandler();
730 : }
731 : }
732 : else
733 3744 : if( VSIStatExL( psPam->pszPamFilename, &sStatBuf,
734 : VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0
735 : && VSI_ISREG( sStatBuf.st_mode ) )
736 : {
737 501 : CPLErrorReset();
738 501 : CPLPushErrorHandler( CPLQuietErrorHandler );
739 501 : psTree = CPLParseXMLFile( psPam->pszPamFilename );
740 501 : CPLPopErrorHandler();
741 : }
742 :
743 : /* -------------------------------------------------------------------- */
744 : /* If we are looking for a subdataset, search for it's subtree */
745 : /* now. */
746 : /* -------------------------------------------------------------------- */
747 7532 : 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 7532 : if( psTree == NULL )
778 7024 : return TryLoadAux(papszSiblingFiles);
779 :
780 : /* -------------------------------------------------------------------- */
781 : /* Initialize ourselves from this XML tree. */
782 : /* -------------------------------------------------------------------- */
783 : CPLErr eErr;
784 :
785 508 : CPLString osVRTPath(CPLGetPath(psPam->pszPamFilename));
786 508 : eErr = XMLInit( psTree, osVRTPath );
787 :
788 508 : CPLDestroyXMLNode( psTree );
789 :
790 508 : if( eErr != CE_None )
791 0 : PamClear();
792 :
793 508 : return eErr;
794 : }
795 :
796 : /************************************************************************/
797 : /* TrySaveXML() */
798 : /************************************************************************/
799 :
800 1027 : CPLErr GDALPamDataset::TrySaveXML()
801 :
802 : {
803 : CPLXMLNode *psTree;
804 1027 : CPLErr eErr = CE_None;
805 :
806 1027 : nPamFlags &= ~GPF_DIRTY;
807 :
808 1027 : if( psPam == NULL || (nPamFlags & GPF_NOSAVE) )
809 276 : return CE_None;
810 :
811 : /* -------------------------------------------------------------------- */
812 : /* Make sure we know the filename we want to store in. */
813 : /* -------------------------------------------------------------------- */
814 751 : if( !BuildPamFilename() )
815 7 : return CE_None;
816 :
817 : /* -------------------------------------------------------------------- */
818 : /* Build the XML representation of the auxilary metadata. */
819 : /* -------------------------------------------------------------------- */
820 744 : psTree = SerializeToXML( NULL );
821 :
822 744 : if( psTree == NULL )
823 : {
824 : /* If we have unset all metadata, we have to delete the PAM file */
825 8 : CPLPushErrorHandler( CPLQuietErrorHandler );
826 8 : VSIUnlink(psPam->pszPamFilename);
827 8 : CPLPopErrorHandler();
828 8 : 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 736 : 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 736 : CPLPushErrorHandler( CPLQuietErrorHandler );
889 736 : bSaved = CPLSerializeXMLTreeToFile( psTree, psPam->pszPamFilename );
890 736 : 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 736 : if( bSaved )
897 733 : 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 736 : CPLDestroyXMLNode( psTree );
927 :
928 736 : return eErr;
929 : }
930 :
931 : /************************************************************************/
932 : /* CloneInfo() */
933 : /************************************************************************/
934 :
935 604 : CPLErr GDALPamDataset::CloneInfo( GDALDataset *poSrcDS, int nCloneFlags )
936 :
937 : {
938 604 : int bOnlyIfMissing = nCloneFlags & GCIF_ONLY_IF_MISSING;
939 604 : int nSavedMOFlags = GetMOFlags();
940 :
941 604 : PamInitialize();
942 :
943 : /* -------------------------------------------------------------------- */
944 : /* Supress NotImplemented error messages - mainly needed if PAM */
945 : /* disabled. */
946 : /* -------------------------------------------------------------------- */
947 604 : SetMOFlags( nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED );
948 :
949 : /* -------------------------------------------------------------------- */
950 : /* GeoTransform */
951 : /* -------------------------------------------------------------------- */
952 604 : if( nCloneFlags & GCIF_GEOTRANSFORM )
953 : {
954 : double adfGeoTransform[6];
955 :
956 604 : if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None )
957 : {
958 : double adfOldGT[6];
959 :
960 480 : if( !bOnlyIfMissing || GetGeoTransform( adfOldGT ) != CE_None )
961 58 : SetGeoTransform( adfGeoTransform );
962 : }
963 : }
964 :
965 : /* -------------------------------------------------------------------- */
966 : /* Projection */
967 : /* -------------------------------------------------------------------- */
968 604 : if( nCloneFlags & GCIF_PROJECTION )
969 : {
970 604 : const char *pszWKT = poSrcDS->GetProjectionRef();
971 :
972 604 : if( pszWKT != NULL && strlen(pszWKT) > 0 )
973 : {
974 1404 : if( !bOnlyIfMissing
975 468 : || GetProjectionRef() == NULL
976 468 : || strlen(GetProjectionRef()) == 0 )
977 111 : SetProjection( pszWKT );
978 : }
979 : }
980 :
981 : /* -------------------------------------------------------------------- */
982 : /* GCPs */
983 : /* -------------------------------------------------------------------- */
984 604 : if( nCloneFlags & GCIF_GCPS )
985 : {
986 604 : if( poSrcDS->GetGCPCount() > 0 )
987 : {
988 7 : 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 604 : if( nCloneFlags & GCIF_METADATA )
1001 : {
1002 604 : if( poSrcDS->GetMetadata() != NULL )
1003 : {
1004 1359 : if( !bOnlyIfMissing
1005 906 : || CSLCount(GetMetadata()) != CSLCount(poSrcDS->GetMetadata()) )
1006 : {
1007 226 : SetMetadata( poSrcDS->GetMetadata() );
1008 : }
1009 : }
1010 604 : 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 604 : if( nCloneFlags & GCIF_PROCESS_BANDS )
1025 : {
1026 : int iBand;
1027 :
1028 1531 : for( iBand = 0; iBand < GetRasterCount(); iBand++ )
1029 : {
1030 : GDALPamRasterBand *poBand = (GDALPamRasterBand *)
1031 927 : GetRasterBand(iBand+1);
1032 :
1033 927 : if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
1034 0 : continue;
1035 :
1036 927 : if( poSrcDS->GetRasterCount() >= iBand+1 )
1037 : poBand->CloneInfo( poSrcDS->GetRasterBand(iBand+1),
1038 927 : 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 604 : if( nCloneFlags & GCIF_MASK )
1051 : {
1052 373 : GDALDriver::DefaultCopyMasks( poSrcDS, this, FALSE );
1053 : }
1054 :
1055 : /* -------------------------------------------------------------------- */
1056 : /* Restore MO flags. */
1057 : /* -------------------------------------------------------------------- */
1058 604 : SetMOFlags( nSavedMOFlags );
1059 :
1060 604 : return CE_None;
1061 : }
1062 :
1063 : /************************************************************************/
1064 : /* GetFileList() */
1065 : /* */
1066 : /* Add .aux.xml or .aux file into file list as appropriate. */
1067 : /************************************************************************/
1068 :
1069 1544 : char **GDALPamDataset::GetFileList()
1070 :
1071 : {
1072 : VSIStatBufL sStatBuf;
1073 1544 : char **papszFileList = GDALDataset::GetFileList();
1074 :
1075 1544 : 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 1544 : if( psPam && psPam->pszPamFilename )
1083 : {
1084 1543 : int bAddPamFile = (nPamFlags & GPF_DIRTY);
1085 1543 : if (!bAddPamFile)
1086 : {
1087 1543 : if (oOvManager.GetSiblingFiles() != NULL && IsPamFilenameAPotentialSiblingFile())
1088 : bAddPamFile = CSLFindString(oOvManager.GetSiblingFiles(),
1089 1039 : CPLGetFilename(psPam->pszPamFilename)) >= 0;
1090 : else
1091 : bAddPamFile = VSIStatExL( psPam->pszPamFilename, &sStatBuf,
1092 504 : VSI_STAT_EXISTS_FLAG ) == 0;
1093 : }
1094 1543 : if (bAddPamFile)
1095 : {
1096 217 : papszFileList = CSLAddString( papszFileList, psPam->pszPamFilename );
1097 : }
1098 : }
1099 :
1100 1544 : if( psPam && psPam->osAuxFilename.size() > 0 &&
1101 : CSLFindString( papszFileList, psPam->osAuxFilename ) == -1 )
1102 : {
1103 0 : papszFileList = CSLAddString( papszFileList, psPam->osAuxFilename );
1104 : }
1105 1544 : return papszFileList;
1106 : }
1107 :
1108 : /************************************************************************/
1109 : /* IBuildOverviews() */
1110 : /************************************************************************/
1111 :
1112 20 : 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 20 : PamInitialize();
1123 20 : 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 19 : 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 15 : pfnProgress, pProgressData );
1145 : }
1146 :
1147 :
1148 : /************************************************************************/
1149 : /* GetProjectionRef() */
1150 : /************************************************************************/
1151 :
1152 4547 : const char *GDALPamDataset::GetProjectionRef()
1153 :
1154 : {
1155 4547 : if( psPam && psPam->pszProjection )
1156 22 : return psPam->pszProjection;
1157 : else
1158 4525 : return GDALDataset::GetProjectionRef();
1159 : }
1160 :
1161 : /************************************************************************/
1162 : /* SetProjection() */
1163 : /************************************************************************/
1164 :
1165 337 : CPLErr GDALPamDataset::SetProjection( const char *pszProjectionIn )
1166 :
1167 : {
1168 337 : PamInitialize();
1169 :
1170 337 : if( psPam == NULL )
1171 5 : return GDALDataset::SetProjection( pszProjectionIn );
1172 : else
1173 : {
1174 332 : CPLFree( psPam->pszProjection );
1175 332 : psPam->pszProjection = CPLStrdup( pszProjectionIn );
1176 332 : MarkPamDirty();
1177 :
1178 332 : return CE_None;
1179 : }
1180 : }
1181 :
1182 : /************************************************************************/
1183 : /* GetGeoTransform() */
1184 : /************************************************************************/
1185 :
1186 4185 : CPLErr GDALPamDataset::GetGeoTransform( double * padfTransform )
1187 :
1188 : {
1189 4185 : if( psPam && psPam->bHaveGeoTransform )
1190 : {
1191 54 : memcpy( padfTransform, psPam->adfGeoTransform, sizeof(double) * 6 );
1192 54 : return CE_None;
1193 : }
1194 : else
1195 4131 : return GDALDataset::GetGeoTransform( padfTransform );
1196 : }
1197 :
1198 : /************************************************************************/
1199 : /* SetGeoTransform() */
1200 : /************************************************************************/
1201 :
1202 198 : CPLErr GDALPamDataset::SetGeoTransform( double * padfTransform )
1203 :
1204 : {
1205 198 : PamInitialize();
1206 :
1207 198 : if( psPam )
1208 : {
1209 193 : MarkPamDirty();
1210 193 : psPam->bHaveGeoTransform = TRUE;
1211 193 : memcpy( psPam->adfGeoTransform, padfTransform, sizeof(double) * 6 );
1212 193 : return( CE_None );
1213 : }
1214 : else
1215 : {
1216 5 : return GDALDataset::SetGeoTransform( padfTransform );
1217 : }
1218 : }
1219 :
1220 : /************************************************************************/
1221 : /* GetGCPCount() */
1222 : /************************************************************************/
1223 :
1224 4012 : int GDALPamDataset::GetGCPCount()
1225 :
1226 : {
1227 4012 : if( psPam && psPam->nGCPCount > 0 )
1228 8 : return psPam->nGCPCount;
1229 : else
1230 4004 : return GDALDataset::GetGCPCount();
1231 : }
1232 :
1233 : /************************************************************************/
1234 : /* GetGCPProjection() */
1235 : /************************************************************************/
1236 :
1237 7 : const char *GDALPamDataset::GetGCPProjection()
1238 :
1239 : {
1240 7 : if( psPam && psPam->pszGCPProjection != NULL )
1241 5 : return psPam->pszGCPProjection;
1242 : else
1243 2 : 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 1850 : CPLErr GDALPamDataset::SetMetadata( char **papszMetadata,
1297 : const char *pszDomain )
1298 :
1299 : {
1300 1850 : PamInitialize();
1301 :
1302 1850 : if( psPam )
1303 1846 : MarkPamDirty();
1304 :
1305 1850 : return GDALDataset::SetMetadata( papszMetadata, pszDomain );
1306 : }
1307 :
1308 : /************************************************************************/
1309 : /* SetMetadataItem() */
1310 : /************************************************************************/
1311 :
1312 7575 : CPLErr GDALPamDataset::SetMetadataItem( const char *pszName,
1313 : const char *pszValue,
1314 : const char *pszDomain )
1315 :
1316 : {
1317 7575 : PamInitialize();
1318 :
1319 7575 : if( psPam )
1320 7573 : MarkPamDirty();
1321 :
1322 7575 : return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain );
1323 : }
1324 :
1325 : /************************************************************************/
1326 : /* GetMetadataItem() */
1327 : /************************************************************************/
1328 :
1329 5539 : 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 5539 : 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 5536 : else if( pszDomain != NULL
1362 : && EQUAL(pszDomain,"OVERVIEWS")
1363 : && EQUAL(pszName,"OVERVIEW_FILE") )
1364 : {
1365 : const char *pszOverviewFile =
1366 1627 : GDALDataset::GetMetadataItem( pszName, pszDomain );
1367 :
1368 1627 : if( pszOverviewFile == NULL
1369 : || !EQUALN(pszOverviewFile,":::BASE:::",10) )
1370 1615 : return pszOverviewFile;
1371 :
1372 12 : CPLString osPath;
1373 :
1374 12 : if( strlen(GetPhysicalFilename()) > 0 )
1375 12 : osPath = CPLGetPath(GetPhysicalFilename());
1376 : else
1377 0 : osPath = CPLGetPath(GetDescription());
1378 :
1379 12 : return CPLFormFilename( osPath, pszOverviewFile + 10, NULL );
1380 : }
1381 :
1382 : /* -------------------------------------------------------------------- */
1383 : /* Everything else is a pass through. */
1384 : /* -------------------------------------------------------------------- */
1385 : else
1386 3909 : return GDALDataset::GetMetadataItem( pszName, pszDomain );
1387 :
1388 : }
1389 :
1390 : /************************************************************************/
1391 : /* GetMetadata() */
1392 : /************************************************************************/
1393 :
1394 929 : char **GDALPamDataset::GetMetadata( const char *pszDomain )
1395 :
1396 : {
1397 : // if( pszDomain == NULL || !EQUAL(pszDomain,"ProxyOverviewRequest") )
1398 929 : return GDALDataset::GetMetadata( pszDomain );
1399 : }
1400 :
1401 : /************************************************************************/
1402 : /* TryLoadAux() */
1403 : /************************************************************************/
1404 :
1405 7024 : CPLErr GDALPamDataset::TryLoadAux(char **papszSiblingFiles)
1406 :
1407 : {
1408 : /* -------------------------------------------------------------------- */
1409 : /* Initialize PAM. */
1410 : /* -------------------------------------------------------------------- */
1411 7024 : PamInitialize();
1412 7024 : 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 7024 : const char *pszPhysicalFile = psPam->osPhysicalFilename;
1420 :
1421 7024 : if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL )
1422 6695 : pszPhysicalFile = GetDescription();
1423 :
1424 7024 : if( strlen(pszPhysicalFile) == 0 )
1425 0 : return CE_None;
1426 :
1427 7024 : if( papszSiblingFiles )
1428 : {
1429 3679 : CPLString osAuxFilename = CPLResetExtension( pszPhysicalFile, "aux");
1430 : int iSibling = CSLFindString( papszSiblingFiles,
1431 3679 : CPLGetFilename(osAuxFilename) );
1432 3679 : if( iSibling < 0 )
1433 : {
1434 3673 : osAuxFilename = pszPhysicalFile;
1435 3673 : osAuxFilename += ".aux";
1436 : iSibling = CSLFindString( papszSiblingFiles,
1437 3673 : CPLGetFilename(osAuxFilename) );
1438 3673 : if( iSibling < 0 )
1439 3673 : return CE_None;
1440 0 : }
1441 : }
1442 :
1443 : /* -------------------------------------------------------------------- */
1444 : /* Try to open .aux file. */
1445 : /* -------------------------------------------------------------------- */
1446 : GDALDataset *poAuxDS = GDALFindAssociatedAuxFile( pszPhysicalFile,
1447 3351 : GA_ReadOnly, this );
1448 :
1449 3351 : if( poAuxDS == NULL )
1450 3343 : return CE_None;
1451 :
1452 8 : psPam->osAuxFilename = poAuxDS->GetDescription();
1453 :
1454 : /* -------------------------------------------------------------------- */
1455 : /* Do we have an SRS on the aux file? */
1456 : /* -------------------------------------------------------------------- */
1457 8 : if( strlen(poAuxDS->GetProjectionRef()) > 0 )
1458 0 : GDALPamDataset::SetProjection( poAuxDS->GetProjectionRef() );
1459 :
1460 : /* -------------------------------------------------------------------- */
1461 : /* Geotransform. */
1462 : /* -------------------------------------------------------------------- */
1463 8 : if( poAuxDS->GetGeoTransform( psPam->adfGeoTransform ) == CE_None )
1464 0 : psPam->bHaveGeoTransform = TRUE;
1465 :
1466 : /* -------------------------------------------------------------------- */
1467 : /* GCPs */
1468 : /* -------------------------------------------------------------------- */
1469 8 : 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 8 : char **papszMD = poAuxDS->GetMetadata();
1481 8 : 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 8 : papszMD = poAuxDS->GetMetadata("XFORMS");
1490 8 : 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 16 : for( iBand = 0; iBand < poAuxDS->GetRasterCount(); iBand++ )
1504 : {
1505 8 : if( iBand >= GetRasterCount() )
1506 0 : break;
1507 :
1508 8 : GDALRasterBand *poAuxBand = poAuxDS->GetRasterBand( iBand+1 );
1509 8 : GDALRasterBand *poBand = GetRasterBand( iBand+1 );
1510 :
1511 8 : papszMD = poAuxBand->GetMetadata();
1512 8 : if( CSLCount(papszMD) > 0 )
1513 : {
1514 : char **papszMerged =
1515 8 : CSLMerge( CSLDuplicate(poBand->GetMetadata()), papszMD );
1516 8 : poBand->SetMetadata( papszMerged );
1517 8 : CSLDestroy( papszMerged );
1518 : }
1519 :
1520 8 : if( strlen(poAuxBand->GetDescription()) > 0 )
1521 8 : poBand->SetDescription( poAuxBand->GetDescription() );
1522 :
1523 8 : if( poAuxBand->GetCategoryNames() != NULL )
1524 0 : poBand->SetCategoryNames( poAuxBand->GetCategoryNames() );
1525 :
1526 8 : if( poAuxBand->GetColorTable() != NULL
1527 0 : && poBand->GetColorTable() == NULL )
1528 0 : poBand->SetColorTable( poAuxBand->GetColorTable() );
1529 :
1530 : // histograms?
1531 : double dfMin, dfMax;
1532 8 : int nBuckets, *panHistogram=NULL;
1533 :
1534 8 : if( poAuxBand->GetDefaultHistogram( &dfMin, &dfMax,
1535 : &nBuckets, &panHistogram,
1536 8 : FALSE, NULL, NULL ) == CE_None )
1537 : {
1538 : poBand->SetDefaultHistogram( dfMin, dfMax, nBuckets,
1539 1 : panHistogram );
1540 1 : CPLFree( panHistogram );
1541 : }
1542 :
1543 : // RAT
1544 8 : if( poAuxBand->GetDefaultRAT() != NULL )
1545 1 : poBand->SetDefaultRAT( poAuxBand->GetDefaultRAT() );
1546 :
1547 : // NoData
1548 8 : int bSuccess = FALSE;
1549 8 : double dfNoDataValue = poAuxBand->GetNoDataValue( &bSuccess );
1550 8 : if( bSuccess )
1551 3 : poBand->SetNoDataValue( dfNoDataValue );
1552 : }
1553 :
1554 8 : GDALClose( poAuxDS );
1555 :
1556 : /* -------------------------------------------------------------------- */
1557 : /* Mark PAM info as clean. */
1558 : /* -------------------------------------------------------------------- */
1559 8 : nPamFlags &= ~GPF_DIRTY;
1560 :
1561 8 : return CE_Failure;
1562 : }
|