1 /******************************************************************************
2  * $Id: gdalpamrasterband.cpp 15320 2008-09-06 17:08:20Z warmerdam $
3  *
4  * Project:  GDAL Core
5  * Purpose:  Implementation of GDALPamRasterBand, a raster band base class
6  *           that knows how to persistently store auxilary metadata in an
7  *           external xml file.
8  * Author:   Frank Warmerdam, warmerdam@pobox.com
9  *
10  ******************************************************************************
11  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  ****************************************************************************/
31 
32 #include "gdal_pam.h"
33 #include "gdal_rat.h"
34 #include "../port/cpl_string.h"
35 
36 CPL_CVSID("$Id: gdalpamrasterband.cpp 15320 2008-09-06 17:08:20Z warmerdam $");
37 
38 /************************************************************************/
39 /*                         GDALPamRasterBand()                          */
40 /************************************************************************/
41 
GDALPamRasterBand()42 GDALPamRasterBand::GDALPamRasterBand()
43 
44 {
45     psPam = NULL;
46     SetMOFlags( GetMOFlags() | GMO_PAM_CLASS );
47 }
48 
49 /************************************************************************/
50 /*                         ~GDALPamRasterBand()                         */
51 /************************************************************************/
52 
~GDALPamRasterBand()53 GDALPamRasterBand::~GDALPamRasterBand()
54 
55 {
56     PamClear();
57 }
58 
59 /************************************************************************/
60 /*                           SerializeToXML()                           */
61 /************************************************************************/
62 
SerializeToXML(const char * pszVRTPath)63 CPLXMLNode *GDALPamRasterBand::SerializeToXML( const char *pszVRTPath )
64 
65 {
66     if( psPam == NULL )
67         return NULL;
68 
69 /* -------------------------------------------------------------------- */
70 /*      Setup root node and attributes.                                 */
71 /* -------------------------------------------------------------------- */
72     CPLString oFmt;
73 
74     CPLXMLNode *psTree;
75 
76     psTree = CPLCreateXMLNode( NULL, CXT_Element, "PAMRasterBand" );
77 
78     if( GetBand() > 0 )
79         CPLSetXMLValue( psTree, "#band", oFmt.Printf( "%d", GetBand() ) );
80 
81 /* -------------------------------------------------------------------- */
82 /*      Serialize information of interest.                              */
83 /* -------------------------------------------------------------------- */
84     if( strlen(GetDescription()) > 0 )
85         CPLSetXMLValue( psTree, "Description", GetDescription() );
86 
87     if( psPam->bNoDataValueSet )
88     {
89         CPLSetXMLValue( psTree, "NoDataValue",
90                         oFmt.Printf( "%.14E", psPam->dfNoDataValue ) );
91 
92         /* hex encode real floating point values */
93         if( psPam->dfNoDataValue != floor(psPam->dfNoDataValue)
94             || psPam->dfNoDataValue != atof(oFmt) )
95         {
96             double dfNoDataLittleEndian;
97 
98             dfNoDataLittleEndian = psPam->dfNoDataValue;
99             CPL_LSBPTR64( &dfNoDataLittleEndian );
100 
101             char *pszHexEncoding =
102                 CPLBinaryToHex( 8, (GByte *) &dfNoDataLittleEndian );
103             CPLSetXMLValue( psTree, "NoDataValue.#le_hex_equiv",pszHexEncoding);
104             CPLFree( pszHexEncoding );
105         }
106     }
107 
108     if( psPam->pszUnitType != NULL )
109         CPLSetXMLValue( psTree, "UnitType", psPam->pszUnitType );
110 
111     if( psPam->dfOffset != 0.0 )
112         CPLSetXMLValue( psTree, "Offset",
113                         oFmt.Printf( "%.16g", psPam->dfOffset ) );
114 
115     if( psPam->dfScale != 1.0 )
116         CPLSetXMLValue( psTree, "Scale",
117                         oFmt.Printf( "%.16g", psPam->dfScale ) );
118 
119     if( psPam->eColorInterp != GCI_Undefined )
120         CPLSetXMLValue( psTree, "ColorInterp",
121                         GDALGetColorInterpretationName( psPam->eColorInterp ));
122 
123 /* -------------------------------------------------------------------- */
124 /*      Category names.                                                 */
125 /* -------------------------------------------------------------------- */
126     if( psPam->papszCategoryNames != NULL )
127     {
128         CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element,
129                                                  "CategoryNames" );
130 
131         for( int iEntry=0; psPam->papszCategoryNames[iEntry] != NULL; iEntry++)
132         {
133             CPLCreateXMLElementAndValue( psCT_XML, "Category",
134                                          psPam->papszCategoryNames[iEntry] );
135         }
136     }
137 
138 /* -------------------------------------------------------------------- */
139 /*      Color Table.                                                    */
140 /* -------------------------------------------------------------------- */
141     if( psPam->poColorTable != NULL )
142     {
143         CPLXMLNode *psCT_XML = CPLCreateXMLNode( psTree, CXT_Element,
144                                                  "ColorTable" );
145 
146         for( int iEntry=0; iEntry < psPam->poColorTable->GetColorEntryCount();
147              iEntry++ )
148         {
149             GDALColorEntry sEntry;
150             CPLXMLNode *psEntry_XML = CPLCreateXMLNode( psCT_XML, CXT_Element,
151                                                         "Entry" );
152 
153             psPam->poColorTable->GetColorEntryAsRGB( iEntry, &sEntry );
154 
155             CPLSetXMLValue( psEntry_XML, "#c1", oFmt.Printf("%d",sEntry.c1) );
156             CPLSetXMLValue( psEntry_XML, "#c2", oFmt.Printf("%d",sEntry.c2) );
157             CPLSetXMLValue( psEntry_XML, "#c3", oFmt.Printf("%d",sEntry.c3) );
158             CPLSetXMLValue( psEntry_XML, "#c4", oFmt.Printf("%d",sEntry.c4) );
159         }
160     }
161 
162 /* -------------------------------------------------------------------- */
163 /*      Min/max.                                                        */
164 /* -------------------------------------------------------------------- */
165     if( psPam->bHaveMinMax )
166     {
167         CPLSetXMLValue( psTree, "Minimum",
168                         oFmt.Printf( "%.16g", psPam->dfMin ) );
169         CPLSetXMLValue( psTree, "Maximum",
170                         oFmt.Printf( "%.16g", psPam->dfMax ) );
171     }
172 
173 /* -------------------------------------------------------------------- */
174 /*      Statistics                                                      */
175 /* -------------------------------------------------------------------- */
176     if( psPam->bHaveStats )
177     {
178         CPLSetXMLValue( psTree, "Mean",
179                         oFmt.Printf( "%.16g", psPam->dfMean ) );
180         CPLSetXMLValue( psTree, "StandardDeviation",
181                         oFmt.Printf( "%.16g", psPam->dfStdDev ) );
182     }
183 
184 /* -------------------------------------------------------------------- */
185 /*      Histograms.                                                     */
186 /* -------------------------------------------------------------------- */
187     if( psPam->psSavedHistograms != NULL )
188         CPLAddXMLChild( psTree, CPLCloneXMLTree( psPam->psSavedHistograms ) );
189 
190 /* -------------------------------------------------------------------- */
191 /*      Raster Attribute Table                                          */
192 /* -------------------------------------------------------------------- */
193     if( psPam->poDefaultRAT != NULL )
194         CPLAddXMLChild( psTree, psPam->poDefaultRAT->Serialize() );
195 
196 /* -------------------------------------------------------------------- */
197 /*      Metadata.                                                       */
198 /* -------------------------------------------------------------------- */
199     CPLXMLNode *psMD;
200 
201     psMD = oMDMD.Serialize();
202     if( psMD != NULL )
203     {
204         if( psMD->psChild == NULL )
205             CPLDestroyXMLNode( psMD );
206         else
207             CPLAddXMLChild( psTree, psMD );
208     }
209 
210 /* -------------------------------------------------------------------- */
211 /*      We don't want to return anything if we had no metadata to       */
212 /*      attach.                                                         */
213 /* -------------------------------------------------------------------- */
214     if( psTree->psChild == NULL || psTree->psChild->psNext == NULL )
215     {
216         CPLDestroyXMLNode( psTree );
217         psTree = NULL;
218     }
219 
220     return psTree;
221 }
222 
223 /************************************************************************/
224 /*                           PamInitialize()                            */
225 /************************************************************************/
226 
PamInitialize()227 void GDALPamRasterBand::PamInitialize()
228 
229 {
230     if( psPam )
231         return;
232 
233     GDALPamDataset *poParentDS = (GDALPamDataset *) GetDataset();
234 
235     if( poParentDS == NULL || !(poParentDS->GetMOFlags() & GMO_PAM_CLASS) )
236         return;
237 
238     poParentDS->PamInitialize();
239     if( poParentDS->psPam == NULL )
240         return;
241 
242     // Often (always?) initializing our parent will have initialized us.
243     if( psPam != NULL )
244         return;
245 
246     psPam = (GDALRasterBandPamInfo *)
247         CPLCalloc(sizeof(GDALRasterBandPamInfo),1);
248 
249     psPam->dfScale = 1.0;
250     psPam->poParentDS = poParentDS;
251     psPam->dfNoDataValue = -1e10;
252     psPam->poDefaultRAT = NULL;
253 }
254 
255 /************************************************************************/
256 /*                              PamClear()                              */
257 /************************************************************************/
258 
PamClear()259 void GDALPamRasterBand::PamClear()
260 
261 {
262     if( psPam )
263     {
264         if( psPam->poColorTable )
265             delete psPam->poColorTable;
266         psPam->poColorTable = NULL;
267 
268         CPLFree( psPam->pszUnitType );
269         CSLDestroy( psPam->papszCategoryNames );
270 
271         if( psPam->poDefaultRAT != NULL )
272         {
273             delete psPam->poDefaultRAT;
274             psPam->poDefaultRAT = NULL;
275         }
276 
277         if (psPam->psSavedHistograms != NULL)
278         {
279             CPLDestroyXMLNode (psPam->psSavedHistograms );
280             psPam->psSavedHistograms = NULL;
281         }
282 
283         CPLFree( psPam );
284         psPam = NULL;
285     }
286 }
287 
288 /************************************************************************/
289 /*                              XMLInit()                               */
290 /************************************************************************/
291 
XMLInit(CPLXMLNode * psTree,const char * pszVRTPath)292 CPLErr GDALPamRasterBand::XMLInit( CPLXMLNode *psTree, const char *pszVRTPath )
293 
294 {
295     PamInitialize();
296 
297 /* -------------------------------------------------------------------- */
298 /*      Apply any dataset level metadata.                               */
299 /* -------------------------------------------------------------------- */
300     oMDMD.XMLInit( psTree, TRUE );
301 
302 /* -------------------------------------------------------------------- */
303 /*      Collect various other items of metadata.                        */
304 /* -------------------------------------------------------------------- */
305     SetDescription( CPLGetXMLValue( psTree, "Description", "" ) );
306 
307     if( CPLGetXMLValue( psTree, "NoDataValue", NULL ) != NULL )
308     {
309         const char *pszLEHex =
310             CPLGetXMLValue( psTree, "NoDataValue.le_hex_equiv", NULL );
311         if( pszLEHex != NULL )
312         {
313             int nBytes;
314             GByte *pabyBin = CPLHexToBinary( pszLEHex, &nBytes );
315             if( nBytes == 8 )
316             {
317                 CPL_LSBPTR64( pabyBin );
318 
319                 GDALPamRasterBand::SetNoDataValue( *((double *) pabyBin) );
320             }
321             else
322             {
323                 GDALPamRasterBand::SetNoDataValue(
324                     atof(CPLGetXMLValue( psTree, "NoDataValue", "0" )) );
325             }
326             CPLFree( pabyBin );
327         }
328         else
329         {
330             GDALPamRasterBand::SetNoDataValue(
331                 atof(CPLGetXMLValue( psTree, "NoDataValue", "0" )) );
332         }
333     }
334 
335     GDALPamRasterBand::SetOffset(
336         atof(CPLGetXMLValue( psTree, "Offset", "0.0" )) );
337     GDALPamRasterBand::SetScale(
338         atof(CPLGetXMLValue( psTree, "Scale", "1.0" )) );
339 
340     GDALPamRasterBand::SetUnitType( CPLGetXMLValue( psTree, "UnitType", NULL));
341 
342     if( CPLGetXMLValue( psTree, "ColorInterp", NULL ) != NULL )
343     {
344         const char *pszInterp = CPLGetXMLValue( psTree, "ColorInterp", NULL );
345         int        iInterp;
346 
347         for( iInterp = 0; iInterp < 13; iInterp++ )
348         {
349             const char *pszCandidate
350                 = GDALGetColorInterpretationName( (GDALColorInterp) iInterp );
351 
352             if( pszCandidate != NULL && EQUAL(pszCandidate,pszInterp) )
353             {
354                 GDALPamRasterBand::SetColorInterpretation( (GDALColorInterp) iInterp );
355                 break;
356             }
357         }
358     }
359 
360 /* -------------------------------------------------------------------- */
361 /*      Category names.                                                 */
362 /* -------------------------------------------------------------------- */
363     if( CPLGetXMLNode( psTree, "CategoryNames" ) != NULL )
364     {
365         CPLXMLNode *psEntry;
366         char **papszCategoryNames = NULL;
367 
368         for( psEntry = CPLGetXMLNode( psTree, "CategoryNames" )->psChild;
369              psEntry != NULL; psEntry = psEntry->psNext )
370         {
371             /* Don't skeep <Category> tag with empty content */
372             if( psEntry->eType != CXT_Element
373                 || !EQUAL(psEntry->pszValue,"Category")
374                 || (psEntry->psChild != NULL && psEntry->psChild->eType != CXT_Text) )
375                 continue;
376 
377             papszCategoryNames = CSLAddString( papszCategoryNames,
378                                  (psEntry->psChild) ? psEntry->psChild->pszValue : "" );
379         }
380 
381         GDALPamRasterBand::SetCategoryNames( papszCategoryNames );
382     }
383 
384 /* -------------------------------------------------------------------- */
385 /*      Collect a color table.                                          */
386 /* -------------------------------------------------------------------- */
387     if( CPLGetXMLNode( psTree, "ColorTable" ) != NULL )
388     {
389         CPLXMLNode *psEntry;
390         GDALColorTable oTable;
391         int        iEntry = 0;
392 
393         for( psEntry = CPLGetXMLNode( psTree, "ColorTable" )->psChild;
394              psEntry != NULL; psEntry = psEntry->psNext )
395         {
396             GDALColorEntry sCEntry;
397 
398             sCEntry.c1 = (short) atoi(CPLGetXMLValue( psEntry, "c1", "0" ));
399             sCEntry.c2 = (short) atoi(CPLGetXMLValue( psEntry, "c2", "0" ));
400             sCEntry.c3 = (short) atoi(CPLGetXMLValue( psEntry, "c3", "0" ));
401             sCEntry.c4 = (short) atoi(CPLGetXMLValue( psEntry, "c4", "255" ));
402 
403             oTable.SetColorEntry( iEntry++, &sCEntry );
404         }
405 
406         GDALPamRasterBand::SetColorTable( &oTable );
407     }
408 
409 /* -------------------------------------------------------------------- */
410 /*      Do we have a complete set of stats?                             */
411 /* -------------------------------------------------------------------- */
412     if( CPLGetXMLNode( psTree, "Minimum" ) != NULL
413         && CPLGetXMLNode( psTree, "Maximum" ) != NULL )
414     {
415         psPam->bHaveMinMax = TRUE;
416         psPam->dfMin = atof(CPLGetXMLValue(psTree, "Minimum","0"));
417         psPam->dfMax = atof(CPLGetXMLValue(psTree, "Maximum","0"));
418     }
419 
420     if( CPLGetXMLNode( psTree, "Mean" ) != NULL
421         && CPLGetXMLNode( psTree, "StandardDeviation" ) != NULL )
422     {
423         psPam->bHaveStats = TRUE;
424         psPam->dfMean = atof(CPLGetXMLValue(psTree, "Mean","0"));
425         psPam->dfStdDev = atof(CPLGetXMLValue(psTree,"StandardDeviation","0"));
426     }
427 
428 /* -------------------------------------------------------------------- */
429 /*      Histograms                                                      */
430 /* -------------------------------------------------------------------- */
431     CPLXMLNode *psHist = CPLGetXMLNode( psTree, "Histograms" );
432     if( psHist != NULL )
433     {
434         CPLXMLNode *psNext = psHist->psNext;
435         psHist->psNext = NULL;
436 
437         psPam->psSavedHistograms = CPLCloneXMLTree( psHist );
438         psHist->psNext = psNext;
439     }
440 
441 /* -------------------------------------------------------------------- */
442 /*      Raster Attribute Table                                          */
443 /* -------------------------------------------------------------------- */
444     CPLXMLNode *psRAT = CPLGetXMLNode( psTree, "GDALRasterAttributeTable" );
445     if( psRAT != NULL )
446     {
447         psPam->poDefaultRAT = new GDALRasterAttributeTable();
448         psPam->poDefaultRAT->XMLInit( psRAT, "" );
449     }
450 
451     return CE_None;
452 }
453 
454 /************************************************************************/
455 /*                             CloneInfo()                              */
456 /************************************************************************/
457 
CloneInfo(GDALRasterBand * poSrcBand,int nCloneFlags)458 CPLErr GDALPamRasterBand::CloneInfo( GDALRasterBand *poSrcBand,
459                                      int nCloneFlags )
460 
461 {
462     int bOnlyIfMissing = nCloneFlags & GCIF_ONLY_IF_MISSING;
463     int bSuccess;
464     int nSavedMOFlags = GetMOFlags();
465 
466     PamInitialize();
467 
468 /* -------------------------------------------------------------------- */
469 /*      Supress NotImplemented error messages - mainly needed if PAM    */
470 /*      disabled.                                                       */
471 /* -------------------------------------------------------------------- */
472     SetMOFlags( nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED );
473 
474 /* -------------------------------------------------------------------- */
475 /*      Metadata                                                        */
476 /* -------------------------------------------------------------------- */
477     if( nCloneFlags & GCIF_BAND_METADATA )
478     {
479         if( poSrcBand->GetMetadata() != NULL )
480         {
481             if( !bOnlyIfMissing
482              || CSLCount(GetMetadata()) != CSLCount(poSrcBand->GetMetadata()) )
483             {
484                 SetMetadata( poSrcBand->GetMetadata() );
485             }
486         }
487     }
488 
489 /* -------------------------------------------------------------------- */
490 /*      NODATA                                                          */
491 /* -------------------------------------------------------------------- */
492     if( nCloneFlags & GCIF_NODATA )
493     {
494         double dfNoData = poSrcBand->GetNoDataValue( &bSuccess );
495 
496         if( bSuccess )
497         {
498             if( !bOnlyIfMissing
499                 || GetNoDataValue( &bSuccess ) != dfNoData
500                 || !bSuccess )
501                 GDALPamRasterBand::SetNoDataValue( dfNoData );
502         }
503     }
504 
505 /* -------------------------------------------------------------------- */
506 /*      Offset/scale                                                    */
507 /* -------------------------------------------------------------------- */
508     if( nCloneFlags & GCIF_SCALEOFFSET )
509     {
510         double dfOffset = poSrcBand->GetOffset( &bSuccess );
511 
512         if( bSuccess )
513         {
514             if( !bOnlyIfMissing || GetOffset() != dfOffset )
515                 GDALPamRasterBand::SetOffset( dfOffset );
516         }
517 
518         double dfScale = poSrcBand->GetScale( &bSuccess );
519 
520         if( bSuccess )
521         {
522             if( !bOnlyIfMissing || GetScale() != dfScale )
523                 GDALPamRasterBand::SetScale( dfScale );
524         }
525     }
526 
527 /* -------------------------------------------------------------------- */
528 /*      Unittype.                                                       */
529 /* -------------------------------------------------------------------- */
530     if( nCloneFlags & GCIF_UNITTYPE )
531     {
532         if( strlen(poSrcBand->GetUnitType()) > 0 )
533         {
534             if( !bOnlyIfMissing
535                 || !EQUAL(GetUnitType(),poSrcBand->GetUnitType()) )
536             {
537                 GDALPamRasterBand::SetUnitType( poSrcBand->GetUnitType() );
538             }
539         }
540     }
541 
542 /* -------------------------------------------------------------------- */
543 /*      ColorInterp                                                     */
544 /* -------------------------------------------------------------------- */
545     if( nCloneFlags & GCIF_COLORINTERP )
546     {
547         if( poSrcBand->GetColorInterpretation() != GCI_Undefined )
548         {
549             if( !bOnlyIfMissing
550                 || poSrcBand->GetColorInterpretation()
551                 != GetColorInterpretation() )
552                 GDALPamRasterBand::SetColorInterpretation(
553                     poSrcBand->GetColorInterpretation() );
554         }
555     }
556 
557 /* -------------------------------------------------------------------- */
558 /*      color table.                                                    */
559 /* -------------------------------------------------------------------- */
560     if( nCloneFlags && GCIF_COLORTABLE )
561     {
562         if( poSrcBand->GetColorTable() != NULL )
563         {
564             if( !bOnlyIfMissing || GetColorTable() == NULL )
565             {
566                 GDALPamRasterBand::SetColorTable(
567                     poSrcBand->GetColorTable() );
568             }
569         }
570     }
571 
572 /* -------------------------------------------------------------------- */
573 /*      Raster Attribute Table.                                         */
574 /* -------------------------------------------------------------------- */
575     if( nCloneFlags && GCIF_RAT )
576     {
577         const GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT();
578 
579         if( poRAT != NULL )
580         {
581             if( !bOnlyIfMissing || GetDefaultRAT() == NULL )
582             {
583                 GDALPamRasterBand::SetDefaultRAT( poRAT );
584             }
585         }
586     }
587 
588 /* -------------------------------------------------------------------- */
589 /*      Restore MO flags.                                               */
590 /* -------------------------------------------------------------------- */
591     SetMOFlags( nSavedMOFlags );
592 
593     return CE_None;
594 }
595 
596 /************************************************************************/
597 /*                            SetMetadata()                             */
598 /************************************************************************/
599 
SetMetadata(char ** papszMetadata,const char * pszDomain)600 CPLErr GDALPamRasterBand::SetMetadata( char **papszMetadata,
601                                     const char *pszDomain )
602 
603 {
604     PamInitialize();
605 
606     if( psPam )
607         psPam->poParentDS->MarkPamDirty();
608 
609     return GDALRasterBand::SetMetadata( papszMetadata, pszDomain );
610 }
611 
612 /************************************************************************/
613 /*                          SetMetadataItem()                           */
614 /************************************************************************/
615 
SetMetadataItem(const char * pszName,const char * pszValue,const char * pszDomain)616 CPLErr GDALPamRasterBand::SetMetadataItem( const char *pszName,
617                                         const char *pszValue,
618                                         const char *pszDomain )
619 
620 {
621     PamInitialize();
622 
623     if( psPam )
624         psPam->poParentDS->MarkPamDirty();
625 
626     return GDALRasterBand::SetMetadataItem( pszName, pszValue, pszDomain );
627 }
628 
629 /************************************************************************/
630 /*                           SetNoDataValue()                           */
631 /************************************************************************/
632 
SetNoDataValue(double dfNewValue)633 CPLErr GDALPamRasterBand::SetNoDataValue( double dfNewValue )
634 
635 {
636     PamInitialize();
637 
638     if( psPam )
639     {
640         psPam->bNoDataValueSet = TRUE;
641         psPam->dfNoDataValue = dfNewValue;
642         psPam->poParentDS->MarkPamDirty();
643         return CE_None;
644     }
645     else
646         return GDALRasterBand::SetNoDataValue( dfNewValue );
647 }
648 
649 /************************************************************************/
650 /*                           GetNoDataValue()                           */
651 /************************************************************************/
652 
GetNoDataValue(int * pbSuccess)653 double GDALPamRasterBand::GetNoDataValue( int *pbSuccess )
654 
655 {
656     if( psPam != NULL )
657     {
658         if( pbSuccess )
659             *pbSuccess = psPam->bNoDataValueSet;
660 
661         return psPam->dfNoDataValue;
662     }
663     else
664         return GDALRasterBand::GetNoDataValue( pbSuccess );
665 }
666 
667 /************************************************************************/
668 /*                             GetOffset()                              */
669 /************************************************************************/
670 
GetOffset(int * pbSuccess)671 double GDALPamRasterBand::GetOffset( int *pbSuccess )
672 
673 {
674     if( psPam )
675     {
676         if( pbSuccess != NULL )
677             *pbSuccess = TRUE;
678 
679         return psPam->dfOffset;
680     }
681     else
682         return GDALRasterBand::GetOffset( pbSuccess );
683 }
684 
685 /************************************************************************/
686 /*                             SetOffset()                              */
687 /************************************************************************/
688 
SetOffset(double dfNewOffset)689 CPLErr GDALPamRasterBand::SetOffset( double dfNewOffset )
690 
691 {
692     PamInitialize();
693 
694     if( psPam != NULL )
695     {
696         psPam->dfOffset = dfNewOffset;
697         psPam->poParentDS->MarkPamDirty();
698 
699         return CE_None;
700     }
701     else
702         return GDALRasterBand::SetOffset( dfNewOffset );
703 }
704 
705 /************************************************************************/
706 /*                              GetScale()                              */
707 /************************************************************************/
708 
GetScale(int * pbSuccess)709 double GDALPamRasterBand::GetScale( int *pbSuccess )
710 
711 {
712     if( psPam )
713     {
714         if( pbSuccess != NULL )
715             *pbSuccess = TRUE;
716 
717         return psPam->dfScale;
718     }
719     else
720         return GDALRasterBand::GetScale( pbSuccess );
721 }
722 
723 /************************************************************************/
724 /*                              SetScale()                              */
725 /************************************************************************/
726 
SetScale(double dfNewScale)727 CPLErr GDALPamRasterBand::SetScale( double dfNewScale )
728 
729 {
730     PamInitialize();
731 
732     if( psPam != NULL )
733     {
734         psPam->dfScale = dfNewScale;
735         psPam->poParentDS->MarkPamDirty();
736         return CE_None;
737     }
738     else
739         return GDALRasterBand::SetScale( dfNewScale );
740 }
741 
742 /************************************************************************/
743 /*                            GetUnitType()                             */
744 /************************************************************************/
745 
GetUnitType()746 const char *GDALPamRasterBand::GetUnitType()
747 
748 {
749     if( psPam != NULL )
750     {
751         if( psPam->pszUnitType == NULL )
752             return "";
753         else
754             return psPam->pszUnitType;
755     }
756     else
757         return GDALRasterBand::GetUnitType();
758 }
759 
760 /************************************************************************/
761 /*                            SetUnitType()                             */
762 /************************************************************************/
763 
SetUnitType(const char * pszNewValue)764 CPLErr GDALPamRasterBand::SetUnitType( const char *pszNewValue )
765 
766 {
767     PamInitialize();
768 
769     if( psPam )
770     {
771         CPLFree( psPam->pszUnitType );
772 
773         if( pszNewValue == NULL )
774             psPam->pszUnitType = NULL;
775         else
776             psPam->pszUnitType = CPLStrdup(pszNewValue);
777 
778         return CE_None;
779     }
780     else
781         return GDALRasterBand::SetUnitType( pszNewValue );
782 }
783 
784 /************************************************************************/
785 /*                          GetCategoryNames()                          */
786 /************************************************************************/
787 
GetCategoryNames()788 char **GDALPamRasterBand::GetCategoryNames()
789 
790 {
791     if( psPam )
792         return psPam->papszCategoryNames;
793     else
794         return GDALRasterBand::GetCategoryNames();
795 }
796 
797 /************************************************************************/
798 /*                          SetCategoryNames()                          */
799 /************************************************************************/
800 
SetCategoryNames(char ** papszNewNames)801 CPLErr GDALPamRasterBand::SetCategoryNames( char ** papszNewNames )
802 
803 {
804     PamInitialize();
805 
806     if( psPam )
807     {
808         CSLDestroy( psPam->papszCategoryNames );
809         psPam->papszCategoryNames = CSLDuplicate( papszNewNames );
810         psPam->poParentDS->MarkPamDirty();
811         return CE_None;
812     }
813     else
814         return GDALRasterBand::SetCategoryNames( papszNewNames );
815 
816 }
817 
818 
819 /************************************************************************/
820 /*                           GetColorTable()                            */
821 /************************************************************************/
822 
GetColorTable()823 GDALColorTable *GDALPamRasterBand::GetColorTable()
824 
825 {
826     if( psPam )
827         return psPam->poColorTable;
828     else
829         return GDALRasterBand::GetColorTable();
830 }
831 
832 /************************************************************************/
833 /*                           SetColorTable()                            */
834 /************************************************************************/
835 
SetColorTable(GDALColorTable * poTableIn)836 CPLErr GDALPamRasterBand::SetColorTable( GDALColorTable *poTableIn )
837 
838 {
839     PamInitialize();
840 
841     if( psPam )
842     {
843         if( psPam->poColorTable != NULL )
844         {
845             delete psPam->poColorTable;
846             psPam->poColorTable = NULL;
847         }
848 
849         if( poTableIn )
850         {
851             psPam->poColorTable = poTableIn->Clone();
852             psPam->eColorInterp = GCI_PaletteIndex;
853         }
854 
855         psPam->poParentDS->MarkPamDirty();
856 
857         return CE_None;
858     }
859     else
860         return GDALRasterBand::SetColorTable( poTableIn );
861 
862 }
863 
864 /************************************************************************/
865 /*                       SetColorInterpretation()                       */
866 /************************************************************************/
867 
SetColorInterpretation(GDALColorInterp eInterpIn)868 CPLErr GDALPamRasterBand::SetColorInterpretation( GDALColorInterp eInterpIn )
869 
870 {
871     PamInitialize();
872 
873     if( psPam )
874     {
875         psPam->poParentDS->MarkPamDirty();
876 
877         psPam->eColorInterp = eInterpIn;
878 
879         return CE_None;
880     }
881     else
882         return GDALRasterBand::SetColorInterpretation( eInterpIn );
883 }
884 
885 /************************************************************************/
886 /*                       GetColorInterpretation()                       */
887 /************************************************************************/
888 
GetColorInterpretation()889 GDALColorInterp GDALPamRasterBand::GetColorInterpretation()
890 
891 {
892     if( psPam )
893         return psPam->eColorInterp;
894     else
895         return GDALRasterBand::GetColorInterpretation();
896 }
897 
898 /************************************************************************/
899 /*                         PamParseHistogram()                          */
900 /************************************************************************/
901 
902 int
PamParseHistogram(CPLXMLNode * psHistItem,double * pdfMin,double * pdfMax,int * pnBuckets,int ** ppanHistogram,int * pbIncludeOutOfRange,int * pbApproxOK)903 PamParseHistogram( CPLXMLNode *psHistItem,
904                    double *pdfMin, double *pdfMax,
905                    int *pnBuckets, int **ppanHistogram,
906                    int *pbIncludeOutOfRange, int *pbApproxOK )
907 
908 {
909     if( psHistItem == NULL )
910         return FALSE;
911 
912     *pdfMin = atof(CPLGetXMLValue( psHistItem, "HistMin", "0"));
913     *pdfMax = atof(CPLGetXMLValue( psHistItem, "HistMax", "1"));
914     *pnBuckets = atoi(CPLGetXMLValue( psHistItem, "BucketCount","2"));
915 
916     if( ppanHistogram == NULL )
917         return TRUE;
918 
919     // Fetch the histogram and use it.
920     int iBucket;
921     const char *pszHistCounts = CPLGetXMLValue( psHistItem,
922                                                 "HistCounts", "" );
923 
924     *ppanHistogram = (int *) CPLCalloc(sizeof(int),*pnBuckets);
925 
926     for( iBucket = 0; iBucket < *pnBuckets; iBucket++ )
927     {
928         (*ppanHistogram)[iBucket] = atoi(pszHistCounts);
929 
930         // skip to next number.
931         while( *pszHistCounts != '\0' && *pszHistCounts != '|' )
932             pszHistCounts++;
933         if( *pszHistCounts == '|' )
934             pszHistCounts++;
935     }
936 
937     return TRUE;
938 }
939 
940 /************************************************************************/
941 /*                      PamFindMatchingHistogram()                      */
942 /************************************************************************/
943 CPLXMLNode *
PamFindMatchingHistogram(CPLXMLNode * psSavedHistograms,double dfMin,double dfMax,int nBuckets,int bIncludeOutOfRange,int bApproxOK)944 PamFindMatchingHistogram( CPLXMLNode *psSavedHistograms,
945                           double dfMin, double dfMax, int nBuckets,
946                           int bIncludeOutOfRange, int bApproxOK )
947 
948 {
949     if( psSavedHistograms == NULL )
950         return NULL;
951 
952     CPLXMLNode *psXMLHist;
953     for( psXMLHist = psSavedHistograms->psChild;
954          psXMLHist != NULL; psXMLHist = psXMLHist->psNext )
955     {
956         if( psXMLHist->eType != CXT_Element
957             || !EQUAL(psXMLHist->pszValue,"HistItem") )
958             continue;
959 
960         // should try and make min/max test a bit fuzzy.
961 
962         if( atof(CPLGetXMLValue( psXMLHist, "HistMin", "0")) != dfMin
963             || atof(CPLGetXMLValue( psXMLHist, "HistMax", "0")) != dfMax
964             || atoi(CPLGetXMLValue( psXMLHist,
965                                     "BucketCount","0")) != nBuckets
966             || !atoi(CPLGetXMLValue( psXMLHist,
967                                      "IncludeOutOfRange","0")) != !bIncludeOutOfRange
968             || (!bApproxOK && atoi(CPLGetXMLValue( psXMLHist,
969                                                    "Approximate","0"))) )
970 
971             continue;
972 
973         return psXMLHist;
974     }
975 
976     return NULL;
977 }
978 
979 /************************************************************************/
980 /*                       PamHistogramToXMLTree()                        */
981 /************************************************************************/
982 
983 CPLXMLNode *
PamHistogramToXMLTree(double dfMin,double dfMax,int nBuckets,int * panHistogram,int bIncludeOutOfRange,int bApprox)984 PamHistogramToXMLTree( double dfMin, double dfMax,
985                        int nBuckets, int * panHistogram,
986                        int bIncludeOutOfRange, int bApprox )
987 
988 {
989     char *pszHistCounts = (char *) CPLMalloc(12 * nBuckets + 10);
990     int iBucket, iHistOffset;
991     CPLXMLNode *psXMLHist;
992     CPLString oFmt;
993 
994     psXMLHist = CPLCreateXMLNode( NULL, CXT_Element, "HistItem" );
995 
996     CPLSetXMLValue( psXMLHist, "HistMin",
997                     oFmt.Printf( "%.16g", dfMin ));
998     CPLSetXMLValue( psXMLHist, "HistMax",
999                     oFmt.Printf( "%.16g", dfMax ));
1000     CPLSetXMLValue( psXMLHist, "BucketCount",
1001                     oFmt.Printf( "%d", nBuckets ));
1002     CPLSetXMLValue( psXMLHist, "IncludeOutOfRange",
1003                     oFmt.Printf( "%d", bIncludeOutOfRange ));
1004     CPLSetXMLValue( psXMLHist, "Approximate",
1005                     oFmt.Printf( "%d", bApprox ));
1006 
1007     iHistOffset = 0;
1008     pszHistCounts[0] = '\0';
1009     for( iBucket = 0; iBucket < nBuckets; iBucket++ )
1010     {
1011         sprintf( pszHistCounts + iHistOffset, "%d", panHistogram[iBucket] );
1012         if( iBucket < nBuckets-1 )
1013             strcat( pszHistCounts + iHistOffset, "|" );
1014         iHistOffset += strlen(pszHistCounts+iHistOffset);
1015     }
1016 
1017     CPLSetXMLValue( psXMLHist, "HistCounts", pszHistCounts );
1018     CPLFree( pszHistCounts );
1019 
1020     return psXMLHist;
1021 }
1022 
1023 /************************************************************************/
1024 /*                            GetHistogram()                            */
1025 /************************************************************************/
1026 
GetHistogram(double dfMin,double dfMax,int nBuckets,int * panHistogram,int bIncludeOutOfRange,int bApproxOK,GDALProgressFunc pfnProgress,void * pProgressData)1027 CPLErr GDALPamRasterBand::GetHistogram( double dfMin, double dfMax,
1028                                         int nBuckets, int * panHistogram,
1029                                         int bIncludeOutOfRange, int bApproxOK,
1030                                         GDALProgressFunc pfnProgress,
1031                                         void *pProgressData )
1032 
1033 {
1034     PamInitialize();
1035 
1036     if( psPam == NULL )
1037         return GDALRasterBand::GetHistogram( dfMin, dfMax,
1038                                              nBuckets, panHistogram,
1039                                              bIncludeOutOfRange, bApproxOK,
1040                                              pfnProgress, pProgressData );
1041 
1042 /* -------------------------------------------------------------------- */
1043 /*      Check if we have a matching histogram.                          */
1044 /* -------------------------------------------------------------------- */
1045     CPLXMLNode *psHistItem;
1046 
1047     psHistItem = PamFindMatchingHistogram( psPam->psSavedHistograms,
1048                                            dfMin, dfMax, nBuckets,
1049                                            bIncludeOutOfRange, bApproxOK );
1050     if( psHistItem != NULL )
1051     {
1052         int *panTempHist = NULL;
1053 
1054         if( PamParseHistogram( psHistItem, &dfMin, &dfMax, &nBuckets,
1055                                &panTempHist,
1056                                &bIncludeOutOfRange, &bApproxOK ) )
1057         {
1058             memcpy( panHistogram, panTempHist, sizeof(int) * nBuckets );
1059             CPLFree( panTempHist );
1060             return CE_None;
1061         }
1062     }
1063 
1064 /* -------------------------------------------------------------------- */
1065 /*      We don't have an existing histogram matching the request, so    */
1066 /*      generate one manually.                                          */
1067 /* -------------------------------------------------------------------- */
1068     CPLErr eErr;
1069 
1070     eErr = GDALRasterBand::GetHistogram( dfMin, dfMax,
1071                                          nBuckets, panHistogram,
1072                                          bIncludeOutOfRange, bApproxOK,
1073                                          pfnProgress, pProgressData );
1074 
1075 /* -------------------------------------------------------------------- */
1076 /*      Save an XML description of this histogram.                      */
1077 /* -------------------------------------------------------------------- */
1078     if( eErr == CE_None )
1079     {
1080         CPLXMLNode *psXMLHist;
1081 
1082         psXMLHist = PamHistogramToXMLTree( dfMin, dfMax, nBuckets,
1083                                            panHistogram,
1084                                            bIncludeOutOfRange, bApproxOK );
1085         if( psXMLHist != NULL )
1086         {
1087             psPam->poParentDS->MarkPamDirty();
1088 
1089             if( psPam->psSavedHistograms == NULL )
1090                 psPam->psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element,
1091                                                              "Histograms" );
1092 
1093             CPLAddXMLChild( psPam->psSavedHistograms, psXMLHist );
1094         }
1095     }
1096 
1097     return eErr;
1098 }
1099 
1100 /************************************************************************/
1101 /*                        SetDefaultHistogram()                         */
1102 /************************************************************************/
1103 
SetDefaultHistogram(double dfMin,double dfMax,int nBuckets,int * panHistogram)1104 CPLErr GDALPamRasterBand::SetDefaultHistogram( double dfMin, double dfMax,
1105                                                int nBuckets, int *panHistogram)
1106 
1107 {
1108     CPLXMLNode *psNode;
1109 
1110     PamInitialize();
1111 
1112     if( psPam == NULL )
1113         return GDALRasterBand::SetDefaultHistogram( dfMin, dfMax,
1114                                                     nBuckets, panHistogram );
1115 
1116 /* -------------------------------------------------------------------- */
1117 /*      Do we have a matching histogram we should replace?              */
1118 /* -------------------------------------------------------------------- */
1119     psNode = PamFindMatchingHistogram( psPam->psSavedHistograms,
1120                                        dfMin, dfMax, nBuckets,
1121                                        TRUE, TRUE );
1122     if( psNode != NULL )
1123     {
1124         /* blow this one away */
1125         CPLRemoveXMLChild( psPam->psSavedHistograms, psNode );
1126         CPLDestroyXMLNode( psNode );
1127     }
1128 
1129 /* -------------------------------------------------------------------- */
1130 /*      Translate into a histogram XML tree.                            */
1131 /* -------------------------------------------------------------------- */
1132     CPLXMLNode *psHistItem;
1133 
1134     psHistItem = PamHistogramToXMLTree( dfMin, dfMax, nBuckets,
1135                                         panHistogram, TRUE, FALSE );
1136 
1137 /* -------------------------------------------------------------------- */
1138 /*      Insert our new default histogram at the front of the            */
1139 /*      histogram list so that it will be the default histogram.        */
1140 /* -------------------------------------------------------------------- */
1141     psPam->poParentDS->MarkPamDirty();
1142 
1143     if( psPam->psSavedHistograms == NULL )
1144         psPam->psSavedHistograms = CPLCreateXMLNode( NULL, CXT_Element,
1145                                                      "Histograms" );
1146 
1147     psHistItem->psNext = psPam->psSavedHistograms->psChild;
1148     psPam->psSavedHistograms->psChild = psHistItem;
1149 
1150     return CE_None;
1151 }
1152 
1153 /************************************************************************/
1154 /*                        GetDefaultHistogram()                         */
1155 /************************************************************************/
1156 
1157 CPLErr
GetDefaultHistogram(double * pdfMin,double * pdfMax,int * pnBuckets,int ** ppanHistogram,int bForce,GDALProgressFunc pfnProgress,void * pProgressData)1158 GDALPamRasterBand::GetDefaultHistogram( double *pdfMin, double *pdfMax,
1159                                         int *pnBuckets, int **ppanHistogram,
1160                                         int bForce,
1161                                         GDALProgressFunc pfnProgress,
1162                                         void *pProgressData )
1163 
1164 {
1165     if( psPam && psPam->psSavedHistograms != NULL )
1166     {
1167         CPLXMLNode *psXMLHist;
1168 
1169         for( psXMLHist = psPam->psSavedHistograms->psChild;
1170              psXMLHist != NULL; psXMLHist = psXMLHist->psNext )
1171         {
1172             int bApprox, bIncludeOutOfRange;
1173 
1174             if( psXMLHist->eType != CXT_Element
1175                 || !EQUAL(psXMLHist->pszValue,"HistItem") )
1176                 continue;
1177 
1178             if( PamParseHistogram( psXMLHist, pdfMin, pdfMax, pnBuckets,
1179                                    ppanHistogram, &bIncludeOutOfRange,
1180                                    &bApprox ) )
1181                 return CE_None;
1182             else
1183                 return CE_Failure;
1184         }
1185     }
1186 
1187     return GDALRasterBand::GetDefaultHistogram( pdfMin, pdfMax, pnBuckets,
1188                                                 ppanHistogram, bForce,
1189                                                 pfnProgress, pProgressData );
1190 }
1191 
1192 /************************************************************************/
1193 /*                           GetDefaultRAT()                            */
1194 /************************************************************************/
1195 
GetDefaultRAT()1196 const GDALRasterAttributeTable *GDALPamRasterBand::GetDefaultRAT()
1197 
1198 {
1199     PamInitialize();
1200 
1201     if( psPam == NULL )
1202         return GDALRasterBand::GetDefaultRAT();
1203 
1204     return psPam->poDefaultRAT;
1205 }
1206 
1207 /************************************************************************/
1208 /*                           SetDefaultRAT()                            */
1209 /************************************************************************/
1210 
SetDefaultRAT(const GDALRasterAttributeTable * poRAT)1211 CPLErr GDALPamRasterBand::SetDefaultRAT( const GDALRasterAttributeTable *poRAT)
1212 
1213 {
1214     PamInitialize();
1215 
1216     if( psPam == NULL )
1217         return GDALRasterBand::SetDefaultRAT( poRAT );
1218 
1219     psPam->poParentDS->MarkPamDirty();
1220 
1221     if( psPam->poDefaultRAT != NULL )
1222     {
1223         delete psPam->poDefaultRAT;
1224         psPam->poDefaultRAT = NULL;
1225     }
1226 
1227     if( poRAT == NULL )
1228         psPam->poDefaultRAT = NULL;
1229     else
1230         psPam->poDefaultRAT = poRAT->Clone();
1231 
1232     return CE_None;
1233 }
1234 
1235