1591 строка
54 KiB
C++
1591 строка
54 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: GDAL Core
|
|
* Purpose: Implementation of GDALPamRasterBand, a raster band base class
|
|
* that knows how to persistently store auxiliary metadata in an
|
|
* external xml file.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
|
|
* Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "cpl_port.h"
|
|
#include "gdal_pam.h"
|
|
|
|
#include <climits>
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <new> // std::nothrow
|
|
|
|
#include "cpl_conv.h"
|
|
#include "cpl_error.h"
|
|
#include "cpl_minixml.h"
|
|
#include "cpl_progress.h"
|
|
#include "cpl_string.h"
|
|
#include "cpl_vsi.h"
|
|
#include "gdal.h"
|
|
#include "gdal_priv.h"
|
|
#include "gdal_rat.h"
|
|
|
|
/************************************************************************/
|
|
/* GDALPamRasterBand() */
|
|
/************************************************************************/
|
|
|
|
GDALPamRasterBand::GDALPamRasterBand()
|
|
|
|
{
|
|
SetMOFlags(GetMOFlags() | GMO_PAM_CLASS);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamRasterBand() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
GDALPamRasterBand::GDALPamRasterBand(int bForceCachedIOIn)
|
|
: GDALRasterBand(bForceCachedIOIn)
|
|
{
|
|
SetMOFlags(GetMOFlags() | GMO_PAM_CLASS);
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* ~GDALPamRasterBand() */
|
|
/************************************************************************/
|
|
|
|
GDALPamRasterBand::~GDALPamRasterBand()
|
|
|
|
{
|
|
PamClear();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SerializeToXML() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
CPLXMLNode *GDALPamRasterBand::SerializeToXML(const char * /* pszUnused */)
|
|
{
|
|
if (psPam == nullptr)
|
|
return nullptr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup root node and attributes. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psTree =
|
|
CPLCreateXMLNode(nullptr, CXT_Element, "PAMRasterBand");
|
|
|
|
CPLString oFmt;
|
|
if (GetBand() > 0)
|
|
CPLSetXMLValue(psTree, "#band", oFmt.Printf("%d", GetBand()));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Serialize information of interest. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (strlen(GetDescription()) > 0)
|
|
CPLSetXMLValue(psTree, "Description", GetDescription());
|
|
|
|
if (psPam->bNoDataValueSet)
|
|
{
|
|
if (CPLIsNan(psPam->dfNoDataValue))
|
|
CPLSetXMLValue(psTree, "NoDataValue", "nan");
|
|
else
|
|
CPLSetXMLValue(psTree, "NoDataValue",
|
|
oFmt.Printf("%.14E", psPam->dfNoDataValue));
|
|
|
|
// Hex encode real floating point values.
|
|
if (psPam->dfNoDataValue != floor(psPam->dfNoDataValue) ||
|
|
psPam->dfNoDataValue != CPLAtof(oFmt))
|
|
{
|
|
double dfNoDataLittleEndian = psPam->dfNoDataValue;
|
|
CPL_LSBPTR64(&dfNoDataLittleEndian);
|
|
|
|
char *pszHexEncoding = CPLBinaryToHex(
|
|
8, reinterpret_cast<GByte *>(&dfNoDataLittleEndian));
|
|
CPLSetXMLValue(psTree, "NoDataValue.#le_hex_equiv", pszHexEncoding);
|
|
CPLFree(pszHexEncoding);
|
|
}
|
|
}
|
|
else if (psPam->bNoDataValueSetAsInt64)
|
|
{
|
|
CPLSetXMLValue(
|
|
psTree, "NoDataValue",
|
|
oFmt.Printf(CPL_FRMT_GIB,
|
|
static_cast<GIntBig>(psPam->nNoDataValueInt64)));
|
|
}
|
|
else if (psPam->bNoDataValueSetAsUInt64)
|
|
{
|
|
CPLSetXMLValue(
|
|
psTree, "NoDataValue",
|
|
oFmt.Printf(CPL_FRMT_GUIB,
|
|
static_cast<GUIntBig>(psPam->nNoDataValueUInt64)));
|
|
}
|
|
|
|
if (psPam->pszUnitType != nullptr)
|
|
CPLSetXMLValue(psTree, "UnitType", psPam->pszUnitType);
|
|
|
|
if (psPam->dfOffset != 0.0)
|
|
CPLSetXMLValue(psTree, "Offset", oFmt.Printf("%.16g", psPam->dfOffset));
|
|
|
|
if (psPam->dfScale != 1.0)
|
|
CPLSetXMLValue(psTree, "Scale", oFmt.Printf("%.16g", psPam->dfScale));
|
|
|
|
if (psPam->eColorInterp != GCI_Undefined)
|
|
CPLSetXMLValue(psTree, "ColorInterp",
|
|
GDALGetColorInterpretationName(psPam->eColorInterp));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Category names. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (psPam->papszCategoryNames != nullptr)
|
|
{
|
|
CPLXMLNode *psCT_XML =
|
|
CPLCreateXMLNode(psTree, CXT_Element, "CategoryNames");
|
|
CPLXMLNode *psLastChild = nullptr;
|
|
|
|
for (int iEntry = 0; psPam->papszCategoryNames[iEntry] != nullptr;
|
|
iEntry++)
|
|
{
|
|
CPLXMLNode *psNode = CPLCreateXMLElementAndValue(
|
|
nullptr, "Category", psPam->papszCategoryNames[iEntry]);
|
|
if (psLastChild == nullptr)
|
|
psCT_XML->psChild = psNode;
|
|
else
|
|
psLastChild->psNext = psNode;
|
|
psLastChild = psNode;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Color Table. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (psPam->poColorTable != nullptr)
|
|
{
|
|
CPLXMLNode *psCT_XML =
|
|
CPLCreateXMLNode(psTree, CXT_Element, "ColorTable");
|
|
CPLXMLNode *psLastChild = nullptr;
|
|
|
|
for (int iEntry = 0; iEntry < psPam->poColorTable->GetColorEntryCount();
|
|
iEntry++)
|
|
{
|
|
CPLXMLNode *psEntry_XML =
|
|
CPLCreateXMLNode(nullptr, CXT_Element, "Entry");
|
|
if (psLastChild == nullptr)
|
|
psCT_XML->psChild = psEntry_XML;
|
|
else
|
|
psLastChild->psNext = psEntry_XML;
|
|
psLastChild = psEntry_XML;
|
|
|
|
GDALColorEntry sEntry;
|
|
psPam->poColorTable->GetColorEntryAsRGB(iEntry, &sEntry);
|
|
|
|
CPLSetXMLValue(psEntry_XML, "#c1", oFmt.Printf("%d", sEntry.c1));
|
|
CPLSetXMLValue(psEntry_XML, "#c2", oFmt.Printf("%d", sEntry.c2));
|
|
CPLSetXMLValue(psEntry_XML, "#c3", oFmt.Printf("%d", sEntry.c3));
|
|
CPLSetXMLValue(psEntry_XML, "#c4", oFmt.Printf("%d", sEntry.c4));
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Min/max. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (psPam->bHaveMinMax)
|
|
{
|
|
CPLSetXMLValue(psTree, "Minimum", oFmt.Printf("%.16g", psPam->dfMin));
|
|
CPLSetXMLValue(psTree, "Maximum", oFmt.Printf("%.16g", psPam->dfMax));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Statistics */
|
|
/* -------------------------------------------------------------------- */
|
|
if (psPam->bHaveStats)
|
|
{
|
|
CPLSetXMLValue(psTree, "Mean", oFmt.Printf("%.16g", psPam->dfMean));
|
|
CPLSetXMLValue(psTree, "StandardDeviation",
|
|
oFmt.Printf("%.16g", psPam->dfStdDev));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Histograms. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (psPam->psSavedHistograms != nullptr)
|
|
CPLAddXMLChild(psTree, CPLCloneXMLTree(psPam->psSavedHistograms));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Raster Attribute Table */
|
|
/* -------------------------------------------------------------------- */
|
|
if (psPam->poDefaultRAT != nullptr)
|
|
{
|
|
CPLXMLNode *psSerializedRAT = psPam->poDefaultRAT->Serialize();
|
|
if (psSerializedRAT != nullptr)
|
|
CPLAddXMLChild(psTree, psSerializedRAT);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Metadata. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psMD = oMDMD.Serialize();
|
|
if (psMD != nullptr)
|
|
{
|
|
CPLAddXMLChild(psTree, psMD);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We don't want to return anything if we had no metadata to */
|
|
/* attach. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (psTree->psChild == nullptr || psTree->psChild->psNext == nullptr)
|
|
{
|
|
CPLDestroyXMLNode(psTree);
|
|
psTree = nullptr;
|
|
}
|
|
|
|
return psTree;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PamInitialize() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamRasterBand::PamInitialize()
|
|
|
|
{
|
|
if (psPam != nullptr && psPam->poParentDS != nullptr)
|
|
return;
|
|
|
|
GDALDataset *poNonPamParentDS = GetDataset();
|
|
if (poNonPamParentDS == nullptr ||
|
|
!(poNonPamParentDS->GetMOFlags() & GMO_PAM_CLASS))
|
|
return;
|
|
|
|
GDALPamDataset *poParentDS =
|
|
dynamic_cast<GDALPamDataset *>(poNonPamParentDS);
|
|
if (poParentDS == nullptr)
|
|
{
|
|
// Should never happen.
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Programming error: found GDALPamRasterBand that is not "
|
|
"attached to a GDALPamDataset.");
|
|
return;
|
|
}
|
|
|
|
if (psPam != nullptr /* && psPam->poParentDS == nullptr */)
|
|
{
|
|
// We can get here if PamInitializeNoParent() was first called.
|
|
delete psPam;
|
|
psPam = nullptr;
|
|
}
|
|
|
|
poParentDS->PamInitialize();
|
|
if (poParentDS->psPam == nullptr)
|
|
return;
|
|
|
|
// Often (always?) initializing our parent will have initialized us.
|
|
if (psPam != nullptr)
|
|
return;
|
|
|
|
psPam = new (std::nothrow) GDALRasterBandPamInfo();
|
|
if (psPam == nullptr)
|
|
return;
|
|
psPam->poParentDS = poParentDS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PamInitializeNoParent() */
|
|
/************************************************************************/
|
|
|
|
/* This method is used by MEMRasterBand to just benefit for the nodata, scale,
|
|
* offset, units, etc. related methods, but not the serialization services */
|
|
void GDALPamRasterBand::PamInitializeNoParent()
|
|
{
|
|
if (psPam == nullptr)
|
|
psPam = new (std::nothrow) GDALRasterBandPamInfo();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* MarkPamDirty() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamRasterBand::MarkPamDirty()
|
|
{
|
|
if (psPam != nullptr && psPam->poParentDS != nullptr)
|
|
psPam->poParentDS->MarkPamDirty();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PamClear() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamRasterBand::PamClear()
|
|
|
|
{
|
|
if (!psPam)
|
|
return;
|
|
|
|
if (psPam->poColorTable)
|
|
delete psPam->poColorTable;
|
|
psPam->poColorTable = nullptr;
|
|
|
|
CPLFree(psPam->pszUnitType);
|
|
CSLDestroy(psPam->papszCategoryNames);
|
|
|
|
if (psPam->poDefaultRAT != nullptr)
|
|
{
|
|
delete psPam->poDefaultRAT;
|
|
psPam->poDefaultRAT = nullptr;
|
|
}
|
|
|
|
if (psPam->psSavedHistograms != nullptr)
|
|
{
|
|
CPLDestroyXMLNode(psPam->psSavedHistograms);
|
|
psPam->psSavedHistograms = nullptr;
|
|
}
|
|
|
|
delete psPam;
|
|
psPam = nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* XMLInit() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::XMLInit(CPLXMLNode *psTree,
|
|
const char * /* pszUnused */)
|
|
{
|
|
PamInitialize();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Apply any dataset level metadata. */
|
|
/* -------------------------------------------------------------------- */
|
|
oMDMD.XMLInit(psTree, TRUE);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Collect various other items of metadata. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALMajorObject::SetDescription(CPLGetXMLValue(psTree, "Description", ""));
|
|
|
|
const char *pszNoDataValue = CPLGetXMLValue(psTree, "NoDataValue", nullptr);
|
|
if (pszNoDataValue != nullptr)
|
|
{
|
|
const char *pszLEHex =
|
|
CPLGetXMLValue(psTree, "NoDataValue.le_hex_equiv", nullptr);
|
|
if (pszLEHex != nullptr)
|
|
{
|
|
int nBytes;
|
|
GByte *pabyBin = CPLHexToBinary(pszLEHex, &nBytes);
|
|
if (nBytes == 8)
|
|
{
|
|
CPL_LSBPTR64(pabyBin);
|
|
|
|
GDALPamRasterBand::SetNoDataValue(
|
|
*reinterpret_cast<const double *>(pabyBin));
|
|
}
|
|
else
|
|
{
|
|
GDALPamRasterBand::SetNoDataValue(CPLAtof(pszNoDataValue));
|
|
}
|
|
CPLFree(pabyBin);
|
|
}
|
|
else
|
|
{
|
|
if (eDataType == GDT_Int64)
|
|
{
|
|
GDALPamRasterBand::SetNoDataValueAsInt64(static_cast<int64_t>(
|
|
std::strtoll(pszNoDataValue, nullptr, 10)));
|
|
}
|
|
else if (eDataType == GDT_UInt64)
|
|
{
|
|
GDALPamRasterBand::SetNoDataValueAsUInt64(static_cast<uint64_t>(
|
|
std::strtoull(pszNoDataValue, nullptr, 10)));
|
|
}
|
|
else
|
|
{
|
|
GDALPamRasterBand::SetNoDataValue(CPLAtof(pszNoDataValue));
|
|
}
|
|
}
|
|
}
|
|
|
|
const char *pszOffset = CPLGetXMLValue(psTree, "Offset", nullptr);
|
|
const char *pszScale = CPLGetXMLValue(psTree, "Scale", nullptr);
|
|
if (pszOffset || pszScale)
|
|
{
|
|
GDALPamRasterBand::SetOffset(pszOffset ? CPLAtof(pszOffset) : 0.0);
|
|
GDALPamRasterBand::SetScale(pszScale ? CPLAtof(pszScale) : 1.0);
|
|
}
|
|
|
|
const char *pszUnitType = CPLGetXMLValue(psTree, "UnitType", nullptr);
|
|
if (pszUnitType)
|
|
GDALPamRasterBand::SetUnitType(pszUnitType);
|
|
|
|
const char *pszInterp = CPLGetXMLValue(psTree, "ColorInterp", nullptr);
|
|
if (pszInterp)
|
|
{
|
|
GDALPamRasterBand::SetColorInterpretation(
|
|
GDALGetColorInterpretationByName(pszInterp));
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Category names. */
|
|
/* -------------------------------------------------------------------- */
|
|
const auto psCategoryNames = CPLGetXMLNode(psTree, "CategoryNames");
|
|
if (psCategoryNames)
|
|
{
|
|
CPLStringList oCategoryNames;
|
|
|
|
for (CPLXMLNode *psEntry = psCategoryNames->psChild; psEntry != nullptr;
|
|
psEntry = psEntry->psNext)
|
|
{
|
|
/* Don't skip <Category> tag with empty content */
|
|
if (psEntry->eType != CXT_Element ||
|
|
!EQUAL(psEntry->pszValue, "Category") ||
|
|
(psEntry->psChild != nullptr &&
|
|
psEntry->psChild->eType != CXT_Text))
|
|
continue;
|
|
|
|
oCategoryNames.AddString(
|
|
psEntry->psChild ? psEntry->psChild->pszValue : "");
|
|
}
|
|
|
|
GDALPamRasterBand::SetCategoryNames(oCategoryNames.List());
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Collect a color table. */
|
|
/* -------------------------------------------------------------------- */
|
|
const auto psColorTable = CPLGetXMLNode(psTree, "ColorTable");
|
|
if (psColorTable)
|
|
{
|
|
GDALColorTable oTable;
|
|
int iEntry = 0;
|
|
|
|
for (CPLXMLNode *psEntry = psColorTable->psChild; psEntry != nullptr;
|
|
psEntry = psEntry->psNext)
|
|
{
|
|
if (!(psEntry->eType == CXT_Element &&
|
|
EQUAL(psEntry->pszValue, "Entry")))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
GDALColorEntry sCEntry = {
|
|
static_cast<short>(atoi(CPLGetXMLValue(psEntry, "c1", "0"))),
|
|
static_cast<short>(atoi(CPLGetXMLValue(psEntry, "c2", "0"))),
|
|
static_cast<short>(atoi(CPLGetXMLValue(psEntry, "c3", "0"))),
|
|
static_cast<short>(atoi(CPLGetXMLValue(psEntry, "c4", "255")))};
|
|
|
|
oTable.SetColorEntry(iEntry++, &sCEntry);
|
|
}
|
|
|
|
GDALPamRasterBand::SetColorTable(&oTable);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a complete set of stats? */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszMinimum = CPLGetXMLValue(psTree, "Minimum", nullptr);
|
|
if (pszMinimum)
|
|
{
|
|
const char *pszMaximum = CPLGetXMLValue(psTree, "Maximum", nullptr);
|
|
if (pszMaximum)
|
|
{
|
|
psPam->bHaveMinMax = TRUE;
|
|
psPam->dfMin = CPLAtofM(pszMinimum);
|
|
psPam->dfMax = CPLAtofM(pszMaximum);
|
|
}
|
|
}
|
|
|
|
const char *pszMean = CPLGetXMLValue(psTree, "Mean", nullptr);
|
|
if (pszMean)
|
|
{
|
|
const char *pszStandardDeviation =
|
|
CPLGetXMLValue(psTree, "StandardDeviation", nullptr);
|
|
if (pszStandardDeviation)
|
|
{
|
|
psPam->bHaveStats = TRUE;
|
|
psPam->dfMean = CPLAtofM(pszMean);
|
|
psPam->dfStdDev = CPLAtofM(pszStandardDeviation);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Histograms */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psHist = CPLGetXMLNode(psTree, "Histograms");
|
|
if (psHist != nullptr)
|
|
{
|
|
CPLXMLNode *psNext = psHist->psNext;
|
|
psHist->psNext = nullptr;
|
|
|
|
if (psPam->psSavedHistograms != nullptr)
|
|
{
|
|
CPLDestroyXMLNode(psPam->psSavedHistograms);
|
|
psPam->psSavedHistograms = nullptr;
|
|
}
|
|
psPam->psSavedHistograms = CPLCloneXMLTree(psHist);
|
|
psHist->psNext = psNext;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Raster Attribute Table */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psRAT = CPLGetXMLNode(psTree, "GDALRasterAttributeTable");
|
|
if (psRAT != nullptr)
|
|
{
|
|
if (psPam->poDefaultRAT != nullptr)
|
|
{
|
|
delete psPam->poDefaultRAT;
|
|
psPam->poDefaultRAT = nullptr;
|
|
}
|
|
psPam->poDefaultRAT = new GDALDefaultRasterAttributeTable();
|
|
psPam->poDefaultRAT->XMLInit(psRAT, "");
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CloneInfo() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::CloneInfo(GDALRasterBand *poSrcBand, int nCloneFlags)
|
|
|
|
{
|
|
const bool bOnlyIfMissing = (nCloneFlags & GCIF_ONLY_IF_MISSING) != 0;
|
|
const int nSavedMOFlags = GetMOFlags();
|
|
|
|
PamInitialize();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Suppress NotImplemented error messages - mainly needed if PAM */
|
|
/* disabled. */
|
|
/* -------------------------------------------------------------------- */
|
|
SetMOFlags(nSavedMOFlags | GMO_IGNORE_UNIMPLEMENTED);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Metadata */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCloneFlags & GCIF_BAND_METADATA)
|
|
{
|
|
if (poSrcBand->GetMetadata() != nullptr)
|
|
{
|
|
if (!bOnlyIfMissing ||
|
|
CSLCount(GetMetadata()) != CSLCount(poSrcBand->GetMetadata()))
|
|
{
|
|
SetMetadata(poSrcBand->GetMetadata());
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Band description. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCloneFlags & GCIF_BAND_DESCRIPTION)
|
|
{
|
|
if (strlen(poSrcBand->GetDescription()) > 0)
|
|
{
|
|
if (!bOnlyIfMissing || strlen(GetDescription()) == 0)
|
|
GDALPamRasterBand::SetDescription(poSrcBand->GetDescription());
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* NODATA */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCloneFlags & GCIF_NODATA)
|
|
{
|
|
int bSuccess = FALSE;
|
|
if (poSrcBand->GetRasterDataType() == GDT_Int64)
|
|
{
|
|
const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
|
|
if (bSuccess)
|
|
{
|
|
if (!bOnlyIfMissing)
|
|
GDALPamRasterBand::SetNoDataValueAsInt64(nNoData);
|
|
else
|
|
{
|
|
const auto nExistingNoData =
|
|
GetNoDataValueAsInt64(&bSuccess);
|
|
if (!bSuccess || nExistingNoData != nNoData)
|
|
{
|
|
GDALPamRasterBand::SetNoDataValueAsInt64(nNoData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (poSrcBand->GetRasterDataType() == GDT_UInt64)
|
|
{
|
|
const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
|
|
if (bSuccess)
|
|
{
|
|
if (!bOnlyIfMissing)
|
|
GDALPamRasterBand::SetNoDataValueAsUInt64(nNoData);
|
|
else
|
|
{
|
|
const auto nExistingNoData =
|
|
GetNoDataValueAsUInt64(&bSuccess);
|
|
if (!bSuccess || nExistingNoData != nNoData)
|
|
{
|
|
GDALPamRasterBand::SetNoDataValueAsUInt64(nNoData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const double dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
|
|
|
|
if (bSuccess)
|
|
{
|
|
if (!bOnlyIfMissing)
|
|
GDALPamRasterBand::SetNoDataValue(dfNoData);
|
|
else
|
|
{
|
|
const double dfExistingNoData = GetNoDataValue(&bSuccess);
|
|
if (!bSuccess || !((std::isnan(dfExistingNoData) &&
|
|
std::isnan(dfNoData)) ||
|
|
dfExistingNoData == dfNoData))
|
|
{
|
|
GDALPamRasterBand::SetNoDataValue(dfNoData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Category names */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCloneFlags & GCIF_CATEGORYNAMES)
|
|
{
|
|
if (poSrcBand->GetCategoryNames() != nullptr)
|
|
{
|
|
if (!bOnlyIfMissing || GetCategoryNames() == nullptr)
|
|
GDALPamRasterBand::SetCategoryNames(
|
|
poSrcBand->GetCategoryNames());
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Offset/scale */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCloneFlags & GCIF_SCALEOFFSET)
|
|
{
|
|
int bSuccess = FALSE; // TODO(schwehr): int -> bool.
|
|
const double dfOffset = poSrcBand->GetOffset(&bSuccess);
|
|
|
|
if (bSuccess)
|
|
{
|
|
if (!bOnlyIfMissing || GetOffset() != dfOffset)
|
|
GDALPamRasterBand::SetOffset(dfOffset);
|
|
}
|
|
|
|
const double dfScale = poSrcBand->GetScale(&bSuccess);
|
|
|
|
if (bSuccess)
|
|
{
|
|
if (!bOnlyIfMissing || GetScale() != dfScale)
|
|
GDALPamRasterBand::SetScale(dfScale);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Unittype. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCloneFlags & GCIF_UNITTYPE)
|
|
{
|
|
if (strlen(poSrcBand->GetUnitType()) > 0)
|
|
{
|
|
if (!bOnlyIfMissing ||
|
|
!EQUAL(GetUnitType(), poSrcBand->GetUnitType()))
|
|
{
|
|
GDALPamRasterBand::SetUnitType(poSrcBand->GetUnitType());
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* ColorInterp */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCloneFlags & GCIF_COLORINTERP)
|
|
{
|
|
if (poSrcBand->GetColorInterpretation() != GCI_Undefined)
|
|
{
|
|
if (!bOnlyIfMissing ||
|
|
poSrcBand->GetColorInterpretation() != GetColorInterpretation())
|
|
GDALPamRasterBand::SetColorInterpretation(
|
|
poSrcBand->GetColorInterpretation());
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* color table. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCloneFlags & GCIF_COLORTABLE)
|
|
{
|
|
if (poSrcBand->GetColorTable() != nullptr)
|
|
{
|
|
if (!bOnlyIfMissing || GetColorTable() == nullptr)
|
|
{
|
|
GDALPamRasterBand::SetColorTable(poSrcBand->GetColorTable());
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Raster Attribute Table. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (nCloneFlags & GCIF_RAT)
|
|
{
|
|
const GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT();
|
|
|
|
if (poRAT != nullptr &&
|
|
(poRAT->GetRowCount() != 0 || poRAT->GetColumnCount() != 0))
|
|
{
|
|
if (!bOnlyIfMissing || GetDefaultRAT() == nullptr)
|
|
{
|
|
GDALPamRasterBand::SetDefaultRAT(poRAT);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Restore MO flags. */
|
|
/* -------------------------------------------------------------------- */
|
|
SetMOFlags(nSavedMOFlags);
|
|
|
|
return CE_None;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* SetMetadata() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetMetadata(char **papszMetadata,
|
|
const char *pszDomain)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
MarkPamDirty();
|
|
|
|
return GDALRasterBand::SetMetadata(papszMetadata, pszDomain);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetMetadataItem() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetMetadataItem(const char *pszName,
|
|
const char *pszValue,
|
|
const char *pszDomain)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
MarkPamDirty();
|
|
|
|
return GDALRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ResetNoDataValues() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamRasterBand::ResetNoDataValues()
|
|
{
|
|
psPam->bNoDataValueSet = false;
|
|
psPam->bNoDataValueSetAsInt64 = false;
|
|
psPam->bNoDataValueSetAsUInt64 = false;
|
|
psPam->dfNoDataValue = GDAL_PAM_DEFAULT_NODATA_VALUE;
|
|
psPam->nNoDataValueInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
|
|
psPam->nNoDataValueUInt64 = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetNoDataValue(double dfNewValue)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (!psPam)
|
|
return GDALRasterBand::SetNoDataValue(dfNewValue);
|
|
|
|
ResetNoDataValues();
|
|
psPam->bNoDataValueSet = true;
|
|
psPam->dfNoDataValue = dfNewValue;
|
|
|
|
MarkPamDirty();
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetNoDataValueAsInt64() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetNoDataValueAsInt64(int64_t nNewValue)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (!psPam)
|
|
return GDALRasterBand::SetNoDataValueAsInt64(nNewValue);
|
|
|
|
ResetNoDataValues();
|
|
psPam->bNoDataValueSetAsInt64 = true;
|
|
psPam->nNoDataValueInt64 = nNewValue;
|
|
|
|
MarkPamDirty();
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetNoDataValueAsUInt64() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetNoDataValueAsUInt64(uint64_t nNewValue)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (!psPam)
|
|
return GDALRasterBand::SetNoDataValueAsUInt64(nNewValue);
|
|
|
|
ResetNoDataValues();
|
|
psPam->bNoDataValueSetAsUInt64 = true;
|
|
psPam->nNoDataValueUInt64 = nNewValue;
|
|
|
|
MarkPamDirty();
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* DeleteNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::DeleteNoDataValue()
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (!psPam)
|
|
return GDALRasterBand::DeleteNoDataValue();
|
|
|
|
ResetNoDataValues();
|
|
|
|
MarkPamDirty();
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
double GDALPamRasterBand::GetNoDataValue(int *pbSuccess)
|
|
|
|
{
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::GetNoDataValue(pbSuccess);
|
|
|
|
if (psPam->bNoDataValueSetAsInt64)
|
|
{
|
|
if (pbSuccess)
|
|
*pbSuccess = TRUE;
|
|
return GDALGetNoDataValueCastToDouble(psPam->nNoDataValueInt64);
|
|
}
|
|
|
|
if (psPam->bNoDataValueSetAsUInt64)
|
|
{
|
|
if (pbSuccess)
|
|
*pbSuccess = TRUE;
|
|
return GDALGetNoDataValueCastToDouble(psPam->nNoDataValueUInt64);
|
|
}
|
|
|
|
if (pbSuccess)
|
|
*pbSuccess = psPam->bNoDataValueSet;
|
|
|
|
return psPam->dfNoDataValue;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValueAsInt64() */
|
|
/************************************************************************/
|
|
|
|
int64_t GDALPamRasterBand::GetNoDataValueAsInt64(int *pbSuccess)
|
|
|
|
{
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::GetNoDataValueAsInt64(pbSuccess);
|
|
|
|
if (eDataType == GDT_UInt64)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GetNoDataValueAsUInt64() should be called instead");
|
|
if (pbSuccess)
|
|
*pbSuccess = FALSE;
|
|
return GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
|
|
}
|
|
if (eDataType != GDT_Int64)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GetNoDataValue() should be called instead");
|
|
if (pbSuccess)
|
|
*pbSuccess = FALSE;
|
|
return GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
|
|
}
|
|
|
|
if (pbSuccess)
|
|
*pbSuccess = psPam->bNoDataValueSetAsInt64 ? 1 : 0;
|
|
|
|
return psPam->nNoDataValueInt64;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValueAsUInt64() */
|
|
/************************************************************************/
|
|
|
|
uint64_t GDALPamRasterBand::GetNoDataValueAsUInt64(int *pbSuccess)
|
|
|
|
{
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::GetNoDataValueAsUInt64(pbSuccess);
|
|
|
|
if (eDataType == GDT_Int64)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GetNoDataValueAsInt64() should be called instead");
|
|
if (pbSuccess)
|
|
*pbSuccess = FALSE;
|
|
return GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
|
|
}
|
|
if (eDataType != GDT_UInt64)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GetNoDataValue() should be called instead");
|
|
if (pbSuccess)
|
|
*pbSuccess = FALSE;
|
|
return GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
|
|
}
|
|
|
|
if (pbSuccess)
|
|
*pbSuccess = psPam->bNoDataValueSetAsUInt64 ? 1 : 0;
|
|
|
|
return psPam->nNoDataValueUInt64;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetOffset() */
|
|
/************************************************************************/
|
|
|
|
double GDALPamRasterBand::GetOffset(int *pbSuccess)
|
|
|
|
{
|
|
if (!psPam)
|
|
return GDALRasterBand::GetOffset(pbSuccess);
|
|
|
|
if (pbSuccess != nullptr)
|
|
*pbSuccess = psPam->bOffsetSet;
|
|
|
|
return psPam->dfOffset;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetOffset() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetOffset(double dfNewOffset)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::SetOffset(dfNewOffset);
|
|
|
|
if (!psPam->bOffsetSet || psPam->dfOffset != dfNewOffset)
|
|
{
|
|
psPam->dfOffset = dfNewOffset;
|
|
psPam->bOffsetSet = true;
|
|
|
|
MarkPamDirty();
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetScale() */
|
|
/************************************************************************/
|
|
|
|
double GDALPamRasterBand::GetScale(int *pbSuccess)
|
|
|
|
{
|
|
if (!psPam)
|
|
return GDALRasterBand::GetScale(pbSuccess);
|
|
|
|
if (pbSuccess != nullptr)
|
|
*pbSuccess = psPam->bScaleSet;
|
|
|
|
return psPam->dfScale;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetScale() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetScale(double dfNewScale)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::SetScale(dfNewScale);
|
|
|
|
if (!psPam->bScaleSet || dfNewScale != psPam->dfScale)
|
|
{
|
|
psPam->dfScale = dfNewScale;
|
|
psPam->bScaleSet = true;
|
|
|
|
MarkPamDirty();
|
|
}
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetUnitType() */
|
|
/************************************************************************/
|
|
|
|
const char *GDALPamRasterBand::GetUnitType()
|
|
|
|
{
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::GetUnitType();
|
|
|
|
if (psPam->pszUnitType == nullptr)
|
|
return "";
|
|
|
|
return psPam->pszUnitType;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetUnitType() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetUnitType(const char *pszNewValue)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (!psPam)
|
|
return GDALRasterBand::SetUnitType(pszNewValue);
|
|
|
|
if (pszNewValue == nullptr || pszNewValue[0] == '\0')
|
|
{
|
|
if (psPam->pszUnitType != nullptr)
|
|
MarkPamDirty();
|
|
CPLFree(psPam->pszUnitType);
|
|
psPam->pszUnitType = nullptr;
|
|
}
|
|
else
|
|
{
|
|
if (psPam->pszUnitType == nullptr ||
|
|
strcmp(psPam->pszUnitType, pszNewValue) != 0)
|
|
MarkPamDirty();
|
|
CPLFree(psPam->pszUnitType);
|
|
psPam->pszUnitType = CPLStrdup(pszNewValue);
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetCategoryNames() */
|
|
/************************************************************************/
|
|
|
|
char **GDALPamRasterBand::GetCategoryNames()
|
|
|
|
{
|
|
if (psPam)
|
|
return psPam->papszCategoryNames;
|
|
|
|
return GDALRasterBand::GetCategoryNames();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetCategoryNames() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetCategoryNames(char **papszNewNames)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (!psPam)
|
|
return GDALRasterBand::SetCategoryNames(papszNewNames);
|
|
|
|
CSLDestroy(psPam->papszCategoryNames);
|
|
psPam->papszCategoryNames = CSLDuplicate(papszNewNames);
|
|
MarkPamDirty();
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetColorTable() */
|
|
/************************************************************************/
|
|
|
|
GDALColorTable *GDALPamRasterBand::GetColorTable()
|
|
|
|
{
|
|
if (psPam)
|
|
return psPam->poColorTable;
|
|
|
|
return GDALRasterBand::GetColorTable();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetColorTable() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetColorTable(GDALColorTable *poTableIn)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (!psPam)
|
|
return GDALRasterBand::SetColorTable(poTableIn);
|
|
|
|
if (psPam->poColorTable != nullptr)
|
|
{
|
|
delete psPam->poColorTable;
|
|
psPam->poColorTable = nullptr;
|
|
}
|
|
|
|
if (poTableIn)
|
|
{
|
|
psPam->poColorTable = poTableIn->Clone();
|
|
psPam->eColorInterp = GCI_PaletteIndex;
|
|
}
|
|
|
|
MarkPamDirty();
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetColorInterpretation() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetColorInterpretation(GDALColorInterp eInterpIn)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (psPam)
|
|
{
|
|
MarkPamDirty();
|
|
|
|
psPam->eColorInterp = eInterpIn;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
return GDALRasterBand::SetColorInterpretation(eInterpIn);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetColorInterpretation() */
|
|
/************************************************************************/
|
|
|
|
GDALColorInterp GDALPamRasterBand::GetColorInterpretation()
|
|
|
|
{
|
|
if (psPam)
|
|
return psPam->eColorInterp;
|
|
|
|
return GDALRasterBand::GetColorInterpretation();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetDescription() */
|
|
/* */
|
|
/* We let the GDALMajorObject hold the description, but we keep */
|
|
/* track of whether it has been changed so we know to save it. */
|
|
/************************************************************************/
|
|
|
|
void GDALPamRasterBand::SetDescription(const char *pszDescription)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (psPam && strcmp(pszDescription, GetDescription()) != 0)
|
|
MarkPamDirty();
|
|
|
|
GDALRasterBand::SetDescription(pszDescription);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PamParseHistogram() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
int PamParseHistogram(CPLXMLNode *psHistItem, double *pdfMin, double *pdfMax,
|
|
int *pnBuckets, GUIntBig **ppanHistogram,
|
|
int * /* pbIncludeOutOfRange */, int * /* pbApproxOK */)
|
|
{
|
|
if (psHistItem == nullptr)
|
|
return FALSE;
|
|
|
|
*pdfMin = CPLAtofM(CPLGetXMLValue(psHistItem, "HistMin", "0"));
|
|
*pdfMax = CPLAtofM(CPLGetXMLValue(psHistItem, "HistMax", "1"));
|
|
*pnBuckets = atoi(CPLGetXMLValue(psHistItem, "BucketCount", "2"));
|
|
|
|
if (*pnBuckets <= 0 || *pnBuckets > INT_MAX / 2)
|
|
return FALSE;
|
|
|
|
if (ppanHistogram == nullptr)
|
|
return TRUE;
|
|
|
|
// Fetch the histogram and use it.
|
|
const char *pszHistCounts = CPLGetXMLValue(psHistItem, "HistCounts", "");
|
|
|
|
// Sanity check to test consistency of BucketCount and HistCounts.
|
|
if (strlen(pszHistCounts) < 2 * static_cast<size_t>(*pnBuckets) - 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"HistCounts content isn't consistent with BucketCount value");
|
|
return FALSE;
|
|
}
|
|
|
|
*ppanHistogram =
|
|
static_cast<GUIntBig *>(VSICalloc(sizeof(GUIntBig), *pnBuckets));
|
|
if (*ppanHistogram == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OutOfMemory,
|
|
"Cannot allocate memory for %d buckets", *pnBuckets);
|
|
return FALSE;
|
|
}
|
|
|
|
for (int iBucket = 0; iBucket < *pnBuckets; iBucket++)
|
|
{
|
|
(*ppanHistogram)[iBucket] = CPLAtoGIntBig(pszHistCounts);
|
|
|
|
// Skip to next number.
|
|
while (*pszHistCounts != '\0' && *pszHistCounts != '|')
|
|
pszHistCounts++;
|
|
if (*pszHistCounts == '|')
|
|
pszHistCounts++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PamFindMatchingHistogram() */
|
|
/************************************************************************/
|
|
CPLXMLNode *PamFindMatchingHistogram(CPLXMLNode *psSavedHistograms,
|
|
double dfMin, double dfMax, int nBuckets,
|
|
int bIncludeOutOfRange, int bApproxOK)
|
|
|
|
{
|
|
if (psSavedHistograms == nullptr)
|
|
return nullptr;
|
|
|
|
for (CPLXMLNode *psXMLHist = psSavedHistograms->psChild;
|
|
psXMLHist != nullptr; psXMLHist = psXMLHist->psNext)
|
|
{
|
|
if (psXMLHist->eType != CXT_Element ||
|
|
!EQUAL(psXMLHist->pszValue, "HistItem"))
|
|
continue;
|
|
|
|
const double dfHistMin =
|
|
CPLAtofM(CPLGetXMLValue(psXMLHist, "HistMin", "0"));
|
|
const double dfHistMax =
|
|
CPLAtofM(CPLGetXMLValue(psXMLHist, "HistMax", "0"));
|
|
|
|
if (!(ARE_REAL_EQUAL(dfHistMin, dfMin)) ||
|
|
!(ARE_REAL_EQUAL(dfHistMax, dfMax)) ||
|
|
atoi(CPLGetXMLValue(psXMLHist, "BucketCount", "0")) != nBuckets ||
|
|
!atoi(CPLGetXMLValue(psXMLHist, "IncludeOutOfRange", "0")) !=
|
|
!bIncludeOutOfRange ||
|
|
(!bApproxOK && atoi(CPLGetXMLValue(psXMLHist, "Approximate", "0"))))
|
|
|
|
continue;
|
|
|
|
return psXMLHist;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* PamHistogramToXMLTree() */
|
|
/************************************************************************/
|
|
|
|
CPLXMLNode *PamHistogramToXMLTree(double dfMin, double dfMax, int nBuckets,
|
|
GUIntBig *panHistogram,
|
|
int bIncludeOutOfRange, int bApprox)
|
|
|
|
{
|
|
if (nBuckets > (INT_MAX - 10) / 12)
|
|
return nullptr;
|
|
|
|
const size_t nLen = 22 * static_cast<size_t>(nBuckets) + 10;
|
|
char *pszHistCounts = static_cast<char *>(VSIMalloc(nLen));
|
|
if (pszHistCounts == nullptr)
|
|
return nullptr;
|
|
|
|
CPLXMLNode *psXMLHist = CPLCreateXMLNode(nullptr, CXT_Element, "HistItem");
|
|
|
|
CPLString oFmt;
|
|
CPLSetXMLValue(psXMLHist, "HistMin", oFmt.Printf("%.16g", dfMin));
|
|
CPLSetXMLValue(psXMLHist, "HistMax", oFmt.Printf("%.16g", dfMax));
|
|
CPLSetXMLValue(psXMLHist, "BucketCount", oFmt.Printf("%d", nBuckets));
|
|
CPLSetXMLValue(psXMLHist, "IncludeOutOfRange",
|
|
oFmt.Printf("%d", bIncludeOutOfRange));
|
|
CPLSetXMLValue(psXMLHist, "Approximate", oFmt.Printf("%d", bApprox));
|
|
|
|
size_t iHistOffset = 0;
|
|
pszHistCounts[0] = '\0';
|
|
for (int iBucket = 0; iBucket < nBuckets; iBucket++)
|
|
{
|
|
snprintf(pszHistCounts + iHistOffset, nLen - iHistOffset, CPL_FRMT_GUIB,
|
|
panHistogram[iBucket]);
|
|
if (iBucket < nBuckets - 1)
|
|
strcat(pszHistCounts + iHistOffset, "|");
|
|
iHistOffset += strlen(pszHistCounts + iHistOffset);
|
|
}
|
|
|
|
CPLSetXMLValue(psXMLHist, "HistCounts", pszHistCounts);
|
|
CPLFree(pszHistCounts);
|
|
|
|
return psXMLHist;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* GetHistogram() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::GetHistogram(double dfMin, double dfMax, int nBuckets,
|
|
GUIntBig *panHistogram,
|
|
int bIncludeOutOfRange, int bApproxOK,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::GetHistogram(
|
|
dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
|
|
pfnProgress, pProgressData);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Check if we have a matching histogram. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *const psHistItem =
|
|
PamFindMatchingHistogram(psPam->psSavedHistograms, dfMin, dfMax,
|
|
nBuckets, bIncludeOutOfRange, bApproxOK);
|
|
if (psHistItem != nullptr)
|
|
{
|
|
GUIntBig *panTempHist = nullptr;
|
|
|
|
if (PamParseHistogram(psHistItem, &dfMin, &dfMax, &nBuckets,
|
|
&panTempHist, &bIncludeOutOfRange, &bApproxOK))
|
|
{
|
|
memcpy(panHistogram, panTempHist, sizeof(GUIntBig) * nBuckets);
|
|
CPLFree(panTempHist);
|
|
return CE_None;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We don't have an existing histogram matching the request, so */
|
|
/* generate one manually. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLErr eErr;
|
|
|
|
eErr = GDALRasterBand::GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
|
|
bIncludeOutOfRange, bApproxOK,
|
|
pfnProgress, pProgressData);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Save an XML description of this histogram. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (eErr != CE_None)
|
|
return eErr;
|
|
|
|
CPLXMLNode *psXMLHist = PamHistogramToXMLTree(
|
|
dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK);
|
|
if (psXMLHist != nullptr)
|
|
{
|
|
MarkPamDirty();
|
|
|
|
if (psPam->psSavedHistograms == nullptr)
|
|
psPam->psSavedHistograms =
|
|
CPLCreateXMLNode(nullptr, CXT_Element, "Histograms");
|
|
|
|
CPLAddXMLChild(psPam->psSavedHistograms, psXMLHist);
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetDefaultHistogram() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetDefaultHistogram(double dfMin, double dfMax,
|
|
int nBuckets,
|
|
GUIntBig *panHistogram)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
|
|
panHistogram);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do we have a matching histogram we should replace? */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psNode = PamFindMatchingHistogram(
|
|
psPam->psSavedHistograms, dfMin, dfMax, nBuckets, TRUE, TRUE);
|
|
if (psNode != nullptr)
|
|
{
|
|
/* blow this one away */
|
|
CPLRemoveXMLChild(psPam->psSavedHistograms, psNode);
|
|
CPLDestroyXMLNode(psNode);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Translate into a histogram XML tree. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psHistItem = PamHistogramToXMLTree(dfMin, dfMax, nBuckets,
|
|
panHistogram, TRUE, FALSE);
|
|
if (psHistItem == nullptr)
|
|
return CE_Failure;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Insert our new default histogram at the front of the */
|
|
/* histogram list so that it will be the default histogram. */
|
|
/* -------------------------------------------------------------------- */
|
|
MarkPamDirty();
|
|
|
|
if (psPam->psSavedHistograms == nullptr)
|
|
psPam->psSavedHistograms =
|
|
CPLCreateXMLNode(nullptr, CXT_Element, "Histograms");
|
|
|
|
psHistItem->psNext = psPam->psSavedHistograms->psChild;
|
|
psPam->psSavedHistograms->psChild = psHistItem;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetDefaultHistogram() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::GetDefaultHistogram(
|
|
double *pdfMin, double *pdfMax, int *pnBuckets, GUIntBig **ppanHistogram,
|
|
int bForce, GDALProgressFunc pfnProgress, void *pProgressData)
|
|
|
|
{
|
|
if (psPam && psPam->psSavedHistograms != nullptr)
|
|
{
|
|
CPLXMLNode *psXMLHist = psPam->psSavedHistograms->psChild;
|
|
|
|
for (; psXMLHist != nullptr; psXMLHist = psXMLHist->psNext)
|
|
{
|
|
if (psXMLHist->eType != CXT_Element ||
|
|
!EQUAL(psXMLHist->pszValue, "HistItem"))
|
|
continue;
|
|
|
|
// TODO(schwehr): int -> bool.
|
|
int bApprox = FALSE;
|
|
int bIncludeOutOfRange = FALSE;
|
|
if (PamParseHistogram(psXMLHist, pdfMin, pdfMax, pnBuckets,
|
|
ppanHistogram, &bIncludeOutOfRange, &bApprox))
|
|
return CE_None;
|
|
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
return GDALRasterBand::GetDefaultHistogram(pdfMin, pdfMax, pnBuckets,
|
|
ppanHistogram, bForce,
|
|
pfnProgress, pProgressData);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetDefaultRAT() */
|
|
/************************************************************************/
|
|
|
|
GDALRasterAttributeTable *GDALPamRasterBand::GetDefaultRAT()
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::GetDefaultRAT();
|
|
|
|
return psPam->poDefaultRAT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetDefaultRAT() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamRasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT)
|
|
|
|
{
|
|
PamInitialize();
|
|
|
|
if (psPam == nullptr)
|
|
return GDALRasterBand::SetDefaultRAT(poRAT);
|
|
|
|
MarkPamDirty();
|
|
|
|
if (psPam->poDefaultRAT != nullptr)
|
|
{
|
|
delete psPam->poDefaultRAT;
|
|
psPam->poDefaultRAT = nullptr;
|
|
}
|
|
|
|
if (poRAT == nullptr)
|
|
psPam->poDefaultRAT = nullptr;
|
|
else
|
|
psPam->poDefaultRAT = poRAT->Clone();
|
|
|
|
return CE_None;
|
|
}
|