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