• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.46
/gcore/gdalpamrasterband.cpp
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)
142✔
44
{
45
    bNoDataValueSet = sOther.bNoDataValueSet;
142✔
46
    bNoDataValueSetAsInt64 = sOther.bNoDataValueSetAsInt64;
142✔
47
    bNoDataValueSetAsUInt64 = sOther.bNoDataValueSetAsUInt64;
142✔
48

49
    dfNoDataValue = sOther.dfNoDataValue;
142✔
50
    nNoDataValueInt64 = sOther.nNoDataValueInt64;
142✔
51
    nNoDataValueUInt64 = sOther.nNoDataValueUInt64;
142✔
52

53
    delete poColorTable;
142✔
54
    poColorTable = sOther.poColorTable
284✔
55
                       ? new GDALColorTable(*(sOther.poColorTable))
142✔
56
                       : nullptr;
57

58
    eColorInterp = sOther.eColorInterp;
142✔
59

60
    CPLFree(pszUnitType);
142✔
61
    pszUnitType = sOther.pszUnitType ? CPLStrdup(sOther.pszUnitType) : nullptr;
142✔
62

63
    CSLDestroy(papszCategoryNames);
142✔
64
    papszCategoryNames = CSLDuplicate(sOther.papszCategoryNames);
142✔
65

66
    dfOffset = sOther.dfOffset;
142✔
67
    dfScale = sOther.dfScale;
142✔
68

69
    bHaveMinMax = sOther.bHaveMinMax;
142✔
70
    dfMin = sOther.dfMin;
142✔
71
    dfMax = sOther.dfMax;
142✔
72

73
    bHaveStats = sOther.bHaveStats;
142✔
74
    dfMean = sOther.dfMean;
142✔
75
    dfStdDev = sOther.dfStdDev;
142✔
76

77
    if (psSavedHistograms)
142✔
78
        CPLDestroyXMLNode(psSavedHistograms);
×
79
    psSavedHistograms = sOther.psSavedHistograms
284✔
80
                            ? CPLCloneXMLTree(sOther.psSavedHistograms)
142✔
81
                            : nullptr;
82

83
    delete poDefaultRAT;
142✔
84
    poDefaultRAT = sOther.poDefaultRAT ? sOther.poDefaultRAT->Clone() : nullptr;
142✔
85

86
    bOffsetSet = sOther.bOffsetSet;
142✔
87
    bScaleSet = sOther.bScaleSet;
142✔
88
}
142✔
89

90
//! @endcond
91

92
/************************************************************************/
93
/*                         GDALPamRasterBand()                          */
94
/************************************************************************/
95

96
GDALPamRasterBand::GDALPamRasterBand()
1,383,680✔
97

98
{
99
    SetMOFlags(GetMOFlags() | GMO_PAM_CLASS);
1,383,670✔
100
}
1,383,680✔
101

102
/************************************************************************/
103
/*                         GDALPamRasterBand()                          */
104
/************************************************************************/
105

106
//! @cond Doxygen_Suppress
107
GDALPamRasterBand::GDALPamRasterBand(int bForceCachedIOIn)
111,736✔
108
    : GDALRasterBand(bForceCachedIOIn)
111,736✔
109
{
110
    SetMOFlags(GetMOFlags() | GMO_PAM_CLASS);
111,714✔
111
}
111,714✔
112

113
//! @endcond
114

115
/************************************************************************/
116
/*                         ~GDALPamRasterBand()                         */
117
/************************************************************************/
118

119
GDALPamRasterBand::~GDALPamRasterBand()
1,495,430✔
120

121
{
122
    PamClear();
1,495,430✔
123
}
1,495,430✔
124

125
/************************************************************************/
126
/*                           SerializeToXML()                           */
127
/************************************************************************/
128

129
//! @cond Doxygen_Suppress
130
CPLXMLNode *GDALPamRasterBand::SerializeToXML(const char * /* pszUnused */)
2,284✔
131
{
132
    if (psPam == nullptr)
2,284✔
133
        return nullptr;
226✔
134

135
    /* -------------------------------------------------------------------- */
136
    /*      Setup root node and attributes.                                 */
137
    /* -------------------------------------------------------------------- */
138
    CPLXMLNode *psTree =
139
        CPLCreateXMLNode(nullptr, CXT_Element, "PAMRasterBand");
2,058✔
140

141
    CPLString oFmt;
2,058✔
142
    if (GetBand() > 0)
2,058✔
143
        CPLSetXMLValue(psTree, "#band", oFmt.Printf("%d", GetBand()));
2,058✔
144

145
    /* -------------------------------------------------------------------- */
146
    /*      Serialize information of interest.                              */
147
    /* -------------------------------------------------------------------- */
148
    if (strlen(GetDescription()) > 0)
2,058✔
149
        CPLSetXMLValue(psTree, "Description", GetDescription());
151✔
150

151
    if (psPam->bNoDataValueSet)
2,058✔
152
    {
153
        if (std::isnan(psPam->dfNoDataValue))
31✔
154
            CPLSetXMLValue(psTree, "NoDataValue", "nan");
4✔
155
        else
156
            CPLSetXMLValue(psTree, "NoDataValue",
27✔
157
                           oFmt.Printf("%.14E", psPam->dfNoDataValue));
27✔
158

159
        // Hex encode real floating point values.
160
        if (psPam->dfNoDataValue != floor(psPam->dfNoDataValue) ||
58✔
161
            psPam->dfNoDataValue != CPLAtof(oFmt))
27✔
162
        {
163
            double dfNoDataLittleEndian = psPam->dfNoDataValue;
5✔
164
            CPL_LSBPTR64(&dfNoDataLittleEndian);
5✔
165

166
            char *pszHexEncoding = CPLBinaryToHex(
5✔
167
                8, reinterpret_cast<GByte *>(&dfNoDataLittleEndian));
168
            CPLSetXMLValue(psTree, "NoDataValue.#le_hex_equiv", pszHexEncoding);
5✔
169
            CPLFree(pszHexEncoding);
5✔
170
        }
171
    }
172
    else if (psPam->bNoDataValueSetAsInt64)
2,027✔
173
    {
174
        CPLSetXMLValue(
1✔
175
            psTree, "NoDataValue",
176
            oFmt.Printf(CPL_FRMT_GIB,
177
                        static_cast<GIntBig>(psPam->nNoDataValueInt64)));
1✔
178
    }
179
    else if (psPam->bNoDataValueSetAsUInt64)
2,026✔
180
    {
181
        CPLSetXMLValue(
1✔
182
            psTree, "NoDataValue",
183
            oFmt.Printf(CPL_FRMT_GUIB,
184
                        static_cast<GUIntBig>(psPam->nNoDataValueUInt64)));
1✔
185
    }
186

187
    if (psPam->pszUnitType != nullptr)
2,058✔
188
        CPLSetXMLValue(psTree, "UnitType", psPam->pszUnitType);
26✔
189

190
    if (psPam->dfOffset != 0.0)
2,058✔
191
        CPLSetXMLValue(psTree, "Offset", oFmt.Printf("%.16g", psPam->dfOffset));
5✔
192

193
    if (psPam->dfScale != 1.0)
2,058✔
194
        CPLSetXMLValue(psTree, "Scale", oFmt.Printf("%.16g", psPam->dfScale));
5✔
195

196
    if (psPam->eColorInterp != GCI_Undefined)
2,058✔
197
        CPLSetXMLValue(psTree, "ColorInterp",
175✔
198
                       GDALGetColorInterpretationName(psPam->eColorInterp));
175✔
199

200
    /* -------------------------------------------------------------------- */
201
    /*      Category names.                                                 */
202
    /* -------------------------------------------------------------------- */
203
    if (psPam->papszCategoryNames != nullptr)
2,058✔
204
    {
205
        CPLXMLNode *psCT_XML =
206
            CPLCreateXMLNode(psTree, CXT_Element, "CategoryNames");
1✔
207
        CPLXMLNode *psLastChild = nullptr;
1✔
208

209
        for (int iEntry = 0; psPam->papszCategoryNames[iEntry] != nullptr;
3✔
210
             iEntry++)
211
        {
212
            CPLXMLNode *psNode = CPLCreateXMLElementAndValue(
4✔
213
                nullptr, "Category", psPam->papszCategoryNames[iEntry]);
2✔
214
            if (psLastChild == nullptr)
2✔
215
                psCT_XML->psChild = psNode;
1✔
216
            else
217
                psLastChild->psNext = psNode;
1✔
218
            psLastChild = psNode;
2✔
219
        }
220
    }
221

222
    /* -------------------------------------------------------------------- */
223
    /*      Color Table.                                                    */
224
    /* -------------------------------------------------------------------- */
225
    if (psPam->poColorTable != nullptr)
2,058✔
226
    {
227
        CPLXMLNode *psCT_XML =
228
            CPLCreateXMLNode(psTree, CXT_Element, "ColorTable");
4✔
229
        CPLXMLNode *psLastChild = nullptr;
4✔
230

231
        for (int iEntry = 0; iEntry < psPam->poColorTable->GetColorEntryCount();
8✔
232
             iEntry++)
233
        {
234
            CPLXMLNode *psEntry_XML =
235
                CPLCreateXMLNode(nullptr, CXT_Element, "Entry");
4✔
236
            if (psLastChild == nullptr)
4✔
237
                psCT_XML->psChild = psEntry_XML;
3✔
238
            else
239
                psLastChild->psNext = psEntry_XML;
1✔
240
            psLastChild = psEntry_XML;
4✔
241

242
            GDALColorEntry sEntry;
243
            psPam->poColorTable->GetColorEntryAsRGB(iEntry, &sEntry);
4✔
244

245
            CPLSetXMLValue(psEntry_XML, "#c1", oFmt.Printf("%d", sEntry.c1));
4✔
246
            CPLSetXMLValue(psEntry_XML, "#c2", oFmt.Printf("%d", sEntry.c2));
4✔
247
            CPLSetXMLValue(psEntry_XML, "#c3", oFmt.Printf("%d", sEntry.c3));
4✔
248
            CPLSetXMLValue(psEntry_XML, "#c4", oFmt.Printf("%d", sEntry.c4));
4✔
249
        }
250
    }
251

252
    /* -------------------------------------------------------------------- */
253
    /*      Min/max.                                                        */
254
    /* -------------------------------------------------------------------- */
255
    if (psPam->bHaveMinMax)
2,058✔
256
    {
257
        CPLSetXMLValue(psTree, "Minimum", oFmt.Printf("%.16g", psPam->dfMin));
×
258
        CPLSetXMLValue(psTree, "Maximum", oFmt.Printf("%.16g", psPam->dfMax));
×
259
    }
260

261
    /* -------------------------------------------------------------------- */
262
    /*      Statistics                                                      */
263
    /* -------------------------------------------------------------------- */
264
    if (psPam->bHaveStats)
2,058✔
265
    {
266
        CPLSetXMLValue(psTree, "Mean", oFmt.Printf("%.16g", psPam->dfMean));
×
267
        CPLSetXMLValue(psTree, "StandardDeviation",
×
268
                       oFmt.Printf("%.16g", psPam->dfStdDev));
×
269
    }
270

271
    /* -------------------------------------------------------------------- */
272
    /*      Histograms.                                                     */
273
    /* -------------------------------------------------------------------- */
274
    if (psPam->psSavedHistograms != nullptr)
2,058✔
275
        CPLAddXMLChild(psTree, CPLCloneXMLTree(psPam->psSavedHistograms));
22✔
276

277
    /* -------------------------------------------------------------------- */
278
    /*      Raster Attribute Table                                          */
279
    /* -------------------------------------------------------------------- */
280
    if (psPam->poDefaultRAT != nullptr)
2,058✔
281
    {
282
        CPLXMLNode *psSerializedRAT = psPam->poDefaultRAT->Serialize();
7✔
283
        if (psSerializedRAT != nullptr)
7✔
284
            CPLAddXMLChild(psTree, psSerializedRAT);
6✔
285
    }
286

287
    /* -------------------------------------------------------------------- */
288
    /*      Metadata.                                                       */
289
    /* -------------------------------------------------------------------- */
290
    CPLXMLNode *psMD = oMDMD.Serialize();
2,058✔
291
    if (psMD != nullptr)
2,058✔
292
    {
293
        CPLAddXMLChild(psTree, psMD);
433✔
294
    }
295

296
    /* -------------------------------------------------------------------- */
297
    /*      We don't want to return anything if we had no metadata to       */
298
    /*      attach.                                                         */
299
    /* -------------------------------------------------------------------- */
300
    if (psTree->psChild == nullptr || psTree->psChild->psNext == nullptr)
2,058✔
301
    {
302
        CPLDestroyXMLNode(psTree);
1,383✔
303
        psTree = nullptr;
1,383✔
304
    }
305

306
    return psTree;
2,058✔
307
}
308

309
/************************************************************************/
310
/*                           PamInitialize()                            */
311
/************************************************************************/
312

313
void GDALPamRasterBand::PamInitialize()
775,303✔
314

315
{
316
    if (psPam != nullptr && psPam->poParentDS != nullptr)
775,303✔
317
        return;
35,945✔
318

319
    GDALDataset *poNonPamParentDS = GetDataset();
739,358✔
320
    if (poNonPamParentDS == nullptr ||
1,478,570✔
321
        !(poNonPamParentDS->GetMOFlags() & GMO_PAM_CLASS))
739,216✔
322
        return;
88,796✔
323

324
    GDALPamDataset *poParentDS =
325
        dynamic_cast<GDALPamDataset *>(poNonPamParentDS);
650,562✔
326
    if (poParentDS == nullptr)
650,562✔
327
    {
328
        // Should never happen.
329
        CPLError(CE_Failure, CPLE_AppDefined,
×
330
                 "Programming error: found GDALPamRasterBand that is not "
331
                 "attached to a GDALPamDataset.");
332
        return;
×
333
    }
334

335
    if (psPam != nullptr /* && psPam->poParentDS == nullptr */)
650,562✔
336
    {
337
        // We can get here if PamInitializeNoParent() was first called.
338
        delete psPam;
×
339
        psPam = nullptr;
×
340
    }
341

342
    poParentDS->PamInitialize();
650,562✔
343
    if (poParentDS->psPam == nullptr)
650,563✔
344
        return;
×
345

346
    // Often (always?) initializing our parent will have initialized us.
347
    if (psPam != nullptr)
650,563✔
348
        return;
798✔
349

350
    psPam = new (std::nothrow) GDALRasterBandPamInfo();
1,299,530✔
351
    if (psPam == nullptr)
649,764✔
352
        return;
×
353
    psPam->poParentDS = poParentDS;
649,764✔
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
void GDALPamRasterBand::PamInitializeNoParent()
111,731✔
363
{
364
    if (psPam == nullptr)
111,731✔
365
        psPam = new (std::nothrow) GDALRasterBandPamInfo();
223,445✔
366
}
111,720✔
367

368
/************************************************************************/
369
/*                            MarkPamDirty()                            */
370
/************************************************************************/
371

372
void GDALPamRasterBand::MarkPamDirty()
21,330✔
373
{
374
    if (psPam != nullptr && psPam->poParentDS != nullptr)
21,330✔
375
        psPam->poParentDS->MarkPamDirty();
16,619✔
376
}
21,330✔
377

378
/************************************************************************/
379
/*                              PamClear()                              */
380
/************************************************************************/
381

382
void GDALPamRasterBand::PamClear()
1,495,430✔
383

384
{
385
    if (!psPam)
1,495,430✔
386
        return;
733,921✔
387

388
    if (psPam->poColorTable)
761,509✔
389
        delete psPam->poColorTable;
72✔
390
    psPam->poColorTable = nullptr;
761,509✔
391

392
    CPLFree(psPam->pszUnitType);
761,509✔
393
    CSLDestroy(psPam->papszCategoryNames);
761,509✔
394

395
    if (psPam->poDefaultRAT != nullptr)
761,509✔
396
    {
397
        delete psPam->poDefaultRAT;
29✔
398
        psPam->poDefaultRAT = nullptr;
29✔
399
    }
400

401
    if (psPam->psSavedHistograms != nullptr)
761,509✔
402
    {
403
        CPLDestroyXMLNode(psPam->psSavedHistograms);
100✔
404
        psPam->psSavedHistograms = nullptr;
100✔
405
    }
406

407
    delete psPam;
761,509✔
408
    psPam = nullptr;
761,509✔
409
}
410

411
/************************************************************************/
412
/*                              XMLInit()                               */
413
/************************************************************************/
414

415
CPLErr GDALPamRasterBand::XMLInit(const CPLXMLNode *psTree,
617✔
416
                                  const char * /* pszUnused */)
417
{
418
    PamInitialize();
617✔
419

420
    /* -------------------------------------------------------------------- */
421
    /*      Apply any dataset level metadata.                               */
422
    /* -------------------------------------------------------------------- */
423
    oMDMD.XMLInit(psTree, TRUE);
617✔
424

425
    /* -------------------------------------------------------------------- */
426
    /*      Collect various other items of metadata.                        */
427
    /* -------------------------------------------------------------------- */
428
    GDALMajorObject::SetDescription(CPLGetXMLValue(psTree, "Description", ""));
617✔
429

430
    if (const char *pszNoDataValue =
617✔
431
            CPLGetXMLValue(psTree, "NoDataValue", nullptr))
617✔
432
    {
433
        const char *pszLEHex =
434
            CPLGetXMLValue(psTree, "NoDataValue.le_hex_equiv", nullptr);
28✔
435
        if (pszLEHex != nullptr)
28✔
436
        {
437
            int nBytes;
438
            GByte *pabyBin = CPLHexToBinary(pszLEHex, &nBytes);
1✔
439
            if (nBytes == 8)
1✔
440
            {
441
                CPL_LSBPTR64(pabyBin);
1✔
442

443
                GDALPamRasterBand::SetNoDataValue(
1✔
444
                    *reinterpret_cast<const double *>(pabyBin));
445
            }
446
            else
447
            {
448
                GDALPamRasterBand::SetNoDataValue(CPLAtof(pszNoDataValue));
×
449
            }
450
            CPLFree(pabyBin);
1✔
451
        }
452
        else
453
        {
454
            if (eDataType == GDT_Int64)
27✔
455
            {
456
                GDALPamRasterBand::SetNoDataValueAsInt64(static_cast<int64_t>(
1✔
457
                    std::strtoll(pszNoDataValue, nullptr, 10)));
1✔
458
            }
459
            else if (eDataType == GDT_UInt64)
26✔
460
            {
461
                GDALPamRasterBand::SetNoDataValueAsUInt64(static_cast<uint64_t>(
1✔
462
                    std::strtoull(pszNoDataValue, nullptr, 10)));
1✔
463
            }
464
            else
465
            {
466
                GDALPamRasterBand::SetNoDataValue(CPLAtof(pszNoDataValue));
25✔
467
            }
468
        }
469
    }
470

471
    const char *pszOffset = CPLGetXMLValue(psTree, "Offset", nullptr);
617✔
472
    const char *pszScale = CPLGetXMLValue(psTree, "Scale", nullptr);
617✔
473
    if (pszOffset || pszScale)
617✔
474
    {
475
        GDALPamRasterBand::SetOffset(pszOffset ? CPLAtof(pszOffset) : 0.0);
11✔
476
        GDALPamRasterBand::SetScale(pszScale ? CPLAtof(pszScale) : 1.0);
11✔
477
    }
478

479
    if (const char *pszUnitType = CPLGetXMLValue(psTree, "UnitType", nullptr))
617✔
480
        GDALPamRasterBand::SetUnitType(pszUnitType);
30✔
481

482
    if (const char *pszInterp = CPLGetXMLValue(psTree, "ColorInterp", nullptr))
617✔
483
    {
484
        GDALPamRasterBand::SetColorInterpretation(
204✔
485
            GDALGetColorInterpretationByName(pszInterp));
486
    }
487

488
    /* -------------------------------------------------------------------- */
489
    /*      Category names.                                                 */
490
    /* -------------------------------------------------------------------- */
491
    if (const auto psCategoryNames = CPLGetXMLNode(psTree, "CategoryNames"))
617✔
492
    {
493
        CPLStringList oCategoryNames;
6✔
494

495
        for (const CPLXMLNode *psEntry = psCategoryNames->psChild; psEntry;
9✔
496
             psEntry = psEntry->psNext)
6✔
497
        {
498
            /* Don't skip <Category> tag with empty content */
499
            if (psEntry->eType != CXT_Element ||
6✔
500
                !EQUAL(psEntry->pszValue, "Category") ||
6✔
501
                (psEntry->psChild != nullptr &&
6✔
502
                 psEntry->psChild->eType != CXT_Text))
6✔
503
                continue;
×
504

505
            oCategoryNames.AddString(
506
                psEntry->psChild ? psEntry->psChild->pszValue : "");
6✔
507
        }
508

509
        GDALPamRasterBand::SetCategoryNames(oCategoryNames.List());
3✔
510
    }
511

512
    /* -------------------------------------------------------------------- */
513
    /*      Collect a color table.                                          */
514
    /* -------------------------------------------------------------------- */
515
    if (const auto psColorTable = CPLGetXMLNode(psTree, "ColorTable"))
617✔
516
    {
517
        GDALColorTable oTable;
14✔
518
        int iEntry = 0;
7✔
519

520
        for (const CPLXMLNode *psEntry = psColorTable->psChild; psEntry;
17✔
521
             psEntry = psEntry->psNext)
10✔
522
        {
523
            if (!(psEntry->eType == CXT_Element &&
10✔
524
                  EQUAL(psEntry->pszValue, "Entry")))
10✔
525
            {
526
                continue;
×
527
            }
528

529
            GDALColorEntry sCEntry = {
530
                static_cast<short>(atoi(CPLGetXMLValue(psEntry, "c1", "0"))),
10✔
531
                static_cast<short>(atoi(CPLGetXMLValue(psEntry, "c2", "0"))),
20✔
532
                static_cast<short>(atoi(CPLGetXMLValue(psEntry, "c3", "0"))),
20✔
533
                static_cast<short>(atoi(CPLGetXMLValue(psEntry, "c4", "255")))};
10✔
534

535
            oTable.SetColorEntry(iEntry++, &sCEntry);
10✔
536
        }
537

538
        GDALPamRasterBand::SetColorTable(&oTable);
7✔
539
    }
540

541
    /* -------------------------------------------------------------------- */
542
    /*      Do we have a complete set of stats?                             */
543
    /* -------------------------------------------------------------------- */
544
    if (const char *pszMinimum = CPLGetXMLValue(psTree, "Minimum", nullptr))
617✔
545
    {
546
        const char *pszMaximum = CPLGetXMLValue(psTree, "Maximum", nullptr);
×
547
        if (pszMaximum)
×
548
        {
549
            psPam->bHaveMinMax = TRUE;
×
550
            psPam->dfMin = CPLAtofM(pszMinimum);
×
551
            psPam->dfMax = CPLAtofM(pszMaximum);
×
552
        }
553
    }
554

555
    if (const char *pszMean = CPLGetXMLValue(psTree, "Mean", nullptr))
617✔
556
    {
557
        const char *pszStandardDeviation =
558
            CPLGetXMLValue(psTree, "StandardDeviation", nullptr);
×
559
        if (pszStandardDeviation)
×
560
        {
561
            psPam->bHaveStats = TRUE;
×
562
            psPam->dfMean = CPLAtofM(pszMean);
×
563
            psPam->dfStdDev = CPLAtofM(pszStandardDeviation);
×
564
        }
565
    }
566

567
    /* -------------------------------------------------------------------- */
568
    /*      Histograms                                                      */
569
    /* -------------------------------------------------------------------- */
570
    if (const CPLXMLNode *psHist = CPLGetXMLNode(psTree, "Histograms"))
617✔
571
    {
572
        CPLXMLNode sHistTemp = *psHist;
67✔
573
        sHistTemp.psNext = nullptr;
67✔
574
        if (psPam->psSavedHistograms != nullptr)
67✔
575
        {
576
            CPLDestroyXMLNode(psPam->psSavedHistograms);
×
577
            psPam->psSavedHistograms = nullptr;
×
578
        }
579
        psPam->psSavedHistograms = CPLCloneXMLTree(&sHistTemp);
67✔
580
    }
581

582
    /* -------------------------------------------------------------------- */
583
    /*      Raster Attribute Table                                          */
584
    /* -------------------------------------------------------------------- */
585
    if (const CPLXMLNode *psRAT =
617✔
586
            CPLGetXMLNode(psTree, "GDALRasterAttributeTable"))
617✔
587
    {
588
        delete psPam->poDefaultRAT;
10✔
589
        auto poNewRAT = new GDALDefaultRasterAttributeTable();
10✔
590
        poNewRAT->XMLInit(psRAT, "");
10✔
591
        psPam->poDefaultRAT = poNewRAT;
10✔
592
    }
593

594
    return CE_None;
617✔
595
}
596

597
/************************************************************************/
598
/*                             CloneInfo()                              */
599
/************************************************************************/
600

601
CPLErr GDALPamRasterBand::CloneInfo(GDALRasterBand *poSrcBand, int nCloneFlags)
19,702✔
602

603
{
604
    const bool bOnlyIfMissing = (nCloneFlags & GCIF_ONLY_IF_MISSING) != 0;
19,702✔
605
    const int nSavedMOFlags = GetMOFlags();
19,702✔
606

607
    PamInitialize();
19,702✔
608

609
    /* -------------------------------------------------------------------- */
610
    /*      Suppress NotImplemented error messages - mainly needed if PAM   */
611
    /*      disabled.                                                       */
612
    /* -------------------------------------------------------------------- */
613
    SetMOFlags(nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED);
19,702✔
614

615
    /* -------------------------------------------------------------------- */
616
    /*      Metadata                                                        */
617
    /* -------------------------------------------------------------------- */
618
    if (nCloneFlags & GCIF_BAND_METADATA)
19,702✔
619
    {
620
        if (poSrcBand->GetMetadata() != nullptr)
19,702✔
621
        {
622
            if (!bOnlyIfMissing ||
264✔
623
                CSLCount(GetMetadata()) != CSLCount(poSrcBand->GetMetadata()))
132✔
624
            {
625
                SetMetadata(poSrcBand->GetMetadata());
36✔
626
            }
627
        }
628
    }
629

630
    /* -------------------------------------------------------------------- */
631
    /*      Band description.                                               */
632
    /* -------------------------------------------------------------------- */
633
    if (nCloneFlags & GCIF_BAND_DESCRIPTION)
19,702✔
634
    {
635
        if (strlen(poSrcBand->GetDescription()) > 0)
19,702✔
636
        {
637
            if (!bOnlyIfMissing || strlen(GetDescription()) == 0)
29✔
638
                GDALPamRasterBand::SetDescription(poSrcBand->GetDescription());
4✔
639
        }
640
    }
641

642
    /* -------------------------------------------------------------------- */
643
    /*      NODATA                                                          */
644
    /* -------------------------------------------------------------------- */
645
    if (nCloneFlags & GCIF_NODATA)
19,702✔
646
    {
647
        int bSuccess = FALSE;
19,702✔
648
        if (poSrcBand->GetRasterDataType() == GDT_Int64)
19,702✔
649
        {
650
            const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
7✔
651
            if (bSuccess)
7✔
652
            {
653
                if (!bOnlyIfMissing)
1✔
654
                    GDALPamRasterBand::SetNoDataValueAsInt64(nNoData);
×
655
                else
656
                {
657
                    const auto nExistingNoData =
658
                        GetNoDataValueAsInt64(&bSuccess);
1✔
659
                    if (!bSuccess || nExistingNoData != nNoData)
1✔
660
                    {
661
                        GDALPamRasterBand::SetNoDataValueAsInt64(nNoData);
×
662
                    }
663
                }
664
            }
665
        }
666
        else if (poSrcBand->GetRasterDataType() == GDT_UInt64)
19,695✔
667
        {
668
            const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
3✔
669
            if (bSuccess)
3✔
670
            {
671
                if (!bOnlyIfMissing)
1✔
672
                    GDALPamRasterBand::SetNoDataValueAsUInt64(nNoData);
×
673
                else
674
                {
675
                    const auto nExistingNoData =
676
                        GetNoDataValueAsUInt64(&bSuccess);
1✔
677
                    if (!bSuccess || nExistingNoData != nNoData)
1✔
678
                    {
679
                        GDALPamRasterBand::SetNoDataValueAsUInt64(nNoData);
×
680
                    }
681
                }
682
            }
683
        }
684
        else
685
        {
686
            const double dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
19,692✔
687

688
            if (bSuccess)
19,692✔
689
            {
690
                if (!bOnlyIfMissing)
252✔
691
                    GDALPamRasterBand::SetNoDataValue(dfNoData);
×
692
                else
693
                {
694
                    const double dfExistingNoData = GetNoDataValue(&bSuccess);
252✔
695
                    if (!bSuccess || !((std::isnan(dfExistingNoData) &&
255✔
696
                                        std::isnan(dfNoData)) ||
3✔
697
                                       dfExistingNoData == dfNoData))
698
                    {
699
                        GDALPamRasterBand::SetNoDataValue(dfNoData);
9✔
700
                    }
701
                }
702
            }
703
        }
704
    }
705

706
    /* -------------------------------------------------------------------- */
707
    /*      Category names                                                  */
708
    /* -------------------------------------------------------------------- */
709
    if (nCloneFlags & GCIF_CATEGORYNAMES)
19,702✔
710
    {
711
        if (poSrcBand->GetCategoryNames() != nullptr)
19,702✔
712
        {
713
            if (!bOnlyIfMissing || GetCategoryNames() == nullptr)
1✔
714
                GDALPamRasterBand::SetCategoryNames(
1✔
715
                    poSrcBand->GetCategoryNames());
1✔
716
        }
717
    }
718

719
    /* -------------------------------------------------------------------- */
720
    /*      Offset/scale                                                    */
721
    /* -------------------------------------------------------------------- */
722
    if (nCloneFlags & GCIF_SCALEOFFSET)
19,702✔
723
    {
724
        int bSuccess = FALSE;  // TODO(schwehr): int -> bool.
19,702✔
725
        const double dfOffset = poSrcBand->GetOffset(&bSuccess);
19,702✔
726

727
        if (bSuccess)
19,702✔
728
        {
729
            if (!bOnlyIfMissing || GetOffset() != dfOffset)
3,408✔
730
                GDALPamRasterBand::SetOffset(dfOffset);
1✔
731
        }
732

733
        const double dfScale = poSrcBand->GetScale(&bSuccess);
19,702✔
734

735
        if (bSuccess)
19,702✔
736
        {
737
            if (!bOnlyIfMissing || GetScale() != dfScale)
3,408✔
738
                GDALPamRasterBand::SetScale(dfScale);
1✔
739
        }
740
    }
741

742
    /* -------------------------------------------------------------------- */
743
    /*      Unittype.                                                       */
744
    /* -------------------------------------------------------------------- */
745
    if (nCloneFlags & GCIF_UNITTYPE)
19,702✔
746
    {
747
        if (strlen(poSrcBand->GetUnitType()) > 0)
19,702✔
748
        {
749
            if (!bOnlyIfMissing ||
76✔
750
                !EQUAL(GetUnitType(), poSrcBand->GetUnitType()))
38✔
751
            {
752
                GDALPamRasterBand::SetUnitType(poSrcBand->GetUnitType());
4✔
753
            }
754
        }
755
    }
756

757
    /* -------------------------------------------------------------------- */
758
    /*      ColorInterp                                                     */
759
    /* -------------------------------------------------------------------- */
760
    if (nCloneFlags & GCIF_COLORINTERP)
19,702✔
761
    {
762
        if (poSrcBand->GetColorInterpretation() != GCI_Undefined)
19,667✔
763
        {
764
            if (!bOnlyIfMissing ||
11,364✔
765
                poSrcBand->GetColorInterpretation() != GetColorInterpretation())
5,682✔
766
                GDALPamRasterBand::SetColorInterpretation(
149✔
767
                    poSrcBand->GetColorInterpretation());
149✔
768
        }
769
    }
770

771
    /* -------------------------------------------------------------------- */
772
    /*      color table.                                                    */
773
    /* -------------------------------------------------------------------- */
774
    if (nCloneFlags & GCIF_COLORTABLE)
19,702✔
775
    {
776
        if (poSrcBand->GetColorTable() != nullptr)
19,667✔
777
        {
778
            if (!bOnlyIfMissing || GetColorTable() == nullptr)
61✔
779
            {
780
                GDALPamRasterBand::SetColorTable(poSrcBand->GetColorTable());
2✔
781
            }
782
        }
783
    }
784

785
    /* -------------------------------------------------------------------- */
786
    /*      Raster Attribute Table.                                         */
787
    /* -------------------------------------------------------------------- */
788
    if (nCloneFlags & GCIF_RAT)
19,702✔
789
    {
790
        const GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT();
19,702✔
791

792
        if (poRAT != nullptr &&
19,710✔
793
            (poRAT->GetRowCount() != 0 || poRAT->GetColumnCount() != 0))
8✔
794
        {
795
            if (!bOnlyIfMissing || GetDefaultRAT() == nullptr)
8✔
796
            {
797
                GDALPamRasterBand::SetDefaultRAT(poRAT);
3✔
798
            }
799
        }
800
    }
801

802
    /* -------------------------------------------------------------------- */
803
    /*      Restore MO flags.                                               */
804
    /* -------------------------------------------------------------------- */
805
    SetMOFlags(nSavedMOFlags);
19,702✔
806

807
    return CE_None;
19,702✔
808
}
809

810
//! @endcond
811

812
/************************************************************************/
813
/*                            SetMetadata()                             */
814
/************************************************************************/
815

816
CPLErr GDALPamRasterBand::SetMetadata(char **papszMetadata,
468✔
817
                                      const char *pszDomain)
818

819
{
820
    PamInitialize();
468✔
821

822
    MarkPamDirty();
468✔
823

824
    return GDALRasterBand::SetMetadata(papszMetadata, pszDomain);
468✔
825
}
826

827
/************************************************************************/
828
/*                          SetMetadataItem()                           */
829
/************************************************************************/
830

831
CPLErr GDALPamRasterBand::SetMetadataItem(const char *pszName,
13,824✔
832
                                          const char *pszValue,
833
                                          const char *pszDomain)
834

835
{
836
    PamInitialize();
13,824✔
837

838
    MarkPamDirty();
13,824✔
839

840
    return GDALRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
13,824✔
841
}
842

843
/************************************************************************/
844
/*                         ResetNoDataValues()                          */
845
/************************************************************************/
846

847
void GDALPamRasterBand::ResetNoDataValues()
1,173✔
848
{
849
    psPam->bNoDataValueSet = false;
1,173✔
850
    psPam->bNoDataValueSetAsInt64 = false;
1,173✔
851
    psPam->bNoDataValueSetAsUInt64 = false;
1,173✔
852
    psPam->dfNoDataValue = GDAL_PAM_DEFAULT_NODATA_VALUE;
1,173✔
853
    psPam->nNoDataValueInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
1,173✔
854
    psPam->nNoDataValueUInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
1,173✔
855
}
1,173✔
856

857
/************************************************************************/
858
/*                           SetNoDataValue()                           */
859
/************************************************************************/
860

861
CPLErr GDALPamRasterBand::SetNoDataValue(double dfNewValue)
1,119✔
862

863
{
864
    PamInitialize();
1,119✔
865

866
    if (!psPam)
1,119✔
867
        return GDALRasterBand::SetNoDataValue(dfNewValue);
×
868

869
    ResetNoDataValues();
1,119✔
870
    psPam->bNoDataValueSet = true;
1,119✔
871
    psPam->dfNoDataValue = dfNewValue;
1,119✔
872

873
    MarkPamDirty();
1,119✔
874

875
    return CE_None;
1,119✔
876
}
877

878
/************************************************************************/
879
/*                       SetNoDataValueAsInt64()                        */
880
/************************************************************************/
881

882
CPLErr GDALPamRasterBand::SetNoDataValueAsInt64(int64_t nNewValue)
14✔
883

884
{
885
    PamInitialize();
14✔
886

887
    if (!psPam)
14✔
888
        return GDALRasterBand::SetNoDataValueAsInt64(nNewValue);
×
889

890
    ResetNoDataValues();
14✔
891
    psPam->bNoDataValueSetAsInt64 = true;
14✔
892
    psPam->nNoDataValueInt64 = nNewValue;
14✔
893

894
    MarkPamDirty();
14✔
895

896
    return CE_None;
14✔
897
}
898

899
/************************************************************************/
900
/*                      SetNoDataValueAsUInt64()                        */
901
/************************************************************************/
902

903
CPLErr GDALPamRasterBand::SetNoDataValueAsUInt64(uint64_t nNewValue)
13✔
904

905
{
906
    PamInitialize();
13✔
907

908
    if (!psPam)
13✔
909
        return GDALRasterBand::SetNoDataValueAsUInt64(nNewValue);
×
910

911
    ResetNoDataValues();
13✔
912
    psPam->bNoDataValueSetAsUInt64 = true;
13✔
913
    psPam->nNoDataValueUInt64 = nNewValue;
13✔
914

915
    MarkPamDirty();
13✔
916

917
    return CE_None;
13✔
918
}
919

920
/************************************************************************/
921
/*                          DeleteNoDataValue()                         */
922
/************************************************************************/
923

924
CPLErr GDALPamRasterBand::DeleteNoDataValue()
27✔
925

926
{
927
    PamInitialize();
27✔
928

929
    if (!psPam)
27✔
930
        return GDALRasterBand::DeleteNoDataValue();
×
931

932
    ResetNoDataValues();
27✔
933

934
    MarkPamDirty();
27✔
935

936
    return CE_None;
27✔
937
}
938

939
/************************************************************************/
940
/*                           GetNoDataValue()                           */
941
/************************************************************************/
942

943
double GDALPamRasterBand::GetNoDataValue(int *pbSuccess)
1,165,810✔
944

945
{
946
    if (psPam == nullptr)
1,165,810✔
947
        return GDALRasterBand::GetNoDataValue(pbSuccess);
31,465✔
948

949
    if (psPam->bNoDataValueSetAsInt64)
1,134,350✔
950
    {
951
        if (pbSuccess)
2✔
952
            *pbSuccess = TRUE;
2✔
953
        return GDALGetNoDataValueCastToDouble(psPam->nNoDataValueInt64);
2✔
954
    }
955

956
    if (psPam->bNoDataValueSetAsUInt64)
1,134,340✔
957
    {
958
        if (pbSuccess)
2✔
959
            *pbSuccess = TRUE;
2✔
960
        return GDALGetNoDataValueCastToDouble(psPam->nNoDataValueUInt64);
2✔
961
    }
962

963
    if (pbSuccess)
1,134,340✔
964
        *pbSuccess = psPam->bNoDataValueSet;
1,133,300✔
965

966
    return psPam->dfNoDataValue;
1,134,340✔
967
}
968

969
/************************************************************************/
970
/*                        GetNoDataValueAsInt64()                       */
971
/************************************************************************/
972

973
int64_t GDALPamRasterBand::GetNoDataValueAsInt64(int *pbSuccess)
92✔
974

975
{
976
    if (psPam == nullptr)
92✔
977
        return GDALRasterBand::GetNoDataValueAsInt64(pbSuccess);
4✔
978

979
    if (eDataType == GDT_UInt64)
88✔
980
    {
981
        CPLError(CE_Failure, CPLE_AppDefined,
×
982
                 "GetNoDataValueAsUInt64() should be called instead");
983
        if (pbSuccess)
×
984
            *pbSuccess = FALSE;
×
985
        return GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
×
986
    }
987
    if (eDataType != GDT_Int64)
88✔
988
    {
989
        CPLError(CE_Failure, CPLE_AppDefined,
×
990
                 "GetNoDataValue() should be called instead");
991
        if (pbSuccess)
×
992
            *pbSuccess = FALSE;
×
993
        return GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
×
994
    }
995

996
    if (pbSuccess)
88✔
997
        *pbSuccess = psPam->bNoDataValueSetAsInt64 ? 1 : 0;
80✔
998

999
    return psPam->nNoDataValueInt64;
88✔
1000
}
1001

1002
/************************************************************************/
1003
/*                       GetNoDataValueAsUInt64()                       */
1004
/************************************************************************/
1005

1006
uint64_t GDALPamRasterBand::GetNoDataValueAsUInt64(int *pbSuccess)
69✔
1007

1008
{
1009
    if (psPam == nullptr)
69✔
1010
        return GDALRasterBand::GetNoDataValueAsUInt64(pbSuccess);
3✔
1011

1012
    if (eDataType == GDT_Int64)
66✔
1013
    {
1014
        CPLError(CE_Failure, CPLE_AppDefined,
×
1015
                 "GetNoDataValueAsInt64() should be called instead");
1016
        if (pbSuccess)
×
1017
            *pbSuccess = FALSE;
×
1018
        return GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
×
1019
    }
1020
    if (eDataType != GDT_UInt64)
66✔
1021
    {
1022
        CPLError(CE_Failure, CPLE_AppDefined,
×
1023
                 "GetNoDataValue() should be called instead");
1024
        if (pbSuccess)
×
1025
            *pbSuccess = FALSE;
×
1026
        return GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
×
1027
    }
1028

1029
    if (pbSuccess)
66✔
1030
        *pbSuccess = psPam->bNoDataValueSetAsUInt64 ? 1 : 0;
58✔
1031

1032
    return psPam->nNoDataValueUInt64;
66✔
1033
}
1034

1035
/************************************************************************/
1036
/*                             GetOffset()                              */
1037
/************************************************************************/
1038

1039
double GDALPamRasterBand::GetOffset(int *pbSuccess)
87,543✔
1040

1041
{
1042
    if (!psPam)
87,543✔
1043
        return GDALRasterBand::GetOffset(pbSuccess);
279✔
1044

1045
    if (pbSuccess != nullptr)
87,264✔
1046
        *pbSuccess = psPam->bOffsetSet;
17,443✔
1047

1048
    return psPam->dfOffset;
87,264✔
1049
}
1050

1051
/************************************************************************/
1052
/*                             SetOffset()                              */
1053
/************************************************************************/
1054

1055
CPLErr GDALPamRasterBand::SetOffset(double dfNewOffset)
341✔
1056

1057
{
1058
    PamInitialize();
341✔
1059

1060
    if (psPam == nullptr)
341✔
1061
        return GDALRasterBand::SetOffset(dfNewOffset);
×
1062

1063
    if (!psPam->bOffsetSet || psPam->dfOffset != dfNewOffset)
341✔
1064
    {
1065
        psPam->dfOffset = dfNewOffset;
335✔
1066
        psPam->bOffsetSet = true;
335✔
1067

1068
        MarkPamDirty();
335✔
1069
    }
1070

1071
    return CE_None;
341✔
1072
}
1073

1074
/************************************************************************/
1075
/*                              GetScale()                              */
1076
/************************************************************************/
1077

1078
double GDALPamRasterBand::GetScale(int *pbSuccess)
470,891✔
1079

1080
{
1081
    if (!psPam)
470,891✔
1082
        return GDALRasterBand::GetScale(pbSuccess);
279✔
1083

1084
    if (pbSuccess != nullptr)
470,612✔
1085
        *pbSuccess = psPam->bScaleSet;
400,791✔
1086

1087
    return psPam->dfScale;
470,612✔
1088
}
1089

1090
/************************************************************************/
1091
/*                              SetScale()                              */
1092
/************************************************************************/
1093

1094
CPLErr GDALPamRasterBand::SetScale(double dfNewScale)
340✔
1095

1096
{
1097
    PamInitialize();
340✔
1098

1099
    if (psPam == nullptr)
340✔
1100
        return GDALRasterBand::SetScale(dfNewScale);
×
1101

1102
    if (!psPam->bScaleSet || dfNewScale != psPam->dfScale)
340✔
1103
    {
1104
        psPam->dfScale = dfNewScale;
334✔
1105
        psPam->bScaleSet = true;
334✔
1106

1107
        MarkPamDirty();
334✔
1108
    }
1109
    return CE_None;
340✔
1110
}
1111

1112
/************************************************************************/
1113
/*                            GetUnitType()                             */
1114
/************************************************************************/
1115

1116
const char *GDALPamRasterBand::GetUnitType()
465,610✔
1117

1118
{
1119
    if (psPam == nullptr)
465,610✔
1120
        return GDALRasterBand::GetUnitType();
104✔
1121

1122
    if (psPam->pszUnitType == nullptr)
465,506✔
1123
        return "";
465,438✔
1124

1125
    return psPam->pszUnitType;
68✔
1126
}
1127

1128
/************************************************************************/
1129
/*                            SetUnitType()                             */
1130
/************************************************************************/
1131

1132
CPLErr GDALPamRasterBand::SetUnitType(const char *pszNewValue)
150✔
1133

1134
{
1135
    PamInitialize();
150✔
1136

1137
    if (!psPam)
150✔
1138
        return GDALRasterBand::SetUnitType(pszNewValue);
×
1139

1140
    if (pszNewValue == nullptr || pszNewValue[0] == '\0')
150✔
1141
    {
1142
        if (psPam->pszUnitType != nullptr)
33✔
1143
            MarkPamDirty();
1✔
1144
        CPLFree(psPam->pszUnitType);
33✔
1145
        psPam->pszUnitType = nullptr;
33✔
1146
    }
1147
    else
1148
    {
1149
        if (psPam->pszUnitType == nullptr ||
117✔
1150
            strcmp(psPam->pszUnitType, pszNewValue) != 0)
10✔
1151
            MarkPamDirty();
110✔
1152
        CPLFree(psPam->pszUnitType);
117✔
1153
        psPam->pszUnitType = CPLStrdup(pszNewValue);
117✔
1154
    }
1155

1156
    return CE_None;
150✔
1157
}
1158

1159
/************************************************************************/
1160
/*                          GetCategoryNames()                          */
1161
/************************************************************************/
1162

1163
char **GDALPamRasterBand::GetCategoryNames()
88,939✔
1164

1165
{
1166
    if (psPam)
88,939✔
1167
        return psPam->papszCategoryNames;
88,792✔
1168

1169
    return GDALRasterBand::GetCategoryNames();
147✔
1170
}
1171

1172
/************************************************************************/
1173
/*                          SetCategoryNames()                          */
1174
/************************************************************************/
1175

1176
CPLErr GDALPamRasterBand::SetCategoryNames(char **papszNewNames)
6✔
1177

1178
{
1179
    PamInitialize();
6✔
1180

1181
    if (!psPam)
6✔
1182
        return GDALRasterBand::SetCategoryNames(papszNewNames);
×
1183

1184
    CSLDestroy(psPam->papszCategoryNames);
6✔
1185
    psPam->papszCategoryNames = CSLDuplicate(papszNewNames);
6✔
1186
    MarkPamDirty();
6✔
1187
    return CE_None;
6✔
1188
}
1189

1190
/************************************************************************/
1191
/*                           GetColorTable()                            */
1192
/************************************************************************/
1193

1194
GDALColorTable *GDALPamRasterBand::GetColorTable()
101,296✔
1195

1196
{
1197
    if (psPam)
101,296✔
1198
        return psPam->poColorTable;
101,185✔
1199

1200
    return GDALRasterBand::GetColorTable();
111✔
1201
}
1202

1203
/************************************************************************/
1204
/*                           SetColorTable()                            */
1205
/************************************************************************/
1206

1207
CPLErr GDALPamRasterBand::SetColorTable(GDALColorTable *poTableIn)
76✔
1208

1209
{
1210
    PamInitialize();
76✔
1211

1212
    if (!psPam)
76✔
1213
        return GDALRasterBand::SetColorTable(poTableIn);
×
1214

1215
    if (psPam->poColorTable != nullptr)
76✔
1216
    {
1217
        delete psPam->poColorTable;
2✔
1218
        psPam->poColorTable = nullptr;
2✔
1219
    }
1220

1221
    if (poTableIn)
76✔
1222
    {
1223
        psPam->poColorTable = poTableIn->Clone();
74✔
1224
        psPam->eColorInterp = GCI_PaletteIndex;
74✔
1225
    }
1226

1227
    MarkPamDirty();
76✔
1228

1229
    return CE_None;
76✔
1230
}
1231

1232
/************************************************************************/
1233
/*                       SetColorInterpretation()                       */
1234
/************************************************************************/
1235

1236
CPLErr GDALPamRasterBand::SetColorInterpretation(GDALColorInterp eInterpIn)
3,833✔
1237

1238
{
1239
    PamInitialize();
3,833✔
1240

1241
    if (psPam)
3,833✔
1242
    {
1243
        MarkPamDirty();
3,833✔
1244

1245
        psPam->eColorInterp = eInterpIn;
3,833✔
1246

1247
        return CE_None;
3,833✔
1248
    }
1249

UNCOV
1250
    return GDALRasterBand::SetColorInterpretation(eInterpIn);
×
1251
}
1252

1253
/************************************************************************/
1254
/*                       GetColorInterpretation()                       */
1255
/************************************************************************/
1256

1257
GDALColorInterp GDALPamRasterBand::GetColorInterpretation()
478,371✔
1258

1259
{
1260
    if (psPam)
478,371✔
1261
        return psPam->eColorInterp;
478,361✔
1262

1263
    return GDALRasterBand::GetColorInterpretation();
10✔
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
void GDALPamRasterBand::SetDescription(const char *pszDescription)
1,308✔
1274

1275
{
1276
    PamInitialize();
1,308✔
1277

1278
    if (psPam && strcmp(pszDescription, GetDescription()) != 0)
1,308✔
1279
        MarkPamDirty();
1,107✔
1280

1281
    GDALRasterBand::SetDescription(pszDescription);
1,308✔
1282
}
1,308✔
1283

1284
/************************************************************************/
1285
/*                         PamParseHistogram()                          */
1286
/************************************************************************/
1287

1288
//! @cond Doxygen_Suppress
1289
int PamParseHistogram(CPLXMLNode *psHistItem, double *pdfMin, double *pdfMax,
15✔
1290
                      int *pnBuckets, GUIntBig **ppanHistogram,
1291
                      int * /* pbIncludeOutOfRange */, int * /* pbApproxOK */)
1292
{
1293
    if (psHistItem == nullptr)
15✔
1294
        return FALSE;
×
1295

1296
    *pdfMin = CPLAtofM(CPLGetXMLValue(psHistItem, "HistMin", "0"));
15✔
1297
    *pdfMax = CPLAtofM(CPLGetXMLValue(psHistItem, "HistMax", "1"));
15✔
1298
    *pnBuckets = atoi(CPLGetXMLValue(psHistItem, "BucketCount", "2"));
15✔
1299

1300
    if (*pnBuckets <= 0 || *pnBuckets > INT_MAX / 2)
15✔
1301
        return FALSE;
×
1302

1303
    if (ppanHistogram == nullptr)
15✔
1304
        return TRUE;
×
1305

1306
    // Fetch the histogram and use it.
1307
    const char *pszHistCounts = CPLGetXMLValue(psHistItem, "HistCounts", "");
15✔
1308

1309
    // Sanity check to test consistency of BucketCount and HistCounts.
1310
    if (strlen(pszHistCounts) < 2 * static_cast<size_t>(*pnBuckets) - 1)
15✔
1311
    {
1312
        CPLError(CE_Failure, CPLE_AppDefined,
×
1313
                 "HistCounts content isn't consistent with BucketCount value");
1314
        return FALSE;
×
1315
    }
1316

1317
    *ppanHistogram =
15✔
1318
        static_cast<GUIntBig *>(VSICalloc(sizeof(GUIntBig), *pnBuckets));
15✔
1319
    if (*ppanHistogram == nullptr)
15✔
1320
    {
1321
        CPLError(CE_Failure, CPLE_OutOfMemory,
×
1322
                 "Cannot allocate memory for %d buckets", *pnBuckets);
1323
        return FALSE;
×
1324
    }
1325

1326
    for (int iBucket = 0; iBucket < *pnBuckets; iBucket++)
3,347✔
1327
    {
1328
        (*ppanHistogram)[iBucket] = CPLAtoGIntBig(pszHistCounts);
3,332✔
1329

1330
        // Skip to next number.
1331
        while (*pszHistCounts != '\0' && *pszHistCounts != '|')
7,327✔
1332
            pszHistCounts++;
3,995✔
1333
        if (*pszHistCounts == '|')
3,332✔
1334
            pszHistCounts++;
3,317✔
1335
    }
1336

1337
    return TRUE;
15✔
1338
}
1339

1340
/************************************************************************/
1341
/*                      PamFindMatchingHistogram()                      */
1342
/************************************************************************/
1343
CPLXMLNode *PamFindMatchingHistogram(CPLXMLNode *psSavedHistograms,
60✔
1344
                                     double dfMin, double dfMax, int nBuckets,
1345
                                     int bIncludeOutOfRange, int bApproxOK)
1346

1347
{
1348
    if (psSavedHistograms == nullptr)
60✔
1349
        return nullptr;
51✔
1350

1351
    for (CPLXMLNode *psXMLHist = psSavedHistograms->psChild;
9✔
1352
         psXMLHist != nullptr; psXMLHist = psXMLHist->psNext)
12✔
1353
    {
1354
        if (psXMLHist->eType != CXT_Element ||
9✔
1355
            !EQUAL(psXMLHist->pszValue, "HistItem"))
9✔
1356
            continue;
×
1357

1358
        const double dfHistMin =
1359
            CPLAtofM(CPLGetXMLValue(psXMLHist, "HistMin", "0"));
9✔
1360
        const double dfHistMax =
1361
            CPLAtofM(CPLGetXMLValue(psXMLHist, "HistMax", "0"));
9✔
1362

1363
        if (!(ARE_REAL_EQUAL(dfHistMin, dfMin)) ||
9✔
1364
            !(ARE_REAL_EQUAL(dfHistMax, dfMax)) ||
8✔
1365
            atoi(CPLGetXMLValue(psXMLHist, "BucketCount", "0")) != nBuckets ||
8✔
1366
            !atoi(CPLGetXMLValue(psXMLHist, "IncludeOutOfRange", "0")) !=
8✔
1367
                !bIncludeOutOfRange ||
18✔
1368
            (!bApproxOK && atoi(CPLGetXMLValue(psXMLHist, "Approximate", "0"))))
1✔
1369

1370
            continue;
3✔
1371

1372
        return psXMLHist;
6✔
1373
    }
1374

1375
    return nullptr;
3✔
1376
}
1377

1378
/************************************************************************/
1379
/*                       PamHistogramToXMLTree()                        */
1380
/************************************************************************/
1381

1382
CPLXMLNode *PamHistogramToXMLTree(double dfMin, double dfMax, int nBuckets,
47✔
1383
                                  GUIntBig *panHistogram,
1384
                                  int bIncludeOutOfRange, int bApprox)
1385

1386
{
1387
    if (nBuckets > (INT_MAX - 10) / 12)
47✔
1388
        return nullptr;
×
1389

1390
    const size_t nLen = 22 * static_cast<size_t>(nBuckets) + 10;
47✔
1391
    char *pszHistCounts = static_cast<char *>(VSIMalloc(nLen));
47✔
1392
    if (pszHistCounts == nullptr)
47✔
1393
        return nullptr;
×
1394

1395
    CPLXMLNode *psXMLHist = CPLCreateXMLNode(nullptr, CXT_Element, "HistItem");
47✔
1396

1397
    CPLString oFmt;
47✔
1398
    CPLSetXMLValue(psXMLHist, "HistMin", oFmt.Printf("%.16g", dfMin));
47✔
1399
    CPLSetXMLValue(psXMLHist, "HistMax", oFmt.Printf("%.16g", dfMax));
47✔
1400
    CPLSetXMLValue(psXMLHist, "BucketCount", oFmt.Printf("%d", nBuckets));
47✔
1401
    CPLSetXMLValue(psXMLHist, "IncludeOutOfRange",
47✔
1402
                   oFmt.Printf("%d", bIncludeOutOfRange));
47✔
1403
    CPLSetXMLValue(psXMLHist, "Approximate", oFmt.Printf("%d", bApprox));
47✔
1404

1405
    size_t iHistOffset = 0;
47✔
1406
    pszHistCounts[0] = '\0';
47✔
1407
    for (int iBucket = 0; iBucket < nBuckets; iBucket++)
9,771✔
1408
    {
1409
        snprintf(pszHistCounts + iHistOffset, nLen - iHistOffset, CPL_FRMT_GUIB,
9,724✔
1410
                 panHistogram[iBucket]);
9,724✔
1411
        if (iBucket < nBuckets - 1)
9,724✔
1412
            strcat(pszHistCounts + iHistOffset, "|");
9,678✔
1413
        iHistOffset += strlen(pszHistCounts + iHistOffset);
9,724✔
1414
    }
1415

1416
    CPLSetXMLValue(psXMLHist, "HistCounts", pszHistCounts);
47✔
1417
    CPLFree(pszHistCounts);
47✔
1418

1419
    return psXMLHist;
47✔
1420
}
1421

1422
//! @endcond
1423

1424
/************************************************************************/
1425
/*                            GetHistogram()                            */
1426
/************************************************************************/
1427

1428
CPLErr GDALPamRasterBand::GetHistogram(double dfMin, double dfMax, int nBuckets,
39✔
1429
                                       GUIntBig *panHistogram,
1430
                                       int bIncludeOutOfRange, int bApproxOK,
1431
                                       GDALProgressFunc pfnProgress,
1432
                                       void *pProgressData)
1433

1434
{
1435
    PamInitialize();
39✔
1436

1437
    if (psPam == nullptr)
39✔
1438
        return GDALRasterBand::GetHistogram(
×
1439
            dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
1440
            pfnProgress, pProgressData);
×
1441

1442
    /* -------------------------------------------------------------------- */
1443
    /*      Check if we have a matching histogram.                          */
1444
    /* -------------------------------------------------------------------- */
1445
    CPLXMLNode *const psHistItem =
1446
        PamFindMatchingHistogram(psPam->psSavedHistograms, dfMin, dfMax,
39✔
1447
                                 nBuckets, bIncludeOutOfRange, bApproxOK);
1448
    if (psHistItem != nullptr)
39✔
1449
    {
1450
        GUIntBig *panTempHist = nullptr;
3✔
1451

1452
        if (PamParseHistogram(psHistItem, &dfMin, &dfMax, &nBuckets,
3✔
1453
                              &panTempHist, &bIncludeOutOfRange, &bApproxOK))
3✔
1454
        {
1455
            memcpy(panHistogram, panTempHist, sizeof(GUIntBig) * nBuckets);
3✔
1456
            CPLFree(panTempHist);
3✔
1457
            return CE_None;
3✔
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
    eErr = GDALRasterBand::GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
36✔
1468
                                        bIncludeOutOfRange, bApproxOK,
1469
                                        pfnProgress, pProgressData);
1470

1471
    /* -------------------------------------------------------------------- */
1472
    /*      Save an XML description of this histogram.                      */
1473
    /* -------------------------------------------------------------------- */
1474
    if (eErr != CE_None)
36✔
1475
        return eErr;
10✔
1476

1477
    CPLXMLNode *psXMLHist = PamHistogramToXMLTree(
26✔
1478
        dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK);
1479
    if (psXMLHist != nullptr)
26✔
1480
    {
1481
        MarkPamDirty();
26✔
1482

1483
        if (psPam->psSavedHistograms == nullptr)
26✔
1484
            psPam->psSavedHistograms =
48✔
1485
                CPLCreateXMLNode(nullptr, CXT_Element, "Histograms");
24✔
1486

1487
        CPLAddXMLChild(psPam->psSavedHistograms, psXMLHist);
26✔
1488
    }
1489

1490
    return CE_None;
26✔
1491
}
1492

1493
/************************************************************************/
1494
/*                        SetDefaultHistogram()                         */
1495
/************************************************************************/
1496

1497
CPLErr GDALPamRasterBand::SetDefaultHistogram(double dfMin, double dfMax,
13✔
1498
                                              int nBuckets,
1499
                                              GUIntBig *panHistogram)
1500

1501
{
1502
    PamInitialize();
13✔
1503

1504
    if (psPam == nullptr)
13✔
1505
        return GDALRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
×
1506
                                                   panHistogram);
×
1507

1508
    /* -------------------------------------------------------------------- */
1509
    /*      Do we have a matching histogram we should replace?              */
1510
    /* -------------------------------------------------------------------- */
1511
    CPLXMLNode *psNode = PamFindMatchingHistogram(
26✔
1512
        psPam->psSavedHistograms, dfMin, dfMax, nBuckets, TRUE, TRUE);
13✔
1513
    if (psNode != nullptr)
13✔
1514
    {
1515
        /* blow this one away */
1516
        CPLRemoveXMLChild(psPam->psSavedHistograms, psNode);
3✔
1517
        CPLDestroyXMLNode(psNode);
3✔
1518
    }
1519

1520
    /* -------------------------------------------------------------------- */
1521
    /*      Translate into a histogram XML tree.                            */
1522
    /* -------------------------------------------------------------------- */
1523
    CPLXMLNode *psHistItem = PamHistogramToXMLTree(dfMin, dfMax, nBuckets,
13✔
1524
                                                   panHistogram, TRUE, FALSE);
1525
    if (psHistItem == nullptr)
13✔
1526
        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
    MarkPamDirty();
13✔
1533

1534
    if (psPam->psSavedHistograms == nullptr)
13✔
1535
        psPam->psSavedHistograms =
18✔
1536
            CPLCreateXMLNode(nullptr, CXT_Element, "Histograms");
9✔
1537

1538
    psHistItem->psNext = psPam->psSavedHistograms->psChild;
13✔
1539
    psPam->psSavedHistograms->psChild = psHistItem;
13✔
1540

1541
    return CE_None;
13✔
1542
}
1543

1544
/************************************************************************/
1545
/*                        GetDefaultHistogram()                         */
1546
/************************************************************************/
1547

1548
CPLErr GDALPamRasterBand::GetDefaultHistogram(
30✔
1549
    double *pdfMin, double *pdfMax, int *pnBuckets, GUIntBig **ppanHistogram,
1550
    int bForce, GDALProgressFunc pfnProgress, void *pProgressData)
1551

1552
{
1553
    if (psPam && psPam->psSavedHistograms != nullptr)
30✔
1554
    {
1555
        CPLXMLNode *psXMLHist = psPam->psSavedHistograms->psChild;
11✔
1556

1557
        for (; psXMLHist != nullptr; psXMLHist = psXMLHist->psNext)
11✔
1558
        {
1559
            if (psXMLHist->eType != CXT_Element ||
11✔
1560
                !EQUAL(psXMLHist->pszValue, "HistItem"))
11✔
1561
                continue;
×
1562

1563
            // TODO(schwehr): int -> bool.
1564
            int bApprox = FALSE;
11✔
1565
            int bIncludeOutOfRange = FALSE;
11✔
1566
            if (PamParseHistogram(psXMLHist, pdfMin, pdfMax, pnBuckets,
11✔
1567
                                  ppanHistogram, &bIncludeOutOfRange, &bApprox))
11✔
1568
                return CE_None;
11✔
1569

1570
            return CE_Failure;
×
1571
        }
1572
    }
1573

1574
    return GDALRasterBand::GetDefaultHistogram(pdfMin, pdfMax, pnBuckets,
19✔
1575
                                               ppanHistogram, bForce,
1576
                                               pfnProgress, pProgressData);
19✔
1577
}
1578

1579
/************************************************************************/
1580
/*                           GetDefaultRAT()                            */
1581
/************************************************************************/
1582

1583
GDALRasterAttributeTable *GDALPamRasterBand::GetDefaultRAT()
88,214✔
1584

1585
{
1586
    PamInitialize();
88,214✔
1587

1588
    if (psPam == nullptr)
88,214✔
1589
        return GDALRasterBand::GetDefaultRAT();
×
1590

1591
    return psPam->poDefaultRAT;
88,214✔
1592
}
1593

1594
/************************************************************************/
1595
/*                           SetDefaultRAT()                            */
1596
/************************************************************************/
1597

1598
CPLErr GDALPamRasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT)
24✔
1599

1600
{
1601
    PamInitialize();
24✔
1602

1603
    if (psPam == nullptr)
24✔
1604
        return GDALRasterBand::SetDefaultRAT(poRAT);
×
1605

1606
    MarkPamDirty();
24✔
1607

1608
    if (psPam->poDefaultRAT != nullptr)
24✔
1609
    {
1610
        delete psPam->poDefaultRAT;
3✔
1611
        psPam->poDefaultRAT = nullptr;
3✔
1612
    }
1613

1614
    if (poRAT == nullptr)
24✔
1615
        psPam->poDefaultRAT = nullptr;
2✔
1616
    else
1617
        psPam->poDefaultRAT = poRAT->Clone();
22✔
1618

1619
    return CE_None;
24✔
1620
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc