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