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