LCOV - code coverage report
Current view: top level - gcore - gdalpamrasterband.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 592 667 88.8 %
Date: 2024-11-21 22:18:42 Functions: 42 43 97.7 %

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

Generated by: LCOV version 1.14