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 19116 : GDALPamDataset::GDALPamDataset()
128 :
129 : {
130 19116 : nPamFlags = 0;
131 19116 : psPam = NULL;
132 19116 : SetMOFlags( GetMOFlags() | GMO_PAM_CLASS );
133 19116 : }
134 :
135 : /************************************************************************/
136 : /* ~GDALPamDataset() */
137 : /************************************************************************/
138 :
139 19116 : GDALPamDataset::~GDALPamDataset()
140 :
141 : {
142 19116 : if( nPamFlags & GPF_DIRTY )
143 : {
144 746 : CPLDebug( "GDALPamDataset", "In destructor with dirty metadata." );
145 746 : FlushCache();
146 : }
147 :
148 19116 : PamClear();
149 19116 : }
150 :
151 : /************************************************************************/
152 : /* FlushCache() */
153 : /************************************************************************/
154 :
155 29086 : void GDALPamDataset::FlushCache()
156 :
157 : {
158 29086 : GDALDataset::FlushCache();
159 29086 : if( nPamFlags & GPF_DIRTY )
160 2028 : TrySaveXML();
161 29086 : }
162 :
163 : /************************************************************************/
164 : /* SerializeToXML() */
165 : /************************************************************************/
166 :
167 1470 : CPLXMLNode *GDALPamDataset::SerializeToXML( const char *pszUnused )
168 :
169 : {
170 1470 : CPLString oFmt;
171 :
172 1470 : if( psPam == NULL )
173 0 : return NULL;
174 :
175 : /* -------------------------------------------------------------------- */
176 : /* Setup root node and attributes. */
177 : /* -------------------------------------------------------------------- */
178 : CPLXMLNode *psDSTree;
179 :
180 1470 : psDSTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" );
181 :
182 : /* -------------------------------------------------------------------- */
183 : /* SRS */
184 : /* -------------------------------------------------------------------- */
185 1470 : if( psPam->pszProjection != NULL && strlen(psPam->pszProjection) > 0 )
186 528 : CPLSetXMLValue( psDSTree, "SRS", psPam->pszProjection );
187 :
188 : /* -------------------------------------------------------------------- */
189 : /* GeoTransform. */
190 : /* -------------------------------------------------------------------- */
191 1470 : 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 382 : psPam->adfGeoTransform[5] ) );
201 : }
202 :
203 : /* -------------------------------------------------------------------- */
204 : /* Metadata. */
205 : /* -------------------------------------------------------------------- */
206 : CPLXMLNode *psMD;
207 :
208 1470 : psMD = oMDMD.Serialize();
209 1470 : if( psMD != NULL )
210 : {
211 1112 : if( psMD->psChild == NULL && psMD->psNext == NULL )
212 38 : CPLDestroyXMLNode( psMD );
213 : else
214 1036 : CPLAddXMLChild( psDSTree, psMD );
215 : }
216 :
217 : /* -------------------------------------------------------------------- */
218 : /* GCPs */
219 : /* -------------------------------------------------------------------- */
220 1470 : if( psPam->nGCPCount > 0 )
221 : {
222 : CPLXMLNode *psPamGCPList = CPLCreateXMLNode( psDSTree, CXT_Element,
223 2 : "GCPList" );
224 :
225 2 : CPLXMLNode* psLastChild = NULL;
226 :
227 2 : if( psPam->pszGCPProjection != NULL
228 : && strlen(psPam->pszGCPProjection) > 0 )
229 : {
230 : CPLSetXMLValue( psPamGCPList, "#Projection",
231 2 : psPam->pszGCPProjection );
232 2 : psLastChild = psPamGCPList->psChild;
233 : }
234 :
235 6 : for( int iGCP = 0; iGCP < psPam->nGCPCount; iGCP++ )
236 : {
237 : CPLXMLNode *psXMLGCP;
238 4 : GDAL_GCP *psGCP = psPam->pasGCPList + iGCP;
239 :
240 4 : psXMLGCP = CPLCreateXMLNode( NULL, CXT_Element, "GCP" );
241 :
242 4 : if( psLastChild == NULL )
243 0 : psPamGCPList->psChild = psXMLGCP;
244 : else
245 4 : psLastChild->psNext = psXMLGCP;
246 4 : psLastChild = psXMLGCP;
247 :
248 4 : CPLSetXMLValue( psXMLGCP, "#Id", psGCP->pszId );
249 :
250 4 : if( psGCP->pszInfo != NULL && strlen(psGCP->pszInfo) > 0 )
251 0 : CPLSetXMLValue( psXMLGCP, "Info", psGCP->pszInfo );
252 :
253 : CPLSetXMLValue( psXMLGCP, "#Pixel",
254 4 : oFmt.Printf( "%.4f", psGCP->dfGCPPixel ) );
255 :
256 : CPLSetXMLValue( psXMLGCP, "#Line",
257 4 : oFmt.Printf( "%.4f", psGCP->dfGCPLine ) );
258 :
259 : CPLSetXMLValue( psXMLGCP, "#X",
260 4 : oFmt.Printf( "%.12E", psGCP->dfGCPX ) );
261 :
262 : CPLSetXMLValue( psXMLGCP, "#Y",
263 4 : oFmt.Printf( "%.12E", psGCP->dfGCPY ) );
264 :
265 4 : 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 3856 : for( iBand = 0; iBand < GetRasterCount(); iBand++ )
277 : {
278 : CPLXMLNode *psBandTree;
279 :
280 : GDALPamRasterBand *poBand = (GDALPamRasterBand *)
281 2386 : GetRasterBand(iBand+1);
282 :
283 2386 : if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
284 0 : continue;
285 :
286 2386 : psBandTree = poBand->SerializeToXML( pszUnused );
287 :
288 2386 : if( psBandTree != NULL )
289 1270 : CPLAddXMLChild( psDSTree, psBandTree );
290 : }
291 :
292 : /* -------------------------------------------------------------------- */
293 : /* We don't want to return anything if we had no metadata to */
294 : /* attach. */
295 : /* -------------------------------------------------------------------- */
296 1470 : if( psDSTree->psChild == NULL )
297 : {
298 12 : CPLDestroyXMLNode( psDSTree );
299 12 : psDSTree = NULL;
300 : }
301 :
302 1470 : return psDSTree;
303 : }
304 :
305 : /************************************************************************/
306 : /* PamInitialize() */
307 : /************************************************************************/
308 :
309 1012255 : 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 1012255 : if( psPam || (nPamFlags & GPF_DISABLED) )
319 996871 : return;
320 :
321 15384 : if( !CSLTestBoolean( CPLGetConfigOption( "GDAL_PAM_ENABLED",
322 : pszPamDefault ) ) )
323 : {
324 10 : nPamFlags |= GPF_DISABLED;
325 10 : return;
326 : }
327 :
328 : /* ERO 2011/04/13 : GPF_AUXMODE seems to be unimplemented */
329 15374 : if( EQUAL( CPLGetConfigOption( "GDAL_PAM_MODE", "PAM" ), "AUX") )
330 0 : nPamFlags |= GPF_AUXMODE;
331 :
332 15374 : psPam = new GDALDatasetPamInfo;
333 15374 : psPam->pszPamFilename = NULL;
334 15374 : psPam->pszProjection = NULL;
335 15374 : psPam->bHaveGeoTransform = FALSE;
336 15374 : psPam->nGCPCount = 0;
337 15374 : psPam->pasGCPList = NULL;
338 15374 : psPam->pszGCPProjection = NULL;
339 :
340 : int iBand;
341 :
342 976694 : for( iBand = 0; iBand < GetRasterCount(); iBand++ )
343 : {
344 : GDALPamRasterBand *poBand = (GDALPamRasterBand *)
345 961320 : GetRasterBand(iBand+1);
346 :
347 961320 : if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
348 18 : continue;
349 :
350 961302 : poBand->PamInitialize();
351 : }
352 : }
353 :
354 : /************************************************************************/
355 : /* PamClear() */
356 : /************************************************************************/
357 :
358 19116 : void GDALPamDataset::PamClear()
359 :
360 : {
361 19116 : if( psPam )
362 : {
363 15374 : CPLFree( psPam->pszPamFilename );
364 15374 : CPLFree( psPam->pszProjection );
365 15374 : CPLFree( psPam->pszGCPProjection );
366 15374 : if( psPam->nGCPCount > 0 )
367 : {
368 12 : GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList );
369 12 : CPLFree( psPam->pasGCPList );
370 : }
371 :
372 15374 : delete psPam;
373 15374 : psPam = NULL;
374 : }
375 19116 : }
376 :
377 : /************************************************************************/
378 : /* XMLInit() */
379 : /************************************************************************/
380 :
381 1003 : CPLErr GDALPamDataset::XMLInit( CPLXMLNode *psTree, const char *pszUnused )
382 :
383 : {
384 : /* -------------------------------------------------------------------- */
385 : /* Check for an SRS node. */
386 : /* -------------------------------------------------------------------- */
387 1003 : if( strlen(CPLGetXMLValue(psTree, "SRS", "")) > 0 )
388 : {
389 341 : OGRSpatialReference oSRS;
390 :
391 341 : CPLFree( psPam->pszProjection );
392 341 : psPam->pszProjection = NULL;
393 :
394 341 : if( oSRS.SetFromUserInput( CPLGetXMLValue(psTree, "SRS", "") )
395 : == OGRERR_NONE )
396 341 : oSRS.exportToWkt( &(psPam->pszProjection) );
397 : }
398 :
399 : /* -------------------------------------------------------------------- */
400 : /* Check for a GeoTransform node. */
401 : /* -------------------------------------------------------------------- */
402 1003 : if( strlen(CPLGetXMLValue(psTree, "GeoTransform", "")) > 0 )
403 : {
404 294 : const char *pszGT = CPLGetXMLValue(psTree, "GeoTransform", "");
405 : char **papszTokens;
406 :
407 294 : papszTokens = CSLTokenizeStringComplex( pszGT, ",", FALSE, FALSE );
408 294 : 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 2058 : for( int iTA = 0; iTA < 6; iTA++ )
416 1764 : psPam->adfGeoTransform[iTA] = atof(papszTokens[iTA]);
417 294 : psPam->bHaveGeoTransform = TRUE;
418 : }
419 :
420 294 : CSLDestroy( papszTokens );
421 : }
422 :
423 : /* -------------------------------------------------------------------- */
424 : /* Check for GCPs. */
425 : /* -------------------------------------------------------------------- */
426 1003 : CPLXMLNode *psGCPList = CPLGetXMLNode( psTree, "GCPList" );
427 :
428 1003 : if( psGCPList != NULL )
429 : {
430 : CPLXMLNode *psXMLGCP;
431 10 : OGRSpatialReference oSRS;
432 10 : const char *pszRawProj = CPLGetXMLValue(psGCPList, "Projection", "");
433 :
434 10 : CPLFree( psPam->pszGCPProjection );
435 :
436 10 : if( strlen(pszRawProj) > 0
437 : && oSRS.SetFromUserInput( pszRawProj ) == OGRERR_NONE )
438 10 : oSRS.exportToWkt( &(psPam->pszGCPProjection) );
439 : else
440 0 : psPam->pszGCPProjection = CPLStrdup("");
441 :
442 : // Count GCPs.
443 10 : int nGCPMax = 0;
444 :
445 48 : for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL;
446 : psXMLGCP = psXMLGCP->psNext )
447 38 : nGCPMax++;
448 :
449 : // Make sure any previous GCPs, perhaps from an .aux file, are cleared
450 : // if we have new ones.
451 10 : 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 10 : psPam->pasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPMax);
460 :
461 48 : for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL;
462 : psXMLGCP = psXMLGCP->psNext )
463 : {
464 38 : GDAL_GCP *psGCP = psPam->pasGCPList + psPam->nGCPCount;
465 :
466 38 : if( !EQUAL(psXMLGCP->pszValue,"GCP") ||
467 : psXMLGCP->eType != CXT_Element )
468 10 : continue;
469 :
470 28 : GDALInitGCPs( 1, psGCP );
471 :
472 28 : CPLFree( psGCP->pszId );
473 28 : psGCP->pszId = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Id",""));
474 :
475 28 : CPLFree( psGCP->pszInfo );
476 28 : psGCP->pszInfo = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Info",""));
477 :
478 28 : psGCP->dfGCPPixel = atof(CPLGetXMLValue(psXMLGCP,"Pixel","0.0"));
479 28 : psGCP->dfGCPLine = atof(CPLGetXMLValue(psXMLGCP,"Line","0.0"));
480 :
481 28 : psGCP->dfGCPX = atof(CPLGetXMLValue(psXMLGCP,"X","0.0"));
482 28 : psGCP->dfGCPY = atof(CPLGetXMLValue(psXMLGCP,"Y","0.0"));
483 28 : psGCP->dfGCPZ = atof(CPLGetXMLValue(psXMLGCP,"Z","0.0"));
484 :
485 28 : psPam->nGCPCount++;
486 10 : }
487 : }
488 :
489 : /* -------------------------------------------------------------------- */
490 : /* Apply any dataset level metadata. */
491 : /* -------------------------------------------------------------------- */
492 1003 : oMDMD.XMLInit( psTree, TRUE );
493 :
494 : /* -------------------------------------------------------------------- */
495 : /* Try loading ESRI xml encoded projection */
496 : /* -------------------------------------------------------------------- */
497 1003 : if (psPam->pszProjection == NULL)
498 : {
499 662 : char** papszXML = oMDMD.GetMetadata( "xml:ESRI" );
500 662 : if (CSLCount(papszXML) == 1)
501 : {
502 12 : CPLXMLNode *psValueAsXML = CPLParseXMLString( papszXML[0] );
503 12 : if (psValueAsXML)
504 : {
505 : const char* pszESRI_WKT = CPLGetXMLValue(psValueAsXML,
506 12 : "=GeodataXform.SpatialReference.WKT", NULL);
507 12 : if (pszESRI_WKT)
508 : {
509 4 : OGRSpatialReference* poSRS = new OGRSpatialReference(NULL);
510 4 : char* pszTmp = (char*)pszESRI_WKT;
511 8 : if (poSRS->importFromWkt(&pszTmp) == OGRERR_NONE &&
512 : poSRS->morphFromESRI() == OGRERR_NONE)
513 : {
514 4 : char* pszWKT = NULL;
515 4 : if (poSRS->exportToWkt(&pszWKT) == OGRERR_NONE)
516 : {
517 4 : psPam->pszProjection = CPLStrdup(pszWKT);
518 : }
519 4 : CPLFree(pszWKT);
520 : }
521 4 : delete poSRS;
522 : }
523 12 : CPLDestroyXMLNode(psValueAsXML);
524 : }
525 : }
526 : }
527 :
528 : /* -------------------------------------------------------------------- */
529 : /* Process bands. */
530 : /* -------------------------------------------------------------------- */
531 : CPLXMLNode *psBandTree;
532 :
533 3164 : for( psBandTree = psTree->psChild;
534 : psBandTree != NULL; psBandTree = psBandTree->psNext )
535 : {
536 2161 : if( psBandTree->eType != CXT_Element
537 : || !EQUAL(psBandTree->pszValue,"PAMRasterBand") )
538 1318 : continue;
539 :
540 843 : int nBand = atoi(CPLGetXMLValue( psBandTree, "band", "0"));
541 :
542 843 : if( nBand < 1 || nBand > GetRasterCount() )
543 2 : continue;
544 :
545 : GDALPamRasterBand *poBand = (GDALPamRasterBand *)
546 841 : GetRasterBand(nBand);
547 :
548 841 : if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
549 0 : continue;
550 :
551 841 : poBand->XMLInit( psBandTree, pszUnused );
552 : }
553 :
554 : /* -------------------------------------------------------------------- */
555 : /* Clear dirty flag. */
556 : /* -------------------------------------------------------------------- */
557 1003 : nPamFlags &= ~GPF_DIRTY;
558 :
559 1003 : return CE_None;
560 : }
561 :
562 : /************************************************************************/
563 : /* SetPhysicalFilename() */
564 : /************************************************************************/
565 :
566 674 : void GDALPamDataset::SetPhysicalFilename( const char *pszFilename )
567 :
568 : {
569 674 : PamInitialize();
570 :
571 674 : if( psPam )
572 674 : psPam->osPhysicalFilename = pszFilename;
573 674 : }
574 :
575 : /************************************************************************/
576 : /* GetPhysicalFilename() */
577 : /************************************************************************/
578 :
579 40 : const char *GDALPamDataset::GetPhysicalFilename()
580 :
581 : {
582 40 : PamInitialize();
583 :
584 40 : if( psPam )
585 40 : return psPam->osPhysicalFilename;
586 : else
587 0 : return "";
588 : }
589 :
590 : /************************************************************************/
591 : /* SetSubdatasetName() */
592 : /************************************************************************/
593 :
594 674 : void GDALPamDataset::SetSubdatasetName( const char *pszSubdataset )
595 :
596 : {
597 674 : PamInitialize();
598 :
599 674 : if( psPam )
600 674 : psPam->osSubdatasetName = pszSubdataset;
601 674 : }
602 :
603 : /************************************************************************/
604 : /* GetSubdatasetName() */
605 : /************************************************************************/
606 :
607 20 : const char *GDALPamDataset::GetSubdatasetName()
608 :
609 : {
610 20 : PamInitialize();
611 :
612 20 : if( psPam )
613 20 : return psPam->osSubdatasetName;
614 : else
615 0 : return "";
616 : }
617 :
618 : /************************************************************************/
619 : /* BuildPamFilename() */
620 : /************************************************************************/
621 :
622 16036 : const char *GDALPamDataset::BuildPamFilename()
623 :
624 : {
625 16036 : if( psPam == NULL )
626 40 : 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 15996 : if( psPam->pszPamFilename != NULL )
633 1210 : return psPam->pszPamFilename;
634 :
635 14786 : const char *pszPhysicalFile = psPam->osPhysicalFilename;
636 :
637 14786 : if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL )
638 14124 : pszPhysicalFile = GetDescription();
639 :
640 14786 : if( strlen(pszPhysicalFile) == 0 )
641 12 : return NULL;
642 :
643 : /* -------------------------------------------------------------------- */
644 : /* Try a proxy lookup, otherwise just add .aux.xml. */
645 : /* -------------------------------------------------------------------- */
646 14774 : const char *pszProxyPam = PamGetProxy( pszPhysicalFile );
647 14774 : if( pszProxyPam != NULL )
648 8 : psPam->pszPamFilename = CPLStrdup(pszProxyPam);
649 : else
650 : {
651 14766 : psPam->pszPamFilename = (char*) CPLMalloc(strlen(pszPhysicalFile)+10);
652 14766 : strcpy( psPam->pszPamFilename, pszPhysicalFile );
653 14766 : strcat( psPam->pszPamFilename, ".aux.xml" );
654 : }
655 :
656 14774 : return psPam->pszPamFilename;
657 : }
658 :
659 : /************************************************************************/
660 : /* IsPamFilenameAPotentialSiblingFile() */
661 : /************************************************************************/
662 :
663 9440 : int GDALPamDataset::IsPamFilenameAPotentialSiblingFile()
664 : {
665 9440 : 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 9440 : const char *pszPhysicalFile = psPam->osPhysicalFilename;
673 :
674 9440 : if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL )
675 9440 : pszPhysicalFile = GetDescription();
676 :
677 9440 : int nLenPhysicalFile = strlen(pszPhysicalFile);
678 : int bIsSiblingPamFile = strncmp(psPam->pszPamFilename, pszPhysicalFile,
679 : nLenPhysicalFile) == 0 &&
680 : strcmp(psPam->pszPamFilename + nLenPhysicalFile,
681 9440 : ".aux.xml") == 0;
682 :
683 9440 : return bIsSiblingPamFile;
684 : }
685 :
686 : /************************************************************************/
687 : /* TryLoadXML() */
688 : /************************************************************************/
689 :
690 14550 : CPLErr GDALPamDataset::TryLoadXML(char **papszSiblingFiles)
691 :
692 : {
693 14550 : CPLXMLNode *psTree = NULL;
694 :
695 14550 : 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 14550 : nPamFlags &= ~GPF_DIRTY;
706 :
707 : /* -------------------------------------------------------------------- */
708 : /* Try reading the file. */
709 : /* -------------------------------------------------------------------- */
710 14550 : if( !BuildPamFilename() )
711 40 : 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 14510 : if (papszSiblingFiles != NULL && IsPamFilenameAPotentialSiblingFile())
721 : {
722 : int iSibling = CSLFindString( papszSiblingFiles,
723 7372 : CPLGetFilename(psPam->pszPamFilename) );
724 7372 : if( iSibling >= 0 )
725 : {
726 204 : CPLErrorReset();
727 204 : CPLPushErrorHandler( CPLQuietErrorHandler );
728 204 : psTree = CPLParseXMLFile( psPam->pszPamFilename );
729 204 : CPLPopErrorHandler();
730 : }
731 : }
732 : else
733 7138 : if( VSIStatExL( psPam->pszPamFilename, &sStatBuf,
734 : VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) == 0
735 : && VSI_ISREG( sStatBuf.st_mode ) )
736 : {
737 1003 : CPLErrorReset();
738 1003 : CPLPushErrorHandler( CPLQuietErrorHandler );
739 1003 : psTree = CPLParseXMLFile( psPam->pszPamFilename );
740 1003 : CPLPopErrorHandler();
741 : }
742 :
743 : /* -------------------------------------------------------------------- */
744 : /* If we are looking for a subdataset, search for it's subtree */
745 : /* now. */
746 : /* -------------------------------------------------------------------- */
747 14510 : if( psTree && psPam->osSubdatasetName.size() )
748 : {
749 : CPLXMLNode *psSubTree;
750 :
751 410 : for( psSubTree = psTree->psChild;
752 : psSubTree != NULL;
753 : psSubTree = psSubTree->psNext )
754 : {
755 210 : if( psSubTree->eType != CXT_Element
756 : || !EQUAL(psSubTree->pszValue,"Subdataset") )
757 192 : continue;
758 :
759 18 : if( !EQUAL(CPLGetXMLValue( psSubTree, "name", "" ),
760 : psPam->osSubdatasetName) )
761 8 : continue;
762 :
763 10 : psSubTree = CPLGetXMLNode( psSubTree, "PAMDataset" );
764 10 : break;
765 : }
766 :
767 210 : if( psSubTree != NULL )
768 10 : psSubTree = CPLCloneXMLTree( psSubTree );
769 :
770 210 : CPLDestroyXMLNode( psTree );
771 210 : psTree = psSubTree;
772 : }
773 :
774 : /* -------------------------------------------------------------------- */
775 : /* If we fail, try .aux. */
776 : /* -------------------------------------------------------------------- */
777 14510 : if( psTree == NULL )
778 13507 : return TryLoadAux(papszSiblingFiles);
779 :
780 : /* -------------------------------------------------------------------- */
781 : /* Initialize ourselves from this XML tree. */
782 : /* -------------------------------------------------------------------- */
783 : CPLErr eErr;
784 :
785 1003 : CPLString osVRTPath(CPLGetPath(psPam->pszPamFilename));
786 1003 : eErr = XMLInit( psTree, osVRTPath );
787 :
788 1003 : CPLDestroyXMLNode( psTree );
789 :
790 1003 : if( eErr != CE_None )
791 0 : PamClear();
792 :
793 1003 : return eErr;
794 : }
795 :
796 : /************************************************************************/
797 : /* TrySaveXML() */
798 : /************************************************************************/
799 :
800 2030 : CPLErr GDALPamDataset::TrySaveXML()
801 :
802 : {
803 : CPLXMLNode *psTree;
804 2030 : CPLErr eErr = CE_None;
805 :
806 2030 : nPamFlags &= ~GPF_DIRTY;
807 :
808 2030 : if( psPam == NULL || (nPamFlags & GPF_NOSAVE) )
809 544 : return CE_None;
810 :
811 : /* -------------------------------------------------------------------- */
812 : /* Make sure we know the filename we want to store in. */
813 : /* -------------------------------------------------------------------- */
814 1486 : if( !BuildPamFilename() )
815 12 : return CE_None;
816 :
817 : /* -------------------------------------------------------------------- */
818 : /* Build the XML representation of the auxilary metadata. */
819 : /* -------------------------------------------------------------------- */
820 1474 : psTree = SerializeToXML( NULL );
821 :
822 1474 : if( psTree == NULL )
823 : {
824 : /* If we have unset all metadata, we have to delete the PAM file */
825 16 : CPLPushErrorHandler( CPLQuietErrorHandler );
826 16 : VSIUnlink(psPam->pszPamFilename);
827 16 : CPLPopErrorHandler();
828 16 : 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 1458 : if( psPam->osSubdatasetName.size() != 0 )
837 : {
838 : CPLXMLNode *psOldTree, *psSubTree;
839 :
840 8 : CPLErrorReset();
841 8 : CPLPushErrorHandler( CPLQuietErrorHandler );
842 8 : psOldTree = CPLParseXMLFile( psPam->pszPamFilename );
843 8 : CPLPopErrorHandler();
844 :
845 8 : if( psOldTree == NULL )
846 8 : psOldTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMDataset" );
847 :
848 8 : 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 8 : if( psSubTree == NULL )
864 : {
865 : psSubTree = CPLCreateXMLNode( psOldTree, CXT_Element,
866 8 : "Subdataset" );
867 : CPLCreateXMLNode(
868 : CPLCreateXMLNode( psSubTree, CXT_Attribute, "name" ),
869 8 : CXT_Text, psPam->osSubdatasetName );
870 : }
871 :
872 8 : CPLXMLNode *psOldPamDataset = CPLGetXMLNode( psSubTree, "PAMDataset");
873 8 : if( psOldPamDataset != NULL )
874 : {
875 0 : CPLRemoveXMLChild( psSubTree, psOldPamDataset );
876 0 : CPLDestroyXMLNode( psOldPamDataset );
877 : }
878 :
879 8 : CPLAddXMLChild( psSubTree, psTree );
880 8 : psTree = psOldTree;
881 : }
882 :
883 : /* -------------------------------------------------------------------- */
884 : /* Try saving the auxilary metadata. */
885 : /* -------------------------------------------------------------------- */
886 : int bSaved;
887 :
888 1458 : CPLPushErrorHandler( CPLQuietErrorHandler );
889 1458 : bSaved = CPLSerializeXMLTreeToFile( psTree, psPam->pszPamFilename );
890 1458 : 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 1458 : if( bSaved )
897 1452 : eErr = CE_None;
898 : else
899 : {
900 : const char *pszNewPam;
901 6 : const char *pszBasename = GetDescription();
902 :
903 6 : if( psPam && psPam->osPhysicalFilename.length() > 0 )
904 0 : pszBasename = psPam->osPhysicalFilename;
905 :
906 6 : if( PamGetProxy(pszBasename) == NULL
907 : && ((pszNewPam = PamAllocateProxy(pszBasename)) != NULL))
908 : {
909 2 : CPLErrorReset();
910 2 : CPLFree( psPam->pszPamFilename );
911 2 : psPam->pszPamFilename = CPLStrdup(pszNewPam);
912 2 : eErr = TrySaveXML();
913 : }
914 : else
915 : {
916 : CPLError( CE_Warning, CPLE_AppDefined,
917 : "Unable to save auxilary information in %s.",
918 4 : psPam->pszPamFilename );
919 4 : eErr = CE_Warning;
920 : }
921 : }
922 :
923 : /* -------------------------------------------------------------------- */
924 : /* Cleanup */
925 : /* -------------------------------------------------------------------- */
926 1458 : CPLDestroyXMLNode( psTree );
927 :
928 1458 : return eErr;
929 : }
930 :
931 : /************************************************************************/
932 : /* CloneInfo() */
933 : /************************************************************************/
934 :
935 1064 : CPLErr GDALPamDataset::CloneInfo( GDALDataset *poSrcDS, int nCloneFlags )
936 :
937 : {
938 1064 : int bOnlyIfMissing = nCloneFlags & GCIF_ONLY_IF_MISSING;
939 1064 : int nSavedMOFlags = GetMOFlags();
940 :
941 1064 : PamInitialize();
942 :
943 : /* -------------------------------------------------------------------- */
944 : /* Supress NotImplemented error messages - mainly needed if PAM */
945 : /* disabled. */
946 : /* -------------------------------------------------------------------- */
947 1064 : SetMOFlags( nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED );
948 :
949 : /* -------------------------------------------------------------------- */
950 : /* GeoTransform */
951 : /* -------------------------------------------------------------------- */
952 1064 : if( nCloneFlags & GCIF_GEOTRANSFORM )
953 : {
954 : double adfGeoTransform[6];
955 :
956 1064 : if( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None )
957 : {
958 : double adfOldGT[6];
959 :
960 858 : if( !bOnlyIfMissing || GetGeoTransform( adfOldGT ) != CE_None )
961 112 : SetGeoTransform( adfGeoTransform );
962 : }
963 : }
964 :
965 : /* -------------------------------------------------------------------- */
966 : /* Projection */
967 : /* -------------------------------------------------------------------- */
968 1064 : if( nCloneFlags & GCIF_PROJECTION )
969 : {
970 1064 : const char *pszWKT = poSrcDS->GetProjectionRef();
971 :
972 1064 : if( pszWKT != NULL && strlen(pszWKT) > 0 )
973 : {
974 2520 : if( !bOnlyIfMissing
975 840 : || GetProjectionRef() == NULL
976 840 : || strlen(GetProjectionRef()) == 0 )
977 152 : SetProjection( pszWKT );
978 : }
979 : }
980 :
981 : /* -------------------------------------------------------------------- */
982 : /* GCPs */
983 : /* -------------------------------------------------------------------- */
984 1064 : if( nCloneFlags & GCIF_GCPS )
985 : {
986 1064 : if( poSrcDS->GetGCPCount() > 0 )
987 : {
988 14 : if( !bOnlyIfMissing || GetGCPCount() == 0 )
989 : {
990 2 : SetGCPs( poSrcDS->GetGCPCount(),
991 2 : poSrcDS->GetGCPs(),
992 6 : poSrcDS->GetGCPProjection() );
993 : }
994 : }
995 : }
996 :
997 : /* -------------------------------------------------------------------- */
998 : /* Metadata */
999 : /* -------------------------------------------------------------------- */
1000 1064 : if( nCloneFlags & GCIF_METADATA )
1001 : {
1002 1064 : if( poSrcDS->GetMetadata() != NULL )
1003 : {
1004 2418 : if( !bOnlyIfMissing
1005 1612 : || CSLCount(GetMetadata()) != CSLCount(poSrcDS->GetMetadata()) )
1006 : {
1007 382 : SetMetadata( poSrcDS->GetMetadata() );
1008 : }
1009 : }
1010 1064 : if( poSrcDS->GetMetadata("RPC") != NULL )
1011 : {
1012 24 : if( !bOnlyIfMissing
1013 8 : || CSLCount(GetMetadata("RPC"))
1014 8 : != CSLCount(poSrcDS->GetMetadata("RPC")) )
1015 : {
1016 0 : SetMetadata( poSrcDS->GetMetadata("RPC"), "RPC" );
1017 : }
1018 : }
1019 : }
1020 :
1021 : /* -------------------------------------------------------------------- */
1022 : /* Process bands. */
1023 : /* -------------------------------------------------------------------- */
1024 1064 : if( nCloneFlags & GCIF_PROCESS_BANDS )
1025 : {
1026 : int iBand;
1027 :
1028 2692 : for( iBand = 0; iBand < GetRasterCount(); iBand++ )
1029 : {
1030 : GDALPamRasterBand *poBand = (GDALPamRasterBand *)
1031 1628 : GetRasterBand(iBand+1);
1032 :
1033 1628 : if( poBand == NULL || !(poBand->GetMOFlags() & GMO_PAM_CLASS) )
1034 0 : continue;
1035 :
1036 1628 : if( poSrcDS->GetRasterCount() >= iBand+1 )
1037 : poBand->CloneInfo( poSrcDS->GetRasterBand(iBand+1),
1038 1628 : 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 1064 : if( nCloneFlags & GCIF_MASK )
1051 : {
1052 644 : GDALDriver::DefaultCopyMasks( poSrcDS, this, FALSE );
1053 : }
1054 :
1055 : /* -------------------------------------------------------------------- */
1056 : /* Restore MO flags. */
1057 : /* -------------------------------------------------------------------- */
1058 1064 : SetMOFlags( nSavedMOFlags );
1059 :
1060 1064 : return CE_None;
1061 : }
1062 :
1063 : /************************************************************************/
1064 : /* GetFileList() */
1065 : /* */
1066 : /* Add .aux.xml or .aux file into file list as appropriate. */
1067 : /************************************************************************/
1068 :
1069 3058 : char **GDALPamDataset::GetFileList()
1070 :
1071 : {
1072 : VSIStatBufL sStatBuf;
1073 3058 : char **papszFileList = GDALDataset::GetFileList();
1074 :
1075 3058 : 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 3058 : if( psPam && psPam->pszPamFilename )
1083 : {
1084 3056 : int bAddPamFile = (nPamFlags & GPF_DIRTY);
1085 3056 : if (!bAddPamFile)
1086 : {
1087 3056 : if (oOvManager.GetSiblingFiles() != NULL && IsPamFilenameAPotentialSiblingFile())
1088 : bAddPamFile = CSLFindString(oOvManager.GetSiblingFiles(),
1089 2054 : CPLGetFilename(psPam->pszPamFilename)) >= 0;
1090 : else
1091 : bAddPamFile = VSIStatExL( psPam->pszPamFilename, &sStatBuf,
1092 1002 : VSI_STAT_EXISTS_FLAG ) == 0;
1093 : }
1094 3056 : if (bAddPamFile)
1095 : {
1096 434 : papszFileList = CSLAddString( papszFileList, psPam->pszPamFilename );
1097 : }
1098 : }
1099 :
1100 3058 : if( psPam && psPam->osAuxFilename.size() > 0 &&
1101 : CSLFindString( papszFileList, psPam->osAuxFilename ) == -1 )
1102 : {
1103 0 : papszFileList = CSLAddString( papszFileList, psPam->osAuxFilename );
1104 : }
1105 3058 : return papszFileList;
1106 : }
1107 :
1108 : /************************************************************************/
1109 : /* IBuildOverviews() */
1110 : /************************************************************************/
1111 :
1112 28 : 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 28 : PamInitialize();
1123 28 : if( psPam == NULL )
1124 : return GDALDataset::IBuildOverviews( pszResampling,
1125 : nOverviews, panOverviewList,
1126 : nListBands, panBandList,
1127 2 : 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 26 : if( oOvManager.IsInitialized() && psPam->osPhysicalFilename.length() != 0 )
1135 : return oOvManager.BuildOverviewsSubDataset(
1136 : psPam->osPhysicalFilename, pszResampling,
1137 : nOverviews, panOverviewList,
1138 : nListBands, panBandList,
1139 8 : pfnProgress, pProgressData );
1140 : else
1141 : return GDALDataset::IBuildOverviews( pszResampling,
1142 : nOverviews, panOverviewList,
1143 : nListBands, panBandList,
1144 18 : pfnProgress, pProgressData );
1145 : }
1146 :
1147 :
1148 : /************************************************************************/
1149 : /* GetProjectionRef() */
1150 : /************************************************************************/
1151 :
1152 8634 : const char *GDALPamDataset::GetProjectionRef()
1153 :
1154 : {
1155 8634 : if( psPam && psPam->pszProjection )
1156 12 : return psPam->pszProjection;
1157 : else
1158 8622 : return GDALDataset::GetProjectionRef();
1159 : }
1160 :
1161 : /************************************************************************/
1162 : /* SetProjection() */
1163 : /************************************************************************/
1164 :
1165 602 : CPLErr GDALPamDataset::SetProjection( const char *pszProjectionIn )
1166 :
1167 : {
1168 602 : PamInitialize();
1169 :
1170 602 : if( psPam == NULL )
1171 10 : return GDALDataset::SetProjection( pszProjectionIn );
1172 : else
1173 : {
1174 592 : CPLFree( psPam->pszProjection );
1175 592 : psPam->pszProjection = CPLStrdup( pszProjectionIn );
1176 592 : MarkPamDirty();
1177 :
1178 592 : return CE_None;
1179 : }
1180 : }
1181 :
1182 : /************************************************************************/
1183 : /* GetGeoTransform() */
1184 : /************************************************************************/
1185 :
1186 8024 : CPLErr GDALPamDataset::GetGeoTransform( double * padfTransform )
1187 :
1188 : {
1189 8024 : if( psPam && psPam->bHaveGeoTransform )
1190 : {
1191 108 : memcpy( padfTransform, psPam->adfGeoTransform, sizeof(double) * 6 );
1192 108 : return CE_None;
1193 : }
1194 : else
1195 7916 : return GDALDataset::GetGeoTransform( padfTransform );
1196 : }
1197 :
1198 : /************************************************************************/
1199 : /* SetGeoTransform() */
1200 : /************************************************************************/
1201 :
1202 392 : CPLErr GDALPamDataset::SetGeoTransform( double * padfTransform )
1203 :
1204 : {
1205 392 : PamInitialize();
1206 :
1207 392 : if( psPam )
1208 : {
1209 382 : MarkPamDirty();
1210 382 : psPam->bHaveGeoTransform = TRUE;
1211 382 : memcpy( psPam->adfGeoTransform, padfTransform, sizeof(double) * 6 );
1212 382 : return( CE_None );
1213 : }
1214 : else
1215 : {
1216 10 : return GDALDataset::SetGeoTransform( padfTransform );
1217 : }
1218 : }
1219 :
1220 : /************************************************************************/
1221 : /* GetGCPCount() */
1222 : /************************************************************************/
1223 :
1224 7766 : int GDALPamDataset::GetGCPCount()
1225 :
1226 : {
1227 7766 : if( psPam && psPam->nGCPCount > 0 )
1228 16 : return psPam->nGCPCount;
1229 : else
1230 7750 : return GDALDataset::GetGCPCount();
1231 : }
1232 :
1233 : /************************************************************************/
1234 : /* GetGCPProjection() */
1235 : /************************************************************************/
1236 :
1237 14 : const char *GDALPamDataset::GetGCPProjection()
1238 :
1239 : {
1240 14 : if( psPam && psPam->pszGCPProjection != NULL )
1241 10 : return psPam->pszGCPProjection;
1242 : else
1243 4 : return GDALDataset::GetGCPProjection();
1244 : }
1245 :
1246 : /************************************************************************/
1247 : /* GetGCPs() */
1248 : /************************************************************************/
1249 :
1250 12 : const GDAL_GCP *GDALPamDataset::GetGCPs()
1251 :
1252 : {
1253 12 : if( psPam && psPam->nGCPCount > 0 )
1254 10 : return psPam->pasGCPList;
1255 : else
1256 2 : return GDALDataset::GetGCPs();
1257 : }
1258 :
1259 : /************************************************************************/
1260 : /* SetGCPs() */
1261 : /************************************************************************/
1262 :
1263 2 : CPLErr GDALPamDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList,
1264 : const char *pszGCPProjection )
1265 :
1266 : {
1267 2 : PamInitialize();
1268 :
1269 2 : if( psPam )
1270 : {
1271 2 : CPLFree( psPam->pszGCPProjection );
1272 2 : if( psPam->nGCPCount > 0 )
1273 : {
1274 0 : GDALDeinitGCPs( psPam->nGCPCount, psPam->pasGCPList );
1275 0 : CPLFree( psPam->pasGCPList );
1276 : }
1277 :
1278 2 : psPam->pszGCPProjection = CPLStrdup(pszGCPProjection);
1279 2 : psPam->nGCPCount = nGCPCount;
1280 2 : psPam->pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPList );
1281 :
1282 2 : MarkPamDirty();
1283 :
1284 2 : return CE_None;
1285 : }
1286 : else
1287 : {
1288 0 : return GDALDataset::SetGCPs( nGCPCount, pasGCPList, pszGCPProjection );
1289 : }
1290 : }
1291 :
1292 : /************************************************************************/
1293 : /* SetMetadata() */
1294 : /************************************************************************/
1295 :
1296 3666 : CPLErr GDALPamDataset::SetMetadata( char **papszMetadata,
1297 : const char *pszDomain )
1298 :
1299 : {
1300 3666 : PamInitialize();
1301 :
1302 3666 : if( psPam )
1303 3658 : MarkPamDirty();
1304 :
1305 3666 : return GDALDataset::SetMetadata( papszMetadata, pszDomain );
1306 : }
1307 :
1308 : /************************************************************************/
1309 : /* SetMetadataItem() */
1310 : /************************************************************************/
1311 :
1312 12242 : CPLErr GDALPamDataset::SetMetadataItem( const char *pszName,
1313 : const char *pszValue,
1314 : const char *pszDomain )
1315 :
1316 : {
1317 12242 : PamInitialize();
1318 :
1319 12242 : if( psPam )
1320 12238 : MarkPamDirty();
1321 :
1322 12242 : return GDALDataset::SetMetadataItem( pszName, pszValue, pszDomain );
1323 : }
1324 :
1325 : /************************************************************************/
1326 : /* GetMetadataItem() */
1327 : /************************************************************************/
1328 :
1329 9804 : 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 9804 : if( pszDomain != NULL && EQUAL(pszDomain,"ProxyOverviewRequest") )
1340 : {
1341 6 : CPLString osPrelimOvr = GetDescription();
1342 6 : osPrelimOvr += ":::OVR";
1343 :
1344 6 : const char *pszProxyOvrFilename = PamAllocateProxy( osPrelimOvr );
1345 6 : if( pszProxyOvrFilename == NULL )
1346 4 : return NULL;
1347 :
1348 2 : SetMetadataItem( "OVERVIEW_FILE", pszProxyOvrFilename, "OVERVIEWS" );
1349 :
1350 2 : 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 9798 : else if( pszDomain != NULL
1362 : && EQUAL(pszDomain,"OVERVIEWS")
1363 : && EQUAL(pszName,"OVERVIEW_FILE") )
1364 : {
1365 : const char *pszOverviewFile =
1366 2372 : GDALDataset::GetMetadataItem( pszName, pszDomain );
1367 :
1368 2372 : if( pszOverviewFile == NULL
1369 : || !EQUALN(pszOverviewFile,":::BASE:::",10) )
1370 2352 : return pszOverviewFile;
1371 :
1372 20 : CPLString osPath;
1373 :
1374 20 : if( strlen(GetPhysicalFilename()) > 0 )
1375 20 : osPath = CPLGetPath(GetPhysicalFilename());
1376 : else
1377 0 : osPath = CPLGetPath(GetDescription());
1378 :
1379 20 : return CPLFormFilename( osPath, pszOverviewFile + 10, NULL );
1380 : }
1381 :
1382 : /* -------------------------------------------------------------------- */
1383 : /* Everything else is a pass through. */
1384 : /* -------------------------------------------------------------------- */
1385 : else
1386 7426 : return GDALDataset::GetMetadataItem( pszName, pszDomain );
1387 :
1388 : }
1389 :
1390 : /************************************************************************/
1391 : /* GetMetadata() */
1392 : /************************************************************************/
1393 :
1394 1604 : char **GDALPamDataset::GetMetadata( const char *pszDomain )
1395 :
1396 : {
1397 : // if( pszDomain == NULL || !EQUAL(pszDomain,"ProxyOverviewRequest") )
1398 1604 : return GDALDataset::GetMetadata( pszDomain );
1399 : }
1400 :
1401 : /************************************************************************/
1402 : /* TryLoadAux() */
1403 : /************************************************************************/
1404 :
1405 13507 : CPLErr GDALPamDataset::TryLoadAux(char **papszSiblingFiles)
1406 :
1407 : {
1408 : /* -------------------------------------------------------------------- */
1409 : /* Initialize PAM. */
1410 : /* -------------------------------------------------------------------- */
1411 13507 : PamInitialize();
1412 13507 : 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 13507 : const char *pszPhysicalFile = psPam->osPhysicalFilename;
1420 :
1421 13507 : if( strlen(pszPhysicalFile) == 0 && GetDescription() != NULL )
1422 12855 : pszPhysicalFile = GetDescription();
1423 :
1424 13507 : if( strlen(pszPhysicalFile) == 0 )
1425 0 : return CE_None;
1426 :
1427 13507 : if( papszSiblingFiles )
1428 : {
1429 7168 : CPLString osAuxFilename = CPLResetExtension( pszPhysicalFile, "aux");
1430 : int iSibling = CSLFindString( papszSiblingFiles,
1431 7168 : CPLGetFilename(osAuxFilename) );
1432 7168 : if( iSibling < 0 )
1433 : {
1434 7156 : osAuxFilename = pszPhysicalFile;
1435 7156 : osAuxFilename += ".aux";
1436 : iSibling = CSLFindString( papszSiblingFiles,
1437 7156 : CPLGetFilename(osAuxFilename) );
1438 7156 : if( iSibling < 0 )
1439 7156 : return CE_None;
1440 0 : }
1441 : }
1442 :
1443 : /* -------------------------------------------------------------------- */
1444 : /* Try to open .aux file. */
1445 : /* -------------------------------------------------------------------- */
1446 : GDALDataset *poAuxDS = GDALFindAssociatedAuxFile( pszPhysicalFile,
1447 6351 : GA_ReadOnly, this );
1448 :
1449 6351 : if( poAuxDS == NULL )
1450 6345 : return CE_None;
1451 :
1452 6 : psPam->osAuxFilename = poAuxDS->GetDescription();
1453 :
1454 : /* -------------------------------------------------------------------- */
1455 : /* Do we have an SRS on the aux file? */
1456 : /* -------------------------------------------------------------------- */
1457 6 : if( strlen(poAuxDS->GetProjectionRef()) > 0 )
1458 0 : GDALPamDataset::SetProjection( poAuxDS->GetProjectionRef() );
1459 :
1460 : /* -------------------------------------------------------------------- */
1461 : /* Geotransform. */
1462 : /* -------------------------------------------------------------------- */
1463 6 : if( poAuxDS->GetGeoTransform( psPam->adfGeoTransform ) == CE_None )
1464 0 : psPam->bHaveGeoTransform = TRUE;
1465 :
1466 : /* -------------------------------------------------------------------- */
1467 : /* GCPs */
1468 : /* -------------------------------------------------------------------- */
1469 6 : 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 6 : char **papszMD = poAuxDS->GetMetadata();
1481 6 : 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 6 : papszMD = poAuxDS->GetMetadata("XFORMS");
1490 6 : 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 12 : for( iBand = 0; iBand < poAuxDS->GetRasterCount(); iBand++ )
1504 : {
1505 6 : if( iBand >= GetRasterCount() )
1506 0 : break;
1507 :
1508 6 : GDALRasterBand *poAuxBand = poAuxDS->GetRasterBand( iBand+1 );
1509 6 : GDALRasterBand *poBand = GetRasterBand( iBand+1 );
1510 :
1511 6 : papszMD = poAuxBand->GetMetadata();
1512 6 : if( CSLCount(papszMD) > 0 )
1513 : {
1514 : char **papszMerged =
1515 6 : CSLMerge( CSLDuplicate(poBand->GetMetadata()), papszMD );
1516 6 : poBand->SetMetadata( papszMerged );
1517 6 : CSLDestroy( papszMerged );
1518 : }
1519 :
1520 6 : if( strlen(poAuxBand->GetDescription()) > 0 )
1521 6 : poBand->SetDescription( poAuxBand->GetDescription() );
1522 :
1523 6 : if( poAuxBand->GetCategoryNames() != NULL )
1524 0 : poBand->SetCategoryNames( poAuxBand->GetCategoryNames() );
1525 :
1526 6 : if( poAuxBand->GetColorTable() != NULL
1527 0 : && poBand->GetColorTable() == NULL )
1528 0 : poBand->SetColorTable( poAuxBand->GetColorTable() );
1529 :
1530 : // histograms?
1531 : double dfMin, dfMax;
1532 6 : int nBuckets, *panHistogram=NULL;
1533 :
1534 6 : if( poAuxBand->GetDefaultHistogram( &dfMin, &dfMax,
1535 : &nBuckets, &panHistogram,
1536 6 : FALSE, NULL, NULL ) == CE_None )
1537 : {
1538 : poBand->SetDefaultHistogram( dfMin, dfMax, nBuckets,
1539 2 : panHistogram );
1540 2 : CPLFree( panHistogram );
1541 : }
1542 :
1543 : // RAT
1544 6 : if( poAuxBand->GetDefaultRAT() != NULL )
1545 2 : poBand->SetDefaultRAT( poAuxBand->GetDefaultRAT() );
1546 :
1547 : // NoData
1548 6 : int bSuccess = FALSE;
1549 6 : double dfNoDataValue = poAuxBand->GetNoDataValue( &bSuccess );
1550 6 : if( bSuccess )
1551 6 : poBand->SetNoDataValue( dfNoDataValue );
1552 : }
1553 :
1554 6 : GDALClose( poAuxDS );
1555 :
1556 : /* -------------------------------------------------------------------- */
1557 : /* Mark PAM info as clean. */
1558 : /* -------------------------------------------------------------------- */
1559 6 : nPamFlags &= ~GPF_DIRTY;
1560 :
1561 6 : return CE_Failure;
1562 : }
|