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