LCOV - code coverage report
Current view: top level - gcore - gdalpamrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 553 635 87.1 %
Date: 2024-05-04 12:52:34 Functions: 41 42 97.6 %

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

Generated by: LCOV version 1.14