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