Coverage Report

Created: 2025-06-13 06:18

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