12284 строки
446 KiB
C++
12284 строки
446 KiB
C++
/******************************************************************************
|
|
* $Id$
|
|
*
|
|
* Name: gdalmultidim.cpp
|
|
* Project: GDAL Core
|
|
* Purpose: GDAL Core C++/Private implementation for multidimensional support
|
|
* Author: Even Rouault <even.rouault at spatialys.com>
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2019, Even Rouault <even.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 <assert.h>
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <queue>
|
|
#include <set>
|
|
|
|
#include <ctype.h> // isalnum
|
|
|
|
#include "cpl_error_internal.h"
|
|
#include "gdal_priv.h"
|
|
#include "gdal_pam.h"
|
|
#include "gdal_utils.h"
|
|
#include "cpl_safemaths.hpp"
|
|
#include "ogrsf_frmts.h"
|
|
|
|
#if defined(__clang__) || defined(_MSC_VER)
|
|
#define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* GetPAM() */
|
|
/************************************************************************/
|
|
|
|
static std::shared_ptr<GDALPamMultiDim>
|
|
GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
|
|
{
|
|
auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
|
|
if (poPamArray)
|
|
return poPamArray->GetPAM();
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayUnscaled */
|
|
/************************************************************************/
|
|
|
|
class GDALMDArrayUnscaled final : public GDALPamMDArray
|
|
{
|
|
private:
|
|
std::shared_ptr<GDALMDArray> m_poParent{};
|
|
GDALExtendedDataType m_dt;
|
|
bool m_bHasNoData;
|
|
double m_adfNoData[2]{std::numeric_limits<double>::quiet_NaN(),
|
|
std::numeric_limits<double>::quiet_NaN()};
|
|
|
|
protected:
|
|
explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent)
|
|
: GDALAbstractMDArray(std::string(),
|
|
"Unscaled view of " + poParent->GetFullName()),
|
|
GDALPamMDArray(std::string(),
|
|
"Unscaled view of " + poParent->GetFullName(),
|
|
::GetPAM(poParent)),
|
|
m_poParent(std::move(poParent)),
|
|
m_dt(GDALExtendedDataType::Create(
|
|
GDALDataTypeIsComplex(
|
|
m_poParent->GetDataType().GetNumericDataType())
|
|
? GDT_CFloat64
|
|
: GDT_Float64)),
|
|
m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr)
|
|
{
|
|
}
|
|
|
|
bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const override;
|
|
|
|
bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
const void *pSrcBuffer) override;
|
|
|
|
bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
CSLConstList papszOptions) const override
|
|
{
|
|
return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
|
|
}
|
|
|
|
public:
|
|
static std::shared_ptr<GDALMDArrayUnscaled>
|
|
Create(const std::shared_ptr<GDALMDArray> &poParent)
|
|
{
|
|
auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(
|
|
new GDALMDArrayUnscaled(poParent)));
|
|
newAr->SetSelf(newAr);
|
|
return newAr;
|
|
}
|
|
|
|
bool IsWritable() const override
|
|
{
|
|
return m_poParent->IsWritable();
|
|
}
|
|
|
|
const std::string &GetFilename() const override
|
|
{
|
|
return m_poParent->GetFilename();
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<GDALDimension>> &
|
|
GetDimensions() const override
|
|
{
|
|
return m_poParent->GetDimensions();
|
|
}
|
|
|
|
const GDALExtendedDataType &GetDataType() const override
|
|
{
|
|
return m_dt;
|
|
}
|
|
|
|
const std::string &GetUnit() const override
|
|
{
|
|
return m_poParent->GetUnit();
|
|
}
|
|
|
|
std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
|
|
{
|
|
return m_poParent->GetSpatialRef();
|
|
}
|
|
|
|
const void *GetRawNoDataValue() const override
|
|
{
|
|
return m_bHasNoData ? &m_adfNoData[0] : nullptr;
|
|
}
|
|
|
|
bool SetRawNoDataValue(const void *pRawNoData) override
|
|
{
|
|
m_bHasNoData = true;
|
|
memcpy(m_adfNoData, pRawNoData, m_dt.GetSize());
|
|
return true;
|
|
}
|
|
|
|
std::vector<GUInt64> GetBlockSize() const override
|
|
{
|
|
return m_poParent->GetBlockSize();
|
|
}
|
|
|
|
std::shared_ptr<GDALAttribute>
|
|
GetAttribute(const std::string &osName) const override
|
|
{
|
|
return m_poParent->GetAttribute(osName);
|
|
}
|
|
|
|
std::vector<std::shared_ptr<GDALAttribute>>
|
|
GetAttributes(CSLConstList papszOptions = nullptr) const override
|
|
{
|
|
return m_poParent->GetAttributes(papszOptions);
|
|
}
|
|
|
|
bool SetUnit(const std::string &osUnit) override
|
|
{
|
|
return m_poParent->SetUnit(osUnit);
|
|
}
|
|
|
|
bool SetSpatialRef(const OGRSpatialReference *poSRS) override
|
|
{
|
|
return m_poParent->SetSpatialRef(poSRS);
|
|
}
|
|
|
|
std::shared_ptr<GDALAttribute>
|
|
CreateAttribute(const std::string &osName,
|
|
const std::vector<GUInt64> &anDimensions,
|
|
const GDALExtendedDataType &oDataType,
|
|
CSLConstList papszOptions = nullptr) override
|
|
{
|
|
return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
|
|
papszOptions);
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* ~GDALIHasAttribute() */
|
|
/************************************************************************/
|
|
|
|
GDALIHasAttribute::~GDALIHasAttribute() = default;
|
|
|
|
/************************************************************************/
|
|
/* GetAttribute() */
|
|
/************************************************************************/
|
|
|
|
/** Return an attribute by its name.
|
|
*
|
|
* If the attribute does not exist, nullptr should be silently returned.
|
|
*
|
|
* @note Driver implementation: this method will fallback to
|
|
* GetAttributeFromAttributes() is not explicitly implemented
|
|
*
|
|
* Drivers known to implement it for groups and arrays: MEM, netCDF.
|
|
*
|
|
* This is the same as the C function GDALGroupGetAttribute() or
|
|
* GDALMDArrayGetAttribute().
|
|
*
|
|
* @param osName Attribute name
|
|
* @return the attribute, or nullptr if it does not exist or an error occurred.
|
|
*/
|
|
std::shared_ptr<GDALAttribute>
|
|
GDALIHasAttribute::GetAttribute(const std::string &osName) const
|
|
{
|
|
return GetAttributeFromAttributes(osName);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetAttributeFromAttributes() */
|
|
/************************************************************************/
|
|
|
|
/** Possible fallback implementation for GetAttribute() using GetAttributes().
|
|
*/
|
|
std::shared_ptr<GDALAttribute>
|
|
GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
|
|
{
|
|
auto attrs(GetAttributes());
|
|
for (const auto &attr : attrs)
|
|
{
|
|
if (attr->GetName() == osName)
|
|
return attr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetAttributes() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of attributes contained in a GDALMDArray or GDALGroup.
|
|
*
|
|
* If the attribute does not exist, nullptr should be silently returned.
|
|
*
|
|
* @note Driver implementation: optionally implemented. If implemented,
|
|
* GetAttribute() should also be implemented.
|
|
*
|
|
* Drivers known to implement it for groups and arrays: MEM, netCDF.
|
|
*
|
|
* This is the same as the C function GDALGroupGetAttributes() or
|
|
* GDALMDArrayGetAttributes().
|
|
|
|
* @param papszOptions Driver specific options determining how attributes
|
|
* should be retrieved. Pass nullptr for default behavior.
|
|
*
|
|
* @return the attributes.
|
|
*/
|
|
std::vector<std::shared_ptr<GDALAttribute>>
|
|
GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
|
|
{
|
|
return {};
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateAttribute() */
|
|
/************************************************************************/
|
|
|
|
/** Create an attribute within a GDALMDArray or GDALGroup.
|
|
*
|
|
* The attribute might not be "physically" created until a value is written
|
|
* into it.
|
|
*
|
|
* Optionally implemented.
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF
|
|
*
|
|
* This is the same as the C function GDALGroupCreateAttribute() or
|
|
* GDALMDArrayCreateAttribute()
|
|
*
|
|
* @param osName Attribute name.
|
|
* @param anDimensions List of dimension sizes, ordered from the slowest varying
|
|
* dimension first to the fastest varying dimension last.
|
|
* Empty for a scalar attribute (common case)
|
|
* @param oDataType Attribute data type.
|
|
* @param papszOptions Driver specific options determining how the attribute.
|
|
* should be created.
|
|
*
|
|
* @return the new attribute, or nullptr if case of error
|
|
*/
|
|
std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
|
|
CPL_UNUSED const std::string &osName,
|
|
CPL_UNUSED const std::vector<GUInt64> &anDimensions,
|
|
CPL_UNUSED const GDALExtendedDataType &oDataType,
|
|
CPL_UNUSED CSLConstList papszOptions)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"CreateAttribute() not implemented");
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroup() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName)
|
|
: m_osName(osParentName.empty() ? "/" : osName),
|
|
m_osFullName(
|
|
!osParentName.empty()
|
|
? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
|
|
: "/")
|
|
{
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* ~GDALGroup() */
|
|
/************************************************************************/
|
|
|
|
GDALGroup::~GDALGroup() = default;
|
|
|
|
/************************************************************************/
|
|
/* GetMDArrayNames() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of multidimensional array names contained in this group.
|
|
*
|
|
* @note Driver implementation: optionally implemented. If implemented,
|
|
* OpenMDArray() should also be implemented.
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF.
|
|
*
|
|
* This is the same as the C function GDALGroupGetMDArrayNames().
|
|
*
|
|
* @param papszOptions Driver specific options determining how arrays
|
|
* should be retrieved. Pass nullptr for default behavior.
|
|
*
|
|
* @return the array names.
|
|
*/
|
|
std::vector<std::string>
|
|
GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
|
|
{
|
|
return {};
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OpenMDArray() */
|
|
/************************************************************************/
|
|
|
|
/** Open and return a multidimensional array.
|
|
*
|
|
* @note Driver implementation: optionally implemented. If implemented,
|
|
* GetMDArrayNames() should also be implemented.
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF.
|
|
*
|
|
* This is the same as the C function GDALGroupOpenMDArray().
|
|
*
|
|
* @param osName Array name.
|
|
* @param papszOptions Driver specific options determining how the array should
|
|
* be opened. Pass nullptr for default behavior.
|
|
*
|
|
* @return the array, or nullptr.
|
|
*/
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
|
|
CPL_UNUSED CSLConstList papszOptions) const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGroupNames() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of sub-groups contained in this group.
|
|
*
|
|
* @note Driver implementation: optionally implemented. If implemented,
|
|
* OpenGroup() should also be implemented.
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF.
|
|
*
|
|
* This is the same as the C function GDALGroupGetGroupNames().
|
|
*
|
|
* @param papszOptions Driver specific options determining how groups
|
|
* should be retrieved. Pass nullptr for default behavior.
|
|
*
|
|
* @return the group names.
|
|
*/
|
|
std::vector<std::string>
|
|
GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
|
|
{
|
|
return {};
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OpenGroup() */
|
|
/************************************************************************/
|
|
|
|
/** Open and return a sub-group.
|
|
*
|
|
* @note Driver implementation: optionally implemented. If implemented,
|
|
* GetGroupNames() should also be implemented.
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF.
|
|
*
|
|
* This is the same as the C function GDALGroupOpenGroup().
|
|
*
|
|
* @param osName Sub-group name.
|
|
* @param papszOptions Driver specific options determining how the sub-group
|
|
* should be opened. Pass nullptr for default behavior.
|
|
*
|
|
* @return the group, or nullptr.
|
|
*/
|
|
std::shared_ptr<GDALGroup>
|
|
GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
|
|
CPL_UNUSED CSLConstList papszOptions) const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetVectorLayerNames() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of layer names contained in this group.
|
|
*
|
|
* @note Driver implementation: optionally implemented. If implemented,
|
|
* OpenVectorLayer() should also be implemented.
|
|
*
|
|
* Drivers known to implement it: OpenFileGDB, FileGDB
|
|
*
|
|
* Other drivers will return an empty list. GDALDataset::GetLayerCount() and
|
|
* GDALDataset::GetLayer() should then be used.
|
|
*
|
|
* This is the same as the C function GDALGroupGetVectorLayerNames().
|
|
*
|
|
* @param papszOptions Driver specific options determining how layers
|
|
* should be retrieved. Pass nullptr for default behavior.
|
|
*
|
|
* @return the vector layer names.
|
|
* @since GDAL 3.4
|
|
*/
|
|
std::vector<std::string>
|
|
GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
|
|
{
|
|
return {};
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OpenVectorLayer() */
|
|
/************************************************************************/
|
|
|
|
/** Open and return a vector layer.
|
|
*
|
|
* Due to the historical ownership of OGRLayer* by GDALDataset*, the
|
|
* lifetime of the returned OGRLayer* is linked to the one of the owner
|
|
* dataset (contrary to the general design of this class where objects can be
|
|
* used independently of the object that returned them)
|
|
*
|
|
* @note Driver implementation: optionally implemented. If implemented,
|
|
* GetVectorLayerNames() should also be implemented.
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF.
|
|
*
|
|
* This is the same as the C function GDALGroupOpenVectorLayer().
|
|
*
|
|
* @param osName Vector layer name.
|
|
* @param papszOptions Driver specific options determining how the layer should
|
|
* be opened. Pass nullptr for default behavior.
|
|
*
|
|
* @return the group, or nullptr.
|
|
*/
|
|
OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
|
|
CPL_UNUSED CSLConstList papszOptions) const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetDimensions() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of dimensions contained in this group and used by its
|
|
* arrays.
|
|
*
|
|
* This is for dimensions that can potentially be used by several arrays.
|
|
* Not all drivers might implement this. To retrieve the dimensions used by
|
|
* a specific array, use GDALMDArray::GetDimensions().
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF
|
|
*
|
|
* This is the same as the C function GDALGroupGetDimensions().
|
|
*
|
|
* @param papszOptions Driver specific options determining how groups
|
|
* should be retrieved. Pass nullptr for default behavior.
|
|
*
|
|
* @return the dimensions.
|
|
*/
|
|
std::vector<std::shared_ptr<GDALDimension>>
|
|
GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
|
|
{
|
|
return {};
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetStructuralInfo() */
|
|
/************************************************************************/
|
|
|
|
/** Return structural information on the group.
|
|
*
|
|
* This may be the compression, etc..
|
|
*
|
|
* The return value should not be freed and is valid until GDALGroup is
|
|
* released or this function called again.
|
|
*
|
|
* This is the same as the C function GDALGroupGetStructuralInfo().
|
|
*/
|
|
CSLConstList GDALGroup::GetStructuralInfo() const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateGroup() */
|
|
/************************************************************************/
|
|
|
|
/** Create a sub-group within a group.
|
|
*
|
|
* Optionally implemented by drivers.
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF
|
|
*
|
|
* This is the same as the C function GDALGroupCreateGroup().
|
|
*
|
|
* @param osName Sub-group name.
|
|
* @param papszOptions Driver specific options determining how the sub-group
|
|
* should be created.
|
|
*
|
|
* @return the new sub-group, or nullptr in case of error.
|
|
*/
|
|
std::shared_ptr<GDALGroup>
|
|
GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
|
|
CPL_UNUSED CSLConstList papszOptions)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateDimension() */
|
|
/************************************************************************/
|
|
|
|
/** Create a dimension within a group.
|
|
*
|
|
* @note Driver implementation: drivers supporting CreateDimension() should
|
|
* implement this method, but do not have necessarily to implement
|
|
* GDALGroup::GetDimensions().
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF
|
|
*
|
|
* This is the same as the C function GDALGroupCreateDimension().
|
|
*
|
|
* @param osName Dimension name.
|
|
* @param osType Dimension type (might be empty, and ignored by drivers)
|
|
* @param osDirection Dimension direction (might be empty, and ignored by
|
|
* drivers)
|
|
* @param nSize Number of values indexed by this dimension. Should be > 0.
|
|
* @param papszOptions Driver specific options determining how the dimension
|
|
* should be created.
|
|
*
|
|
* @return the new dimension, or nullptr if case of error
|
|
*/
|
|
std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
|
|
CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
|
|
CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
|
|
CPL_UNUSED CSLConstList papszOptions)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"CreateDimension() not implemented");
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateMDArray() */
|
|
/************************************************************************/
|
|
|
|
/** Create a multidimensional array within a group.
|
|
*
|
|
* It is recommended that the GDALDimension objects passed in aoDimensions
|
|
* belong to this group, either by retrieving them with GetDimensions()
|
|
* or creating a new one with CreateDimension().
|
|
*
|
|
* Optionally implemented.
|
|
*
|
|
* Drivers known to implement it: MEM, netCDF
|
|
*
|
|
* This is the same as the C function GDALGroupCreateMDArray().
|
|
*
|
|
* @note Driver implementation: drivers should take into account the possibility
|
|
* that GDALDimension object passed in aoDimensions might belong to a different
|
|
* group / dataset / driver and act accordingly.
|
|
*
|
|
* @param osName Array name.
|
|
* @param aoDimensions List of dimensions, ordered from the slowest varying
|
|
* dimension first to the fastest varying dimension last.
|
|
* Might be empty for a scalar array (if supported by
|
|
* driver)
|
|
* @param oDataType Array data type.
|
|
* @param papszOptions Driver specific options determining how the array
|
|
* should be created.
|
|
*
|
|
* @return the new array, or nullptr if case of error
|
|
*/
|
|
std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
|
|
CPL_UNUSED const std::string &osName,
|
|
CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
|
|
CPL_UNUSED const GDALExtendedDataType &oDataType,
|
|
CPL_UNUSED CSLConstList papszOptions)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetTotalCopyCost() */
|
|
/************************************************************************/
|
|
|
|
/** Return a total "cost" to copy the group.
|
|
*
|
|
* Used as a parameter for CopFrom()
|
|
*/
|
|
GUInt64 GDALGroup::GetTotalCopyCost() const
|
|
{
|
|
GUInt64 nCost = COPY_COST;
|
|
nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
|
|
|
|
auto groupNames = GetGroupNames();
|
|
for (const auto &name : groupNames)
|
|
{
|
|
auto subGroup = OpenGroup(name);
|
|
if (subGroup)
|
|
{
|
|
nCost += subGroup->GetTotalCopyCost();
|
|
}
|
|
}
|
|
|
|
auto arrayNames = GetMDArrayNames();
|
|
for (const auto &name : arrayNames)
|
|
{
|
|
auto array = OpenMDArray(name);
|
|
if (array)
|
|
{
|
|
nCost += array->GetTotalCopyCost();
|
|
}
|
|
}
|
|
return nCost;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CopyFrom() */
|
|
/************************************************************************/
|
|
|
|
/** Copy the content of a group into a new (generally empty) group.
|
|
*
|
|
* @param poDstRootGroup Destination root group. Must NOT be nullptr.
|
|
* @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
|
|
* of some output drivers this is not recommended)
|
|
* @param poSrcGroup Source group. Must NOT be nullptr.
|
|
* @param bStrict Whether to enable stict mode. In strict mode, any error will
|
|
* stop the copy. In relaxed mode, the copy will be attempted to
|
|
* be pursued.
|
|
* @param nCurCost Should be provided as a variable initially set to 0.
|
|
* @param nTotalCost Total cost from GetTotalCopyCost().
|
|
* @param pfnProgress Progress callback, or nullptr.
|
|
* @param pProgressData Progress user data, or nulptr.
|
|
* @param papszOptions Creation options. Currently, only array creation
|
|
* options are supported. They must be prefixed with
|
|
* "ARRAY:" . The scope may be further restricted to arrays of a certain
|
|
* dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
|
|
* For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
|
|
* restrict BLOCKSIZE=256,256 to arrays of dimension 2.
|
|
* Restriction to arrays of a given name is done with adding
|
|
* "IF(NAME={name}):" after "ARRAY:". {name} can also be
|
|
* a full qualified name.
|
|
* A non-driver specific ARRAY option, "AUTOSCALE=YES" can
|
|
* be used to ask (non indexing) variables of type Float32 or Float64 to be
|
|
* scaled to UInt16 with scale and offset values being computed from the minimum
|
|
* and maximum of the source array. The integer data type used can be set with
|
|
* AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
|
|
*
|
|
* @return true in case of success (or partial success if bStrict == false).
|
|
*/
|
|
bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
|
|
GDALDataset *poSrcDS,
|
|
const std::shared_ptr<GDALGroup> &poSrcGroup,
|
|
bool bStrict, GUInt64 &nCurCost,
|
|
const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
|
|
void *pProgressData, CSLConstList papszOptions)
|
|
{
|
|
if (pfnProgress == nullptr)
|
|
pfnProgress = GDALDummyProgress;
|
|
|
|
#define EXIT_OR_CONTINUE_IF_NULL(x) \
|
|
if (!(x)) \
|
|
{ \
|
|
if (bStrict) \
|
|
return false; \
|
|
continue; \
|
|
} \
|
|
(void)0
|
|
|
|
try
|
|
{
|
|
nCurCost += GDALGroup::COPY_COST;
|
|
|
|
const auto srcDims = poSrcGroup->GetDimensions();
|
|
std::map<std::string, std::shared_ptr<GDALDimension>>
|
|
mapExistingDstDims;
|
|
std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
|
|
for (const auto &dim : srcDims)
|
|
{
|
|
auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
|
|
dim->GetDirection(), dim->GetSize());
|
|
EXIT_OR_CONTINUE_IF_NULL(dstDim);
|
|
mapExistingDstDims[dim->GetName()] = dstDim;
|
|
auto poIndexingVarSrc(dim->GetIndexingVariable());
|
|
if (poIndexingVarSrc)
|
|
{
|
|
mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
|
|
->GetName()] =
|
|
dim->GetName();
|
|
}
|
|
}
|
|
|
|
auto attrs = poSrcGroup->GetAttributes();
|
|
for (const auto &attr : attrs)
|
|
{
|
|
auto dstAttr =
|
|
CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
|
|
attr->GetDataType());
|
|
EXIT_OR_CONTINUE_IF_NULL(dstAttr);
|
|
auto raw(attr->ReadAsRaw());
|
|
if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
|
|
return false;
|
|
}
|
|
if (!attrs.empty())
|
|
{
|
|
nCurCost += attrs.size() * GDALAttribute::COPY_COST;
|
|
if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
|
|
return false;
|
|
}
|
|
|
|
const auto CopyArray =
|
|
[this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
|
|
&mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
|
|
papszOptions, bStrict, &nCurCost,
|
|
nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
|
|
{
|
|
// Map source dimensions to target dimensions
|
|
std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
|
|
const auto &srcArrayDims(srcArray->GetDimensions());
|
|
for (const auto &dim : srcArrayDims)
|
|
{
|
|
auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
|
|
dim->GetFullName());
|
|
if (dstDim && dstDim->GetSize() == dim->GetSize())
|
|
{
|
|
dstArrayDims.emplace_back(dstDim);
|
|
}
|
|
else
|
|
{
|
|
auto oIter = mapExistingDstDims.find(dim->GetName());
|
|
if (oIter != mapExistingDstDims.end() &&
|
|
oIter->second->GetSize() == dim->GetSize())
|
|
{
|
|
dstArrayDims.emplace_back(oIter->second);
|
|
}
|
|
else
|
|
{
|
|
std::string newDimName;
|
|
if (oIter == mapExistingDstDims.end())
|
|
{
|
|
newDimName = dim->GetName();
|
|
}
|
|
else
|
|
{
|
|
std::string newDimNamePrefix(srcArray->GetName() +
|
|
'_' + dim->GetName());
|
|
newDimName = newDimNamePrefix;
|
|
int nIterCount = 2;
|
|
while (mapExistingDstDims.find(newDimName) !=
|
|
mapExistingDstDims.end())
|
|
{
|
|
newDimName = newDimNamePrefix +
|
|
CPLSPrintf("_%d", nIterCount);
|
|
nIterCount++;
|
|
}
|
|
}
|
|
dstDim = CreateDimension(newDimName, dim->GetType(),
|
|
dim->GetDirection(),
|
|
dim->GetSize());
|
|
if (!dstDim)
|
|
return false;
|
|
mapExistingDstDims[newDimName] = dstDim;
|
|
dstArrayDims.emplace_back(dstDim);
|
|
}
|
|
}
|
|
}
|
|
|
|
CPLStringList aosArrayCO;
|
|
bool bAutoScale = false;
|
|
GDALDataType eAutoScaleType = GDT_UInt16;
|
|
for (CSLConstList papszIter = papszOptions; papszIter && *papszIter;
|
|
++papszIter)
|
|
{
|
|
if (STARTS_WITH_CI(*papszIter, "ARRAY:"))
|
|
{
|
|
const char *pszOption = *papszIter + strlen("ARRAY:");
|
|
if (STARTS_WITH_CI(pszOption, "IF(DIM="))
|
|
{
|
|
const char *pszNext = strchr(pszOption, ':');
|
|
if (pszNext != nullptr)
|
|
{
|
|
int nDim = atoi(pszOption + strlen("IF(DIM="));
|
|
if (static_cast<size_t>(nDim) ==
|
|
dstArrayDims.size())
|
|
{
|
|
pszOption = pszNext + 1;
|
|
}
|
|
else
|
|
{
|
|
pszOption = nullptr;
|
|
}
|
|
}
|
|
}
|
|
else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
|
|
{
|
|
const char *pszName = pszOption + strlen("IF(NAME=");
|
|
const char *pszNext = strchr(pszName, ':');
|
|
if (pszNext != nullptr && pszNext > pszName &&
|
|
pszNext[-1] == ')')
|
|
{
|
|
CPLString osName;
|
|
osName.assign(pszName, pszNext - pszName - 1);
|
|
if (osName == srcArray->GetName() ||
|
|
osName == srcArray->GetFullName())
|
|
{
|
|
pszOption = pszNext + 1;
|
|
}
|
|
else
|
|
{
|
|
pszOption = nullptr;
|
|
}
|
|
}
|
|
}
|
|
if (pszOption)
|
|
{
|
|
if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
|
|
{
|
|
bAutoScale =
|
|
CPLTestBool(pszOption + strlen("AUTOSCALE="));
|
|
}
|
|
else if (STARTS_WITH_CI(pszOption,
|
|
"AUTOSCALE_DATA_TYPE="))
|
|
{
|
|
const char *pszDataType =
|
|
pszOption + strlen("AUTOSCALE_DATA_TYPE=");
|
|
eAutoScaleType = GDALGetDataTypeByName(pszDataType);
|
|
if (GDALDataTypeIsComplex(eAutoScaleType) ||
|
|
GDALDataTypeIsFloating(eAutoScaleType))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Unsupported value for "
|
|
"AUTOSCALE_DATA_TYPE");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aosArrayCO.AddString(pszOption);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto oIterDimName =
|
|
mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
|
|
const auto &srcArrayType = srcArray->GetDataType();
|
|
|
|
std::shared_ptr<GDALMDArray> dstArray;
|
|
|
|
// Only autoscale non-indexing variables
|
|
bool bHasOffset = false;
|
|
bool bHasScale = false;
|
|
if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
|
|
(srcArrayType.GetNumericDataType() == GDT_Float32 ||
|
|
srcArrayType.GetNumericDataType() == GDT_Float64) &&
|
|
srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
|
|
srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
|
|
oIterDimName == mapSrcVariableNameToIndexedDimName.end())
|
|
{
|
|
constexpr bool bApproxOK = false;
|
|
constexpr bool bForce = true;
|
|
double dfMin = 0.0;
|
|
double dfMax = 0.0;
|
|
if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
|
|
nullptr, nullptr, nullptr, nullptr,
|
|
nullptr) != CE_None)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Could not retrieve statistics for array %s",
|
|
srcArray->GetName().c_str());
|
|
return false;
|
|
}
|
|
double dfDTMin = 0;
|
|
double dfDTMax = 0;
|
|
#define setDTMinMax(ctype) \
|
|
do \
|
|
{ \
|
|
dfDTMin = static_cast<double>(std::numeric_limits<ctype>::min()); \
|
|
dfDTMax = static_cast<double>(std::numeric_limits<ctype>::max()); \
|
|
} while (0)
|
|
|
|
switch (eAutoScaleType)
|
|
{
|
|
case GDT_Byte:
|
|
setDTMinMax(GByte);
|
|
break;
|
|
case GDT_Int8:
|
|
setDTMinMax(GInt8);
|
|
break;
|
|
case GDT_UInt16:
|
|
setDTMinMax(GUInt16);
|
|
break;
|
|
case GDT_Int16:
|
|
setDTMinMax(GInt16);
|
|
break;
|
|
case GDT_UInt32:
|
|
setDTMinMax(GUInt32);
|
|
break;
|
|
case GDT_Int32:
|
|
setDTMinMax(GInt32);
|
|
break;
|
|
case GDT_UInt64:
|
|
setDTMinMax(std::uint64_t);
|
|
break;
|
|
case GDT_Int64:
|
|
setDTMinMax(std::int64_t);
|
|
break;
|
|
case GDT_Float32:
|
|
case GDT_Float64:
|
|
case GDT_Unknown:
|
|
case GDT_CInt16:
|
|
case GDT_CInt32:
|
|
case GDT_CFloat32:
|
|
case GDT_CFloat64:
|
|
case GDT_TypeCount:
|
|
CPLAssert(false);
|
|
}
|
|
|
|
dstArray =
|
|
CreateMDArray(srcArray->GetName(), dstArrayDims,
|
|
GDALExtendedDataType::Create(eAutoScaleType),
|
|
aosArrayCO.List());
|
|
if (!dstArray)
|
|
return !bStrict;
|
|
|
|
if (srcArray->GetRawNoDataValue() != nullptr)
|
|
{
|
|
// If there's a nodata value in the source array, reserve
|
|
// DTMax for that purpose in the target scaled array
|
|
if (!dstArray->SetNoDataValue(dfDTMax))
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Cannot set nodata value");
|
|
return false;
|
|
}
|
|
dfDTMax--;
|
|
}
|
|
const double dfScale =
|
|
dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
|
|
const double dfOffset = dfMin - dfDTMin * dfScale;
|
|
|
|
if (!dstArray->SetOffset(dfOffset) ||
|
|
!dstArray->SetScale(dfScale))
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Cannot set scale/offset");
|
|
return false;
|
|
}
|
|
|
|
auto poUnscaled = dstArray->GetUnscaled();
|
|
if (srcArray->GetRawNoDataValue() != nullptr)
|
|
{
|
|
poUnscaled->SetNoDataValue(
|
|
srcArray->GetNoDataValueAsDouble());
|
|
}
|
|
|
|
// Copy source array into unscaled array
|
|
if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
|
|
nCurCost, nTotalCost, pfnProgress,
|
|
pProgressData))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
|
|
srcArrayType, aosArrayCO.List());
|
|
if (!dstArray)
|
|
return !bStrict;
|
|
|
|
if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
|
|
nCurCost, nTotalCost, pfnProgress,
|
|
pProgressData))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If this array is the indexing variable of a dimension, link them
|
|
// together.
|
|
if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
|
|
{
|
|
auto oCorrespondingDimIter =
|
|
mapExistingDstDims.find(oIterDimName->second);
|
|
if (oCorrespondingDimIter != mapExistingDstDims.end())
|
|
{
|
|
CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
|
|
CPLErrorStateBackuper oErrorStateBackuper;
|
|
oCorrespondingDimIter->second->SetIndexingVariable(
|
|
dstArray);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const auto arrayNames = poSrcGroup->GetMDArrayNames();
|
|
|
|
// Start by copying arrays that are indexing variables of dimensions
|
|
for (const auto &name : arrayNames)
|
|
{
|
|
auto srcArray = poSrcGroup->OpenMDArray(name);
|
|
EXIT_OR_CONTINUE_IF_NULL(srcArray);
|
|
|
|
const auto oIterDimName =
|
|
mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
|
|
if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
|
|
{
|
|
if (!CopyArray(srcArray))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Then copy regular arrays
|
|
for (const auto &name : arrayNames)
|
|
{
|
|
auto srcArray = poSrcGroup->OpenMDArray(name);
|
|
EXIT_OR_CONTINUE_IF_NULL(srcArray);
|
|
|
|
const auto oIterDimName =
|
|
mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
|
|
if (oIterDimName == mapSrcVariableNameToIndexedDimName.end())
|
|
{
|
|
if (!CopyArray(srcArray))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const auto groupNames = poSrcGroup->GetGroupNames();
|
|
for (const auto &name : groupNames)
|
|
{
|
|
auto srcSubGroup = poSrcGroup->OpenGroup(name);
|
|
EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
|
|
auto dstSubGroup = CreateGroup(name);
|
|
EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
|
|
if (!dstSubGroup->CopyFrom(
|
|
poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
|
|
nTotalCost, pfnProgress, pProgressData, papszOptions))
|
|
return false;
|
|
}
|
|
|
|
if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetInnerMostGroup() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
const GDALGroup *
|
|
GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
|
|
std::shared_ptr<GDALGroup> &curGroupHolder,
|
|
std::string &osLastPart) const
|
|
{
|
|
if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
|
|
return nullptr;
|
|
const GDALGroup *poCurGroup = this;
|
|
CPLStringList aosTokens(
|
|
CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
|
|
if (aosTokens.size() == 0)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
for (int i = 0; i < aosTokens.size() - 1; i++)
|
|
{
|
|
curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
|
|
if (!curGroupHolder)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
|
|
aosTokens[i]);
|
|
return nullptr;
|
|
}
|
|
poCurGroup = curGroupHolder.get();
|
|
}
|
|
osLastPart = aosTokens[aosTokens.size() - 1];
|
|
return poCurGroup;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* OpenMDArrayFromFullname() */
|
|
/************************************************************************/
|
|
|
|
/** Get an array from its fully qualified name */
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
|
|
CSLConstList papszOptions) const
|
|
{
|
|
std::string osName;
|
|
std::shared_ptr<GDALGroup> curGroupHolder;
|
|
auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
|
|
if (poGroup == nullptr)
|
|
return nullptr;
|
|
return poGroup->OpenMDArray(osName, papszOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ResolveMDArray() */
|
|
/************************************************************************/
|
|
|
|
/** Locate an array in a group and its subgroups by name.
|
|
*
|
|
* If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
|
|
* used
|
|
* Otherwise the search will start from the group identified by osStartingPath,
|
|
* and an array whose name is osName will be looked for in this group (if
|
|
* osStartingPath is empty or "/", then the current group is used). If there
|
|
* is no match, then a recursive descendent search will be made in its
|
|
* subgroups. If there is no match in the subgroups, then the parent (if
|
|
* existing) of the group pointed by osStartingPath will be used as the new
|
|
* starting point for the search.
|
|
*
|
|
* @param osName name, qualified or not
|
|
* @param osStartingPath fully qualified name of the (sub-)group from which
|
|
* the search should be started. If this is a non-empty
|
|
* string, the group on which this method is called should
|
|
* nominally be the root group (otherwise the path will
|
|
* be interpreted as from the current group)
|
|
* @param papszOptions options to pass to OpenMDArray()
|
|
* @since GDAL 3.2
|
|
*/
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALGroup::ResolveMDArray(const std::string &osName,
|
|
const std::string &osStartingPath,
|
|
CSLConstList papszOptions) const
|
|
{
|
|
if (!osName.empty() && osName[0] == '/')
|
|
{
|
|
auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
|
|
if (poArray)
|
|
return poArray;
|
|
}
|
|
std::string osPath(osStartingPath);
|
|
std::set<std::string> oSetAlreadyVisited;
|
|
|
|
while (true)
|
|
{
|
|
std::shared_ptr<GDALGroup> curGroupHolder;
|
|
std::shared_ptr<GDALGroup> poGroup;
|
|
|
|
std::queue<std::shared_ptr<GDALGroup>> oQueue;
|
|
bool goOn = false;
|
|
if (osPath.empty() || osPath == "/")
|
|
{
|
|
goOn = true;
|
|
}
|
|
else
|
|
{
|
|
std::string osLastPart;
|
|
const GDALGroup *poGroupPtr =
|
|
GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
|
|
if (poGroupPtr)
|
|
poGroup = poGroupPtr->OpenGroup(osLastPart);
|
|
if (poGroup && oSetAlreadyVisited.find(poGroup->GetFullName()) ==
|
|
oSetAlreadyVisited.end())
|
|
{
|
|
oQueue.push(poGroup);
|
|
goOn = true;
|
|
}
|
|
}
|
|
|
|
if (goOn)
|
|
{
|
|
do
|
|
{
|
|
const GDALGroup *groupPtr;
|
|
if (!oQueue.empty())
|
|
{
|
|
poGroup = oQueue.front();
|
|
oQueue.pop();
|
|
groupPtr = poGroup.get();
|
|
}
|
|
else
|
|
{
|
|
groupPtr = this;
|
|
}
|
|
|
|
auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
|
|
if (poArray)
|
|
return poArray;
|
|
|
|
const auto aosGroupNames = groupPtr->GetGroupNames();
|
|
for (const auto &osGroupName : aosGroupNames)
|
|
{
|
|
auto poSubGroup = groupPtr->OpenGroup(osGroupName);
|
|
if (poSubGroup &&
|
|
oSetAlreadyVisited.find(poSubGroup->GetFullName()) ==
|
|
oSetAlreadyVisited.end())
|
|
{
|
|
oQueue.push(poSubGroup);
|
|
oSetAlreadyVisited.insert(poSubGroup->GetFullName());
|
|
}
|
|
}
|
|
} while (!oQueue.empty());
|
|
}
|
|
|
|
if (osPath.empty() || osPath == "/")
|
|
break;
|
|
|
|
const auto nPos = osPath.rfind('/');
|
|
if (nPos == 0)
|
|
osPath = "/";
|
|
else
|
|
{
|
|
if (nPos == std::string::npos)
|
|
break;
|
|
osPath.resize(nPos);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OpenGroupFromFullname() */
|
|
/************************************************************************/
|
|
|
|
/** Get a group from its fully qualified name.
|
|
* @since GDAL 3.2
|
|
*/
|
|
std::shared_ptr<GDALGroup>
|
|
GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
|
|
CSLConstList papszOptions) const
|
|
{
|
|
std::string osName;
|
|
std::shared_ptr<GDALGroup> curGroupHolder;
|
|
auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
|
|
if (poGroup == nullptr)
|
|
return nullptr;
|
|
return poGroup->OpenGroup(osName, papszOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* OpenDimensionFromFullname() */
|
|
/************************************************************************/
|
|
|
|
/** Get a dimension from its fully qualified name */
|
|
std::shared_ptr<GDALDimension>
|
|
GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
|
|
{
|
|
std::string osName;
|
|
std::shared_ptr<GDALGroup> curGroupHolder;
|
|
auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
|
|
if (poGroup == nullptr)
|
|
return nullptr;
|
|
auto dims(poGroup->GetDimensions());
|
|
for (auto &dim : dims)
|
|
{
|
|
if (dim->GetName() == osName)
|
|
return dim;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ClearStatistics() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Clear statistics.
|
|
*
|
|
* @since GDAL 3.4
|
|
*/
|
|
void GDALGroup::ClearStatistics()
|
|
{
|
|
auto groupNames = GetGroupNames();
|
|
for (const auto &name : groupNames)
|
|
{
|
|
auto subGroup = OpenGroup(name);
|
|
if (subGroup)
|
|
{
|
|
subGroup->ClearStatistics();
|
|
}
|
|
}
|
|
|
|
auto arrayNames = GetMDArrayNames();
|
|
for (const auto &name : arrayNames)
|
|
{
|
|
auto array = OpenMDArray(name);
|
|
if (array)
|
|
{
|
|
array->ClearStatistics();
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GDALAbstractMDArray() */
|
|
/************************************************************************/
|
|
|
|
GDALAbstractMDArray::~GDALAbstractMDArray() = default;
|
|
|
|
/************************************************************************/
|
|
/* GDALAbstractMDArray() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
|
|
const std::string &osName)
|
|
: m_osName(osName),
|
|
m_osFullName(
|
|
!osParentName.empty()
|
|
? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
|
|
: osName)
|
|
{
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* GetDimensions() */
|
|
/************************************************************************/
|
|
|
|
/** \fn GDALAbstractMDArray::GetDimensions() const
|
|
* \brief Return the dimensions of an attribute/array.
|
|
*
|
|
* This is the same as the C functions GDALMDArrayGetDimensions() and
|
|
* similar to GDALAttributeGetDimensionsSize().
|
|
*/
|
|
|
|
/************************************************************************/
|
|
/* GetDataType() */
|
|
/************************************************************************/
|
|
|
|
/** \fn GDALAbstractMDArray::GetDataType() const
|
|
* \brief Return the data type of an attribute/array.
|
|
*
|
|
* This is the same as the C functions GDALMDArrayGetDataType() and
|
|
* GDALAttributeGetDataType()
|
|
*/
|
|
|
|
/************************************************************************/
|
|
/* GetDimensionCount() */
|
|
/************************************************************************/
|
|
|
|
/** Return the number of dimensions.
|
|
*
|
|
* Default implementation is GetDimensions().size(), and may be overridden by
|
|
* drivers if they have a faster / less expensive implementations.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetDimensionCount() or
|
|
* GDALAttributeGetDimensionCount().
|
|
*
|
|
*/
|
|
size_t GDALAbstractMDArray::GetDimensionCount() const
|
|
{
|
|
return GetDimensions().size();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CopyValue() */
|
|
/************************************************************************/
|
|
|
|
/** Convert a value from a source type to a destination type.
|
|
*
|
|
* If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
|
|
* that must be freed with CPLFree().
|
|
*/
|
|
bool GDALExtendedDataType::CopyValue(const void *pSrc,
|
|
const GDALExtendedDataType &srcType,
|
|
void *pDst,
|
|
const GDALExtendedDataType &dstType)
|
|
{
|
|
if (srcType.GetClass() == GEDTC_NUMERIC &&
|
|
dstType.GetClass() == GEDTC_NUMERIC)
|
|
{
|
|
GDALCopyWords(pSrc, srcType.GetNumericDataType(), 0, pDst,
|
|
dstType.GetNumericDataType(), 0, 1);
|
|
return true;
|
|
}
|
|
if (srcType.GetClass() == GEDTC_STRING &&
|
|
dstType.GetClass() == GEDTC_STRING)
|
|
{
|
|
const char *srcStrPtr;
|
|
memcpy(&srcStrPtr, pSrc, sizeof(const char *));
|
|
char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
|
|
*reinterpret_cast<void **>(pDst) = pszDup;
|
|
return true;
|
|
}
|
|
if (srcType.GetClass() == GEDTC_NUMERIC &&
|
|
dstType.GetClass() == GEDTC_STRING)
|
|
{
|
|
const char *str = nullptr;
|
|
switch (srcType.GetNumericDataType())
|
|
{
|
|
case GDT_Unknown:
|
|
break;
|
|
case GDT_Byte:
|
|
str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
|
|
break;
|
|
case GDT_Int8:
|
|
str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
|
|
break;
|
|
case GDT_UInt16:
|
|
str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
|
|
break;
|
|
case GDT_Int16:
|
|
str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
|
|
break;
|
|
case GDT_UInt32:
|
|
str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
|
|
break;
|
|
case GDT_Int32:
|
|
str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
|
|
break;
|
|
case GDT_UInt64:
|
|
str =
|
|
CPLSPrintf(CPL_FRMT_GUIB,
|
|
static_cast<GUIntBig>(
|
|
*static_cast<const std::uint64_t *>(pSrc)));
|
|
break;
|
|
case GDT_Int64:
|
|
str = CPLSPrintf(CPL_FRMT_GIB,
|
|
static_cast<GIntBig>(
|
|
*static_cast<const std::int64_t *>(pSrc)));
|
|
break;
|
|
case GDT_Float32:
|
|
str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
|
|
break;
|
|
case GDT_Float64:
|
|
str = CPLSPrintf("%.18g", *static_cast<const double *>(pSrc));
|
|
break;
|
|
case GDT_CInt16:
|
|
{
|
|
const GInt16 *src = static_cast<const GInt16 *>(pSrc);
|
|
str = CPLSPrintf("%d+%dj", src[0], src[1]);
|
|
break;
|
|
}
|
|
case GDT_CInt32:
|
|
{
|
|
const GInt32 *src = static_cast<const GInt32 *>(pSrc);
|
|
str = CPLSPrintf("%d+%dj", src[0], src[1]);
|
|
break;
|
|
}
|
|
case GDT_CFloat32:
|
|
{
|
|
const float *src = static_cast<const float *>(pSrc);
|
|
str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
|
|
break;
|
|
}
|
|
case GDT_CFloat64:
|
|
{
|
|
const double *src = static_cast<const double *>(pSrc);
|
|
str = CPLSPrintf("%.18g+%.18gj", src[0], src[1]);
|
|
break;
|
|
}
|
|
case GDT_TypeCount:
|
|
CPLAssert(false);
|
|
break;
|
|
}
|
|
char *pszDup = str ? CPLStrdup(str) : nullptr;
|
|
*reinterpret_cast<void **>(pDst) = pszDup;
|
|
return true;
|
|
}
|
|
if (srcType.GetClass() == GEDTC_STRING &&
|
|
dstType.GetClass() == GEDTC_NUMERIC)
|
|
{
|
|
const char *srcStrPtr;
|
|
memcpy(&srcStrPtr, pSrc, sizeof(const char *));
|
|
if (dstType.GetNumericDataType() == GDT_Int64)
|
|
{
|
|
*(static_cast<int64_t *>(pDst)) =
|
|
srcStrPtr == nullptr ? 0
|
|
: static_cast<int64_t>(atoll(srcStrPtr));
|
|
}
|
|
else if (dstType.GetNumericDataType() == GDT_UInt64)
|
|
{
|
|
*(static_cast<uint64_t *>(pDst)) =
|
|
srcStrPtr == nullptr
|
|
? 0
|
|
: static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
|
|
}
|
|
else
|
|
{
|
|
// FIXME GDT_UInt64
|
|
const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
|
|
GDALCopyWords(&dfVal, GDT_Float64, 0, pDst,
|
|
dstType.GetNumericDataType(), 0, 1);
|
|
}
|
|
return true;
|
|
}
|
|
if (srcType.GetClass() == GEDTC_COMPOUND &&
|
|
dstType.GetClass() == GEDTC_COMPOUND)
|
|
{
|
|
const auto &srcComponents = srcType.GetComponents();
|
|
const auto &dstComponents = dstType.GetComponents();
|
|
const GByte *pabySrc = static_cast<const GByte *>(pSrc);
|
|
GByte *pabyDst = static_cast<GByte *>(pDst);
|
|
|
|
std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
|
|
srcComponentMap;
|
|
for (const auto &srcComp : srcComponents)
|
|
{
|
|
srcComponentMap[srcComp->GetName()] = &srcComp;
|
|
}
|
|
for (const auto &dstComp : dstComponents)
|
|
{
|
|
auto oIter = srcComponentMap.find(dstComp->GetName());
|
|
if (oIter == srcComponentMap.end())
|
|
return false;
|
|
const auto &srcComp = *(oIter->second);
|
|
if (!GDALExtendedDataType::CopyValue(
|
|
pabySrc + srcComp->GetOffset(), srcComp->GetType(),
|
|
pabyDst + dstComp->GetOffset(), dstComp->GetType()))
|
|
{
|
|
return false;
|
|
};
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CopyValues() */
|
|
/************************************************************************/
|
|
|
|
/** Convert severals value from a source type to a destination type.
|
|
*
|
|
* If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
|
|
* that must be freed with CPLFree().
|
|
*/
|
|
bool GDALExtendedDataType::CopyValues(const void *pSrc,
|
|
const GDALExtendedDataType &srcType,
|
|
GPtrDiff_t nSrcStrideInElts, void *pDst,
|
|
const GDALExtendedDataType &dstType,
|
|
GPtrDiff_t nDstStrideInElts,
|
|
size_t nValues)
|
|
{
|
|
const auto nSrcStrideInBytes =
|
|
nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
|
|
const auto nDstStrideInBytes =
|
|
nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
|
|
if (srcType.GetClass() == GEDTC_NUMERIC &&
|
|
dstType.GetClass() == GEDTC_NUMERIC &&
|
|
nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
|
|
nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
|
|
nDstStrideInBytes >= std::numeric_limits<int>::min() &&
|
|
nDstStrideInBytes <= std::numeric_limits<int>::max())
|
|
{
|
|
GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
|
|
static_cast<int>(nSrcStrideInBytes), pDst,
|
|
dstType.GetNumericDataType(),
|
|
static_cast<int>(nDstStrideInBytes), nValues);
|
|
}
|
|
else
|
|
{
|
|
const GByte *pabySrc = static_cast<const GByte *>(pSrc);
|
|
GByte *pabyDst = static_cast<GByte *>(pDst);
|
|
for (size_t i = 0; i < nValues; ++i)
|
|
{
|
|
if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
|
|
return false;
|
|
pabySrc += nSrcStrideInBytes;
|
|
pabyDst += nDstStrideInBytes;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CheckReadWriteParams() */
|
|
/************************************************************************/
|
|
//! @cond Doxygen_Suppress
|
|
bool GDALAbstractMDArray::CheckReadWriteParams(
|
|
const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
|
|
const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
|
|
const void *buffer, const void *buffer_alloc_start,
|
|
size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
|
|
std::vector<GPtrDiff_t> &tmp_bufferStride) const
|
|
{
|
|
const auto lamda_error = []()
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Not all elements pointed by buffer will fit in "
|
|
"[buffer_alloc_start, "
|
|
"buffer_alloc_start + buffer_alloc_size[");
|
|
};
|
|
|
|
const auto &dims = GetDimensions();
|
|
if (dims.empty())
|
|
{
|
|
if (buffer_alloc_start)
|
|
{
|
|
const size_t elementSize = bufferDataType.GetSize();
|
|
const GByte *paby_buffer = static_cast<const GByte *>(buffer);
|
|
const GByte *paby_buffer_alloc_start =
|
|
static_cast<const GByte *>(buffer_alloc_start);
|
|
const GByte *paby_buffer_alloc_end =
|
|
paby_buffer_alloc_start + buffer_alloc_size;
|
|
|
|
if (paby_buffer < paby_buffer_alloc_start ||
|
|
paby_buffer + elementSize > paby_buffer_alloc_end)
|
|
{
|
|
lamda_error();
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (arrayStep == nullptr)
|
|
{
|
|
tmp_arrayStep.resize(dims.size(), 1);
|
|
arrayStep = tmp_arrayStep.data();
|
|
}
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
if (count[i] == 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
|
|
static_cast<unsigned>(i));
|
|
return false;
|
|
}
|
|
}
|
|
bool bufferStride_all_positive = true;
|
|
if (bufferStride == nullptr)
|
|
{
|
|
GPtrDiff_t stride = 1;
|
|
// To compute strides we must proceed from the fastest varying dimension
|
|
// (the last one), and then reverse the result
|
|
for (size_t i = dims.size(); i != 0;)
|
|
{
|
|
--i;
|
|
tmp_bufferStride.push_back(stride);
|
|
GUInt64 newStride = 0;
|
|
bool bOK;
|
|
try
|
|
{
|
|
newStride = (CPLSM(static_cast<GUInt64>(stride)) *
|
|
CPLSM(static_cast<GUInt64>(count[i])))
|
|
.v();
|
|
bOK = static_cast<size_t>(newStride) == newStride &&
|
|
newStride < std::numeric_limits<size_t>::max() / 2;
|
|
}
|
|
catch (...)
|
|
{
|
|
bOK = false;
|
|
}
|
|
if (!bOK)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
|
|
return false;
|
|
}
|
|
stride = static_cast<GPtrDiff_t>(newStride);
|
|
}
|
|
std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
|
|
bufferStride = tmp_bufferStride.data();
|
|
}
|
|
else
|
|
{
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
if (bufferStride[i] < 0)
|
|
{
|
|
bufferStride_all_positive = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
if (arrayStartIdx[i] >= dims[i]->GetSize())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
|
|
static_cast<unsigned>(i),
|
|
static_cast<GUInt64>(arrayStartIdx[i]),
|
|
static_cast<GUInt64>(dims[i]->GetSize()));
|
|
return false;
|
|
}
|
|
bool bOverflow;
|
|
if (arrayStep[i] >= 0)
|
|
{
|
|
try
|
|
{
|
|
bOverflow = (CPLSM(static_cast<GUInt64>(arrayStartIdx[i])) +
|
|
CPLSM(static_cast<GUInt64>(count[i] - 1)) *
|
|
CPLSM(static_cast<GUInt64>(arrayStep[i])))
|
|
.v() >= dims[i]->GetSize();
|
|
}
|
|
catch (...)
|
|
{
|
|
bOverflow = true;
|
|
}
|
|
if (bOverflow)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
|
|
">= " CPL_FRMT_GUIB,
|
|
static_cast<unsigned>(i), static_cast<unsigned>(i),
|
|
static_cast<unsigned>(i),
|
|
static_cast<GUInt64>(dims[i]->GetSize()));
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
bOverflow =
|
|
arrayStartIdx[i] <
|
|
(CPLSM(static_cast<GUInt64>(count[i] - 1)) *
|
|
CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
|
|
? (static_cast<GUInt64>(1) << 63)
|
|
: static_cast<GUInt64>(-arrayStep[i])))
|
|
.v();
|
|
}
|
|
catch (...)
|
|
{
|
|
bOverflow = true;
|
|
}
|
|
if (bOverflow)
|
|
{
|
|
CPLError(
|
|
CE_Failure, CPLE_AppDefined,
|
|
"arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
|
|
static_cast<unsigned>(i), static_cast<unsigned>(i),
|
|
static_cast<unsigned>(i));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (buffer_alloc_start)
|
|
{
|
|
const size_t elementSize = bufferDataType.GetSize();
|
|
const GByte *paby_buffer = static_cast<const GByte *>(buffer);
|
|
const GByte *paby_buffer_alloc_start =
|
|
static_cast<const GByte *>(buffer_alloc_start);
|
|
const GByte *paby_buffer_alloc_end =
|
|
paby_buffer_alloc_start + buffer_alloc_size;
|
|
if (bufferStride_all_positive)
|
|
{
|
|
if (paby_buffer < paby_buffer_alloc_start)
|
|
{
|
|
lamda_error();
|
|
return false;
|
|
}
|
|
GUInt64 nOffset = elementSize;
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
try
|
|
{
|
|
nOffset = (CPLSM(static_cast<GUInt64>(nOffset)) +
|
|
CPLSM(static_cast<GUInt64>(bufferStride[i])) *
|
|
CPLSM(static_cast<GUInt64>(count[i] - 1)) *
|
|
CPLSM(static_cast<GUInt64>(elementSize)))
|
|
.v();
|
|
}
|
|
catch (...)
|
|
{
|
|
lamda_error();
|
|
return false;
|
|
}
|
|
}
|
|
#if SIZEOF_VOIDP == 4
|
|
if (static_cast<size_t>(nOffset) != nOffset)
|
|
{
|
|
lamda_error();
|
|
return false;
|
|
}
|
|
#endif
|
|
if (paby_buffer + nOffset > paby_buffer_alloc_end)
|
|
{
|
|
lamda_error();
|
|
return false;
|
|
}
|
|
}
|
|
else if (dims.size() < 31)
|
|
{
|
|
// Check all corners of the hypercube
|
|
const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
|
|
for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
|
|
{
|
|
const GByte *paby = paby_buffer;
|
|
for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
|
|
i++)
|
|
{
|
|
if (iCornerCode & (1U << i))
|
|
{
|
|
// We should check for integer overflows
|
|
paby += bufferStride[i] * (count[i] - 1) * elementSize;
|
|
}
|
|
}
|
|
if (paby < paby_buffer_alloc_start ||
|
|
paby + elementSize > paby_buffer_alloc_end)
|
|
{
|
|
lamda_error();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* Read() */
|
|
/************************************************************************/
|
|
|
|
/** Read part or totality of a multidimensional array or attribute.
|
|
*
|
|
* This will extract the content of a hyper-rectangle from the array into
|
|
* a user supplied buffer.
|
|
*
|
|
* If bufferDataType is of type string, the values written in pDstBuffer
|
|
* will be char* pointers and the strings should be freed with CPLFree().
|
|
*
|
|
* This is the same as the C function GDALMDArrayRead().
|
|
*
|
|
* @param arrayStartIdx Values representing the starting index to read
|
|
* in each dimension (in [0, aoDims[i].GetSize()-1] range).
|
|
* Array of GetDimensionCount() values. Must not be
|
|
* nullptr, unless for a zero-dimensional array.
|
|
*
|
|
* @param count Values representing the number of values to extract in
|
|
* each dimension.
|
|
* Array of GetDimensionCount() values. Must not be
|
|
* nullptr, unless for a zero-dimensional array.
|
|
*
|
|
* @param arrayStep Spacing between values to extract in each dimension.
|
|
* The spacing is in number of array elements, not bytes.
|
|
* If provided, must contain GetDimensionCount() values.
|
|
* If set to nullptr, [1, 1, ... 1] will be used as a
|
|
* default to indicate consecutive elements.
|
|
*
|
|
* @param bufferStride Spacing between values to store in pDstBuffer.
|
|
* The spacing is in number of array elements, not bytes.
|
|
* If provided, must contain GetDimensionCount() values.
|
|
* Negative values are possible (for example to reorder
|
|
* from bottom-to-top to top-to-bottom).
|
|
* If set to nullptr, will be set so that pDstBuffer is
|
|
* written in a compact way, with elements of the last /
|
|
* fastest varying dimension being consecutive.
|
|
*
|
|
* @param bufferDataType Data type of values in pDstBuffer.
|
|
*
|
|
* @param pDstBuffer User buffer to store the values read. Should be big
|
|
* enough to store the number of values indicated by
|
|
* count[] and with the spacing of bufferStride[].
|
|
*
|
|
* @param pDstBufferAllocStart Optional pointer that can be used to validate the
|
|
* validty of pDstBuffer. pDstBufferAllocStart
|
|
* should be the pointer returned by the malloc() or equivalent call used to
|
|
* allocate the buffer. It will generally be equal to pDstBuffer (when
|
|
* bufferStride[] values are all positive), but not necessarily. If specified,
|
|
* nDstBufferAllocSize should be also set to the appropriate value. If no
|
|
* validation is needed, nullptr can be passed.
|
|
*
|
|
* @param nDstBufferAllocSize Optional buffer size, that can be used to
|
|
* validate the validty of pDstBuffer. This is the size of the buffer starting
|
|
* at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
|
|
* set to the appropriate value.
|
|
* If no validation is needed, 0 can be passed.
|
|
*
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALAbstractMDArray::Read(
|
|
const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, // step in elements
|
|
const GPtrDiff_t *bufferStride, // stride in elements
|
|
const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
|
|
const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
|
|
{
|
|
if (!GetDataType().CanConvertTo(bufferDataType))
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Array data type is not convertible to buffer data type");
|
|
return false;
|
|
}
|
|
|
|
std::vector<GInt64> tmp_arrayStep;
|
|
std::vector<GPtrDiff_t> tmp_bufferStride;
|
|
if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
|
|
bufferDataType, pDstBuffer, pDstBufferAllocStart,
|
|
nDstBufferAllocSize, tmp_arrayStep,
|
|
tmp_bufferStride))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
|
|
pDstBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWrite() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
|
|
const GInt64 *, const GPtrDiff_t *,
|
|
const GDALExtendedDataType &, const void *)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
|
|
return false;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* Write() */
|
|
/************************************************************************/
|
|
|
|
/** Write part or totality of a multidimensional array or attribute.
|
|
*
|
|
* This will set the content of a hyper-rectangle into the array from
|
|
* a user supplied buffer.
|
|
*
|
|
* If bufferDataType is of type string, the values read from pSrcBuffer
|
|
* will be char* pointers.
|
|
*
|
|
* This is the same as the C function GDALMDArrayWrite().
|
|
*
|
|
* @param arrayStartIdx Values representing the starting index to write
|
|
* in each dimension (in [0, aoDims[i].GetSize()-1] range).
|
|
* Array of GetDimensionCount() values. Must not be
|
|
* nullptr, unless for a zero-dimensional array.
|
|
*
|
|
* @param count Values representing the number of values to write in
|
|
* each dimension.
|
|
* Array of GetDimensionCount() values. Must not be
|
|
* nullptr, unless for a zero-dimensional array.
|
|
*
|
|
* @param arrayStep Spacing between values to write in each dimension.
|
|
* The spacing is in number of array elements, not bytes.
|
|
* If provided, must contain GetDimensionCount() values.
|
|
* If set to nullptr, [1, 1, ... 1] will be used as a
|
|
* default to indicate consecutive elements.
|
|
*
|
|
* @param bufferStride Spacing between values to read from pSrcBuffer.
|
|
* The spacing is in number of array elements, not bytes.
|
|
* If provided, must contain GetDimensionCount() values.
|
|
* Negative values are possible (for example to reorder
|
|
* from bottom-to-top to top-to-bottom).
|
|
* If set to nullptr, will be set so that pSrcBuffer is
|
|
* written in a compact way, with elements of the last /
|
|
* fastest varying dimension being consecutive.
|
|
*
|
|
* @param bufferDataType Data type of values in pSrcBuffer.
|
|
*
|
|
* @param pSrcBuffer User buffer to read the values from. Should be big
|
|
* enough to store the number of values indicated by
|
|
* count[] and with the spacing of bufferStride[].
|
|
*
|
|
* @param pSrcBufferAllocStart Optional pointer that can be used to validate the
|
|
* validty of pSrcBuffer. pSrcBufferAllocStart
|
|
* should be the pointer returned by the malloc() or equivalent call used to
|
|
* allocate the buffer. It will generally be equal to pSrcBuffer (when
|
|
* bufferStride[] values are all positive), but not necessarily. If specified,
|
|
* nSrcBufferAllocSize should be also set to the appropriate value. If no
|
|
* validation is needed, nullptr can be passed.
|
|
*
|
|
* @param nSrcBufferAllocSize Optional buffer size, that can be used to
|
|
* validate the validty of pSrcBuffer. This is the size of the buffer starting
|
|
* at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
|
|
* set to the appropriate value.
|
|
* If no validation is needed, 0 can be passed.
|
|
*
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
const void *pSrcBuffer,
|
|
const void *pSrcBufferAllocStart,
|
|
size_t nSrcBufferAllocSize)
|
|
{
|
|
if (!bufferDataType.CanConvertTo(GetDataType()))
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Buffer data type is not convertible to array data type");
|
|
return false;
|
|
}
|
|
|
|
std::vector<GInt64> tmp_arrayStep;
|
|
std::vector<GPtrDiff_t> tmp_bufferStride;
|
|
if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
|
|
bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
|
|
nSrcBufferAllocSize, tmp_arrayStep,
|
|
tmp_bufferStride))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
|
|
pSrcBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetTotalElementsCount() */
|
|
/************************************************************************/
|
|
|
|
/** Return the total number of values in the array.
|
|
*
|
|
* This is the same as the C functions GDALMDArrayGetTotalElementsCount()
|
|
* and GDALAttributeGetTotalElementsCount().
|
|
*
|
|
*/
|
|
GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
|
|
{
|
|
const auto &dims = GetDimensions();
|
|
if (dims.empty())
|
|
return 1;
|
|
GUInt64 nElts = 1;
|
|
for (const auto &dim : dims)
|
|
{
|
|
try
|
|
{
|
|
nElts = (CPLSM(static_cast<GUInt64>(nElts)) *
|
|
CPLSM(static_cast<GUInt64>(dim->GetSize())))
|
|
.v();
|
|
}
|
|
catch (...)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
return nElts;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetBlockSize() */
|
|
/************************************************************************/
|
|
|
|
/** Return the "natural" block size of the array along all dimensions.
|
|
*
|
|
* Some drivers might organize the array in tiles/blocks and reading/writing
|
|
* aligned on those tile/block boundaries will be more efficient.
|
|
*
|
|
* The returned number of elements in the vector is the same as
|
|
* GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
|
|
* the natural block size along the considered dimension.
|
|
* "Flat" arrays will typically return a vector of values set to 0.
|
|
*
|
|
* The default implementation will return a vector of values set to 0.
|
|
*
|
|
* This method is used by GetProcessingChunkSize().
|
|
*
|
|
* Pedantic note: the returned type is GUInt64, so in the highly unlikeley
|
|
* theoretical case of a 32-bit platform, this might exceed its size_t
|
|
* allocation capabilities.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetBlockSize().
|
|
*
|
|
* @return the block size, in number of elements along each dimension.
|
|
*/
|
|
std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
|
|
{
|
|
return std::vector<GUInt64>(GetDimensionCount());
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetProcessingChunkSize() */
|
|
/************************************************************************/
|
|
|
|
/** \brief Return an optimal chunk size for read/write operations, given the
|
|
* natural block size and memory constraints specified.
|
|
*
|
|
* This method will use GetBlockSize() to define a chunk whose dimensions are
|
|
* multiple of those returned by GetBlockSize() (unless the block define by
|
|
* GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
|
|
* returned by this method).
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetProcessingChunkSize().
|
|
*
|
|
* @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
|
|
* chunk.
|
|
*
|
|
* @return the chunk size, in number of elements along each dimension.
|
|
*/
|
|
std::vector<size_t>
|
|
GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
|
|
{
|
|
const auto &dims = GetDimensions();
|
|
const auto &nDTSize = GetDataType().GetSize();
|
|
std::vector<size_t> anChunkSize;
|
|
auto blockSize = GetBlockSize();
|
|
CPLAssert(blockSize.size() == dims.size());
|
|
size_t nChunkSize = nDTSize;
|
|
bool bOverflow = false;
|
|
constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
|
|
// Initialize anChunkSize[i] with blockSize[i] by properly clamping in
|
|
// [1, min(sizet_max, dim_size[i])]
|
|
// Also make sure that the product of all anChunkSize[i]) fits on size_t
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
const auto sizeDimI =
|
|
std::max(static_cast<size_t>(1),
|
|
static_cast<size_t>(
|
|
std::min(static_cast<GUInt64>(kSIZE_T_MAX),
|
|
std::min(blockSize[i], dims[i]->GetSize()))));
|
|
anChunkSize.push_back(sizeDimI);
|
|
if (nChunkSize > kSIZE_T_MAX / sizeDimI)
|
|
{
|
|
bOverflow = true;
|
|
}
|
|
else
|
|
{
|
|
nChunkSize *= sizeDimI;
|
|
}
|
|
}
|
|
if (nChunkSize == 0)
|
|
return anChunkSize;
|
|
|
|
// If the product of all anChunkSize[i] does not fit on size_t, then
|
|
// set lowest anChunkSize[i] to 1.
|
|
if (bOverflow)
|
|
{
|
|
nChunkSize = nDTSize;
|
|
bOverflow = false;
|
|
for (size_t i = dims.size(); i > 0;)
|
|
{
|
|
--i;
|
|
if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
|
|
{
|
|
bOverflow = true;
|
|
anChunkSize[i] = 1;
|
|
}
|
|
else
|
|
{
|
|
nChunkSize *= anChunkSize[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
nChunkSize = nDTSize;
|
|
std::vector<size_t> anAccBlockSizeFromStart;
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
nChunkSize *= anChunkSize[i];
|
|
anAccBlockSizeFromStart.push_back(nChunkSize);
|
|
}
|
|
if (nChunkSize <= nMaxChunkMemory / 2)
|
|
{
|
|
size_t nVoxelsFromEnd = 1;
|
|
for (size_t i = dims.size(); i > 0;)
|
|
{
|
|
--i;
|
|
const auto nCurBlockSize =
|
|
anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
|
|
const auto nMul = nMaxChunkMemory / nCurBlockSize;
|
|
if (nMul >= 2)
|
|
{
|
|
const auto nSizeThisDim(dims[i]->GetSize());
|
|
const auto nBlocksThisDim =
|
|
DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
|
|
anChunkSize[i] = static_cast<size_t>(std::min(
|
|
anChunkSize[i] *
|
|
std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
|
|
nSizeThisDim));
|
|
}
|
|
nVoxelsFromEnd *= anChunkSize[i];
|
|
}
|
|
}
|
|
return anChunkSize;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetUnit() */
|
|
/************************************************************************/
|
|
|
|
/** Set the variable unit.
|
|
*
|
|
* Values should conform as much as possible with those allowed by
|
|
* the NetCDF CF conventions:
|
|
* http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
|
|
* but others might be returned.
|
|
*
|
|
* Few examples are "meter", "degrees", "second", ...
|
|
* Empty value means unknown.
|
|
*
|
|
* This is the same as the C function GDALMDArraySetUnit()
|
|
*
|
|
* @note Driver implementation: optionally implemented.
|
|
*
|
|
* @param osUnit unit name.
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetUnit() */
|
|
/************************************************************************/
|
|
|
|
/** Return the array unit.
|
|
*
|
|
* Values should conform as much as possible with those allowed by
|
|
* the NetCDF CF conventions:
|
|
* http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
|
|
* but others might be returned.
|
|
*
|
|
* Few examples are "meter", "degrees", "second", ...
|
|
* Empty value means unknown.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetUnit()
|
|
*/
|
|
const std::string &GDALMDArray::GetUnit() const
|
|
{
|
|
static const std::string emptyString;
|
|
return emptyString;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetSpatialRef() */
|
|
/************************************************************************/
|
|
|
|
/** Assign a spatial reference system object to the array.
|
|
*
|
|
* This is the same as the C function GDALMDArraySetSpatialRef().
|
|
*/
|
|
bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetSpatialRef() */
|
|
/************************************************************************/
|
|
|
|
/** Return the spatial reference system object associated with the array.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetSpatialRef().
|
|
*/
|
|
std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetRawNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
/** Return the nodata value as a "raw" value.
|
|
*
|
|
* The value returned might be nullptr in case of no nodata value. When
|
|
* a nodata value is registered, a non-nullptr will be returned whose size in
|
|
* bytes is GetDataType().GetSize().
|
|
*
|
|
* The returned value should not be modified or freed. It is valid until
|
|
* the array is destroyed, or the next call to GetRawNoDataValue() or
|
|
* SetRawNoDataValue(), or any similar methods.
|
|
*
|
|
* @note Driver implementation: this method shall be implemented if nodata
|
|
* is supported.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetRawNoDataValue().
|
|
*
|
|
* @return nullptr or a pointer to GetDataType().GetSize() bytes.
|
|
*/
|
|
const void *GDALMDArray::GetRawNoDataValue() const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValueAsDouble() */
|
|
/************************************************************************/
|
|
|
|
/** Return the nodata value as a double.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
|
|
*
|
|
* @param pbHasNoData Pointer to a output boolean that will be set to true if
|
|
* a nodata value exists and can be converted to double. Might be nullptr.
|
|
*
|
|
* @return the nodata value as a double. A 0.0 value might also indicate the
|
|
* absence of a nodata value or an error in the conversion (*pbHasNoData will be
|
|
* set to false then).
|
|
*/
|
|
double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
|
|
{
|
|
const void *pNoData = GetRawNoDataValue();
|
|
double dfNoData = 0.0;
|
|
const auto &eDT = GetDataType();
|
|
const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
|
|
if (ok)
|
|
{
|
|
GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
|
|
GDT_Float64, 0, 1);
|
|
}
|
|
if (pbHasNoData)
|
|
*pbHasNoData = ok;
|
|
return dfNoData;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValueAsInt64() */
|
|
/************************************************************************/
|
|
|
|
/** Return the nodata value as a Int64.
|
|
*
|
|
* @param pbHasNoData Pointer to a output boolean that will be set to true if
|
|
* a nodata value exists and can be converted to Int64. Might be nullptr.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
|
|
*
|
|
* @return the nodata value as a Int64
|
|
*
|
|
* @since GDAL 3.5
|
|
*/
|
|
int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
|
|
{
|
|
const void *pNoData = GetRawNoDataValue();
|
|
int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
|
|
const auto &eDT = GetDataType();
|
|
const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
|
|
if (ok)
|
|
{
|
|
GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData, GDT_Int64,
|
|
0, 1);
|
|
}
|
|
if (pbHasNoData)
|
|
*pbHasNoData = ok;
|
|
return nNoData;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValueAsUInt64() */
|
|
/************************************************************************/
|
|
|
|
/** Return the nodata value as a UInt64.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
|
|
|
|
* @param pbHasNoData Pointer to a output boolean that will be set to true if
|
|
* a nodata value exists and can be converted to UInt64. Might be nullptr.
|
|
*
|
|
* @return the nodata value as a UInt64
|
|
*
|
|
* @since GDAL 3.5
|
|
*/
|
|
uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
|
|
{
|
|
const void *pNoData = GetRawNoDataValue();
|
|
uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
|
|
const auto &eDT = GetDataType();
|
|
const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
|
|
if (ok)
|
|
{
|
|
GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
|
|
GDT_UInt64, 0, 1);
|
|
}
|
|
if (pbHasNoData)
|
|
*pbHasNoData = ok;
|
|
return nNoData;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetRawNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
/** Set the nodata value as a "raw" value.
|
|
*
|
|
* The value passed might be nullptr in case of no nodata value. When
|
|
* a nodata value is registered, a non-nullptr whose size in
|
|
* bytes is GetDataType().GetSize() must be passed.
|
|
*
|
|
* This is the same as the C function GDALMDArraySetRawNoDataValue().
|
|
*
|
|
* @note Driver implementation: this method shall be implemented if setting
|
|
nodata
|
|
* is supported.
|
|
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"SetRawNoDataValue() not implemented");
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
/** Set the nodata value as a double.
|
|
*
|
|
* If the natural data type of the attribute/array is not double, type
|
|
* conversion will occur to the type returned by GetDataType().
|
|
*
|
|
* This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
|
|
*
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALMDArray::SetNoDataValue(double dfNoData)
|
|
{
|
|
void *pRawNoData = CPLMalloc(GetDataType().GetSize());
|
|
bool bRet = false;
|
|
if (GDALExtendedDataType::CopyValue(
|
|
&dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
|
|
GetDataType()))
|
|
{
|
|
bRet = SetRawNoDataValue(pRawNoData);
|
|
}
|
|
CPLFree(pRawNoData);
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
/** Set the nodata value as a Int64.
|
|
*
|
|
* If the natural data type of the attribute/array is not Int64, type conversion
|
|
* will occur to the type returned by GetDataType().
|
|
*
|
|
* This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
|
|
*
|
|
* @return true in case of success.
|
|
*
|
|
* @since GDAL 3.5
|
|
*/
|
|
bool GDALMDArray::SetNoDataValue(int64_t nNoData)
|
|
{
|
|
void *pRawNoData = CPLMalloc(GetDataType().GetSize());
|
|
bool bRet = false;
|
|
if (GDALExtendedDataType::CopyValue(&nNoData,
|
|
GDALExtendedDataType::Create(GDT_Int64),
|
|
pRawNoData, GetDataType()))
|
|
{
|
|
bRet = SetRawNoDataValue(pRawNoData);
|
|
}
|
|
CPLFree(pRawNoData);
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
/** Set the nodata value as a Int64.
|
|
*
|
|
* If the natural data type of the attribute/array is not Int64, type conversion
|
|
* will occur to the type returned by GetDataType().
|
|
*
|
|
* This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
|
|
*
|
|
* @return true in case of success.
|
|
*
|
|
* @since GDAL 3.5
|
|
*/
|
|
bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
|
|
{
|
|
void *pRawNoData = CPLMalloc(GetDataType().GetSize());
|
|
bool bRet = false;
|
|
if (GDALExtendedDataType::CopyValue(
|
|
&nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
|
|
GetDataType()))
|
|
{
|
|
bRet = SetRawNoDataValue(pRawNoData);
|
|
}
|
|
CPLFree(pRawNoData);
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Resize() */
|
|
/************************************************************************/
|
|
|
|
/** Resize an array to new dimensions.
|
|
*
|
|
* Not all drivers may allow this operation, and with restrictions (e.g.
|
|
* for netCDF, this is limited to growing of "unlimited" dimensions)
|
|
*
|
|
* Resizing a dimension used in other arrays will cause those other arrays
|
|
* to be resized.
|
|
*
|
|
* This is the same as the C function GDALMDArrayResize().
|
|
*
|
|
* @param anNewDimSizes Array of GetDimensionCount() values containing the
|
|
* new size of each indexing dimension.
|
|
* @param papszOptions Options. (Driver specific)
|
|
* @return true in case of success.
|
|
* @since GDAL 3.7
|
|
*/
|
|
bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
|
|
CPL_UNUSED CSLConstList papszOptions)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Resize() is not supported for this array");
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetScale() */
|
|
/************************************************************************/
|
|
|
|
/** Set the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C function GDALMDArraySetScale() /
|
|
* GDALMDArraySetScaleEx().
|
|
*
|
|
* @note Driver implementation: this method shall be implemented if setting
|
|
* scale is supported.
|
|
*
|
|
* @param dfScale scale
|
|
* @param eStorageType Data type to which create the potential attribute that
|
|
* will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
|
|
* implementation will decide automatically the data type. Note that changing
|
|
* the data type after initial setting might not be supported.
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
|
|
CPL_UNUSED GDALDataType eStorageType)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetOffset) */
|
|
/************************************************************************/
|
|
|
|
/** Set the offset value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C function GDALMDArraySetOffset() /
|
|
* GDALMDArraySetOffsetEx().
|
|
*
|
|
* @note Driver implementation: this method shall be implemented if setting
|
|
* offset is supported.
|
|
*
|
|
* @param dfOffset Offset
|
|
* @param eStorageType Data type to which create the potential attribute that
|
|
* will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
|
|
* implementation will decide automatically the data type. Note that changing
|
|
* the data type after initial setting might not be supported.
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
|
|
CPL_UNUSED GDALDataType eStorageType)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetScale() */
|
|
/************************************************************************/
|
|
|
|
/** Get the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetScale().
|
|
*
|
|
* @note Driver implementation: this method shall be implemented if gettings
|
|
* scale is supported.
|
|
*
|
|
* @param pbHasScale Pointer to a output boolean that will be set to true if
|
|
* a scale value exists. Might be nullptr.
|
|
* @param peStorageType Pointer to a output GDALDataType that will be set to
|
|
* the storage type of the scale value, when known/relevant. Otherwise will be
|
|
* set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
|
|
*
|
|
* @return the scale value. A 1.0 value might also indicate the
|
|
* absence of a scale value.
|
|
*/
|
|
double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
|
|
CPL_UNUSED GDALDataType *peStorageType) const
|
|
{
|
|
if (pbHasScale)
|
|
*pbHasScale = false;
|
|
return 1.0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetOffset() */
|
|
/************************************************************************/
|
|
|
|
/** Get the offset value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetOffset().
|
|
*
|
|
* @note Driver implementation: this method shall be implemented if gettings
|
|
* offset is supported.
|
|
*
|
|
* @param pbHasOffset Pointer to a output boolean that will be set to true if
|
|
* a offset value exists. Might be nullptr.
|
|
* @param peStorageType Pointer to a output GDALDataType that will be set to
|
|
* the storage type of the offset value, when known/relevant. Otherwise will be
|
|
* set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
|
|
*
|
|
* @return the offset value. A 0.0 value might also indicate the
|
|
* absence of a offset value.
|
|
*/
|
|
double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
|
|
CPL_UNUSED GDALDataType *peStorageType) const
|
|
{
|
|
if (pbHasOffset)
|
|
*pbHasOffset = false;
|
|
return 0.0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ProcessPerChunk() */
|
|
/************************************************************************/
|
|
|
|
namespace
|
|
{
|
|
enum class Caller
|
|
{
|
|
CALLER_END_OF_LOOP,
|
|
CALLER_IN_LOOP,
|
|
};
|
|
}
|
|
|
|
/** \brief Call a user-provided function to operate on an array chunk by chunk.
|
|
*
|
|
* This method is to be used when doing operations on an array, or a subset of
|
|
* it, in a chunk by chunk way.
|
|
*
|
|
* @param arrayStartIdx Values representing the starting index to use
|
|
* in each dimension (in [0, aoDims[i].GetSize()-1] range).
|
|
* Array of GetDimensionCount() values. Must not be
|
|
* nullptr, unless for a zero-dimensional array.
|
|
*
|
|
* @param count Values representing the number of values to use in
|
|
* each dimension.
|
|
* Array of GetDimensionCount() values. Must not be
|
|
* nullptr, unless for a zero-dimensional array.
|
|
*
|
|
* @param chunkSize Values representing the chunk size in each dimension.
|
|
* Might typically the output of GetProcessingChunkSize().
|
|
* Array of GetDimensionCount() values. Must not be
|
|
* nullptr, unless for a zero-dimensional array.
|
|
*
|
|
* @param pfnFunc User-provided function of type FuncProcessPerChunkType.
|
|
* Must NOT be nullptr.
|
|
*
|
|
* @param pUserData Pointer to pass as the value of the pUserData argument
|
|
* of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
|
|
*
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
|
|
const GUInt64 *count,
|
|
const size_t *chunkSize,
|
|
FuncProcessPerChunkType pfnFunc,
|
|
void *pUserData)
|
|
{
|
|
const auto &dims = GetDimensions();
|
|
if (dims.empty())
|
|
{
|
|
return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
|
|
}
|
|
|
|
// Sanity check
|
|
size_t nTotalChunkSize = 1;
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
const auto nSizeThisDim(dims[i]->GetSize());
|
|
if (count[i] == 0 || count[i] > nSizeThisDim ||
|
|
arrayStartIdx[i] > nSizeThisDim - count[i])
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Inconsistent arrayStartIdx[] / count[] values "
|
|
"regarding array size");
|
|
return false;
|
|
}
|
|
if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
|
|
chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Inconsistent chunkSize[] values");
|
|
return false;
|
|
}
|
|
nTotalChunkSize *= chunkSize[i];
|
|
}
|
|
|
|
size_t dimIdx = 0;
|
|
std::vector<GUInt64> chunkArrayStartIdx(dims.size());
|
|
std::vector<size_t> chunkCount(dims.size());
|
|
struct Stack
|
|
{
|
|
GUInt64 nBlockCounter = 0;
|
|
GUInt64 nBlocksMinusOne = 0;
|
|
size_t first_count = 0; // only used if nBlocks > 1
|
|
Caller return_point = Caller::CALLER_END_OF_LOOP;
|
|
};
|
|
std::vector<Stack> stack(dims.size());
|
|
GUInt64 iCurChunk = 0;
|
|
GUInt64 nChunkCount = 1;
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
|
|
const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
|
|
stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
|
|
nChunkCount *= 1 + stack[i].nBlocksMinusOne;
|
|
if (stack[i].nBlocksMinusOne == 0)
|
|
{
|
|
chunkArrayStartIdx[i] = arrayStartIdx[i];
|
|
chunkCount[i] = static_cast<size_t>(count[i]);
|
|
}
|
|
else
|
|
{
|
|
stack[i].first_count = static_cast<size_t>(
|
|
(nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
|
|
}
|
|
}
|
|
|
|
lbl_next_depth:
|
|
if (dimIdx == dims.size())
|
|
{
|
|
++iCurChunk;
|
|
if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
|
|
iCurChunk, nChunkCount, pUserData))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (stack[dimIdx].nBlocksMinusOne != 0)
|
|
{
|
|
stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
|
|
chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
|
|
chunkCount[dimIdx] = stack[dimIdx].first_count;
|
|
stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
|
|
while (true)
|
|
{
|
|
dimIdx++;
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller_in_loop:
|
|
--stack[dimIdx].nBlockCounter;
|
|
if (stack[dimIdx].nBlockCounter == 0)
|
|
break;
|
|
chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
|
|
chunkCount[dimIdx] = chunkSize[dimIdx];
|
|
}
|
|
|
|
chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
|
|
chunkCount[dimIdx] =
|
|
static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
|
|
chunkArrayStartIdx[dimIdx]);
|
|
stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
|
|
}
|
|
dimIdx++;
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller_end_of_loop:
|
|
if (dimIdx == 0)
|
|
goto end;
|
|
}
|
|
|
|
dimIdx--;
|
|
// cppcheck-suppress negativeContainerIndex
|
|
switch (stack[dimIdx].return_point)
|
|
{
|
|
case Caller::CALLER_END_OF_LOOP:
|
|
goto lbl_return_to_caller_end_of_loop;
|
|
case Caller::CALLER_IN_LOOP:
|
|
goto lbl_return_to_caller_in_loop;
|
|
}
|
|
end:
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttribute() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
|
|
CPL_UNUSED const std::string &osName)
|
|
#if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
|
|
: GDALAbstractMDArray(osParentName, osName)
|
|
#endif
|
|
{
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* GetDimensionSize() */
|
|
/************************************************************************/
|
|
|
|
/** Return the size of the dimensions of the attribute.
|
|
*
|
|
* This will be an empty array for a scalar (single value) attribute.
|
|
*
|
|
* This is the same as the C function GDALAttributeGetDimensionsSize().
|
|
*/
|
|
std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
|
|
{
|
|
const auto &dims = GetDimensions();
|
|
std::vector<GUInt64> ret;
|
|
ret.reserve(dims.size());
|
|
for (const auto &dim : dims)
|
|
ret.push_back(dim->GetSize());
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRawResult() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
|
|
size_t nEltCount)
|
|
: m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
|
|
m_raw(raw)
|
|
{
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* GDALRawResult() */
|
|
/************************************************************************/
|
|
|
|
/** Move constructor. */
|
|
GDALRawResult::GDALRawResult(GDALRawResult &&other)
|
|
: m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
|
|
m_nSize(other.m_nSize), m_raw(other.m_raw)
|
|
{
|
|
other.m_nEltCount = 0;
|
|
other.m_nSize = 0;
|
|
other.m_raw = nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* FreeMe() */
|
|
/************************************************************************/
|
|
|
|
void GDALRawResult::FreeMe()
|
|
{
|
|
if (m_raw && m_dt.NeedsFreeDynamicMemory())
|
|
{
|
|
GByte *pabyPtr = m_raw;
|
|
const auto nDTSize(m_dt.GetSize());
|
|
for (size_t i = 0; i < m_nEltCount; ++i)
|
|
{
|
|
m_dt.FreeDynamicMemory(pabyPtr);
|
|
pabyPtr += nDTSize;
|
|
}
|
|
}
|
|
VSIFree(m_raw);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* operator=() */
|
|
/************************************************************************/
|
|
|
|
/** Move assignment. */
|
|
GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
|
|
{
|
|
FreeMe();
|
|
m_dt = std::move(other.m_dt);
|
|
m_nEltCount = other.m_nEltCount;
|
|
m_nSize = other.m_nSize;
|
|
m_raw = other.m_raw;
|
|
other.m_nEltCount = 0;
|
|
other.m_nSize = 0;
|
|
other.m_raw = nullptr;
|
|
return *this;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GDALRawResult() */
|
|
/************************************************************************/
|
|
|
|
/** Destructor. */
|
|
GDALRawResult::~GDALRawResult()
|
|
{
|
|
FreeMe();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* StealData() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
/** Return buffer to caller which becomes owner of it.
|
|
* Only to be used by GDALAttributeReadAsRaw().
|
|
*/
|
|
GByte *GDALRawResult::StealData()
|
|
{
|
|
GByte *ret = m_raw;
|
|
m_raw = nullptr;
|
|
m_nEltCount = 0;
|
|
m_nSize = 0;
|
|
return ret;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* ReadAsRaw() */
|
|
/************************************************************************/
|
|
|
|
/** Return the raw value of an attribute.
|
|
*
|
|
*
|
|
* This is the same as the C function GDALAttributeReadAsRaw().
|
|
*/
|
|
GDALRawResult GDALAttribute::ReadAsRaw() const
|
|
{
|
|
const auto nEltCount(GetTotalElementsCount());
|
|
const auto dt(GetDataType());
|
|
const auto nDTSize(dt.GetSize());
|
|
GByte *res = static_cast<GByte *>(
|
|
VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
|
|
if (!res)
|
|
return GDALRawResult(nullptr, dt, 0);
|
|
const auto &dims = GetDimensions();
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims);
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
count[i] = static_cast<size_t>(dims[i]->GetSize());
|
|
}
|
|
if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
|
|
&res[0], static_cast<size_t>(nEltCount * nDTSize)))
|
|
{
|
|
VSIFree(res);
|
|
return GDALRawResult(nullptr, dt, 0);
|
|
}
|
|
return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ReadAsString() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as a string.
|
|
*
|
|
* The returned string should not be freed, and its lifetime does not
|
|
* excess a next call to ReadAsString() on the same object, or the deletion
|
|
* of the object itself.
|
|
*
|
|
* This function will only return the first element if there are several.
|
|
*
|
|
* This is the same as the C function GDALAttributeReadAsString()
|
|
*
|
|
* @return a string, or nullptr.
|
|
*/
|
|
const char *GDALAttribute::ReadAsString() const
|
|
{
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims, 1);
|
|
char *szRet = nullptr;
|
|
if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::CreateString(), &szRet, &szRet,
|
|
sizeof(szRet)) ||
|
|
szRet == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
m_osCachedVal = szRet;
|
|
CPLFree(szRet);
|
|
return m_osCachedVal.c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ReadAsInt() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as a integer.
|
|
*
|
|
* This function will only return the first element if there are several.
|
|
*
|
|
* It can fail if its value can be converted to integer.
|
|
*
|
|
* This is the same as the C function GDALAttributeReadAsInt()
|
|
*
|
|
* @return a integer, or INT_MIN in case of error.
|
|
*/
|
|
int GDALAttribute::ReadAsInt() const
|
|
{
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims, 1);
|
|
int nRet = INT_MIN;
|
|
Read(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
|
|
return nRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ReadAsDouble() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as a double.
|
|
*
|
|
* This function will only return the first element if there are several.
|
|
*
|
|
* It can fail if its value can be converted to double.
|
|
*
|
|
* This is the same as the C function GDALAttributeReadAsInt()
|
|
*
|
|
* @return a double value.
|
|
*/
|
|
double GDALAttribute::ReadAsDouble() const
|
|
{
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims, 1);
|
|
double dfRet = 0;
|
|
Read(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
|
|
sizeof(dfRet));
|
|
return dfRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ReadAsStringArray() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as an array of strings.
|
|
*
|
|
* This is the same as the C function GDALAttributeReadAsStringArray()
|
|
*/
|
|
CPLStringList GDALAttribute::ReadAsStringArray() const
|
|
{
|
|
const auto nElts = GetTotalElementsCount();
|
|
if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
|
|
return CPLStringList();
|
|
char **papszList = static_cast<char **>(
|
|
VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
|
|
const auto &dims = GetDimensions();
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims);
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
count[i] = static_cast<size_t>(dims[i]->GetSize());
|
|
}
|
|
Read(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::CreateString(), papszList, papszList,
|
|
sizeof(char *) * static_cast<int>(nElts));
|
|
for (int i = 0; i < static_cast<int>(nElts); i++)
|
|
{
|
|
if (papszList[i] == nullptr)
|
|
papszList[i] = CPLStrdup("");
|
|
}
|
|
return CPLStringList(papszList);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ReadAsIntArray() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as an array of integers.
|
|
*
|
|
* This is the same as the C function GDALAttributeReadAsIntArray().
|
|
*/
|
|
std::vector<int> GDALAttribute::ReadAsIntArray() const
|
|
{
|
|
const auto nElts = GetTotalElementsCount();
|
|
#if SIZEOF_VOIDP == 4
|
|
if (nElts > static_cast<size_t>(nElts))
|
|
return {};
|
|
#endif
|
|
std::vector<int> res(static_cast<size_t>(nElts));
|
|
const auto &dims = GetDimensions();
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims);
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
count[i] = static_cast<size_t>(dims[i]->GetSize());
|
|
}
|
|
Read(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
|
|
res.size() * sizeof(res[0]));
|
|
return res;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ReadAsDoubleArray() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as an array of double.
|
|
*
|
|
* This is the same as the C function GDALAttributeReadAsDoubleArray().
|
|
*/
|
|
std::vector<double> GDALAttribute::ReadAsDoubleArray() const
|
|
{
|
|
const auto nElts = GetTotalElementsCount();
|
|
#if SIZEOF_VOIDP == 4
|
|
if (nElts > static_cast<size_t>(nElts))
|
|
return {};
|
|
#endif
|
|
std::vector<double> res(static_cast<size_t>(nElts));
|
|
const auto &dims = GetDimensions();
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims);
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
count[i] = static_cast<size_t>(dims[i]->GetSize());
|
|
}
|
|
Read(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
|
|
res.size() * sizeof(res[0]));
|
|
return res;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Write() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from raw values expressed in GetDataType()
|
|
*
|
|
* The values should be provided in the type of GetDataType() and there should
|
|
* be exactly GetTotalElementsCount() of them.
|
|
* If GetDataType() is a string, each value should be a char* pointer.
|
|
*
|
|
* This is the same as the C function GDALAttributeWriteRaw().
|
|
*
|
|
* @param pabyValue Buffer of nLen bytes.
|
|
* @param nLen Size of pabyValue in bytes. Should be equal to
|
|
* GetTotalElementsCount() * GetDataType().GetSize()
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
|
|
{
|
|
if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Length is not of expected value");
|
|
return false;
|
|
}
|
|
const auto &dims = GetDimensions();
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims);
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
count[i] = static_cast<size_t>(dims[i]->GetSize());
|
|
}
|
|
return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
|
|
pabyValue, pabyValue, nLen);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Write() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from a string value.
|
|
*
|
|
* Type conversion will be performed if needed. If the attribute contains
|
|
* multiple values, only the first one will be updated.
|
|
*
|
|
* This is the same as the C function GDALAttributeWriteString().
|
|
*
|
|
* @param pszValue Pointer to a string.
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALAttribute::Write(const char *pszValue)
|
|
{
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims, 1);
|
|
return Write(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
|
|
sizeof(pszValue));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WriteInt() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from a integer value.
|
|
*
|
|
* Type conversion will be performed if needed. If the attribute contains
|
|
* multiple values, only the first one will be updated.
|
|
*
|
|
* This is the same as the C function GDALAttributeWriteInt().
|
|
*
|
|
* @param nVal Value.
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALAttribute::WriteInt(int nVal)
|
|
{
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims, 1);
|
|
return Write(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
|
|
sizeof(nVal));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Write() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from a double value.
|
|
*
|
|
* Type conversion will be performed if needed. If the attribute contains
|
|
* multiple values, only the first one will be updated.
|
|
*
|
|
* This is the same as the C function GDALAttributeWriteDouble().
|
|
*
|
|
* @param dfVal Value.
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALAttribute::Write(double dfVal)
|
|
{
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims, 1);
|
|
return Write(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
|
|
sizeof(dfVal));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Write() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from an array of strings.
|
|
*
|
|
* Type conversion will be performed if needed.
|
|
*
|
|
* Exactly GetTotalElementsCount() strings must be provided
|
|
*
|
|
* This is the same as the C function GDALAttributeWriteStringArray().
|
|
*
|
|
* @param vals Array of strings.
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALAttribute::Write(CSLConstList vals)
|
|
{
|
|
if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
|
|
return false;
|
|
}
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims);
|
|
const auto &dims = GetDimensions();
|
|
for (size_t i = 0; i < nDims; i++)
|
|
count[i] = static_cast<size_t>(dims[i]->GetSize());
|
|
return Write(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::CreateString(), vals, vals,
|
|
static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Write() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from an array of double.
|
|
*
|
|
* Type conversion will be performed if needed.
|
|
*
|
|
* Exactly GetTotalElementsCount() strings must be provided
|
|
*
|
|
* This is the same as the C function GDALAttributeWriteDoubleArray()
|
|
*
|
|
* @param vals Array of double.
|
|
* @param nVals Should be equal to GetTotalElementsCount().
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALAttribute::Write(const double *vals, size_t nVals)
|
|
{
|
|
if (nVals != GetTotalElementsCount())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
|
|
return false;
|
|
}
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<GUInt64> startIdx(1 + nDims, 0);
|
|
std::vector<size_t> count(1 + nDims);
|
|
const auto &dims = GetDimensions();
|
|
for (size_t i = 0; i < nDims; i++)
|
|
count[i] = static_cast<size_t>(dims[i]->GetSize());
|
|
return Write(startIdx.data(), count.data(), nullptr, nullptr,
|
|
GDALExtendedDataType::Create(GDT_Float64), vals, vals,
|
|
static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArray() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
|
|
CPL_UNUSED const std::string &osName)
|
|
#if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
|
|
: GDALAbstractMDArray(osParentName, osName)
|
|
#endif
|
|
{
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* GetTotalCopyCost() */
|
|
/************************************************************************/
|
|
|
|
/** Return a total "cost" to copy the array.
|
|
*
|
|
* Used as a parameter for CopyFrom()
|
|
*/
|
|
GUInt64 GDALMDArray::GetTotalCopyCost() const
|
|
{
|
|
return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
|
|
GetTotalElementsCount() * GetDataType().GetSize();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CopyFromAllExceptValues() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
|
|
bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
|
|
bool bStrict, GUInt64 &nCurCost,
|
|
const GUInt64 nTotalCost,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
{
|
|
const bool bThisIsUnscaledArray =
|
|
dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
|
|
auto attrs = poSrcArray->GetAttributes();
|
|
for (const auto &attr : attrs)
|
|
{
|
|
const auto &osAttrName = attr->GetName();
|
|
if (bThisIsUnscaledArray)
|
|
{
|
|
if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
|
|
osAttrName == "valid_min" || osAttrName == "valid_max" ||
|
|
osAttrName == "valid_range")
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
|
|
attr->GetDataType());
|
|
if (!dstAttr)
|
|
{
|
|
if (bStrict)
|
|
return false;
|
|
continue;
|
|
}
|
|
auto raw = attr->ReadAsRaw();
|
|
if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
|
|
return false;
|
|
}
|
|
if (!attrs.empty())
|
|
{
|
|
nCurCost += attrs.size() * GDALAttribute::COPY_COST;
|
|
if (pfnProgress &&
|
|
!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
|
|
return false;
|
|
}
|
|
|
|
auto srcSRS = poSrcArray->GetSpatialRef();
|
|
if (srcSRS)
|
|
{
|
|
SetSpatialRef(srcSRS.get());
|
|
}
|
|
|
|
const void *pNoData = poSrcArray->GetRawNoDataValue();
|
|
if (pNoData && poSrcArray->GetDataType() == GetDataType())
|
|
{
|
|
SetRawNoDataValue(pNoData);
|
|
}
|
|
|
|
const std::string &osUnit(poSrcArray->GetUnit());
|
|
if (!osUnit.empty())
|
|
{
|
|
SetUnit(osUnit);
|
|
}
|
|
|
|
bool bGotValue = false;
|
|
GDALDataType eOffsetStorageType = GDT_Unknown;
|
|
const double dfOffset =
|
|
poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
|
|
if (bGotValue)
|
|
{
|
|
SetOffset(dfOffset, eOffsetStorageType);
|
|
}
|
|
|
|
bGotValue = false;
|
|
GDALDataType eScaleStorageType = GDT_Unknown;
|
|
const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
|
|
if (bGotValue)
|
|
{
|
|
SetScale(dfScale, eScaleStorageType);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* CopyFrom() */
|
|
/************************************************************************/
|
|
|
|
/** Copy the content of an array into a new (generally empty) array.
|
|
*
|
|
* @param poSrcDS Source dataset. Might be nullptr (but for correct behavior
|
|
* of some output drivers this is not recommended)
|
|
* @param poSrcArray Source array. Should NOT be nullptr.
|
|
* @param bStrict Whether to enable stict mode. In strict mode, any error will
|
|
* stop the copy. In relaxed mode, the copy will be attempted to
|
|
* be pursued.
|
|
* @param nCurCost Should be provided as a variable initially set to 0.
|
|
* @param nTotalCost Total cost from GetTotalCopyCost().
|
|
* @param pfnProgress Progress callback, or nullptr.
|
|
* @param pProgressData Progress user data, or nulptr.
|
|
*
|
|
* @return true in case of success (or partial success if bStrict == false).
|
|
*/
|
|
bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
|
|
const GDALMDArray *poSrcArray, bool bStrict,
|
|
GUInt64 &nCurCost, const GUInt64 nTotalCost,
|
|
GDALProgressFunc pfnProgress, void *pProgressData)
|
|
{
|
|
if (pfnProgress == nullptr)
|
|
pfnProgress = GDALDummyProgress;
|
|
|
|
nCurCost += GDALMDArray::COPY_COST;
|
|
|
|
if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
|
|
pfnProgress, pProgressData))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto &dims = poSrcArray->GetDimensions();
|
|
const auto nDTSize = poSrcArray->GetDataType().GetSize();
|
|
if (dims.empty())
|
|
{
|
|
std::vector<GByte> abyTmp(nDTSize);
|
|
if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
|
|
GetDataType(), &abyTmp[0]) &&
|
|
Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
|
|
&abyTmp[0])) &&
|
|
bStrict)
|
|
{
|
|
return false;
|
|
}
|
|
nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
|
|
if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
std::vector<GUInt64> arrayStartIdx(dims.size());
|
|
std::vector<GUInt64> count(dims.size());
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
count[i] = static_cast<size_t>(dims[i]->GetSize());
|
|
}
|
|
|
|
struct CopyFunc
|
|
{
|
|
GDALMDArray *poDstArray = nullptr;
|
|
std::vector<GByte> abyTmp{};
|
|
GDALProgressFunc pfnProgress = nullptr;
|
|
void *pProgressData = nullptr;
|
|
GUInt64 nCurCost = 0;
|
|
GUInt64 nTotalCost = 0;
|
|
GUInt64 nTotalBytesThisArray = 0;
|
|
bool bStop = false;
|
|
|
|
static bool f(GDALAbstractMDArray *l_poSrcArray,
|
|
const GUInt64 *chunkArrayStartIdx,
|
|
const size_t *chunkCount, GUInt64 iCurChunk,
|
|
GUInt64 nChunkCount, void *pUserData)
|
|
{
|
|
const auto dt(l_poSrcArray->GetDataType());
|
|
auto data = static_cast<CopyFunc *>(pUserData);
|
|
auto poDstArray = data->poDstArray;
|
|
if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
|
|
nullptr, dt, &data->abyTmp[0]))
|
|
{
|
|
return false;
|
|
}
|
|
bool bRet =
|
|
poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
|
|
nullptr, dt, &data->abyTmp[0]);
|
|
if (dt.NeedsFreeDynamicMemory())
|
|
{
|
|
const auto l_nDTSize = dt.GetSize();
|
|
GByte *ptr = &data->abyTmp[0];
|
|
const size_t l_nDims(l_poSrcArray->GetDimensionCount());
|
|
size_t nEltCount = 1;
|
|
for (size_t i = 0; i < l_nDims; ++i)
|
|
{
|
|
nEltCount *= chunkCount[i];
|
|
}
|
|
for (size_t i = 0; i < nEltCount; i++)
|
|
{
|
|
dt.FreeDynamicMemory(ptr);
|
|
ptr += l_nDTSize;
|
|
}
|
|
}
|
|
if (!bRet)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
double dfCurCost =
|
|
double(data->nCurCost) + double(iCurChunk) / nChunkCount *
|
|
data->nTotalBytesThisArray;
|
|
if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
|
|
data->pProgressData))
|
|
{
|
|
data->bStop = true;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
CopyFunc copyFunc;
|
|
copyFunc.poDstArray = this;
|
|
copyFunc.nCurCost = nCurCost;
|
|
copyFunc.nTotalCost = nTotalCost;
|
|
copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
|
|
copyFunc.pfnProgress = pfnProgress;
|
|
copyFunc.pProgressData = pProgressData;
|
|
const char *pszSwathSize =
|
|
CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
|
|
const size_t nMaxChunkSize =
|
|
pszSwathSize
|
|
? static_cast<size_t>(
|
|
std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
|
|
CPLAtoGIntBig(pszSwathSize)))
|
|
: static_cast<size_t>(
|
|
std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
|
|
GDALGetCacheMax64() / 4));
|
|
const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
|
|
size_t nRealChunkSize = nDTSize;
|
|
for (const auto &nChunkSize : anChunkSizes)
|
|
{
|
|
nRealChunkSize *= nChunkSize;
|
|
}
|
|
try
|
|
{
|
|
copyFunc.abyTmp.resize(nRealChunkSize);
|
|
}
|
|
catch (const std::exception &)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OutOfMemory,
|
|
"Cannot allocate temporary buffer");
|
|
nCurCost += copyFunc.nTotalBytesThisArray;
|
|
return false;
|
|
}
|
|
if (copyFunc.nTotalBytesThisArray != 0 &&
|
|
!const_cast<GDALMDArray *>(poSrcArray)
|
|
->ProcessPerChunk(arrayStartIdx.data(), count.data(),
|
|
anChunkSizes.data(), CopyFunc::f,
|
|
©Func) &&
|
|
(bStrict || copyFunc.bStop))
|
|
{
|
|
nCurCost += copyFunc.nTotalBytesThisArray;
|
|
return false;
|
|
}
|
|
nCurCost += copyFunc.nTotalBytesThisArray;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetStructuralInfo() */
|
|
/************************************************************************/
|
|
|
|
/** Return structural information on the array.
|
|
*
|
|
* This may be the compression, etc..
|
|
*
|
|
* The return value should not be freed and is valid until GDALMDArray is
|
|
* released or this function called again.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetStructuralInfo().
|
|
*/
|
|
CSLConstList GDALMDArray::GetStructuralInfo() const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* AdviseRead() */
|
|
/************************************************************************/
|
|
|
|
/** Advise driver of upcoming read requests.
|
|
*
|
|
* Some GDAL drivers operate more efficiently if they know in advance what
|
|
* set of upcoming read requests will be made. The AdviseRead() method allows
|
|
* an application to notify the driver of the region of interest.
|
|
*
|
|
* Many drivers just ignore the AdviseRead() call, but it can dramatically
|
|
* accelerate access via some drivers. One such case is when reading through
|
|
* a DAP dataset with the netCDF driver (a in-memory cache array is then created
|
|
* with the region of interest defined by AdviseRead())
|
|
*
|
|
* This is the same as the C function GDALMDArrayAdviseRead().
|
|
*
|
|
* @param arrayStartIdx Values representing the starting index to read
|
|
* in each dimension (in [0, aoDims[i].GetSize()-1] range).
|
|
* Array of GetDimensionCount() values.
|
|
* Can be nullptr as a synonymous for [0 for i in
|
|
* range(GetDimensionCount() ]
|
|
*
|
|
* @param count Values representing the number of values to extract in
|
|
* each dimension.
|
|
* Array of GetDimensionCount() values.
|
|
* Can be nullptr as a synonymous for
|
|
* [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
|
|
* range(GetDimensionCount() ]
|
|
*
|
|
* @param papszOptions Driver specific options, or nullptr. Consult driver
|
|
* documentation.
|
|
*
|
|
* @return true in case of success (ignoring the advice is a success)
|
|
*
|
|
* @since GDAL 3.2
|
|
*/
|
|
bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
CSLConstList papszOptions) const
|
|
{
|
|
const auto nDimCount = GetDimensionCount();
|
|
if (nDimCount == 0)
|
|
return true;
|
|
|
|
std::vector<GUInt64> tmp_arrayStartIdx;
|
|
if (arrayStartIdx == nullptr)
|
|
{
|
|
tmp_arrayStartIdx.resize(nDimCount);
|
|
arrayStartIdx = tmp_arrayStartIdx.data();
|
|
}
|
|
|
|
std::vector<size_t> tmp_count;
|
|
if (count == nullptr)
|
|
{
|
|
tmp_count.resize(nDimCount);
|
|
const auto &dims = GetDimensions();
|
|
for (size_t i = 0; i < nDimCount; i++)
|
|
{
|
|
const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
|
|
#if SIZEOF_VOIDP < 8
|
|
if (nSize != static_cast<size_t>(nSize))
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
|
|
return false;
|
|
}
|
|
#endif
|
|
tmp_count[i] = static_cast<size_t>(nSize);
|
|
}
|
|
count = tmp_count.data();
|
|
}
|
|
|
|
std::vector<GInt64> tmp_arrayStep;
|
|
std::vector<GPtrDiff_t> tmp_bufferStride;
|
|
const GInt64 *arrayStep = nullptr;
|
|
const GPtrDiff_t *bufferStride = nullptr;
|
|
if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
|
|
GDALExtendedDataType::Create(GDT_Unknown),
|
|
nullptr, nullptr, 0, tmp_arrayStep,
|
|
tmp_bufferStride))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return IAdviseRead(arrayStartIdx, count, papszOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IAdviseRead() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
|
|
CSLConstList /* papszOptions*/) const
|
|
{
|
|
return true;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* MassageName() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
/*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
|
|
{
|
|
std::string ret;
|
|
for (const char ch : inputName)
|
|
{
|
|
if (!isalnum(ch))
|
|
ret += '_';
|
|
else
|
|
ret += ch;
|
|
}
|
|
return ret;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* GetCacheRootGroup() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
std::shared_ptr<GDALGroup>
|
|
GDALMDArray::GetCacheRootGroup(bool bCanCreate,
|
|
std::string &osCacheFilenameOut) const
|
|
{
|
|
const auto &osFilename = GetFilename();
|
|
if (osFilename.empty())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Cannot cache an array with an empty filename");
|
|
return nullptr;
|
|
}
|
|
|
|
osCacheFilenameOut = osFilename + ".gmac";
|
|
const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
|
|
if (pszProxy != nullptr)
|
|
osCacheFilenameOut = pszProxy;
|
|
|
|
std::unique_ptr<GDALDataset> poDS;
|
|
VSIStatBufL sStat;
|
|
if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
|
|
{
|
|
poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
|
|
GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
|
|
nullptr, nullptr, nullptr));
|
|
}
|
|
if (poDS)
|
|
{
|
|
CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
|
|
return poDS->GetRootGroup();
|
|
}
|
|
|
|
if (bCanCreate)
|
|
{
|
|
const char *pszDrvName = "netCDF";
|
|
GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
|
|
if (poDrv == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
|
|
pszDrvName);
|
|
return nullptr;
|
|
}
|
|
{
|
|
CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
|
|
CPLErrorStateBackuper oErrorStateBackuper;
|
|
poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
|
|
nullptr, nullptr));
|
|
}
|
|
if (!poDS)
|
|
{
|
|
pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
|
|
if (pszProxy)
|
|
{
|
|
osCacheFilenameOut = pszProxy;
|
|
poDS.reset(poDrv->CreateMultiDimensional(
|
|
osCacheFilenameOut.c_str(), nullptr, nullptr));
|
|
}
|
|
}
|
|
if (poDS)
|
|
{
|
|
CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
|
|
return poDS->GetRootGroup();
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
|
|
"configuration option to write the cache in "
|
|
"another directory",
|
|
osCacheFilenameOut.c_str());
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* Cache() */
|
|
/************************************************************************/
|
|
|
|
/** Cache the content of the array into an auxiliary filename.
|
|
*
|
|
* The main purpose of this method is to be able to cache views that are
|
|
* expensive to compute, such as transposed arrays.
|
|
*
|
|
* The array will be stored in a file whose name is the one of
|
|
* GetFilename(), with an extra .gmac extension (stands for GDAL
|
|
* Multidimensional Array Cache). The cache is a netCDF dataset.
|
|
*
|
|
* If the .gmac file cannot be written next to the dataset, the
|
|
* GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
|
|
* directory.
|
|
*
|
|
* The GDALMDArray::Read() method will automatically use the cache when it
|
|
* exists. There is no timestamp checks between the source array and the cached
|
|
* array. If the source arrays changes, the cache must be manually deleted.
|
|
*
|
|
* This is the same as the C function GDALMDArrayCache()
|
|
*
|
|
* @note Driver implementation: optionally implemented.
|
|
*
|
|
* @param papszOptions List of options, null terminated, or NULL. Currently
|
|
* the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
|
|
* to specify the block size of the cached array.
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALMDArray::Cache(CSLConstList papszOptions) const
|
|
{
|
|
std::string osCacheFilename;
|
|
auto poRG = GetCacheRootGroup(true, osCacheFilename);
|
|
if (!poRG)
|
|
return false;
|
|
|
|
const std::string osCachedArrayName(MassageName(GetFullName()));
|
|
if (poRG->OpenMDArray(osCachedArrayName))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"An array with same name %s already exists in %s",
|
|
osCachedArrayName.c_str(), osCacheFilename.c_str());
|
|
return false;
|
|
}
|
|
|
|
CPLStringList aosOptions;
|
|
aosOptions.SetNameValue("COMPRESS", "DEFLATE");
|
|
const auto &aoDims = GetDimensions();
|
|
std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
|
|
if (!aoDims.empty())
|
|
{
|
|
std::string osBlockSize(
|
|
CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
|
|
if (osBlockSize.empty())
|
|
{
|
|
const auto anBlockSize = GetBlockSize();
|
|
int idxDim = 0;
|
|
for (auto nBlockSize : anBlockSize)
|
|
{
|
|
if (idxDim > 0)
|
|
osBlockSize += ',';
|
|
if (nBlockSize == 0)
|
|
nBlockSize = 256;
|
|
nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
|
|
osBlockSize +=
|
|
std::to_string(static_cast<uint64_t>(nBlockSize));
|
|
idxDim++;
|
|
}
|
|
}
|
|
aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
|
|
|
|
int idxDim = 0;
|
|
for (const auto &poDim : aoDims)
|
|
{
|
|
auto poNewDim = poRG->CreateDimension(
|
|
osCachedArrayName + '_' + std::to_string(idxDim),
|
|
poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
|
|
if (!poNewDim)
|
|
return false;
|
|
aoNewDims.emplace_back(poNewDim);
|
|
idxDim++;
|
|
}
|
|
}
|
|
|
|
auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
|
|
GetDataType(), aosOptions.List());
|
|
if (!poCachedArray)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
|
|
osCachedArrayName.c_str(), osCacheFilename.c_str());
|
|
return false;
|
|
}
|
|
|
|
GUInt64 nCost = 0;
|
|
return poCachedArray->CopyFrom(nullptr, this,
|
|
false, // strict
|
|
nCost, GetTotalCopyCost(), nullptr, nullptr);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Read() */
|
|
/************************************************************************/
|
|
|
|
bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, // step in elements
|
|
const GPtrDiff_t *bufferStride, // stride in elements
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer, const void *pDstBufferAllocStart,
|
|
size_t nDstBufferAllocSize) const
|
|
{
|
|
if (!m_bHasTriedCachedArray)
|
|
{
|
|
m_bHasTriedCachedArray = true;
|
|
if (IsCacheable())
|
|
{
|
|
const auto &osFilename = GetFilename();
|
|
if (!osFilename.empty() &&
|
|
!EQUAL(CPLGetExtension(osFilename.c_str()), "gmac"))
|
|
{
|
|
std::string osCacheFilename;
|
|
auto poRG = GetCacheRootGroup(false, osCacheFilename);
|
|
if (poRG)
|
|
{
|
|
const std::string osCachedArrayName(
|
|
MassageName(GetFullName()));
|
|
m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
|
|
if (m_poCachedArray)
|
|
{
|
|
const auto &dims = GetDimensions();
|
|
const auto &cachedDims =
|
|
m_poCachedArray->GetDimensions();
|
|
const size_t nDims = dims.size();
|
|
bool ok =
|
|
m_poCachedArray->GetDataType() == GetDataType() &&
|
|
cachedDims.size() == nDims;
|
|
for (size_t i = 0; ok && i < nDims; ++i)
|
|
{
|
|
ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
|
|
}
|
|
if (ok)
|
|
{
|
|
CPLDebug("GDAL", "Cached array for %s found in %s",
|
|
osCachedArrayName.c_str(),
|
|
osCacheFilename.c_str());
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Cached array %s in %s has incompatible "
|
|
"characteristics with current array.",
|
|
osCachedArrayName.c_str(),
|
|
osCacheFilename.c_str());
|
|
m_poCachedArray.reset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
|
|
if (!array->GetDataType().CanConvertTo(bufferDataType))
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Array data type is not convertible to buffer data type");
|
|
return false;
|
|
}
|
|
|
|
std::vector<GInt64> tmp_arrayStep;
|
|
std::vector<GPtrDiff_t> tmp_bufferStride;
|
|
if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
|
|
bufferStride, bufferDataType, pDstBuffer,
|
|
pDstBufferAllocStart, nDstBufferAllocSize,
|
|
tmp_arrayStep, tmp_bufferStride))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
|
|
bufferDataType, pDstBuffer);
|
|
}
|
|
|
|
//! @cond Doxygen_Suppress
|
|
|
|
/************************************************************************/
|
|
/* IsTransposedRequest() */
|
|
/************************************************************************/
|
|
|
|
bool GDALMDArray::IsTransposedRequest(
|
|
const size_t *count,
|
|
const GPtrDiff_t *bufferStride) const // stride in elements
|
|
{
|
|
/*
|
|
For example:
|
|
count = [2,3,4]
|
|
strides = [12, 4, 1] (2-1)*12+(3-1)*4+(4-1)*1=23 ==> row major
|
|
stride [12, 1, 3] (2-1)*12+(3-1)*1+(4-1)*3=23 ==>
|
|
(axis[0],axis[2],axis[1]) transposition [1, 8, 2] (2-1)*1+
|
|
(3-1)*8+(4-1)*2=23 ==> (axis[2],axis[1],axis[0]) transposition
|
|
*/
|
|
const size_t nDims(GetDimensionCount());
|
|
size_t nCurStrideForRowMajorStrides = 1;
|
|
bool bRowMajorStrides = true;
|
|
size_t nElts = 1;
|
|
size_t nLastIdx = 0;
|
|
for (size_t i = nDims; i > 0;)
|
|
{
|
|
--i;
|
|
if (bufferStride[i] < 0)
|
|
return false;
|
|
if (static_cast<size_t>(bufferStride[i]) !=
|
|
nCurStrideForRowMajorStrides)
|
|
{
|
|
bRowMajorStrides = false;
|
|
}
|
|
// Integer overflows have already been checked in CheckReadWriteParams()
|
|
nCurStrideForRowMajorStrides *= count[i];
|
|
nElts *= count[i];
|
|
nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
|
|
}
|
|
if (bRowMajorStrides)
|
|
return false;
|
|
return nLastIdx == nElts - 1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CopyToFinalBufferSameDataType() */
|
|
/************************************************************************/
|
|
|
|
template <size_t N>
|
|
void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
|
|
size_t nDims, const size_t *count,
|
|
const GPtrDiff_t *bufferStride)
|
|
{
|
|
std::vector<size_t> anStackCount(nDims);
|
|
std::vector<GByte *> pabyDstBufferStack(nDims + 1);
|
|
const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
|
|
pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
|
|
size_t iDim = 0;
|
|
|
|
lbl_next_depth:
|
|
if (iDim == nDims - 1)
|
|
{
|
|
size_t n = count[iDim];
|
|
GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
|
|
const auto bufferStrideLastDim = bufferStride[iDim] * N;
|
|
while (n > 0)
|
|
{
|
|
--n;
|
|
memcpy(pabyDstBuffer, pabySrcBuffer, N);
|
|
pabyDstBuffer += bufferStrideLastDim;
|
|
pabySrcBuffer += N;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
anStackCount[iDim] = count[iDim];
|
|
while (true)
|
|
{
|
|
++iDim;
|
|
pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller_in_loop:
|
|
--iDim;
|
|
--anStackCount[iDim];
|
|
if (anStackCount[iDim] == 0)
|
|
break;
|
|
pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
|
|
}
|
|
}
|
|
if (iDim > 0)
|
|
goto lbl_return_to_caller_in_loop;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CopyToFinalBuffer() */
|
|
/************************************************************************/
|
|
|
|
static void CopyToFinalBuffer(const void *pSrcBuffer,
|
|
const GDALExtendedDataType &eSrcDataType,
|
|
void *pDstBuffer,
|
|
const GDALExtendedDataType &eDstDataType,
|
|
size_t nDims, const size_t *count,
|
|
const GPtrDiff_t *bufferStride)
|
|
{
|
|
const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
|
|
// Use specialized implementation for well-known data types when no
|
|
// type conversion is needed
|
|
if (eSrcDataType == eDstDataType)
|
|
{
|
|
if (nSrcDataTypeSize == 1)
|
|
{
|
|
CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
|
|
count, bufferStride);
|
|
return;
|
|
}
|
|
else if (nSrcDataTypeSize == 2)
|
|
{
|
|
CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
|
|
count, bufferStride);
|
|
return;
|
|
}
|
|
else if (nSrcDataTypeSize == 4)
|
|
{
|
|
CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
|
|
count, bufferStride);
|
|
return;
|
|
}
|
|
else if (nSrcDataTypeSize == 8)
|
|
{
|
|
CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
|
|
count, bufferStride);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const size_t nDstDataTypeSize(eDstDataType.GetSize());
|
|
std::vector<size_t> anStackCount(nDims);
|
|
std::vector<GByte *> pabyDstBufferStack(nDims + 1);
|
|
const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
|
|
pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
|
|
size_t iDim = 0;
|
|
|
|
lbl_next_depth:
|
|
if (iDim == nDims - 1)
|
|
{
|
|
GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
|
|
pabyDstBufferStack[iDim], eDstDataType,
|
|
bufferStride[iDim], count[iDim]);
|
|
pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
|
|
}
|
|
else
|
|
{
|
|
anStackCount[iDim] = count[iDim];
|
|
while (true)
|
|
{
|
|
++iDim;
|
|
pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller_in_loop:
|
|
--iDim;
|
|
--anStackCount[iDim];
|
|
if (anStackCount[iDim] == 0)
|
|
break;
|
|
pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
|
|
}
|
|
}
|
|
if (iDim > 0)
|
|
goto lbl_return_to_caller_in_loop;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Transpose2D() */
|
|
/************************************************************************/
|
|
|
|
template <class T>
|
|
static void Transpose2D(T *dst, const T *src, size_t src_height,
|
|
size_t src_width)
|
|
{
|
|
constexpr size_t blocksize = 32;
|
|
for (size_t i = 0; i < src_height; i += blocksize)
|
|
{
|
|
for (size_t j = 0; j < src_width; j += blocksize)
|
|
{
|
|
// transpose the block beginning at [i,j]
|
|
const size_t max_k = std::min(i + blocksize, src_height);
|
|
for (size_t k = i; k < max_k; ++k)
|
|
{
|
|
const size_t max_l = std::min(j + blocksize, src_width);
|
|
for (size_t l = j; l < max_l; ++l)
|
|
{
|
|
dst[k + l * src_height] = src[l + k * src_width];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* TransposeLast2Dims() */
|
|
/************************************************************************/
|
|
|
|
static bool TransposeLast2Dims(void *pDstBuffer,
|
|
const GDALExtendedDataType &eDT,
|
|
const size_t nDims, const size_t *count,
|
|
const size_t nEltsNonLast2Dims)
|
|
{
|
|
const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
|
|
const auto nDTSize = eDT.GetSize();
|
|
void *pTempBufferForLast2DimsTranspose =
|
|
VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
|
|
if (pTempBufferForLast2DimsTranspose == nullptr)
|
|
return false;
|
|
|
|
GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
|
|
for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
|
|
{
|
|
if (nDTSize == 1)
|
|
{
|
|
Transpose2D(
|
|
static_cast<uint8_t *>(pTempBufferForLast2DimsTranspose),
|
|
reinterpret_cast<const uint8_t *>(pabyDstBuffer),
|
|
count[nDims - 2], count[nDims - 1]);
|
|
}
|
|
else if (nDTSize == 2)
|
|
{
|
|
Transpose2D(
|
|
static_cast<uint16_t *>(pTempBufferForLast2DimsTranspose),
|
|
reinterpret_cast<const uint16_t *>(pabyDstBuffer),
|
|
count[nDims - 2], count[nDims - 1]);
|
|
}
|
|
else if (nDTSize == 4)
|
|
{
|
|
Transpose2D(
|
|
static_cast<uint32_t *>(pTempBufferForLast2DimsTranspose),
|
|
reinterpret_cast<const uint32_t *>(pabyDstBuffer),
|
|
count[nDims - 2], count[nDims - 1]);
|
|
}
|
|
else if (nDTSize == 8)
|
|
{
|
|
Transpose2D(
|
|
static_cast<uint64_t *>(pTempBufferForLast2DimsTranspose),
|
|
reinterpret_cast<const uint64_t *>(pabyDstBuffer),
|
|
count[nDims - 2], count[nDims - 1]);
|
|
}
|
|
else
|
|
{
|
|
CPLAssert(false);
|
|
}
|
|
memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
|
|
nDTSize * nEltsLast2Dims);
|
|
pabyDstBuffer += nDTSize * nEltsLast2Dims;
|
|
}
|
|
|
|
VSIFree(pTempBufferForLast2DimsTranspose);
|
|
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ReadForTransposedRequest() */
|
|
/************************************************************************/
|
|
|
|
// Using the netCDF/HDF5 APIs to read a slice with strides that express a
|
|
// transposed view yield to extremely poor/unusable performance. This fixes
|
|
// this by using temporary memory to read in a contiguous buffer in a
|
|
// row-major order, and then do the transposition to the final buffer.
|
|
|
|
bool GDALMDArray::ReadForTransposedRequest(
|
|
const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
const size_t nDims(GetDimensionCount());
|
|
if (nDims == 0)
|
|
{
|
|
CPLAssert(false);
|
|
return false; // shouldn't happen
|
|
}
|
|
size_t nElts = 1;
|
|
for (size_t i = 0; i < nDims; ++i)
|
|
nElts *= count[i];
|
|
|
|
std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
|
|
tmpBufferStrides.back() = 1;
|
|
for (size_t i = nDims - 1; i > 0;)
|
|
{
|
|
--i;
|
|
tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
|
|
}
|
|
|
|
const auto &eDT = GetDataType();
|
|
const auto nDTSize = eDT.GetSize();
|
|
if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
|
|
static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
|
|
(nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
|
|
{
|
|
// Optimization of the optimization if only the last 2 dims are
|
|
// transposed that saves on temporary buffer allocation
|
|
const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
|
|
size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
|
|
bool bRowMajorStridesForNonLast2Dims = true;
|
|
size_t nEltsNonLast2Dims = 1;
|
|
for (size_t i = nDims - 2; i > 0;)
|
|
{
|
|
--i;
|
|
if (static_cast<size_t>(bufferStride[i]) !=
|
|
nCurStrideForRowMajorStrides)
|
|
{
|
|
bRowMajorStridesForNonLast2Dims = false;
|
|
}
|
|
// Integer overflows have already been checked in
|
|
// CheckReadWriteParams()
|
|
nCurStrideForRowMajorStrides *= count[i];
|
|
nEltsNonLast2Dims *= count[i];
|
|
}
|
|
if (bRowMajorStridesForNonLast2Dims)
|
|
{
|
|
// We read in the final buffer!
|
|
if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
|
|
eDT, pDstBuffer))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
|
|
nEltsNonLast2Dims);
|
|
}
|
|
}
|
|
|
|
void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
|
|
if (pTempBuffer == nullptr)
|
|
return false;
|
|
|
|
if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
|
|
pTempBuffer))
|
|
{
|
|
VSIFree(pTempBuffer);
|
|
return false;
|
|
}
|
|
CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
|
|
count, bufferStride);
|
|
|
|
if (eDT.NeedsFreeDynamicMemory())
|
|
{
|
|
GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
|
|
for (size_t i = 0; i < nElts; ++i)
|
|
{
|
|
eDT.FreeDynamicMemory(pabyPtr);
|
|
pabyPtr += nDTSize;
|
|
}
|
|
}
|
|
|
|
VSIFree(pTempBuffer);
|
|
return true;
|
|
}
|
|
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* GDALSlicedMDArray */
|
|
/************************************************************************/
|
|
|
|
class GDALSlicedMDArray final : public GDALPamMDArray
|
|
{
|
|
private:
|
|
std::shared_ptr<GDALMDArray> m_poParent{};
|
|
std::vector<std::shared_ptr<GDALDimension>> m_dims{};
|
|
std::vector<size_t> m_mapDimIdxToParentDimIdx{}; // of size m_dims.size()
|
|
std::vector<Range>
|
|
m_parentRanges{}; // of size m_poParent->GetDimensionCount()
|
|
|
|
mutable std::vector<GUInt64> m_parentStart;
|
|
mutable std::vector<size_t> m_parentCount;
|
|
mutable std::vector<GInt64> m_parentStep;
|
|
mutable std::vector<GPtrDiff_t> m_parentStride;
|
|
|
|
void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride) const;
|
|
|
|
protected:
|
|
explicit GDALSlicedMDArray(
|
|
const std::shared_ptr<GDALMDArray> &poParent,
|
|
const std::string &viewExpr,
|
|
std::vector<std::shared_ptr<GDALDimension>> &&dims,
|
|
std::vector<size_t> &&mapDimIdxToParentDimIdx,
|
|
std::vector<Range> &&parentRanges)
|
|
: GDALAbstractMDArray(std::string(), "Sliced view of " +
|
|
poParent->GetFullName() +
|
|
" (" + viewExpr + ")"),
|
|
GDALPamMDArray(std::string(),
|
|
"Sliced view of " + poParent->GetFullName() + " (" +
|
|
viewExpr + ")",
|
|
::GetPAM(poParent)),
|
|
m_poParent(std::move(poParent)), m_dims(std::move(dims)),
|
|
m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
|
|
m_parentRanges(parentRanges),
|
|
m_parentStart(m_poParent->GetDimensionCount()),
|
|
m_parentCount(m_poParent->GetDimensionCount(), 1),
|
|
m_parentStep(m_poParent->GetDimensionCount()),
|
|
m_parentStride(m_poParent->GetDimensionCount())
|
|
{
|
|
}
|
|
|
|
bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const override;
|
|
|
|
bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
const void *pSrcBuffer) override;
|
|
|
|
bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
CSLConstList papszOptions) const override;
|
|
|
|
public:
|
|
static std::shared_ptr<GDALSlicedMDArray>
|
|
Create(const std::shared_ptr<GDALMDArray> &poParent,
|
|
const std::string &viewExpr,
|
|
std::vector<std::shared_ptr<GDALDimension>> &&dims,
|
|
std::vector<size_t> &&mapDimIdxToParentDimIdx,
|
|
std::vector<Range> &&parentRanges)
|
|
{
|
|
CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
|
|
CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
|
|
|
|
auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
|
|
poParent, viewExpr, std::move(dims),
|
|
std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
|
|
newAr->SetSelf(newAr);
|
|
return newAr;
|
|
}
|
|
|
|
bool IsWritable() const override
|
|
{
|
|
return m_poParent->IsWritable();
|
|
}
|
|
|
|
const std::string &GetFilename() const override
|
|
{
|
|
return m_poParent->GetFilename();
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<GDALDimension>> &
|
|
GetDimensions() const override
|
|
{
|
|
return m_dims;
|
|
}
|
|
|
|
const GDALExtendedDataType &GetDataType() const override
|
|
{
|
|
return m_poParent->GetDataType();
|
|
}
|
|
|
|
const std::string &GetUnit() const override
|
|
{
|
|
return m_poParent->GetUnit();
|
|
}
|
|
|
|
// bool SetUnit(const std::string& osUnit) override { return
|
|
// m_poParent->SetUnit(osUnit); }
|
|
|
|
std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
|
|
{
|
|
auto poSrcSRS = m_poParent->GetSpatialRef();
|
|
if (!poSrcSRS)
|
|
return nullptr;
|
|
auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
|
|
std::vector<int> dstMapping;
|
|
for (int srcAxis : srcMapping)
|
|
{
|
|
bool bFound = false;
|
|
for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
|
|
{
|
|
if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
|
|
srcAxis - 1)
|
|
{
|
|
dstMapping.push_back(static_cast<int>(i) + 1);
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!bFound)
|
|
{
|
|
dstMapping.push_back(0);
|
|
}
|
|
}
|
|
auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
|
|
poClone->SetDataAxisToSRSAxisMapping(dstMapping);
|
|
return poClone;
|
|
}
|
|
|
|
const void *GetRawNoDataValue() const override
|
|
{
|
|
return m_poParent->GetRawNoDataValue();
|
|
}
|
|
|
|
// bool SetRawNoDataValue(const void* pRawNoData) override { return
|
|
// m_poParent->SetRawNoDataValue(pRawNoData); }
|
|
|
|
double GetOffset(bool *pbHasOffset,
|
|
GDALDataType *peStorageType) const override
|
|
{
|
|
return m_poParent->GetOffset(pbHasOffset, peStorageType);
|
|
}
|
|
|
|
double GetScale(bool *pbHasScale,
|
|
GDALDataType *peStorageType) const override
|
|
{
|
|
return m_poParent->GetScale(pbHasScale, peStorageType);
|
|
}
|
|
|
|
// bool SetOffset(double dfOffset) override { return
|
|
// m_poParent->SetOffset(dfOffset); }
|
|
|
|
// bool SetScale(double dfScale) override { return
|
|
// m_poParent->SetScale(dfScale); }
|
|
|
|
std::vector<GUInt64> GetBlockSize() const override
|
|
{
|
|
std::vector<GUInt64> ret(GetDimensionCount());
|
|
const auto parentBlockSize(m_poParent->GetBlockSize());
|
|
for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
|
|
{
|
|
const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
|
|
if (iOldAxis != static_cast<size_t>(-1))
|
|
{
|
|
ret[i] = parentBlockSize[iOldAxis];
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::shared_ptr<GDALAttribute>
|
|
GetAttribute(const std::string &osName) const override
|
|
{
|
|
return m_poParent->GetAttribute(osName);
|
|
}
|
|
|
|
std::vector<std::shared_ptr<GDALAttribute>>
|
|
GetAttributes(CSLConstList papszOptions = nullptr) const override
|
|
{
|
|
return m_poParent->GetAttributes(papszOptions);
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* PrepareParentArrays() */
|
|
/************************************************************************/
|
|
|
|
void GDALSlicedMDArray::PrepareParentArrays(
|
|
const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride) const
|
|
{
|
|
const size_t nParentDimCount = m_parentRanges.size();
|
|
for (size_t i = 0; i < nParentDimCount; i++)
|
|
{
|
|
// For dimensions in parent that have no existence in sliced array
|
|
m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
|
|
}
|
|
|
|
for (size_t i = 0; i < m_dims.size(); i++)
|
|
{
|
|
const auto iParent = m_mapDimIdxToParentDimIdx[i];
|
|
if (iParent != static_cast<size_t>(-1))
|
|
{
|
|
m_parentStart[iParent] =
|
|
m_parentRanges[iParent].m_nIncr >= 0
|
|
? m_parentRanges[iParent].m_nStartIdx +
|
|
arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
|
|
: m_parentRanges[iParent].m_nStartIdx -
|
|
arrayStartIdx[i] *
|
|
static_cast<GUInt64>(
|
|
-m_parentRanges[iParent].m_nIncr);
|
|
m_parentCount[iParent] = count[i];
|
|
if (arrayStep)
|
|
{
|
|
m_parentStep[iParent] =
|
|
count[i] == 1 ? 1 :
|
|
// other checks should have ensured this does
|
|
// not overflow
|
|
arrayStep[i] * m_parentRanges[iParent].m_nIncr;
|
|
}
|
|
if (bufferStride)
|
|
{
|
|
m_parentStride[iParent] = bufferStride[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IRead() */
|
|
/************************************************************************/
|
|
|
|
bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
|
|
return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
|
|
m_parentStep.data(), m_parentStride.data(),
|
|
bufferDataType, pDstBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWrite() */
|
|
/************************************************************************/
|
|
|
|
bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
const void *pSrcBuffer)
|
|
{
|
|
PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
|
|
return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
|
|
m_parentStep.data(), m_parentStride.data(),
|
|
bufferDataType, pSrcBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IAdviseRead() */
|
|
/************************************************************************/
|
|
|
|
bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
|
|
const size_t *count,
|
|
CSLConstList papszOptions) const
|
|
{
|
|
PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
|
|
return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
|
|
papszOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateSlicedArray() */
|
|
/************************************************************************/
|
|
|
|
static std::shared_ptr<GDALMDArray>
|
|
CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
|
|
const std::string &viewExpr, const std::string &activeSlice,
|
|
bool bRenameDimensions,
|
|
std::vector<GDALMDArray::ViewSpec> &viewSpecs)
|
|
{
|
|
const auto &srcDims(self->GetDimensions());
|
|
if (srcDims.empty())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
|
|
return nullptr;
|
|
}
|
|
|
|
CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
|
|
const auto nTokens = static_cast<size_t>(aosTokens.size());
|
|
|
|
std::vector<std::shared_ptr<GDALDimension>> newDims;
|
|
std::vector<size_t> mapDimIdxToParentDimIdx;
|
|
std::vector<GDALSlicedMDArray::Range> parentRanges;
|
|
newDims.reserve(nTokens);
|
|
mapDimIdxToParentDimIdx.reserve(nTokens);
|
|
parentRanges.reserve(nTokens);
|
|
|
|
bool bGotEllipsis = false;
|
|
size_t nCurSrcDim = 0;
|
|
for (size_t i = 0; i < nTokens; i++)
|
|
{
|
|
const char *pszIdxSpec = aosTokens[i];
|
|
if (EQUAL(pszIdxSpec, "..."))
|
|
{
|
|
if (bGotEllipsis)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Only one single ellipsis is supported");
|
|
return nullptr;
|
|
}
|
|
bGotEllipsis = true;
|
|
const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
|
|
for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
|
|
{
|
|
parentRanges.emplace_back(0, 1);
|
|
newDims.push_back(srcDims[nCurSrcDim]);
|
|
mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
|
|
}
|
|
continue;
|
|
}
|
|
else if (EQUAL(pszIdxSpec, "newaxis") ||
|
|
EQUAL(pszIdxSpec, "np.newaxis"))
|
|
{
|
|
newDims.push_back(std::make_shared<GDALDimension>(
|
|
std::string(), "newaxis", std::string(), std::string(), 1));
|
|
mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
|
|
continue;
|
|
}
|
|
else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
|
|
{
|
|
if (nCurSrcDim >= srcDims.size())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
|
|
activeSlice.c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
auto nVal = CPLAtoGIntBig(pszIdxSpec);
|
|
GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
|
|
if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
|
|
(nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Index " CPL_FRMT_GIB " is out of bounds", nVal);
|
|
return nullptr;
|
|
}
|
|
if (nVal < 0)
|
|
nVal += nDimSize;
|
|
parentRanges.emplace_back(nVal, 0);
|
|
}
|
|
else
|
|
{
|
|
if (nCurSrcDim >= srcDims.size())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
|
|
activeSlice.c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
CPLStringList aosRangeTokens(
|
|
CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
|
|
int nRangeTokens = aosRangeTokens.size();
|
|
if (nRangeTokens > 3)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
|
|
pszIdxSpec);
|
|
return nullptr;
|
|
}
|
|
if (nRangeTokens <= 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
|
|
pszIdxSpec);
|
|
return nullptr;
|
|
}
|
|
const char *pszStart = aosRangeTokens[0];
|
|
const char *pszEnd = aosRangeTokens[1];
|
|
const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
|
|
GDALSlicedMDArray::Range range;
|
|
const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
|
|
range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
|
|
if (range.m_nIncr == 0 ||
|
|
range.m_nIncr == std::numeric_limits<GInt64>::min())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
|
|
return nullptr;
|
|
}
|
|
auto startIdx(CPLAtoGIntBig(pszStart));
|
|
if (startIdx < 0)
|
|
{
|
|
if (nDimSize < static_cast<GUInt64>(-startIdx))
|
|
startIdx = 0;
|
|
else
|
|
startIdx = nDimSize + startIdx;
|
|
}
|
|
const bool bPosIncr = range.m_nIncr > 0;
|
|
range.m_nStartIdx = startIdx;
|
|
range.m_nStartIdx = EQUAL(pszStart, "")
|
|
? (bPosIncr ? 0 : nDimSize - 1)
|
|
: range.m_nStartIdx;
|
|
if (range.m_nStartIdx >= nDimSize - 1)
|
|
range.m_nStartIdx = nDimSize - 1;
|
|
auto endIdx(CPLAtoGIntBig(pszEnd));
|
|
if (endIdx < 0)
|
|
{
|
|
const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
|
|
if (nDimSize < positiveEndIdx)
|
|
endIdx = 0;
|
|
else
|
|
endIdx = nDimSize - positiveEndIdx;
|
|
}
|
|
GUInt64 nEndIdx = endIdx;
|
|
nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
|
|
if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
|
|
(!bPosIncr && range.m_nStartIdx <= nEndIdx))
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Output dimension of size 0 is not allowed");
|
|
return nullptr;
|
|
}
|
|
int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
|
|
const auto nAbsIncr = std::abs((int)range.m_nIncr);
|
|
const GUInt64 newSize =
|
|
bPosIncr
|
|
? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
|
|
: DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
|
|
if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
|
|
newSize == srcDims[nCurSrcDim]->GetSize())
|
|
{
|
|
newDims.push_back(srcDims[nCurSrcDim]);
|
|
}
|
|
else
|
|
{
|
|
std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
|
|
if (bRenameDimensions)
|
|
{
|
|
osNewDimName =
|
|
"subset_" + srcDims[nCurSrcDim]->GetName() +
|
|
CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
|
|
"_" CPL_FRMT_GUIB,
|
|
static_cast<GUIntBig>(range.m_nStartIdx),
|
|
static_cast<GIntBig>(range.m_nIncr),
|
|
static_cast<GUIntBig>(newSize));
|
|
}
|
|
newDims.push_back(std::make_shared<GDALDimension>(
|
|
std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
|
|
range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
|
|
: std::string(),
|
|
newSize));
|
|
}
|
|
mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
|
|
parentRanges.emplace_back(range);
|
|
}
|
|
|
|
nCurSrcDim++;
|
|
}
|
|
for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
|
|
{
|
|
parentRanges.emplace_back(0, 1);
|
|
newDims.push_back(srcDims[nCurSrcDim]);
|
|
mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
|
|
}
|
|
|
|
GDALMDArray::ViewSpec viewSpec;
|
|
viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
|
|
viewSpec.m_parentRanges = parentRanges;
|
|
viewSpecs.emplace_back(std::move(viewSpec));
|
|
|
|
return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
|
|
std::move(mapDimIdxToParentDimIdx),
|
|
std::move(parentRanges));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtractFieldMDArray */
|
|
/************************************************************************/
|
|
|
|
class GDALExtractFieldMDArray final : public GDALPamMDArray
|
|
{
|
|
private:
|
|
std::shared_ptr<GDALMDArray> m_poParent{};
|
|
GDALExtendedDataType m_dt;
|
|
std::string m_srcCompName;
|
|
mutable std::vector<GByte> m_pabyNoData{};
|
|
|
|
protected:
|
|
GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
|
|
const std::string &fieldName,
|
|
const std::unique_ptr<GDALEDTComponent> &srcComp)
|
|
: GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
|
|
" of " +
|
|
poParent->GetFullName()),
|
|
GDALPamMDArray(std::string(),
|
|
"Extract field " + fieldName + " of " +
|
|
poParent->GetFullName(),
|
|
::GetPAM(poParent)),
|
|
m_poParent(poParent), m_dt(srcComp->GetType()),
|
|
m_srcCompName(srcComp->GetName())
|
|
{
|
|
m_pabyNoData.resize(m_dt.GetSize());
|
|
}
|
|
|
|
bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const override;
|
|
|
|
bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
CSLConstList papszOptions) const override
|
|
{
|
|
return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
|
|
}
|
|
|
|
public:
|
|
static std::shared_ptr<GDALExtractFieldMDArray>
|
|
Create(const std::shared_ptr<GDALMDArray> &poParent,
|
|
const std::string &fieldName,
|
|
const std::unique_ptr<GDALEDTComponent> &srcComp)
|
|
{
|
|
auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
|
|
new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
|
|
newAr->SetSelf(newAr);
|
|
return newAr;
|
|
}
|
|
~GDALExtractFieldMDArray()
|
|
{
|
|
m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
|
|
}
|
|
|
|
bool IsWritable() const override
|
|
{
|
|
return m_poParent->IsWritable();
|
|
}
|
|
|
|
const std::string &GetFilename() const override
|
|
{
|
|
return m_poParent->GetFilename();
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<GDALDimension>> &
|
|
GetDimensions() const override
|
|
{
|
|
return m_poParent->GetDimensions();
|
|
}
|
|
|
|
const GDALExtendedDataType &GetDataType() const override
|
|
{
|
|
return m_dt;
|
|
}
|
|
|
|
const std::string &GetUnit() const override
|
|
{
|
|
return m_poParent->GetUnit();
|
|
}
|
|
|
|
std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
|
|
{
|
|
return m_poParent->GetSpatialRef();
|
|
}
|
|
|
|
const void *GetRawNoDataValue() const override
|
|
{
|
|
const void *parentNoData = m_poParent->GetRawNoDataValue();
|
|
if (parentNoData == nullptr)
|
|
return nullptr;
|
|
|
|
m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
|
|
memset(&m_pabyNoData[0], 0, m_dt.GetSize());
|
|
|
|
std::vector<std::unique_ptr<GDALEDTComponent>> comps;
|
|
comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
|
|
new GDALEDTComponent(m_srcCompName, 0, m_dt)));
|
|
auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
|
|
std::move(comps)));
|
|
|
|
GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
|
|
&m_pabyNoData[0], tmpDT);
|
|
|
|
return &m_pabyNoData[0];
|
|
}
|
|
|
|
double GetOffset(bool *pbHasOffset,
|
|
GDALDataType *peStorageType) const override
|
|
{
|
|
return m_poParent->GetOffset(pbHasOffset, peStorageType);
|
|
}
|
|
|
|
double GetScale(bool *pbHasScale,
|
|
GDALDataType *peStorageType) const override
|
|
{
|
|
return m_poParent->GetScale(pbHasScale, peStorageType);
|
|
}
|
|
|
|
std::vector<GUInt64> GetBlockSize() const override
|
|
{
|
|
return m_poParent->GetBlockSize();
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* IRead() */
|
|
/************************************************************************/
|
|
|
|
bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
|
|
const size_t *count,
|
|
const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
std::vector<std::unique_ptr<GDALEDTComponent>> comps;
|
|
comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
|
|
new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
|
|
auto tmpDT(GDALExtendedDataType::Create(
|
|
std::string(), bufferDataType.GetSize(), std::move(comps)));
|
|
|
|
return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
|
|
tmpDT, pDstBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CreateFieldNameExtractArray() */
|
|
/************************************************************************/
|
|
|
|
static std::shared_ptr<GDALMDArray>
|
|
CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
|
|
const std::string &fieldName)
|
|
{
|
|
CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
|
|
const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
|
|
for (const auto &comp : self->GetDataType().GetComponents())
|
|
{
|
|
if (comp->GetName() == fieldName)
|
|
{
|
|
srcComp = ∁
|
|
break;
|
|
}
|
|
}
|
|
if (srcComp == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
|
|
fieldName.c_str());
|
|
return nullptr;
|
|
}
|
|
return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetView() */
|
|
/************************************************************************/
|
|
|
|
/** Return a view of the array using slicing or field access.
|
|
*
|
|
* The slice expression uses the same syntax as NumPy basic slicing and
|
|
* indexing. See
|
|
* https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
|
|
* Or it can use field access by name. See
|
|
* https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
|
|
*
|
|
* Multiple [] bracket elements can be concatenated, with a slice expression
|
|
* or field name inside each.
|
|
*
|
|
* For basic slicing and indexing, inside each [] bracket element, a list of
|
|
* indexes that apply to successive source dimensions, can be specified, using
|
|
* integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
|
|
* or newaxis, using a comma separator.
|
|
*
|
|
* Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
|
|
* <ul>
|
|
* <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
|
|
* at index 1 in the first dimension, and index 2 in the second dimension
|
|
* from the source array. That is 5</li>
|
|
* <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
|
|
* implemented internally doing this intermediate slicing approach.</li>
|
|
* <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
|
|
* <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
|
|
* first dimension. That is [4,5,6,7].</li>
|
|
* <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
|
|
* second dimension. That is [2,6].</li>
|
|
* <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
|
|
* the second dimension. That is [[2],[6]].</li> <li>GetView("[::,2]"): Same as
|
|
* above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
|
|
* ellipsis only expands to one dimension here.</li> <li>GetView("[:,::2]"):
|
|
* returns a 2-dimensional array, with even-indexed elements of the second
|
|
* dimension. That is [[0,2],[4,6]].</li> <li>GetView("[:,1::2]"): returns a
|
|
* 2-dimensional array, with odd-indexed elements of the second dimension. That
|
|
* is [[1,3],[5,7]].</li> <li>GetView("[:,1:3:]"): returns a 2-dimensional
|
|
* array, with elements of the second dimension with index in the range [1,3[.
|
|
* That is [[1,2],[5,6]].</li> <li>GetView("[::-1,:]"): returns a 2-dimensional
|
|
* array, with the values in first dimension reversed. That is
|
|
* [[4,5,6,7],[0,1,2,3]].</li> <li>GetView("[newaxis,...]"): returns a
|
|
* 3-dimensional array, with an addditional dimension of size 1 put at the
|
|
* beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
|
|
* </ul>
|
|
*
|
|
* One difference with NumPy behavior is that ranges that would result in
|
|
* zero elements are not allowed (dimensions of size 0 not being allowed in the
|
|
* GDAL multidimensional model).
|
|
*
|
|
* For field access, the syntax to use is ["field_name"] or ['field_name'].
|
|
* Multiple field specification is not supported currently.
|
|
*
|
|
* Both type of access can be combined, e.g. GetView("[1]['field_name']")
|
|
*
|
|
* \note When using the GDAL Python bindings, natural Python syntax can be
|
|
* used. That is ar[0,::,1]["foo"] will be internally translated to
|
|
* ar.GetView("[0,::,1]['foo']")
|
|
* \note When using the C++ API and integer indexing only, you may use the
|
|
* at(idx0, idx1, ...) method.
|
|
*
|
|
* The returned array holds a reference to the original one, and thus is
|
|
* a view of it (not a copy). If the content of the original array changes,
|
|
* the content of the view array too. When using basic slicing and indexing,
|
|
* the view can be written if the underlying array is writable.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetView()
|
|
*
|
|
* @param viewExpr Expression expressing basic slicing and indexing, or field
|
|
* access.
|
|
* @return a new array, that holds a reference to the original one, and thus is
|
|
* a view of it (not a copy), or nullptr in case of error.
|
|
*/
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALMDArray::GetView(const std::string &viewExpr) const
|
|
{
|
|
std::vector<ViewSpec> viewSpecs;
|
|
return GetView(viewExpr, true, viewSpecs);
|
|
}
|
|
|
|
//! @cond Doxygen_Suppress
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
|
|
std::vector<ViewSpec> &viewSpecs) const
|
|
{
|
|
auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
|
|
if (!self)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Driver implementation issue: m_pSelf not set !");
|
|
return nullptr;
|
|
}
|
|
std::string curExpr(viewExpr);
|
|
while (true)
|
|
{
|
|
if (curExpr.empty() || curExpr[0] != '[')
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Slice string should start with ['");
|
|
return nullptr;
|
|
}
|
|
|
|
std::string fieldName;
|
|
size_t endExpr;
|
|
if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
|
|
{
|
|
if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Field access not allowed on non-compound data type");
|
|
return nullptr;
|
|
}
|
|
size_t idx = 2;
|
|
for (; idx < curExpr.size(); idx++)
|
|
{
|
|
const char ch = curExpr[idx];
|
|
if (ch == curExpr[1])
|
|
break;
|
|
if (ch == '\\' && idx + 1 < curExpr.size())
|
|
{
|
|
fieldName += curExpr[idx + 1];
|
|
idx++;
|
|
}
|
|
else
|
|
{
|
|
fieldName += ch;
|
|
}
|
|
}
|
|
if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Invalid field access specification");
|
|
return nullptr;
|
|
}
|
|
endExpr = idx + 1;
|
|
}
|
|
else
|
|
{
|
|
endExpr = curExpr.find(']');
|
|
}
|
|
if (endExpr == std::string::npos)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
|
|
return nullptr;
|
|
}
|
|
if (endExpr == 1)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
|
|
return nullptr;
|
|
}
|
|
std::string activeSlice(curExpr.substr(1, endExpr - 1));
|
|
|
|
if (!fieldName.empty())
|
|
{
|
|
ViewSpec viewSpec;
|
|
viewSpec.m_osFieldName = fieldName;
|
|
viewSpecs.emplace_back(std::move(viewSpec));
|
|
}
|
|
|
|
auto newArray = !fieldName.empty()
|
|
? CreateFieldNameExtractArray(self, fieldName)
|
|
: CreateSlicedArray(self, viewExpr, activeSlice,
|
|
bRenameDimensions, viewSpecs);
|
|
|
|
if (endExpr == curExpr.size() - 1)
|
|
{
|
|
return newArray;
|
|
}
|
|
self = std::move(newArray);
|
|
curExpr = curExpr.substr(endExpr + 1);
|
|
}
|
|
}
|
|
//! @endcond
|
|
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
|
|
{
|
|
std::string osExpr("[");
|
|
bool bFirst = true;
|
|
for (const auto &idx : indices)
|
|
{
|
|
if (!bFirst)
|
|
osExpr += ',';
|
|
bFirst = false;
|
|
osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
|
|
}
|
|
return GetView(osExpr + ']');
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* operator[] */
|
|
/************************************************************************/
|
|
|
|
/** Return a view of the array using field access
|
|
*
|
|
* Equivalent of GetView("['fieldName']")
|
|
*
|
|
* \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
|
|
*/
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALMDArray::operator[](const std::string &fieldName) const
|
|
{
|
|
return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
|
|
.replaceAll('\\', "\\\\")
|
|
.replaceAll('\'', "\\\'")
|
|
.c_str()));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayTransposed */
|
|
/************************************************************************/
|
|
|
|
class GDALMDArrayTransposed final : public GDALPamMDArray
|
|
{
|
|
private:
|
|
std::shared_ptr<GDALMDArray> m_poParent{};
|
|
std::vector<int> m_anMapNewAxisToOldAxis{};
|
|
std::vector<std::shared_ptr<GDALDimension>> m_dims{};
|
|
|
|
mutable std::vector<GUInt64> m_parentStart;
|
|
mutable std::vector<size_t> m_parentCount;
|
|
mutable std::vector<GInt64> m_parentStep;
|
|
mutable std::vector<GPtrDiff_t> m_parentStride;
|
|
|
|
void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride) const;
|
|
|
|
static std::string
|
|
MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
|
|
{
|
|
std::string ret;
|
|
ret += '[';
|
|
for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
|
|
{
|
|
if (i > 0)
|
|
ret += ',';
|
|
ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
|
|
}
|
|
ret += ']';
|
|
return ret;
|
|
}
|
|
|
|
protected:
|
|
GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
|
|
const std::vector<int> &anMapNewAxisToOldAxis,
|
|
std::vector<std::shared_ptr<GDALDimension>> &&dims)
|
|
: GDALAbstractMDArray(std::string(),
|
|
"Transposed view of " + poParent->GetFullName() +
|
|
" along " +
|
|
MappingToStr(anMapNewAxisToOldAxis)),
|
|
GDALPamMDArray(std::string(),
|
|
"Transposed view of " + poParent->GetFullName() +
|
|
" along " + MappingToStr(anMapNewAxisToOldAxis),
|
|
::GetPAM(poParent)),
|
|
m_poParent(std::move(poParent)),
|
|
m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
|
|
m_dims(std::move(dims)),
|
|
m_parentStart(m_poParent->GetDimensionCount()),
|
|
m_parentCount(m_poParent->GetDimensionCount()),
|
|
m_parentStep(m_poParent->GetDimensionCount()),
|
|
m_parentStride(m_poParent->GetDimensionCount())
|
|
{
|
|
}
|
|
|
|
bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const override;
|
|
|
|
bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
const void *pSrcBuffer) override;
|
|
|
|
bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
CSLConstList papszOptions) const override;
|
|
|
|
public:
|
|
static std::shared_ptr<GDALMDArrayTransposed>
|
|
Create(const std::shared_ptr<GDALMDArray> &poParent,
|
|
const std::vector<int> &anMapNewAxisToOldAxis)
|
|
{
|
|
const auto &parentDims(poParent->GetDimensions());
|
|
std::vector<std::shared_ptr<GDALDimension>> dims;
|
|
for (const auto iOldAxis : anMapNewAxisToOldAxis)
|
|
{
|
|
if (iOldAxis < 0)
|
|
{
|
|
dims.push_back(std::make_shared<GDALDimension>(
|
|
std::string(), "newaxis", std::string(), std::string(), 1));
|
|
}
|
|
else
|
|
{
|
|
dims.emplace_back(parentDims[iOldAxis]);
|
|
}
|
|
}
|
|
|
|
auto newAr(
|
|
std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
|
|
poParent, anMapNewAxisToOldAxis, std::move(dims))));
|
|
newAr->SetSelf(newAr);
|
|
return newAr;
|
|
}
|
|
|
|
bool IsWritable() const override
|
|
{
|
|
return m_poParent->IsWritable();
|
|
}
|
|
|
|
const std::string &GetFilename() const override
|
|
{
|
|
return m_poParent->GetFilename();
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<GDALDimension>> &
|
|
GetDimensions() const override
|
|
{
|
|
return m_dims;
|
|
}
|
|
|
|
const GDALExtendedDataType &GetDataType() const override
|
|
{
|
|
return m_poParent->GetDataType();
|
|
}
|
|
|
|
const std::string &GetUnit() const override
|
|
{
|
|
return m_poParent->GetUnit();
|
|
}
|
|
|
|
std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
|
|
{
|
|
auto poSrcSRS = m_poParent->GetSpatialRef();
|
|
if (!poSrcSRS)
|
|
return nullptr;
|
|
auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
|
|
std::vector<int> dstMapping;
|
|
for (int srcAxis : srcMapping)
|
|
{
|
|
bool bFound = false;
|
|
for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
|
|
{
|
|
if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
|
|
{
|
|
dstMapping.push_back(static_cast<int>(i) + 1);
|
|
bFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!bFound)
|
|
{
|
|
dstMapping.push_back(0);
|
|
}
|
|
}
|
|
auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
|
|
poClone->SetDataAxisToSRSAxisMapping(dstMapping);
|
|
return poClone;
|
|
}
|
|
|
|
const void *GetRawNoDataValue() const override
|
|
{
|
|
return m_poParent->GetRawNoDataValue();
|
|
}
|
|
|
|
// bool SetRawNoDataValue(const void* pRawNoData) override { return
|
|
// m_poParent->SetRawNoDataValue(pRawNoData); }
|
|
|
|
double GetOffset(bool *pbHasOffset,
|
|
GDALDataType *peStorageType) const override
|
|
{
|
|
return m_poParent->GetOffset(pbHasOffset, peStorageType);
|
|
}
|
|
|
|
double GetScale(bool *pbHasScale,
|
|
GDALDataType *peStorageType) const override
|
|
{
|
|
return m_poParent->GetScale(pbHasScale, peStorageType);
|
|
}
|
|
|
|
// bool SetOffset(double dfOffset) override { return
|
|
// m_poParent->SetOffset(dfOffset); }
|
|
|
|
// bool SetScale(double dfScale) override { return
|
|
// m_poParent->SetScale(dfScale); }
|
|
|
|
std::vector<GUInt64> GetBlockSize() const override
|
|
{
|
|
std::vector<GUInt64> ret(GetDimensionCount());
|
|
const auto parentBlockSize(m_poParent->GetBlockSize());
|
|
for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
|
|
{
|
|
const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
|
|
if (iOldAxis >= 0)
|
|
{
|
|
ret[i] = parentBlockSize[iOldAxis];
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
std::shared_ptr<GDALAttribute>
|
|
GetAttribute(const std::string &osName) const override
|
|
{
|
|
return m_poParent->GetAttribute(osName);
|
|
}
|
|
|
|
std::vector<std::shared_ptr<GDALAttribute>>
|
|
GetAttributes(CSLConstList papszOptions = nullptr) const override
|
|
{
|
|
return m_poParent->GetAttributes(papszOptions);
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* PrepareParentArrays() */
|
|
/************************************************************************/
|
|
|
|
void GDALMDArrayTransposed::PrepareParentArrays(
|
|
const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride) const
|
|
{
|
|
for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
|
|
{
|
|
const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
|
|
if (iOldAxis >= 0)
|
|
{
|
|
m_parentStart[iOldAxis] = arrayStartIdx[i];
|
|
m_parentCount[iOldAxis] = count[i];
|
|
if (arrayStep) // only null when called from IAdviseRead()
|
|
{
|
|
m_parentStep[iOldAxis] = arrayStep[i];
|
|
}
|
|
if (bufferStride) // only null when called from IAdviseRead()
|
|
{
|
|
m_parentStride[iOldAxis] = bufferStride[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IRead() */
|
|
/************************************************************************/
|
|
|
|
bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
|
|
return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
|
|
m_parentStep.data(), m_parentStride.data(),
|
|
bufferDataType, pDstBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWrite() */
|
|
/************************************************************************/
|
|
|
|
bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
const void *pSrcBuffer)
|
|
{
|
|
PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
|
|
return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
|
|
m_parentStep.data(), m_parentStride.data(),
|
|
bufferDataType, pSrcBuffer);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IAdviseRead() */
|
|
/************************************************************************/
|
|
|
|
bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
|
|
const size_t *count,
|
|
CSLConstList papszOptions) const
|
|
{
|
|
PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
|
|
return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
|
|
papszOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Transpose() */
|
|
/************************************************************************/
|
|
|
|
/** Return a view of the array whose axis have been reordered.
|
|
*
|
|
* The anMapNewAxisToOldAxis parameter should contain all the values between 0
|
|
* and GetDimensionCount() - 1, and each only once.
|
|
* -1 can be used as a special index value to ask for the insertion of a new
|
|
* axis of size 1.
|
|
* The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
|
|
* index of one of its dimension, it corresponds to the axis of index
|
|
* anMapNewAxisToOldAxis[i] from the current array.
|
|
*
|
|
* This is similar to the numpy.transpose() method
|
|
*
|
|
* The returned array holds a reference to the original one, and thus is
|
|
* a view of it (not a copy). If the content of the original array changes,
|
|
* the content of the view array too. The view can be written if the underlying
|
|
* array is writable.
|
|
*
|
|
* Note that I/O performance in such a transposed view might be poor.
|
|
*
|
|
* This is the same as the C function GDALMDArrayTranspose().
|
|
*
|
|
* @return a new array, that holds a reference to the original one, and thus is
|
|
* a view of it (not a copy), or nullptr in case of error.
|
|
*/
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
|
|
{
|
|
auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
|
|
if (!self)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Driver implementation issue: m_pSelf not set !");
|
|
return nullptr;
|
|
}
|
|
const int nDims = static_cast<int>(GetDimensionCount());
|
|
std::vector<bool> alreadyUsedOldAxis(nDims, false);
|
|
int nCountOldAxis = 0;
|
|
for (const auto iOldAxis : anMapNewAxisToOldAxis)
|
|
{
|
|
if (iOldAxis < -1 || iOldAxis >= nDims)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
|
|
return nullptr;
|
|
}
|
|
if (iOldAxis >= 0)
|
|
{
|
|
if (alreadyUsedOldAxis[iOldAxis])
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
|
|
iOldAxis);
|
|
return nullptr;
|
|
}
|
|
alreadyUsedOldAxis[iOldAxis] = true;
|
|
nCountOldAxis++;
|
|
}
|
|
}
|
|
if (nCountOldAxis != nDims)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"One or several original axis missing");
|
|
return nullptr;
|
|
}
|
|
return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IRead() */
|
|
/************************************************************************/
|
|
|
|
bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
const double dfScale = m_poParent->GetScale();
|
|
const double dfOffset = m_poParent->GetOffset();
|
|
const bool bDTIsComplex = m_dt.GetNumericDataType() == GDT_CFloat64;
|
|
const size_t nDTSize = m_dt.GetSize();
|
|
const bool bTempBufferNeeded = (m_dt != bufferDataType);
|
|
|
|
double adfSrcNoData[2] = {0, 0};
|
|
if (m_bHasNoData)
|
|
{
|
|
GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
|
|
m_poParent->GetDataType(),
|
|
&adfSrcNoData[0], m_dt);
|
|
}
|
|
|
|
const auto nDims = GetDimensions().size();
|
|
if (nDims == 0)
|
|
{
|
|
double adfVal[2];
|
|
if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
|
|
m_dt, &adfVal[0]))
|
|
{
|
|
return false;
|
|
}
|
|
if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
|
|
{
|
|
adfVal[0] = adfVal[0] * dfScale + dfOffset;
|
|
if (bDTIsComplex)
|
|
{
|
|
adfVal[1] = adfVal[1] * dfScale + dfOffset;
|
|
}
|
|
GDALExtendedDataType::CopyValue(&adfVal[0], m_dt, pDstBuffer,
|
|
bufferDataType);
|
|
}
|
|
else
|
|
{
|
|
GDALExtendedDataType::CopyValue(&m_adfNoData[0], m_dt, pDstBuffer,
|
|
bufferDataType);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::vector<GPtrDiff_t> actualBufferStrideVector;
|
|
const GPtrDiff_t *actualBufferStridePtr = bufferStride;
|
|
void *pTempBuffer = pDstBuffer;
|
|
if (bTempBufferNeeded)
|
|
{
|
|
size_t nElts = 1;
|
|
actualBufferStrideVector.resize(nDims);
|
|
for (size_t i = 0; i < nDims; i++)
|
|
nElts *= count[i];
|
|
actualBufferStrideVector.back() = 1;
|
|
for (size_t i = nDims - 1; i > 0;)
|
|
{
|
|
--i;
|
|
actualBufferStrideVector[i] =
|
|
actualBufferStrideVector[i + 1] * count[i + 1];
|
|
}
|
|
actualBufferStridePtr = actualBufferStrideVector.data();
|
|
pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
|
|
if (!pTempBuffer)
|
|
return false;
|
|
}
|
|
if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
|
|
actualBufferStridePtr, m_dt, pTempBuffer))
|
|
{
|
|
if (bTempBufferNeeded)
|
|
VSIFree(pTempBuffer);
|
|
return false;
|
|
}
|
|
|
|
struct Stack
|
|
{
|
|
size_t nIters = 0;
|
|
double *src_ptr = nullptr;
|
|
GByte *dst_ptr = nullptr;
|
|
GPtrDiff_t src_inc_offset = 0;
|
|
GPtrDiff_t dst_inc_offset = 0;
|
|
};
|
|
std::vector<Stack> stack(nDims);
|
|
const size_t nBufferDTSize = bufferDataType.GetSize();
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
stack[i].src_inc_offset =
|
|
actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
|
|
stack[i].dst_inc_offset =
|
|
static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
|
|
}
|
|
stack[0].src_ptr = static_cast<double *>(pTempBuffer);
|
|
stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
|
|
|
|
size_t dimIdx = 0;
|
|
const size_t nDimsMinus1 = nDims - 1;
|
|
GByte abyDstNoData[16];
|
|
CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
|
|
GDALExtendedDataType::CopyValue(&m_adfNoData[0], m_dt, abyDstNoData,
|
|
bufferDataType);
|
|
|
|
lbl_next_depth:
|
|
if (dimIdx == nDimsMinus1)
|
|
{
|
|
auto nIters = count[dimIdx];
|
|
double *padfVal = stack[dimIdx].src_ptr;
|
|
GByte *dst_ptr = stack[dimIdx].dst_ptr;
|
|
while (true)
|
|
{
|
|
if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
|
|
{
|
|
padfVal[0] = padfVal[0] * dfScale + dfOffset;
|
|
if (bDTIsComplex)
|
|
{
|
|
padfVal[1] = padfVal[1] * dfScale + dfOffset;
|
|
}
|
|
if (bTempBufferNeeded)
|
|
{
|
|
GDALExtendedDataType::CopyValue(&padfVal[0], m_dt, dst_ptr,
|
|
bufferDataType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
|
|
}
|
|
|
|
if ((--nIters) == 0)
|
|
break;
|
|
padfVal += stack[dimIdx].src_inc_offset;
|
|
dst_ptr += stack[dimIdx].dst_inc_offset;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stack[dimIdx].nIters = count[dimIdx];
|
|
while (true)
|
|
{
|
|
dimIdx++;
|
|
stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
|
|
stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller:
|
|
dimIdx--;
|
|
if ((--stack[dimIdx].nIters) == 0)
|
|
break;
|
|
stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
|
|
stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
|
|
}
|
|
}
|
|
if (dimIdx > 0)
|
|
goto lbl_return_to_caller;
|
|
|
|
if (bTempBufferNeeded)
|
|
VSIFree(pTempBuffer);
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWrite() */
|
|
/************************************************************************/
|
|
|
|
bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
const void *pSrcBuffer)
|
|
{
|
|
const double dfScale = m_poParent->GetScale();
|
|
const double dfOffset = m_poParent->GetOffset();
|
|
const bool bDTIsComplex = m_dt.GetNumericDataType() == GDT_CFloat64;
|
|
const size_t nDTSize = m_dt.GetSize();
|
|
CPLAssert(nDTSize == 8 || nDTSize == 16);
|
|
const bool bIsBufferDataTypeNativeDataType = (m_dt == bufferDataType);
|
|
const bool bSelfAndParentHaveNoData =
|
|
m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
|
|
|
|
double adfSrcNoData[2] = {0, 0};
|
|
if (bSelfAndParentHaveNoData)
|
|
{
|
|
GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
|
|
m_poParent->GetDataType(),
|
|
&adfSrcNoData[0], m_dt);
|
|
}
|
|
|
|
const auto nDims = GetDimensions().size();
|
|
if (nDims == 0)
|
|
{
|
|
double adfVal[2];
|
|
GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
|
|
m_dt);
|
|
if (bSelfAndParentHaveNoData &&
|
|
(std::isnan(adfVal[0]) || adfVal[0] == m_adfNoData[0]))
|
|
{
|
|
return m_poParent->Write(arrayStartIdx, count, arrayStep,
|
|
bufferStride, m_poParent->GetDataType(),
|
|
m_poParent->GetRawNoDataValue());
|
|
}
|
|
else
|
|
{
|
|
adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
|
|
if (bDTIsComplex)
|
|
{
|
|
adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
|
|
}
|
|
return m_poParent->Write(arrayStartIdx, count, arrayStep,
|
|
bufferStride, m_dt, &adfVal[0]);
|
|
}
|
|
}
|
|
|
|
std::vector<GPtrDiff_t> tmpBufferStrideVector;
|
|
size_t nElts = 1;
|
|
tmpBufferStrideVector.resize(nDims);
|
|
for (size_t i = 0; i < nDims; i++)
|
|
nElts *= count[i];
|
|
tmpBufferStrideVector.back() = 1;
|
|
for (size_t i = nDims - 1; i > 0;)
|
|
{
|
|
--i;
|
|
tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
|
|
}
|
|
const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
|
|
void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
|
|
if (!pTempBuffer)
|
|
return false;
|
|
|
|
struct Stack
|
|
{
|
|
size_t nIters = 0;
|
|
double *dst_ptr = nullptr;
|
|
const GByte *src_ptr = nullptr;
|
|
GPtrDiff_t src_inc_offset = 0;
|
|
GPtrDiff_t dst_inc_offset = 0;
|
|
};
|
|
std::vector<Stack> stack(nDims);
|
|
const size_t nBufferDTSize = bufferDataType.GetSize();
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
stack[i].dst_inc_offset =
|
|
tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
|
|
stack[i].src_inc_offset =
|
|
static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
|
|
}
|
|
stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
|
|
stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
|
|
|
|
size_t dimIdx = 0;
|
|
const size_t nDimsMinus1 = nDims - 1;
|
|
|
|
lbl_next_depth:
|
|
if (dimIdx == nDimsMinus1)
|
|
{
|
|
auto nIters = count[dimIdx];
|
|
double *dst_ptr = stack[dimIdx].dst_ptr;
|
|
const GByte *src_ptr = stack[dimIdx].src_ptr;
|
|
while (true)
|
|
{
|
|
double adfVal[2];
|
|
const double *padfSrcVal;
|
|
if (bIsBufferDataTypeNativeDataType)
|
|
{
|
|
padfSrcVal = reinterpret_cast<const double *>(src_ptr);
|
|
}
|
|
else
|
|
{
|
|
GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
|
|
&adfVal[0], m_dt);
|
|
padfSrcVal = adfVal;
|
|
}
|
|
|
|
if (bSelfAndParentHaveNoData &&
|
|
(std::isnan(padfSrcVal[0]) || padfSrcVal[0] == m_adfNoData[0]))
|
|
{
|
|
dst_ptr[0] = adfSrcNoData[0];
|
|
if (bDTIsComplex)
|
|
{
|
|
dst_ptr[1] = adfSrcNoData[1];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
|
|
if (bDTIsComplex)
|
|
{
|
|
dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
|
|
}
|
|
}
|
|
|
|
if ((--nIters) == 0)
|
|
break;
|
|
dst_ptr += stack[dimIdx].dst_inc_offset;
|
|
src_ptr += stack[dimIdx].src_inc_offset;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stack[dimIdx].nIters = count[dimIdx];
|
|
while (true)
|
|
{
|
|
dimIdx++;
|
|
stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
|
|
stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller:
|
|
dimIdx--;
|
|
if ((--stack[dimIdx].nIters) == 0)
|
|
break;
|
|
stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
|
|
stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
|
|
}
|
|
}
|
|
if (dimIdx > 0)
|
|
goto lbl_return_to_caller;
|
|
|
|
// If the parent array is not double/complex-double, then convert the
|
|
// values to it, before calling Write(), as some implementations can be
|
|
// very slow when doing the type conversion.
|
|
const auto &eParentDT = m_poParent->GetDataType();
|
|
const size_t nParentDTSize = eParentDT.GetSize();
|
|
if (nParentDTSize <= nDTSize / 2)
|
|
{
|
|
// Copy in-place by making sure that source and target do not overlap
|
|
const auto eNumericDT = m_dt.GetNumericDataType();
|
|
const auto eParentNumericDT = eParentDT.GetNumericDataType();
|
|
|
|
// Copy first element
|
|
{
|
|
std::vector<GByte> abyTemp(nParentDTSize);
|
|
GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
|
|
static_cast<int>(nDTSize), &abyTemp[0],
|
|
eParentNumericDT, static_cast<int>(nParentDTSize),
|
|
1);
|
|
memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
|
|
}
|
|
// Remaining elements
|
|
for (size_t i = 1; i < nElts; ++i)
|
|
{
|
|
GDALCopyWords(static_cast<GByte *>(pTempBuffer) + i * nDTSize,
|
|
eNumericDT, 0,
|
|
static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
|
|
eParentNumericDT, 0, 1);
|
|
}
|
|
}
|
|
|
|
const bool ret =
|
|
m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
|
|
eParentDT, pTempBuffer);
|
|
|
|
VSIFree(pTempBuffer);
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetUnscaled() */
|
|
/************************************************************************/
|
|
|
|
/** Return an array that is the unscaled version of the current one.
|
|
*
|
|
* That is each value of the unscaled array will be
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* Starting with GDAL 3.3, the Write() method is implemented and will convert
|
|
* from unscaled values to raw values.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetUnscaled().
|
|
*
|
|
* @return a new array, that holds a reference to the original one, and thus is
|
|
* a view of it (not a copy), or nullptr in case of error.
|
|
*/
|
|
std::shared_ptr<GDALMDArray> GDALMDArray::GetUnscaled() const
|
|
{
|
|
auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
|
|
if (!self)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Driver implementation issue: m_pSelf not set !");
|
|
return nullptr;
|
|
}
|
|
if (GetDataType().GetClass() != GEDTC_NUMERIC)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GetUnscaled() only supports numeric data type");
|
|
return nullptr;
|
|
}
|
|
const double dfScale = GetScale();
|
|
const double dfOffset = GetOffset();
|
|
if (dfScale == 1.0 && dfOffset == 0.0)
|
|
return self;
|
|
|
|
return GDALMDArrayUnscaled::Create(self);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayMask */
|
|
/************************************************************************/
|
|
|
|
class GDALMDArrayMask final : public GDALPamMDArray
|
|
{
|
|
private:
|
|
std::shared_ptr<GDALMDArray> m_poParent{};
|
|
GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
|
|
|
|
template <typename Type>
|
|
void ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer, const void *pTempBuffer,
|
|
const GDALExtendedDataType &oTmpBufferDT,
|
|
const std::vector<GPtrDiff_t> &tmpBufferStrideVector,
|
|
bool bHasMissingValue, double dfMissingValue,
|
|
bool bHasFillValue, double dfFillValue, bool bHasValidMin,
|
|
double dfValidMin, bool bHasValidMax,
|
|
double dfValidMax) const;
|
|
|
|
protected:
|
|
explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
|
|
: GDALAbstractMDArray(std::string(),
|
|
"Mask of " + poParent->GetFullName()),
|
|
GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
|
|
::GetPAM(poParent)),
|
|
m_poParent(std::move(poParent))
|
|
{
|
|
}
|
|
|
|
bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const override;
|
|
|
|
bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
CSLConstList papszOptions) const override
|
|
{
|
|
return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
|
|
}
|
|
|
|
public:
|
|
static std::shared_ptr<GDALMDArrayMask>
|
|
Create(const std::shared_ptr<GDALMDArray> &poParent)
|
|
{
|
|
auto newAr(
|
|
std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
|
|
newAr->SetSelf(newAr);
|
|
return newAr;
|
|
}
|
|
|
|
bool IsWritable() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const std::string &GetFilename() const override
|
|
{
|
|
return m_poParent->GetFilename();
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<GDALDimension>> &
|
|
GetDimensions() const override
|
|
{
|
|
return m_poParent->GetDimensions();
|
|
}
|
|
|
|
const GDALExtendedDataType &GetDataType() const override
|
|
{
|
|
return m_dt;
|
|
}
|
|
|
|
std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
|
|
{
|
|
return m_poParent->GetSpatialRef();
|
|
}
|
|
|
|
std::vector<GUInt64> GetBlockSize() const override
|
|
{
|
|
return m_poParent->GetBlockSize();
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* IRead() */
|
|
/************************************************************************/
|
|
|
|
bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
size_t nElts = 1;
|
|
const size_t nDims = GetDimensionCount();
|
|
std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
|
|
for (size_t i = 0; i < nDims; i++)
|
|
nElts *= count[i];
|
|
if (nDims > 0)
|
|
{
|
|
tmpBufferStrideVector.back() = 1;
|
|
for (size_t i = nDims - 1; i > 0;)
|
|
{
|
|
--i;
|
|
tmpBufferStrideVector[i] =
|
|
tmpBufferStrideVector[i + 1] * count[i + 1];
|
|
}
|
|
}
|
|
|
|
const auto GetSingleValNumericAttr =
|
|
[this](const char *pszAttrName, bool &bHasVal, double &dfVal)
|
|
{
|
|
auto poAttr = m_poParent->GetAttribute(pszAttrName);
|
|
if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
|
|
{
|
|
const auto anDimSizes = poAttr->GetDimensionsSize();
|
|
if (anDimSizes.empty() ||
|
|
(anDimSizes.size() == 1 && anDimSizes[0] == 1))
|
|
{
|
|
bHasVal = true;
|
|
dfVal = poAttr->ReadAsDouble();
|
|
}
|
|
}
|
|
};
|
|
|
|
double dfMissingValue = 0.0;
|
|
bool bHasMissingValue = false;
|
|
GetSingleValNumericAttr("missing_value", bHasMissingValue, dfMissingValue);
|
|
|
|
double dfFillValue = 0.0;
|
|
bool bHasFillValue = false;
|
|
GetSingleValNumericAttr("_FillValue", bHasFillValue, dfFillValue);
|
|
|
|
double dfValidMin = 0.0;
|
|
bool bHasValidMin = false;
|
|
GetSingleValNumericAttr("valid_min", bHasValidMin, dfValidMin);
|
|
|
|
double dfValidMax = 0.0;
|
|
bool bHasValidMax = false;
|
|
GetSingleValNumericAttr("valid_max", bHasValidMax, dfValidMax);
|
|
|
|
{
|
|
auto poValidRange = m_poParent->GetAttribute("valid_range");
|
|
if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
|
|
poValidRange->GetDimensionsSize()[0] == 2 &&
|
|
poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
|
|
{
|
|
bHasValidMin = true;
|
|
bHasValidMax = true;
|
|
auto vals = poValidRange->ReadAsDoubleArray();
|
|
CPLAssert(vals.size() == 2);
|
|
dfValidMin = vals[0];
|
|
dfValidMax = vals[1];
|
|
}
|
|
}
|
|
|
|
/* Optimized case: if we are an integer data type and that there is no */
|
|
/* attribute that can be used to set mask = 0, then fill the mask buffer */
|
|
/* directly */
|
|
if (!bHasMissingValue && !bHasFillValue && !bHasValidMin && !bHasValidMax &&
|
|
m_poParent->GetRawNoDataValue() == nullptr &&
|
|
GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
|
|
{
|
|
if (bufferDataType == m_dt) // Byte case
|
|
{
|
|
bool bContiguous = true;
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
if (bufferStride[i] != tmpBufferStrideVector[i])
|
|
{
|
|
bContiguous = false;
|
|
break;
|
|
}
|
|
}
|
|
if (bContiguous)
|
|
{
|
|
// CPLDebug("GDAL", "GetMask(): contiguous case");
|
|
memset(pDstBuffer, 1, nElts);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
struct Stack
|
|
{
|
|
size_t nIters = 0;
|
|
GByte *dst_ptr = nullptr;
|
|
GPtrDiff_t dst_inc_offset = 0;
|
|
};
|
|
std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
|
|
const size_t nBufferDTSize = bufferDataType.GetSize();
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
stack[i].dst_inc_offset =
|
|
static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
|
|
}
|
|
stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
|
|
|
|
size_t dimIdx = 0;
|
|
const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
|
|
const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
|
|
GByte abyOne[16]; // 16 is sizeof GDT_CFloat64
|
|
CPLAssert(nBufferDTSize <= 16);
|
|
const GByte flag = 1;
|
|
// Coverity misses that m_dt is of type Byte
|
|
// coverity[overrun-buffer-val]
|
|
GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
|
|
|
|
lbl_next_depth:
|
|
if (dimIdx == nDimsMinus1)
|
|
{
|
|
auto nIters = nDims > 0 ? count[dimIdx] : 1;
|
|
GByte *dst_ptr = stack[dimIdx].dst_ptr;
|
|
|
|
while (true)
|
|
{
|
|
if (bBufferDataTypeIsByte)
|
|
{
|
|
*dst_ptr = flag;
|
|
}
|
|
else
|
|
{
|
|
memcpy(dst_ptr, abyOne, nBufferDTSize);
|
|
}
|
|
|
|
if ((--nIters) == 0)
|
|
break;
|
|
dst_ptr += stack[dimIdx].dst_inc_offset;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stack[dimIdx].nIters = count[dimIdx];
|
|
while (true)
|
|
{
|
|
dimIdx++;
|
|
stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller:
|
|
dimIdx--;
|
|
if ((--stack[dimIdx].nIters) == 0)
|
|
break;
|
|
stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
|
|
}
|
|
}
|
|
if (dimIdx > 0)
|
|
goto lbl_return_to_caller;
|
|
|
|
return true;
|
|
}
|
|
|
|
const auto oTmpBufferDT =
|
|
GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
|
|
? GDALExtendedDataType::Create(GDT_Float64)
|
|
: m_poParent->GetDataType();
|
|
const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
|
|
void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
|
|
if (!pTempBuffer)
|
|
return false;
|
|
if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
|
|
tmpBufferStrideVector.data(), oTmpBufferDT,
|
|
pTempBuffer))
|
|
{
|
|
VSIFree(pTempBuffer);
|
|
return false;
|
|
}
|
|
|
|
switch (oTmpBufferDT.GetNumericDataType())
|
|
{
|
|
case GDT_Byte:
|
|
ReadInternal<GByte>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
|
|
case GDT_Int8:
|
|
ReadInternal<GInt8>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
|
|
case GDT_UInt16:
|
|
ReadInternal<GUInt16>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
|
|
case GDT_Int16:
|
|
ReadInternal<GInt16>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
|
|
case GDT_UInt32:
|
|
ReadInternal<GUInt32>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
|
|
case GDT_Int32:
|
|
ReadInternal<GInt32>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
|
|
case GDT_UInt64:
|
|
ReadInternal<std::uint64_t>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
|
|
case GDT_Int64:
|
|
ReadInternal<std::int64_t>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
|
|
case GDT_Float32:
|
|
ReadInternal<float>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
|
|
case GDT_Float64:
|
|
ReadInternal<double>(
|
|
count, bufferStride, bufferDataType, pDstBuffer, pTempBuffer,
|
|
oTmpBufferDT, tmpBufferStrideVector, bHasMissingValue,
|
|
dfMissingValue, bHasFillValue, dfFillValue, bHasValidMin,
|
|
dfValidMin, bHasValidMax, dfValidMax);
|
|
break;
|
|
case GDT_Unknown:
|
|
case GDT_CInt16:
|
|
case GDT_CInt32:
|
|
case GDT_CFloat32:
|
|
case GDT_CFloat64:
|
|
case GDT_TypeCount:
|
|
CPLAssert(false);
|
|
break;
|
|
}
|
|
|
|
VSIFree(pTempBuffer);
|
|
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IsValidForDT() */
|
|
/************************************************************************/
|
|
|
|
template <typename Type> static bool IsValidForDT(double dfVal)
|
|
{
|
|
if (std::isnan(dfVal))
|
|
return false;
|
|
if (dfVal < static_cast<double>(std::numeric_limits<Type>::lowest()))
|
|
return false;
|
|
if (dfVal > static_cast<double>(std::numeric_limits<Type>::max()))
|
|
return false;
|
|
return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
|
|
}
|
|
|
|
template <> bool IsValidForDT<double>(double)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IsNan() */
|
|
/************************************************************************/
|
|
|
|
template <typename Type> inline bool IsNan(Type)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
template <> bool IsNan<double>(double val)
|
|
{
|
|
return std::isnan(val);
|
|
}
|
|
|
|
template <> bool IsNan<float>(float val)
|
|
{
|
|
return std::isnan(val);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ReadInternal() */
|
|
/************************************************************************/
|
|
|
|
template <typename Type>
|
|
void GDALMDArrayMask::ReadInternal(
|
|
const size_t *count, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
|
|
const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
|
|
const std::vector<GPtrDiff_t> &tmpBufferStrideVector, bool bHasMissingValue,
|
|
double dfMissingValue, bool bHasFillValue, double dfFillValue,
|
|
bool bHasValidMin, double dfValidMin, bool bHasValidMax,
|
|
double dfValidMax) const
|
|
{
|
|
const size_t nDims = GetDimensionCount();
|
|
|
|
const auto castValue = [](bool &bHasVal, double dfVal) -> Type
|
|
{
|
|
if (bHasVal)
|
|
{
|
|
if (IsValidForDT<Type>(dfVal))
|
|
{
|
|
return static_cast<Type>(dfVal);
|
|
}
|
|
else
|
|
{
|
|
bHasVal = false;
|
|
}
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
|
|
bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
|
|
const Type nNoDataValue =
|
|
castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
|
|
const Type nMissingValue = castValue(bHasMissingValue, dfMissingValue);
|
|
const Type nFillValue = castValue(bHasFillValue, dfFillValue);
|
|
const Type nValidMin = castValue(bHasValidMin, dfValidMin);
|
|
const Type nValidMax = castValue(bHasValidMax, dfValidMax);
|
|
|
|
#define GET_MASK_FOR_SAMPLE(v) \
|
|
static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
|
|
!(bHasMissingValue && v == nMissingValue) && \
|
|
!(bHasFillValue && v == nFillValue) && \
|
|
!(bHasValidMin && v < nValidMin) && \
|
|
!(bHasValidMax && v > nValidMax))
|
|
|
|
const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
|
|
/* Optimized case: Byte output and output buffer is contiguous */
|
|
if (bBufferDataTypeIsByte)
|
|
{
|
|
bool bContiguous = true;
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
if (bufferStride[i] != tmpBufferStrideVector[i])
|
|
{
|
|
bContiguous = false;
|
|
break;
|
|
}
|
|
}
|
|
if (bContiguous)
|
|
{
|
|
size_t nElts = 1;
|
|
for (size_t i = 0; i < nDims; i++)
|
|
nElts *= count[i];
|
|
|
|
for (size_t i = 0; i < nElts; i++)
|
|
{
|
|
const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
|
|
static_cast<GByte *>(pDstBuffer)[i] =
|
|
GET_MASK_FOR_SAMPLE(*pSrc);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
|
|
struct Stack
|
|
{
|
|
size_t nIters = 0;
|
|
const GByte *src_ptr = nullptr;
|
|
GByte *dst_ptr = nullptr;
|
|
GPtrDiff_t src_inc_offset = 0;
|
|
GPtrDiff_t dst_inc_offset = 0;
|
|
};
|
|
std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
|
|
const size_t nBufferDTSize = bufferDataType.GetSize();
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
|
|
tmpBufferStrideVector[i] * nTmpBufferDTSize);
|
|
stack[i].dst_inc_offset =
|
|
static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
|
|
}
|
|
stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
|
|
stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
|
|
|
|
size_t dimIdx = 0;
|
|
const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
|
|
GByte abyZeroOrOne[2][16]; // 16 is sizeof GDT_CFloat64
|
|
CPLAssert(nBufferDTSize <= 16);
|
|
for (GByte flag = 0; flag <= 1; flag++)
|
|
{
|
|
// Coverity misses that m_dt is of type Byte
|
|
// coverity[overrun-buffer-val]
|
|
GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
|
|
bufferDataType);
|
|
}
|
|
|
|
lbl_next_depth:
|
|
if (dimIdx == nDimsMinus1)
|
|
{
|
|
auto nIters = nDims > 0 ? count[dimIdx] : 1;
|
|
const GByte *src_ptr = stack[dimIdx].src_ptr;
|
|
GByte *dst_ptr = stack[dimIdx].dst_ptr;
|
|
|
|
while (true)
|
|
{
|
|
const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
|
|
const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
|
|
|
|
if (bBufferDataTypeIsByte)
|
|
{
|
|
*dst_ptr = flag;
|
|
}
|
|
else
|
|
{
|
|
memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
|
|
}
|
|
|
|
if ((--nIters) == 0)
|
|
break;
|
|
src_ptr += stack[dimIdx].src_inc_offset;
|
|
dst_ptr += stack[dimIdx].dst_inc_offset;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stack[dimIdx].nIters = count[dimIdx];
|
|
while (true)
|
|
{
|
|
dimIdx++;
|
|
stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
|
|
stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller:
|
|
dimIdx--;
|
|
if ((--stack[dimIdx].nIters) == 0)
|
|
break;
|
|
stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
|
|
stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
|
|
}
|
|
}
|
|
if (dimIdx > 0)
|
|
goto lbl_return_to_caller;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMask() */
|
|
/************************************************************************/
|
|
|
|
/** Return an array that is a mask for the current array
|
|
*
|
|
* This array will be of type Byte, with values set to 0 to indicate invalid
|
|
* pixels of the current array, and values set to 1 to indicate valid pixels.
|
|
*
|
|
* The generic implementation honours the NoDataValue, as well as various
|
|
* netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
|
|
* and valid_range.
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetMask().
|
|
*
|
|
* @param papszOptions NULL-terminated list of options, or NULL.
|
|
*
|
|
* @return a new array, that holds a reference to the original one, and thus is
|
|
* a view of it (not a copy), or nullptr in case of error.
|
|
*/
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALMDArray::GetMask(CPL_UNUSED CSLConstList papszOptions) const
|
|
{
|
|
auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
|
|
if (!self)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Driver implementation issue: m_pSelf not set !");
|
|
return nullptr;
|
|
}
|
|
if (GetDataType().GetClass() != GEDTC_NUMERIC)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GetMask() only supports numeric data type");
|
|
return nullptr;
|
|
}
|
|
return GDALMDArrayMask::Create(self);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IsRegularlySpaced() */
|
|
/************************************************************************/
|
|
|
|
/** Returns whether an array is a 1D regularly spaced array.
|
|
*
|
|
* @param[out] dfStart First value in the array
|
|
* @param[out] dfIncrement Increment/spacing between consecutive values.
|
|
* @return true if the array is regularly spaced.
|
|
*/
|
|
bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
|
|
{
|
|
dfStart = 0;
|
|
dfIncrement = 0;
|
|
if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
|
|
return false;
|
|
const auto nSize = GetDimensions()[0]->GetSize();
|
|
if (nSize <= 1 || nSize > 10 * 1000 * 1000)
|
|
return false;
|
|
|
|
size_t nCount = static_cast<size_t>(nSize);
|
|
std::vector<double> adfTmp;
|
|
try
|
|
{
|
|
adfTmp.resize(nCount);
|
|
}
|
|
catch (const std::exception &)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
GUInt64 anStart[1] = {0};
|
|
size_t anCount[1] = {nCount};
|
|
|
|
const auto IsRegularlySpacedInternal =
|
|
[&dfStart, &dfIncrement, &anCount, &adfTmp]()
|
|
{
|
|
dfStart = adfTmp[0];
|
|
dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
|
|
if (dfIncrement == 0)
|
|
{
|
|
return false;
|
|
}
|
|
for (size_t i = 1; i < anCount[0]; i++)
|
|
{
|
|
if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
|
|
1e-3 * fabs(dfIncrement))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// First try with the first block(s). This can avoid excessive processing
|
|
// time, for example with Zarr datasets.
|
|
// https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
|
|
// https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
|
|
const auto nBlockSize = GetBlockSize()[0];
|
|
if (nCount >= 5 && nBlockSize <= nCount / 2)
|
|
{
|
|
size_t nReducedCount =
|
|
std::max<size_t>(3, static_cast<size_t>(nBlockSize));
|
|
while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
|
|
nReducedCount *= 2;
|
|
anCount[0] = nReducedCount;
|
|
if (!Read(anStart, anCount, nullptr, nullptr,
|
|
GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
|
|
{
|
|
return false;
|
|
}
|
|
if (!IsRegularlySpacedInternal())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get next values
|
|
anStart[0] = nReducedCount;
|
|
anCount[0] = nCount - nReducedCount;
|
|
}
|
|
|
|
if (!Read(anStart, anCount, nullptr, nullptr,
|
|
GDALExtendedDataType::Create(GDT_Float64),
|
|
&adfTmp[static_cast<size_t>(anStart[0])]))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return IsRegularlySpacedInternal();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GuessGeoTransform() */
|
|
/************************************************************************/
|
|
|
|
/** Returns whether 2 specified dimensions form a geotransform
|
|
*
|
|
* @param nDimX Index of the X axis.
|
|
* @param nDimY Index of the Y axis.
|
|
* @param bPixelIsPoint Whether the geotransform should be returned
|
|
* with the pixel-is-point (pixel-center) convention
|
|
* (bPixelIsPoint = true), or with the pixel-is-area
|
|
* (top left corner convention)
|
|
* (bPixelIsPoint = false)
|
|
* @param[out] adfGeoTransform Computed geotransform
|
|
* @return true if a geotransform could be computed.
|
|
*/
|
|
bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
|
|
bool bPixelIsPoint,
|
|
double adfGeoTransform[6]) const
|
|
{
|
|
const auto &dims(GetDimensions());
|
|
auto poVarX = dims[nDimX]->GetIndexingVariable();
|
|
auto poVarY = dims[nDimY]->GetIndexingVariable();
|
|
double dfXStart = 0.0;
|
|
double dfXSpacing = 0.0;
|
|
double dfYStart = 0.0;
|
|
double dfYSpacing = 0.0;
|
|
if (poVarX && poVarX->GetDimensionCount() == 1 &&
|
|
poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
|
|
poVarY && poVarY->GetDimensionCount() == 1 &&
|
|
poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
|
|
poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
|
|
poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
|
|
{
|
|
adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
|
|
adfGeoTransform[1] = dfXSpacing;
|
|
adfGeoTransform[2] = 0;
|
|
adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
|
|
adfGeoTransform[4] = 0;
|
|
adfGeoTransform[5] = dfYSpacing;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayResampled */
|
|
/************************************************************************/
|
|
|
|
class GDALMDArrayResampledDataset;
|
|
|
|
class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
|
|
{
|
|
protected:
|
|
CPLErr IReadBlock(int, int, void *) override;
|
|
CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
|
|
int nYSize, void *pData, int nBufXSize, int nBufYSize,
|
|
GDALDataType eBufType, GSpacing nPixelSpaceBuf,
|
|
GSpacing nLineSpaceBuf,
|
|
GDALRasterIOExtraArg *psExtraArg) override;
|
|
|
|
public:
|
|
explicit GDALMDArrayResampledDatasetRasterBand(
|
|
GDALMDArrayResampledDataset *poDSIn);
|
|
|
|
double GetNoDataValue(int *pbHasNoData) override;
|
|
};
|
|
|
|
class GDALMDArrayResampledDataset final : public GDALPamDataset
|
|
{
|
|
friend class GDALMDArrayResampled;
|
|
friend class GDALMDArrayResampledDatasetRasterBand;
|
|
|
|
std::shared_ptr<GDALMDArray> m_poArray;
|
|
size_t m_iXDim;
|
|
size_t m_iYDim;
|
|
double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
|
|
bool m_bHasGT = false;
|
|
mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
|
|
|
|
std::vector<GUInt64> m_anOffset{};
|
|
std::vector<size_t> m_anCount{};
|
|
std::vector<GPtrDiff_t> m_anStride{};
|
|
|
|
std::string m_osFilenameLong{};
|
|
std::string m_osFilenameLat{};
|
|
|
|
public:
|
|
GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
|
|
size_t iXDim, size_t iYDim)
|
|
: m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
|
|
m_anOffset(m_poArray->GetDimensionCount(), 0),
|
|
m_anCount(m_poArray->GetDimensionCount(), 1),
|
|
m_anStride(m_poArray->GetDimensionCount(), 0)
|
|
{
|
|
const auto &dims(m_poArray->GetDimensions());
|
|
|
|
nRasterYSize = static_cast<int>(
|
|
std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
|
|
nRasterXSize = static_cast<int>(
|
|
std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
|
|
|
|
m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
|
|
m_adfGeoTransform);
|
|
|
|
SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
|
|
}
|
|
|
|
~GDALMDArrayResampledDataset()
|
|
{
|
|
if (!m_osFilenameLong.empty())
|
|
VSIUnlink(m_osFilenameLong.c_str());
|
|
if (!m_osFilenameLat.empty())
|
|
VSIUnlink(m_osFilenameLat.c_str());
|
|
}
|
|
|
|
CPLErr GetGeoTransform(double *padfGeoTransform) override
|
|
{
|
|
memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
|
|
return m_bHasGT ? CE_None : CE_Failure;
|
|
}
|
|
|
|
const OGRSpatialReference *GetSpatialRef() const override
|
|
{
|
|
m_poSRS = m_poArray->GetSpatialRef();
|
|
if (m_poSRS)
|
|
{
|
|
m_poSRS.reset(m_poSRS->Clone());
|
|
auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
|
|
for (auto &m : axisMapping)
|
|
{
|
|
if (m == static_cast<int>(m_iXDim) + 1)
|
|
m = 1;
|
|
else if (m == static_cast<int>(m_iYDim) + 1)
|
|
m = 2;
|
|
else
|
|
m = 0;
|
|
}
|
|
m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
|
|
}
|
|
return m_poSRS.get();
|
|
}
|
|
|
|
void SetGeolocationArray(const std::string &osFilenameLong,
|
|
const std::string &osFilenameLat)
|
|
{
|
|
m_osFilenameLong = osFilenameLong;
|
|
m_osFilenameLat = osFilenameLat;
|
|
CPLStringList aosGeoLoc;
|
|
aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
|
|
aosGeoLoc.SetNameValue("LINE_STEP", "1");
|
|
aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
|
|
aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
|
|
aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG); // FIXME?
|
|
aosGeoLoc.SetNameValue("X_BAND", "1");
|
|
aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
|
|
aosGeoLoc.SetNameValue("Y_BAND", "1");
|
|
aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
|
|
aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
|
|
SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterBandFromArray() */
|
|
/************************************************************************/
|
|
|
|
GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
|
|
GDALMDArrayResampledDataset *poDSIn)
|
|
{
|
|
const auto &poArray(poDSIn->m_poArray);
|
|
const auto blockSize(poArray->GetBlockSize());
|
|
nBlockYSize = (blockSize[poDSIn->m_iYDim])
|
|
? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
|
|
blockSize[poDSIn->m_iYDim]))
|
|
: 1;
|
|
nBlockXSize = blockSize[poDSIn->m_iXDim]
|
|
? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
|
|
blockSize[poDSIn->m_iXDim]))
|
|
: poDSIn->GetRasterXSize();
|
|
eDataType = poArray->GetDataType().GetNumericDataType();
|
|
eAccess = poDSIn->eAccess;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
|
|
{
|
|
auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
|
|
const auto &poArray(l_poDS->m_poArray);
|
|
bool bHasNodata = false;
|
|
double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
|
|
if (pbHasNoData)
|
|
*pbHasNoData = bHasNodata;
|
|
return dfRes;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
|
|
int nBlockYOff,
|
|
void *pImage)
|
|
{
|
|
const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
|
|
const int nXOff = nBlockXOff * nBlockXSize;
|
|
const int nYOff = nBlockYOff * nBlockYSize;
|
|
const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
|
|
const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
|
|
GDALRasterIOExtraArg sExtraArg;
|
|
INIT_RASTERIO_EXTRA_ARG(sExtraArg);
|
|
return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
|
|
nReqXSize, nReqYSize, eDataType, nDTSize,
|
|
nDTSize * nBlockXSize, &sExtraArg);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IRasterIO() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
|
|
GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
|
|
void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
|
|
GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
|
|
GDALRasterIOExtraArg *psExtraArg)
|
|
{
|
|
auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
|
|
const auto &poArray(l_poDS->m_poArray);
|
|
const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
|
|
if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
|
|
nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
|
|
(nLineSpaceBuf % nBufferDTSize) == 0)
|
|
{
|
|
l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
|
|
l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
|
|
l_poDS->m_anStride[l_poDS->m_iXDim] =
|
|
static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
|
|
|
|
l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
|
|
l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
|
|
l_poDS->m_anStride[l_poDS->m_iYDim] =
|
|
static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
|
|
|
|
return poArray->Read(l_poDS->m_anOffset.data(),
|
|
l_poDS->m_anCount.data(), nullptr,
|
|
l_poDS->m_anStride.data(),
|
|
GDALExtendedDataType::Create(eBufType), pData)
|
|
? CE_None
|
|
: CE_Failure;
|
|
}
|
|
return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
|
|
pData, nBufXSize, nBufYSize, eBufType,
|
|
nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
|
|
}
|
|
|
|
class GDALMDArrayResampled final : public GDALPamMDArray
|
|
{
|
|
private:
|
|
std::shared_ptr<GDALMDArray> m_poParent{};
|
|
std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
|
|
std::vector<GUInt64> m_anBlockSize;
|
|
GDALExtendedDataType m_dt;
|
|
std::shared_ptr<OGRSpatialReference> m_poSRS{};
|
|
std::shared_ptr<GDALMDArray> m_poVarX{};
|
|
std::shared_ptr<GDALMDArray> m_poVarY{};
|
|
std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
|
|
std::unique_ptr<GDALDataset> m_poReprojectedDS{};
|
|
|
|
protected:
|
|
GDALMDArrayResampled(
|
|
const std::shared_ptr<GDALMDArray> &poParent,
|
|
const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
|
|
const std::vector<GUInt64> &anBlockSize)
|
|
: GDALAbstractMDArray(std::string(),
|
|
"Resampled view of " + poParent->GetFullName()),
|
|
GDALPamMDArray(std::string(),
|
|
"Resampled view of " + poParent->GetFullName(),
|
|
::GetPAM(poParent)),
|
|
m_poParent(std::move(poParent)), m_apoDims(apoDims),
|
|
m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
|
|
{
|
|
CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
|
|
CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
|
|
}
|
|
|
|
bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
|
|
const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const override;
|
|
|
|
public:
|
|
static std::shared_ptr<GDALMDArrayResampled>
|
|
Create(const std::shared_ptr<GDALMDArray> &poParent,
|
|
const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
|
|
GDALRIOResampleAlg resampleAlg,
|
|
const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
|
|
|
|
~GDALMDArrayResampled()
|
|
{
|
|
// First close the warped VRT
|
|
m_poReprojectedDS.reset();
|
|
m_poParentDS.reset();
|
|
}
|
|
|
|
bool IsWritable() const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const std::string &GetFilename() const override
|
|
{
|
|
return m_poParent->GetFilename();
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<GDALDimension>> &
|
|
GetDimensions() const override
|
|
{
|
|
return m_apoDims;
|
|
}
|
|
|
|
const GDALExtendedDataType &GetDataType() const override
|
|
{
|
|
return m_dt;
|
|
}
|
|
|
|
std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
|
|
{
|
|
return m_poSRS;
|
|
}
|
|
|
|
std::vector<GUInt64> GetBlockSize() const override
|
|
{
|
|
return m_anBlockSize;
|
|
}
|
|
|
|
std::shared_ptr<GDALAttribute>
|
|
GetAttribute(const std::string &osName) const override
|
|
{
|
|
return m_poParent->GetAttribute(osName);
|
|
}
|
|
|
|
std::vector<std::shared_ptr<GDALAttribute>>
|
|
GetAttributes(CSLConstList papszOptions = nullptr) const override
|
|
{
|
|
return m_poParent->GetAttributes(papszOptions);
|
|
}
|
|
|
|
const std::string &GetUnit() const override
|
|
{
|
|
return m_poParent->GetUnit();
|
|
}
|
|
|
|
const void *GetRawNoDataValue() const override
|
|
{
|
|
return m_poParent->GetRawNoDataValue();
|
|
}
|
|
|
|
double GetOffset(bool *pbHasOffset,
|
|
GDALDataType *peStorageType) const override
|
|
{
|
|
return m_poParent->GetOffset(pbHasOffset, peStorageType);
|
|
}
|
|
|
|
double GetScale(bool *pbHasScale,
|
|
GDALDataType *peStorageType) const override
|
|
{
|
|
return m_poParent->GetScale(pbHasScale, peStorageType);
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayResampled::Create() */
|
|
/************************************************************************/
|
|
|
|
std::shared_ptr<GDALMDArrayResampled> GDALMDArrayResampled::Create(
|
|
const std::shared_ptr<GDALMDArray> &poParent,
|
|
const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
|
|
GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
|
|
CSLConstList /* papszOptions */)
|
|
{
|
|
const char *pszResampleAlg = "nearest";
|
|
bool unsupported = false;
|
|
switch (resampleAlg)
|
|
{
|
|
case GRIORA_NearestNeighbour:
|
|
pszResampleAlg = "nearest";
|
|
break;
|
|
case GRIORA_Bilinear:
|
|
pszResampleAlg = "bilinear";
|
|
break;
|
|
case GRIORA_Cubic:
|
|
pszResampleAlg = "cubic";
|
|
break;
|
|
case GRIORA_CubicSpline:
|
|
pszResampleAlg = "cubicspline";
|
|
break;
|
|
case GRIORA_Lanczos:
|
|
pszResampleAlg = "lanczos";
|
|
break;
|
|
case GRIORA_Average:
|
|
pszResampleAlg = "average";
|
|
break;
|
|
case GRIORA_Mode:
|
|
pszResampleAlg = "mode";
|
|
break;
|
|
case GRIORA_Gauss:
|
|
unsupported = true;
|
|
break;
|
|
case GRIORA_RESERVED_START:
|
|
unsupported = true;
|
|
break;
|
|
case GRIORA_RESERVED_END:
|
|
unsupported = true;
|
|
break;
|
|
case GRIORA_RMS:
|
|
pszResampleAlg = "rms";
|
|
break;
|
|
}
|
|
if (unsupported)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Unsupported resample method for GetResampled()");
|
|
return nullptr;
|
|
}
|
|
|
|
if (poParent->GetDimensionCount() < 2)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"GetResampled() only supports 2 dimensions or more");
|
|
return nullptr;
|
|
}
|
|
|
|
const auto &aoParentDims = poParent->GetDimensions();
|
|
if (apoNewDimsIn.size() != aoParentDims.size())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GetResampled(): apoNewDims size should be the same as "
|
|
"GetDimensionCount()");
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
|
|
apoNewDims.reserve(apoNewDimsIn.size());
|
|
|
|
std::vector<GUInt64> anBlockSize;
|
|
anBlockSize.reserve(apoNewDimsIn.size());
|
|
const auto &anParentBlockSize = poParent->GetBlockSize();
|
|
|
|
for (unsigned i = 0; i + 2 < apoNewDimsIn.size(); ++i)
|
|
{
|
|
if (apoNewDimsIn[i] == nullptr)
|
|
{
|
|
apoNewDims.emplace_back(aoParentDims[i]);
|
|
}
|
|
else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
|
|
apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GetResampled(): apoNewDims[%u] should be the same "
|
|
"as its parent",
|
|
i);
|
|
return nullptr;
|
|
}
|
|
anBlockSize.emplace_back(anParentBlockSize[i]);
|
|
}
|
|
|
|
const size_t iYDim = poParent->GetDimensionCount() - 2;
|
|
const size_t iXDim = poParent->GetDimensionCount() - 1;
|
|
std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
|
|
new GDALMDArrayResampledDataset(poParent, iXDim, iYDim));
|
|
|
|
double dfXStart = 0.0;
|
|
double dfXSpacing = 0.0;
|
|
bool gotXSpacing = false;
|
|
auto poNewDimX = apoNewDimsIn[iXDim];
|
|
if (poNewDimX)
|
|
{
|
|
if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Too big size for X dimension");
|
|
return nullptr;
|
|
}
|
|
auto var = poNewDimX->GetIndexingVariable();
|
|
if (var)
|
|
{
|
|
if (var->GetDimensionCount() != 1 ||
|
|
var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
|
|
!var->IsRegularlySpaced(dfXStart, dfXSpacing))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"New X dimension should be indexed by a regularly "
|
|
"spaced variable");
|
|
return nullptr;
|
|
}
|
|
gotXSpacing = true;
|
|
}
|
|
}
|
|
|
|
double dfYStart = 0.0;
|
|
double dfYSpacing = 0.0;
|
|
auto poNewDimY = apoNewDimsIn[iYDim];
|
|
bool gotYSpacing = false;
|
|
if (poNewDimY)
|
|
{
|
|
if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Too big size for Y dimension");
|
|
return nullptr;
|
|
}
|
|
auto var = poNewDimY->GetIndexingVariable();
|
|
if (var)
|
|
{
|
|
if (var->GetDimensionCount() != 1 ||
|
|
var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
|
|
!var->IsRegularlySpaced(dfYStart, dfYSpacing))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"New Y dimension should be indexed by a regularly "
|
|
"spaced variable");
|
|
return nullptr;
|
|
}
|
|
gotYSpacing = true;
|
|
}
|
|
}
|
|
|
|
// This limitation could probably be removed
|
|
if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Either none of new X or Y dimension should have an indexing "
|
|
"variable, or both should both should have one.");
|
|
return nullptr;
|
|
}
|
|
|
|
std::string osDstWKT;
|
|
if (poTargetSRS)
|
|
{
|
|
char *pszDstWKT = nullptr;
|
|
if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
|
|
{
|
|
CPLFree(pszDstWKT);
|
|
return nullptr;
|
|
}
|
|
osDstWKT = pszDstWKT;
|
|
CPLFree(pszDstWKT);
|
|
}
|
|
|
|
// Use coordinate variables for geolocation array
|
|
const auto apoCoordinateVars = poParent->GetCoordinateVariables();
|
|
bool useGeolocationArray = false;
|
|
if (apoCoordinateVars.size() >= 2)
|
|
{
|
|
std::shared_ptr<GDALMDArray> poLongVar;
|
|
std::shared_ptr<GDALMDArray> poLatVar;
|
|
for (const auto &poCoordVar : apoCoordinateVars)
|
|
{
|
|
const auto &osName = poCoordVar->GetName();
|
|
const auto poAttr = poCoordVar->GetAttribute("standard_name");
|
|
std::string osStandardName;
|
|
if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
|
|
poAttr->GetDimensionCount() == 0)
|
|
{
|
|
const char *pszStandardName = poAttr->ReadAsString();
|
|
if (pszStandardName)
|
|
osStandardName = pszStandardName;
|
|
}
|
|
if (osName == "lon" || osName == "longitude" ||
|
|
osName == "Longitude" || osStandardName == "longitude")
|
|
{
|
|
poLongVar = poCoordVar;
|
|
}
|
|
else if (osName == "lat" || osName == "latitude" ||
|
|
osName == "Latitude" || osStandardName == "latitude")
|
|
{
|
|
poLatVar = poCoordVar;
|
|
}
|
|
}
|
|
if (poLatVar != nullptr && poLongVar != nullptr)
|
|
{
|
|
const auto longDimCount = poLongVar->GetDimensionCount();
|
|
const auto &longDims = poLongVar->GetDimensions();
|
|
const auto latDimCount = poLatVar->GetDimensionCount();
|
|
const auto &latDims = poLatVar->GetDimensions();
|
|
const auto xDimSize = aoParentDims[iXDim]->GetSize();
|
|
const auto yDimSize = aoParentDims[iYDim]->GetSize();
|
|
if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
|
|
latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
|
|
{
|
|
// Geolocation arrays are 1D, and of consistent size with
|
|
// the variable
|
|
useGeolocationArray = true;
|
|
}
|
|
else if ((longDimCount == 2 ||
|
|
(longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
|
|
longDims[longDimCount - 2]->GetSize() == yDimSize &&
|
|
longDims[longDimCount - 1]->GetSize() == xDimSize &&
|
|
(latDimCount == 2 ||
|
|
(latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
|
|
latDims[latDimCount - 2]->GetSize() == yDimSize &&
|
|
latDims[latDimCount - 1]->GetSize() == xDimSize)
|
|
|
|
{
|
|
// Geolocation arrays are 2D (or 3D with first dimension of
|
|
// size 1, as found in Sentinel 5P products), and of consistent
|
|
// size with the variable
|
|
useGeolocationArray = true;
|
|
}
|
|
else
|
|
{
|
|
CPLDebug(
|
|
"GDAL",
|
|
"Longitude and latitude coordinate variables found, "
|
|
"but their characteristics are not compatible of using "
|
|
"them as geolocation arrays");
|
|
}
|
|
if (useGeolocationArray)
|
|
{
|
|
CPLDebug("GDAL",
|
|
"Setting geolocation array from variables %s and %s",
|
|
poLongVar->GetName().c_str(),
|
|
poLatVar->GetName().c_str());
|
|
std::string osFilenameLong =
|
|
CPLSPrintf("/vsimem/%p/longitude.tif", poParent.get());
|
|
std::string osFilenameLat =
|
|
CPLSPrintf("/vsimem/%p/latitude.tif", poParent.get());
|
|
std::unique_ptr<GDALDataset> poTmpLongDS(
|
|
longDimCount == 1
|
|
? poLongVar->AsClassicDataset(0, 0)
|
|
: poLongVar->AsClassicDataset(longDimCount - 1,
|
|
longDimCount - 2));
|
|
auto hTIFFLongDS = GDALTranslate(
|
|
osFilenameLong.c_str(),
|
|
GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
|
|
std::unique_ptr<GDALDataset> poTmpLatDS(
|
|
latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
|
|
: poLatVar->AsClassicDataset(
|
|
latDimCount - 1, latDimCount - 2));
|
|
auto hTIFFLatDS = GDALTranslate(
|
|
osFilenameLat.c_str(),
|
|
GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
|
|
const bool bError =
|
|
(hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
|
|
GDALClose(hTIFFLongDS);
|
|
GDALClose(hTIFFLatDS);
|
|
if (bError)
|
|
{
|
|
VSIUnlink(osFilenameLong.c_str());
|
|
VSIUnlink(osFilenameLat.c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CPLDebug("GDAL",
|
|
"Coordinate variables available for %s, but "
|
|
"longitude and/or latitude variables were not identified",
|
|
poParent->GetName().c_str());
|
|
}
|
|
}
|
|
|
|
// Build gdalwarp arguments
|
|
CPLStringList aosArgv;
|
|
|
|
aosArgv.AddString("-of");
|
|
aosArgv.AddString("VRT");
|
|
|
|
aosArgv.AddString("-r");
|
|
aosArgv.AddString(pszResampleAlg);
|
|
|
|
if (!osDstWKT.empty())
|
|
{
|
|
aosArgv.AddString("-t_srs");
|
|
aosArgv.AddString(osDstWKT.c_str());
|
|
}
|
|
|
|
if (useGeolocationArray)
|
|
aosArgv.AddString("-geoloc");
|
|
|
|
if (gotXSpacing && gotYSpacing)
|
|
{
|
|
const double dfXMin = dfXStart - dfXSpacing / 2;
|
|
const double dfXMax =
|
|
dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
|
|
const double dfYMax = dfYStart - dfYSpacing / 2;
|
|
const double dfYMin =
|
|
dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
|
|
aosArgv.AddString("-te");
|
|
aosArgv.AddString(CPLSPrintf("%.18g", dfXMin));
|
|
aosArgv.AddString(CPLSPrintf("%.18g", dfYMin));
|
|
aosArgv.AddString(CPLSPrintf("%.18g", dfXMax));
|
|
aosArgv.AddString(CPLSPrintf("%.18g", dfYMax));
|
|
}
|
|
|
|
if (poNewDimX && poNewDimY)
|
|
{
|
|
aosArgv.AddString("-ts");
|
|
aosArgv.AddString(
|
|
CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
|
|
aosArgv.AddString(
|
|
CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
|
|
}
|
|
else if (poNewDimX)
|
|
{
|
|
aosArgv.AddString("-ts");
|
|
aosArgv.AddString(
|
|
CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
|
|
aosArgv.AddString("0");
|
|
}
|
|
else if (poNewDimY)
|
|
{
|
|
aosArgv.AddString("-ts");
|
|
aosArgv.AddString("0");
|
|
aosArgv.AddString(
|
|
CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
|
|
}
|
|
|
|
// Create a warped VRT dataset
|
|
GDALWarpAppOptions *psOptions =
|
|
GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
|
|
GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
|
|
std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
|
|
GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
|
|
GDALWarpAppOptionsFree(psOptions);
|
|
if (poReprojectedDS == nullptr)
|
|
return nullptr;
|
|
|
|
int nBlockXSize;
|
|
int nBlockYSize;
|
|
poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
|
|
anBlockSize.emplace_back(nBlockYSize);
|
|
anBlockSize.emplace_back(nBlockXSize);
|
|
|
|
double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
|
|
CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
|
|
CPLAssert(eErr == CE_None);
|
|
CPL_IGNORE_RET_VAL(eErr);
|
|
|
|
auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
|
|
std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
|
|
poReprojectedDS->GetRasterYSize());
|
|
auto varY = GDALMDArrayRegularlySpaced::Create(
|
|
std::string(), poDimY->GetName(), poDimY,
|
|
adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
|
|
poDimY->SetIndexingVariable(varY);
|
|
|
|
auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
|
|
std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
|
|
poReprojectedDS->GetRasterXSize());
|
|
auto varX = GDALMDArrayRegularlySpaced::Create(
|
|
std::string(), poDimX->GetName(), poDimX,
|
|
adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
|
|
poDimX->SetIndexingVariable(varX);
|
|
|
|
apoNewDims.emplace_back(poDimY);
|
|
apoNewDims.emplace_back(poDimX);
|
|
auto newAr(std::shared_ptr<GDALMDArrayResampled>(
|
|
new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
|
|
newAr->SetSelf(newAr);
|
|
if (poTargetSRS)
|
|
{
|
|
newAr->m_poSRS.reset(poTargetSRS->Clone());
|
|
}
|
|
else
|
|
{
|
|
newAr->m_poSRS = poParent->GetSpatialRef();
|
|
}
|
|
newAr->m_poVarX = varX;
|
|
newAr->m_poVarY = varY;
|
|
newAr->m_poReprojectedDS = std::move(poReprojectedDS);
|
|
newAr->m_poParentDS = std::move(poParentDS);
|
|
return newAr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayResampled::IRead() */
|
|
/************************************************************************/
|
|
|
|
bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
if (bufferDataType.GetClass() != GEDTC_NUMERIC)
|
|
return false;
|
|
|
|
struct Stack
|
|
{
|
|
size_t nIters = 0;
|
|
GByte *dst_ptr = nullptr;
|
|
GPtrDiff_t dst_inc_offset = 0;
|
|
};
|
|
const auto nDims = GetDimensionCount();
|
|
std::vector<Stack> stack(nDims + 1); // +1 to avoid -Wnull-dereference
|
|
const size_t nBufferDTSize = bufferDataType.GetSize();
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
stack[i].dst_inc_offset =
|
|
static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
|
|
}
|
|
stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
|
|
|
|
size_t dimIdx = 0;
|
|
const size_t iDimY = nDims - 2;
|
|
const size_t iDimX = nDims - 1;
|
|
// Use an array to avoid a false positive warning from CLang Static
|
|
// Analyzer about flushCaches being never read
|
|
bool flushCaches[] = {false};
|
|
|
|
lbl_next_depth:
|
|
if (dimIdx == iDimY)
|
|
{
|
|
if (flushCaches[0])
|
|
{
|
|
flushCaches[0] = false;
|
|
// When changing of 2D slice, flush GDAL 2D buffers
|
|
m_poParentDS->FlushCache(false);
|
|
m_poReprojectedDS->FlushCache(false);
|
|
}
|
|
|
|
if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
|
|
GF_Read, iDimX, iDimY, arrayStartIdx, count,
|
|
arrayStep, bufferStride, bufferDataType,
|
|
stack[dimIdx].dst_ptr))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stack[dimIdx].nIters = count[dimIdx];
|
|
if (m_poParentDS->m_anOffset[dimIdx] != arrayStartIdx[dimIdx])
|
|
{
|
|
flushCaches[0] = true;
|
|
}
|
|
m_poParentDS->m_anOffset[dimIdx] = arrayStartIdx[dimIdx];
|
|
while (true)
|
|
{
|
|
dimIdx++;
|
|
stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller:
|
|
dimIdx--;
|
|
if ((--stack[dimIdx].nIters) == 0)
|
|
break;
|
|
flushCaches[0] = true;
|
|
++m_poParentDS->m_anOffset[dimIdx];
|
|
stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
|
|
}
|
|
}
|
|
if (dimIdx > 0)
|
|
goto lbl_return_to_caller;
|
|
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetResampled() */
|
|
/************************************************************************/
|
|
|
|
/** Return an array that is a resampled / reprojected view of the current array
|
|
*
|
|
* This is the same as the C function GDALMDArrayGetResampled().
|
|
*
|
|
* Currently this method can only resample along the last 2 dimensions.
|
|
*
|
|
* @param apoNewDims New dimensions. Its size should be GetDimensionCount().
|
|
* apoNewDims[i] can be NULL to let the method automatically
|
|
* determine it.
|
|
* @param resampleAlg Resampling algorithm
|
|
* @param poTargetSRS Target SRS, or nullptr
|
|
* @param papszOptions NULL-terminated list of options, or NULL.
|
|
*
|
|
* @return a new array, that holds a reference to the original one, and thus is
|
|
* a view of it (not a copy), or nullptr in case of error.
|
|
*
|
|
* @since 3.4
|
|
*/
|
|
std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
|
|
const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
|
|
GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
|
|
CSLConstList papszOptions) const
|
|
{
|
|
auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
|
|
if (!self)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Driver implementation issue: m_pSelf not set !");
|
|
return nullptr;
|
|
}
|
|
if (GetDataType().GetClass() != GEDTC_NUMERIC)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GetResampled() only supports numeric data type");
|
|
return nullptr;
|
|
}
|
|
return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
|
|
poTargetSRS, papszOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDatasetFromArray() */
|
|
/************************************************************************/
|
|
|
|
class GDALDatasetFromArray;
|
|
|
|
class GDALRasterBandFromArray final : public GDALRasterBand
|
|
{
|
|
std::vector<GUInt64> m_anOffset{};
|
|
std::vector<size_t> m_anCount{};
|
|
std::vector<GPtrDiff_t> m_anStride{};
|
|
|
|
protected:
|
|
CPLErr IReadBlock(int, int, void *) override;
|
|
CPLErr IWriteBlock(int, int, void *) override;
|
|
CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
|
|
int nYSize, void *pData, int nBufXSize, int nBufYSize,
|
|
GDALDataType eBufType, GSpacing nPixelSpaceBuf,
|
|
GSpacing nLineSpaceBuf,
|
|
GDALRasterIOExtraArg *psExtraArg) override;
|
|
|
|
public:
|
|
explicit GDALRasterBandFromArray(
|
|
GDALDatasetFromArray *poDSIn,
|
|
const std::vector<GUInt64> &anOtherDimCoord);
|
|
|
|
double GetNoDataValue(int *pbHasNoData) override;
|
|
int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
|
|
uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
|
|
double GetOffset(int *pbHasOffset) override;
|
|
double GetScale(int *pbHasScale) override;
|
|
const char *GetUnitType() override;
|
|
};
|
|
|
|
class GDALDatasetFromArray final : public GDALDataset
|
|
{
|
|
friend class GDALRasterBandFromArray;
|
|
|
|
std::shared_ptr<GDALMDArray> m_poArray;
|
|
size_t m_iXDim;
|
|
size_t m_iYDim;
|
|
double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
|
|
bool m_bHasGT = false;
|
|
mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
|
|
GDALMultiDomainMetadata m_oMDD{};
|
|
|
|
public:
|
|
GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
|
|
size_t iXDim, size_t iYDim)
|
|
: m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
|
|
{
|
|
const auto &dims(m_poArray->GetDimensions());
|
|
const auto nDimCount = dims.size();
|
|
nRasterYSize =
|
|
nDimCount < 2
|
|
? 1
|
|
: static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
|
|
dims[iYDim]->GetSize()));
|
|
nRasterXSize = static_cast<int>(
|
|
std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
|
|
eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
|
|
|
|
const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
|
|
std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
|
|
std::vector<GUInt64> anStackIters(nDimCount);
|
|
std::vector<size_t> anMapNewToOld(nNewDimCount);
|
|
for (size_t i = 0, j = 0; i < nDimCount; ++i)
|
|
{
|
|
if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
|
|
{
|
|
anMapNewToOld[j] = i;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
|
|
m_adfGeoTransform);
|
|
|
|
const auto attrs(array->GetAttributes());
|
|
for (const auto &attr : attrs)
|
|
{
|
|
auto stringArray = attr->ReadAsStringArray();
|
|
std::string val;
|
|
if (stringArray.size() > 1)
|
|
{
|
|
val += '{';
|
|
}
|
|
for (int i = 0; i < stringArray.size(); ++i)
|
|
{
|
|
if (i > 0)
|
|
val += ',';
|
|
val += stringArray[i];
|
|
}
|
|
if (stringArray.size() > 1)
|
|
{
|
|
val += '}';
|
|
}
|
|
m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
|
|
}
|
|
|
|
// Instantiate bands by iterating over non-XY variables
|
|
size_t iDim = 0;
|
|
lbl_next_depth:
|
|
if (iDim < nNewDimCount)
|
|
{
|
|
anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
|
|
anOtherDimCoord[iDim] = 0;
|
|
while (true)
|
|
{
|
|
++iDim;
|
|
goto lbl_next_depth;
|
|
lbl_return_to_caller:
|
|
--iDim;
|
|
--anStackIters[iDim];
|
|
if (anStackIters[iDim] == 0)
|
|
break;
|
|
++anOtherDimCoord[iDim];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetBand(nBands + 1,
|
|
new GDALRasterBandFromArray(this, anOtherDimCoord));
|
|
}
|
|
if (iDim > 0)
|
|
goto lbl_return_to_caller;
|
|
}
|
|
|
|
CPLErr GetGeoTransform(double *padfGeoTransform) override
|
|
{
|
|
memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
|
|
return m_bHasGT ? CE_None : CE_Failure;
|
|
}
|
|
|
|
const OGRSpatialReference *GetSpatialRef() const override
|
|
{
|
|
if (m_poArray->GetDimensionCount() < 2)
|
|
return nullptr;
|
|
m_poSRS = m_poArray->GetSpatialRef();
|
|
if (m_poSRS)
|
|
{
|
|
m_poSRS.reset(m_poSRS->Clone());
|
|
auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
|
|
for (auto &m : axisMapping)
|
|
{
|
|
if (m == static_cast<int>(m_iXDim) + 1)
|
|
m = 1;
|
|
else if (m == static_cast<int>(m_iYDim) + 1)
|
|
m = 2;
|
|
else
|
|
m = 0;
|
|
}
|
|
m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
|
|
}
|
|
return m_poSRS.get();
|
|
}
|
|
|
|
CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
|
|
{
|
|
return m_oMDD.SetMetadata(papszMetadata, pszDomain);
|
|
}
|
|
|
|
char **GetMetadata(const char *pszDomain) override
|
|
{
|
|
return m_oMDD.GetMetadata(pszDomain);
|
|
}
|
|
|
|
const char *GetMetadataItem(const char *pszName,
|
|
const char *pszDomain) override
|
|
{
|
|
return m_oMDD.GetMetadataItem(pszName, pszDomain);
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterBandFromArray() */
|
|
/************************************************************************/
|
|
|
|
GDALRasterBandFromArray::GDALRasterBandFromArray(
|
|
GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord)
|
|
{
|
|
const auto &poArray(poDSIn->m_poArray);
|
|
const auto &dims(poArray->GetDimensions());
|
|
const auto nDimCount(dims.size());
|
|
const auto blockSize(poArray->GetBlockSize());
|
|
nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
|
|
? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
|
|
blockSize[poDSIn->m_iYDim]))
|
|
: 1;
|
|
nBlockXSize = blockSize[poDSIn->m_iXDim]
|
|
? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
|
|
blockSize[poDSIn->m_iXDim]))
|
|
: poDSIn->GetRasterXSize();
|
|
eDataType = poArray->GetDataType().GetNumericDataType();
|
|
eAccess = poDSIn->eAccess;
|
|
m_anOffset.resize(nDimCount);
|
|
m_anCount.resize(nDimCount, 1);
|
|
m_anStride.resize(nDimCount);
|
|
for (size_t i = 0, j = 0; i < nDimCount; ++i)
|
|
{
|
|
if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
|
|
{
|
|
std::string dimName(dims[i]->GetName());
|
|
GUInt64 nIndex = anOtherDimCoord[j];
|
|
// Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
|
|
// subsetted dimensions as generated by GetView()
|
|
if (STARTS_WITH(dimName.c_str(), "subset_"))
|
|
{
|
|
CPLStringList aosTokens(
|
|
CSLTokenizeString2(dimName.c_str(), "_", 0));
|
|
if (aosTokens.size() == 5)
|
|
{
|
|
dimName = aosTokens[1];
|
|
const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
|
|
aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
|
|
const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
|
|
nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
|
|
: nStartDim - (nIndex * -nIncrDim);
|
|
}
|
|
}
|
|
SetMetadataItem(
|
|
CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
|
|
CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
|
|
auto indexingVar = dims[i]->GetIndexingVariable();
|
|
if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
|
|
indexingVar->GetDimensions()[0]->GetSize() ==
|
|
dims[i]->GetSize())
|
|
{
|
|
size_t nCount = 1;
|
|
const auto &dt(indexingVar->GetDataType());
|
|
std::vector<GByte> abyTmp(dt.GetSize());
|
|
if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
|
|
nullptr, dt, &abyTmp[0]))
|
|
{
|
|
char *pszTmp = nullptr;
|
|
GDALExtendedDataType::CopyValue(
|
|
&abyTmp[0], dt, &pszTmp,
|
|
GDALExtendedDataType::CreateString());
|
|
if (pszTmp)
|
|
{
|
|
SetMetadataItem(
|
|
CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
|
|
pszTmp);
|
|
CPLFree(pszTmp);
|
|
}
|
|
|
|
const auto unit(indexingVar->GetUnit());
|
|
if (!unit.empty())
|
|
{
|
|
SetMetadataItem(
|
|
CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
|
|
unit.c_str());
|
|
}
|
|
}
|
|
}
|
|
m_anOffset[i] = anOtherDimCoord[j];
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
|
|
{
|
|
auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
|
|
const auto &poArray(l_poDS->m_poArray);
|
|
bool bHasNodata = false;
|
|
const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
|
|
if (pbHasNoData)
|
|
*pbHasNoData = bHasNodata;
|
|
return res;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValueAsInt64() */
|
|
/************************************************************************/
|
|
|
|
int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
|
|
{
|
|
auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
|
|
const auto &poArray(l_poDS->m_poArray);
|
|
bool bHasNodata = false;
|
|
const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
|
|
if (pbHasNoData)
|
|
*pbHasNoData = bHasNodata;
|
|
return nodata;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetNoDataValueAsUInt64() */
|
|
/************************************************************************/
|
|
|
|
uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
|
|
{
|
|
auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
|
|
const auto &poArray(l_poDS->m_poArray);
|
|
bool bHasNodata = false;
|
|
const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
|
|
if (pbHasNoData)
|
|
*pbHasNoData = bHasNodata;
|
|
return nodata;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetOffset() */
|
|
/************************************************************************/
|
|
|
|
double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
|
|
{
|
|
auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
|
|
const auto &poArray(l_poDS->m_poArray);
|
|
bool bHasValue = false;
|
|
double dfRes = poArray->GetOffset(&bHasValue);
|
|
if (pbHasOffset)
|
|
*pbHasOffset = bHasValue;
|
|
return dfRes;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetUnitType() */
|
|
/************************************************************************/
|
|
|
|
const char *GDALRasterBandFromArray::GetUnitType()
|
|
{
|
|
auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
|
|
const auto &poArray(l_poDS->m_poArray);
|
|
return poArray->GetUnit().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetScale() */
|
|
/************************************************************************/
|
|
|
|
double GDALRasterBandFromArray::GetScale(int *pbHasScale)
|
|
{
|
|
auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
|
|
const auto &poArray(l_poDS->m_poArray);
|
|
bool bHasValue = false;
|
|
double dfRes = poArray->GetScale(&bHasValue);
|
|
if (pbHasScale)
|
|
*pbHasScale = bHasValue;
|
|
return dfRes;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IReadBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
|
|
void *pImage)
|
|
{
|
|
const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
|
|
const int nXOff = nBlockXOff * nBlockXSize;
|
|
const int nYOff = nBlockYOff * nBlockYSize;
|
|
const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
|
|
const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
|
|
GDALRasterIOExtraArg sExtraArg;
|
|
INIT_RASTERIO_EXTRA_ARG(sExtraArg);
|
|
return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
|
|
nReqXSize, nReqYSize, eDataType, nDTSize,
|
|
nDTSize * nBlockXSize, &sExtraArg);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IWriteBlock() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
|
|
void *pImage)
|
|
{
|
|
const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
|
|
const int nXOff = nBlockXOff * nBlockXSize;
|
|
const int nYOff = nBlockYOff * nBlockYSize;
|
|
const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
|
|
const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
|
|
GDALRasterIOExtraArg sExtraArg;
|
|
INIT_RASTERIO_EXTRA_ARG(sExtraArg);
|
|
return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
|
|
nReqXSize, nReqYSize, eDataType, nDTSize,
|
|
nDTSize * nBlockXSize, &sExtraArg);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IRasterIO() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
|
|
int nYOff, int nXSize, int nYSize,
|
|
void *pData, int nBufXSize,
|
|
int nBufYSize, GDALDataType eBufType,
|
|
GSpacing nPixelSpaceBuf,
|
|
GSpacing nLineSpaceBuf,
|
|
GDALRasterIOExtraArg *psExtraArg)
|
|
{
|
|
auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
|
|
const auto &poArray(l_poDS->m_poArray);
|
|
const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
|
|
if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
|
|
(nPixelSpaceBuf % nBufferDTSize) == 0 &&
|
|
(nLineSpaceBuf % nBufferDTSize) == 0)
|
|
{
|
|
m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
|
|
m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
|
|
m_anStride[l_poDS->m_iXDim] =
|
|
static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
|
|
if (poArray->GetDimensionCount() >= 2)
|
|
{
|
|
m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
|
|
m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
|
|
m_anStride[l_poDS->m_iYDim] =
|
|
static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
|
|
}
|
|
if (eRWFlag == GF_Read)
|
|
{
|
|
return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
|
|
m_anStride.data(),
|
|
GDALExtendedDataType::Create(eBufType), pData)
|
|
? CE_None
|
|
: CE_Failure;
|
|
}
|
|
else
|
|
{
|
|
return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
|
|
m_anStride.data(),
|
|
GDALExtendedDataType::Create(eBufType), pData)
|
|
? CE_None
|
|
: CE_Failure;
|
|
}
|
|
}
|
|
return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
|
|
pData, nBufXSize, nBufYSize, eBufType,
|
|
nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* AsClassicDataset() */
|
|
/************************************************************************/
|
|
|
|
/** Return a view of this array as a "classic" GDALDataset (ie 2D)
|
|
*
|
|
* In the case of > 2D arrays, additional dimensions will be represented as
|
|
* raster bands.
|
|
*
|
|
* The "reverse" method is GDALRasterBand::AsMDArray().
|
|
*
|
|
* This is the same as the C function GDALMDArrayAsClassicDataset().
|
|
*
|
|
* @param iXDim Index of the dimension that will be used as the X/width axis.
|
|
* @param iYDim Index of the dimension that will be used as the Y/height axis.
|
|
* Ignored if the dimension count is 1.
|
|
* @return a new GDALDataset that must be freed with GDALClose(), or nullptr
|
|
*/
|
|
GDALDataset *GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim) const
|
|
{
|
|
auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
|
|
if (!self)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Driver implementation issue: m_pSelf not set !");
|
|
return nullptr;
|
|
}
|
|
const auto nDimCount(GetDimensionCount());
|
|
if (nDimCount == 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Unsupported number of dimensions");
|
|
return nullptr;
|
|
}
|
|
if (GetDataType().GetClass() != GEDTC_NUMERIC ||
|
|
GetDataType().GetNumericDataType() == GDT_Unknown)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"Only arrays with numeric data types "
|
|
"can be exposed as classic GDALDataset");
|
|
return nullptr;
|
|
}
|
|
if (iXDim >= nDimCount ||
|
|
(nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
|
|
return nullptr;
|
|
}
|
|
GUInt64 nBands = 1;
|
|
const auto &dims(GetDimensions());
|
|
for (size_t i = 0; i < nDimCount; ++i)
|
|
{
|
|
if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
|
|
{
|
|
if (dims[i]->GetSize() > 65536 / nBands)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Too many bands. Operate on a sliced view");
|
|
return nullptr;
|
|
}
|
|
nBands *= dims[i]->GetSize();
|
|
}
|
|
}
|
|
return new GDALDatasetFromArray(self, iXDim, iYDim);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetStatistics() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Fetch statistics.
|
|
*
|
|
* Returns the minimum, maximum, mean and standard deviation of all
|
|
* pixel values in this array.
|
|
*
|
|
* If bForce is FALSE results will only be returned if it can be done
|
|
* quickly (i.e. without scanning the data). If bForce is FALSE and
|
|
* results cannot be returned efficiently, the method will return CE_Warning
|
|
* but no warning will have been issued. This is a non-standard use of
|
|
* the CE_Warning return value to indicate "nothing done".
|
|
*
|
|
* When cached statistics are not available, and bForce is TRUE,
|
|
* ComputeStatistics() is called.
|
|
*
|
|
* Note that file formats using PAM (Persistent Auxiliary Metadata) services
|
|
* will generally cache statistics in the .aux.xml file allowing fast fetch
|
|
* after the first request.
|
|
*
|
|
* Cached statistics can be cleared with GDALDataset::ClearStatistics().
|
|
*
|
|
* This method is the same as the C function GDALMDArrayGetStatistics().
|
|
*
|
|
* @param bApproxOK Currently ignored. In the future, should be set to true
|
|
* if statistics on the whole array are wished, or to false if a subset of it
|
|
* may be used.
|
|
*
|
|
* @param bForce If false statistics will only be returned if it can
|
|
* be done without rescanning the image.
|
|
*
|
|
* @param pdfMin Location into which to load image minimum (may be NULL).
|
|
*
|
|
* @param pdfMax Location into which to load image maximum (may be NULL).-
|
|
*
|
|
* @param pdfMean Location into which to load image mean (may be NULL).
|
|
*
|
|
* @param pdfStdDev Location into which to load image standard deviation
|
|
* (may be NULL).
|
|
*
|
|
* @param pnValidCount Number of samples whose value is different from the
|
|
* nodata value. (may be NULL)
|
|
*
|
|
* @param pfnProgress a function to call to report progress, or NULL.
|
|
*
|
|
* @param pProgressData application data to pass to the progress function.
|
|
*
|
|
* @return CE_None on success, CE_Warning if no values returned,
|
|
* CE_Failure if an error occurs.
|
|
*
|
|
* @since GDAL 3.2
|
|
*/
|
|
|
|
CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
|
|
double *pdfMax, double *pdfMean,
|
|
double *pdfStdDev, GUInt64 *pnValidCount,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
{
|
|
if (!bForce)
|
|
return CE_Warning;
|
|
|
|
return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
|
|
pnValidCount, pfnProgress, pProgressData)
|
|
? CE_None
|
|
: CE_Failure;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ComputeStatistics() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Compute statistics.
|
|
*
|
|
* Returns the minimum, maximum, mean and standard deviation of all
|
|
* pixel values in this array.
|
|
*
|
|
* Pixels taken into account in statistics are those whose mask value
|
|
* (as determined by GetMask()) is non-zero.
|
|
*
|
|
* Once computed, the statistics will generally be "set" back on the
|
|
* owing dataset.
|
|
*
|
|
* Cached statistics can be cleared with GDALDataset::ClearStatistics().
|
|
*
|
|
* This method is the same as the C function GDALMDArrayComputeStatistics().
|
|
*
|
|
* @param bApproxOK Currently ignored. In the future, should be set to true
|
|
* if statistics on the whole array are wished, or to false if a subset of it
|
|
* may be used.
|
|
*
|
|
* @param pdfMin Location into which to load image minimum (may be NULL).
|
|
*
|
|
* @param pdfMax Location into which to load image maximum (may be NULL).-
|
|
*
|
|
* @param pdfMean Location into which to load image mean (may be NULL).
|
|
*
|
|
* @param pdfStdDev Location into which to load image standard deviation
|
|
* (may be NULL).
|
|
*
|
|
* @param pnValidCount Number of samples whose value is different from the
|
|
* nodata value. (may be NULL)
|
|
*
|
|
* @param pfnProgress a function to call to report progress, or NULL.
|
|
*
|
|
* @param pProgressData application data to pass to the progress function.
|
|
*
|
|
* @return true on success
|
|
*
|
|
* @since GDAL 3.2
|
|
*/
|
|
|
|
bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
|
|
double *pdfMax, double *pdfMean,
|
|
double *pdfStdDev, GUInt64 *pnValidCount,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
{
|
|
struct StatsPerChunkType
|
|
{
|
|
const GDALMDArray *array = nullptr;
|
|
std::shared_ptr<GDALMDArray> poMask{};
|
|
double dfMin = std::numeric_limits<double>::max();
|
|
double dfMax = -std::numeric_limits<double>::max();
|
|
double dfMean = 0.0;
|
|
double dfM2 = 0.0;
|
|
GUInt64 nValidCount = 0;
|
|
std::vector<GByte> abyData{};
|
|
std::vector<double> adfData{};
|
|
std::vector<GByte> abyMaskData{};
|
|
GDALProgressFunc pfnProgress = nullptr;
|
|
void *pProgressData = nullptr;
|
|
};
|
|
|
|
const auto PerChunkFunc = [](GDALAbstractMDArray *,
|
|
const GUInt64 *chunkArrayStartIdx,
|
|
const size_t *chunkCount, GUInt64 iCurChunk,
|
|
GUInt64 nChunkCount, void *pUserData)
|
|
{
|
|
StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
|
|
const GDALMDArray *array = data->array;
|
|
const GDALMDArray *poMask = data->poMask.get();
|
|
const size_t nDims = array->GetDimensionCount();
|
|
size_t nVals = 1;
|
|
for (size_t i = 0; i < nDims; i++)
|
|
nVals *= chunkCount[i];
|
|
|
|
// Get mask
|
|
data->abyMaskData.resize(nVals);
|
|
if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
|
|
poMask->GetDataType(), &data->abyMaskData[0])))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get data
|
|
const auto &oType = array->GetDataType();
|
|
if (oType.GetNumericDataType() == GDT_Float64)
|
|
{
|
|
data->adfData.resize(nVals);
|
|
if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
|
|
oType, &data->adfData[0]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data->abyData.resize(nVals * oType.GetSize());
|
|
if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
|
|
oType, &data->abyData[0]))
|
|
{
|
|
return false;
|
|
}
|
|
data->adfData.resize(nVals);
|
|
GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
|
|
static_cast<int>(oType.GetSize()),
|
|
&data->adfData[0], GDT_Float64,
|
|
static_cast<int>(sizeof(double)),
|
|
static_cast<GPtrDiff_t>(nVals));
|
|
}
|
|
for (size_t i = 0; i < nVals; i++)
|
|
{
|
|
if (data->abyMaskData[i])
|
|
{
|
|
const double dfValue = data->adfData[i];
|
|
data->dfMin = std::min(data->dfMin, dfValue);
|
|
data->dfMax = std::max(data->dfMax, dfValue);
|
|
data->nValidCount++;
|
|
const double dfDelta = dfValue - data->dfMean;
|
|
data->dfMean += dfDelta / data->nValidCount;
|
|
data->dfM2 += dfDelta * (dfValue - data->dfMean);
|
|
}
|
|
}
|
|
if (data->pfnProgress &&
|
|
!data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
|
|
"", data->pProgressData))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const auto &oType = GetDataType();
|
|
if (oType.GetClass() != GEDTC_NUMERIC ||
|
|
GDALDataTypeIsComplex(oType.GetNumericDataType()))
|
|
{
|
|
CPLError(
|
|
CE_Failure, CPLE_NotSupported,
|
|
"Statistics can only be computed on non-complex numeric data type");
|
|
return false;
|
|
}
|
|
|
|
const size_t nDims = GetDimensionCount();
|
|
std::vector<GUInt64> arrayStartIdx(nDims);
|
|
std::vector<GUInt64> count(nDims);
|
|
const auto &poDims = GetDimensions();
|
|
for (size_t i = 0; i < nDims; i++)
|
|
{
|
|
count[i] = poDims[i]->GetSize();
|
|
}
|
|
const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
|
|
const size_t nMaxChunkSize =
|
|
pszSwathSize
|
|
? static_cast<size_t>(
|
|
std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
|
|
CPLAtoGIntBig(pszSwathSize)))
|
|
: static_cast<size_t>(
|
|
std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
|
|
GDALGetCacheMax64() / 4));
|
|
StatsPerChunkType sData;
|
|
sData.array = this;
|
|
sData.poMask = GetMask(nullptr);
|
|
if (sData.poMask == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
sData.pfnProgress = pfnProgress;
|
|
sData.pProgressData = pProgressData;
|
|
if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
|
|
GetProcessingChunkSize(nMaxChunkSize).data(),
|
|
PerChunkFunc, &sData))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (pdfMin)
|
|
*pdfMin = sData.dfMin;
|
|
|
|
if (pdfMax)
|
|
*pdfMax = sData.dfMax;
|
|
|
|
if (pdfMean)
|
|
*pdfMean = sData.dfMean;
|
|
|
|
const double dfStdDev =
|
|
sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
|
|
if (pdfStdDev)
|
|
*pdfStdDev = dfStdDev;
|
|
|
|
if (pnValidCount)
|
|
*pnValidCount = sData.nValidCount;
|
|
|
|
SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
|
|
sData.nValidCount);
|
|
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetStatistics() */
|
|
/************************************************************************/
|
|
//! @cond Doxygen_Suppress
|
|
bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
|
|
double /* dfMax */, double /* dfMean */,
|
|
double /* dfStdDev */,
|
|
GUInt64 /* nValidCount */)
|
|
{
|
|
CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
|
|
return false;
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* ClearStatistics() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Clear statistics.
|
|
*
|
|
* @since GDAL 3.4
|
|
*/
|
|
void GDALMDArray::ClearStatistics()
|
|
{
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetCoordinateVariables() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Return coordinate variables.
|
|
*
|
|
* Coordinate variables are an alternate way of indexing an array that can
|
|
* be sometimes used. For example, an array collected through remote sensing
|
|
* might be indexed by (scanline, pixel). But there can be
|
|
* a longitude and latitude arrays alongside that are also both indexed by
|
|
* (scanline, pixel), and are referenced from operational arrays for
|
|
* reprojection purposes.
|
|
*
|
|
* For netCDF, this will return the arrays referenced by the "coordinates"
|
|
* attribute.
|
|
*
|
|
* This method is the same as the C function
|
|
* GDALMDArrayGetCoordinateVariables().
|
|
*
|
|
* @return a vector of arrays
|
|
*
|
|
* @since GDAL 3.4
|
|
*/
|
|
|
|
std::vector<std::shared_ptr<GDALMDArray>>
|
|
GDALMDArray::GetCoordinateVariables() const
|
|
{
|
|
return {};
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GDALExtendedDataType() */
|
|
/************************************************************************/
|
|
|
|
GDALExtendedDataType::~GDALExtendedDataType() = default;
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataType() */
|
|
/************************************************************************/
|
|
|
|
GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
|
|
GDALExtendedDataTypeSubType eSubType)
|
|
: m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
|
|
m_nMaxStringLength(nMaxStringLength)
|
|
{
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataType() */
|
|
/************************************************************************/
|
|
|
|
GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
|
|
: m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
|
|
m_nSize(GDALGetDataTypeSizeBytes(eType))
|
|
{
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataType() */
|
|
/************************************************************************/
|
|
|
|
GDALExtendedDataType::GDALExtendedDataType(
|
|
const std::string &osName, size_t nTotalSize,
|
|
std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
|
|
: m_osName(osName), m_eClass(GEDTC_COMPOUND),
|
|
m_aoComponents(std::move(components)), m_nSize(nTotalSize)
|
|
{
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataType() */
|
|
/************************************************************************/
|
|
|
|
/** Copy constructor. */
|
|
GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
|
|
: m_osName(other.m_osName), m_eClass(other.m_eClass),
|
|
m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
|
|
m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength)
|
|
{
|
|
if (m_eClass == GEDTC_COMPOUND)
|
|
{
|
|
for (const auto &elt : other.m_aoComponents)
|
|
{
|
|
m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* operator= () */
|
|
/************************************************************************/
|
|
|
|
/** Move assignment. */
|
|
GDALExtendedDataType &
|
|
GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
|
|
{
|
|
m_osName = std::move(other.m_osName);
|
|
m_eClass = other.m_eClass;
|
|
m_eSubType = other.m_eSubType;
|
|
m_eNumericDT = other.m_eNumericDT;
|
|
m_nSize = other.m_nSize;
|
|
m_nMaxStringLength = other.m_nMaxStringLength;
|
|
m_aoComponents = std::move(other.m_aoComponents);
|
|
other.m_eClass = GEDTC_NUMERIC;
|
|
other.m_eNumericDT = GDT_Unknown;
|
|
other.m_nSize = 0;
|
|
other.m_nMaxStringLength = 0;
|
|
return *this;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Create() */
|
|
/************************************************************************/
|
|
|
|
/** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
|
|
*
|
|
* This is the same as the C function GDALExtendedDataTypeCreate()
|
|
*
|
|
* @param eType Numeric data type.
|
|
*/
|
|
GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
|
|
{
|
|
return GDALExtendedDataType(eType);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Create() */
|
|
/************************************************************************/
|
|
|
|
/** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
|
|
*
|
|
* This is the same as the C function GDALExtendedDataTypeCreateCompound()
|
|
*
|
|
* @param osName Type name.
|
|
* @param nTotalSize Total size of the type in bytes.
|
|
* Should be large enough to store all components.
|
|
* @param components Components of the compound type.
|
|
*/
|
|
GDALExtendedDataType GDALExtendedDataType::Create(
|
|
const std::string &osName, size_t nTotalSize,
|
|
std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
|
|
{
|
|
size_t nLastOffset = 0;
|
|
// Some arbitrary threshold to avoid potential integer overflows
|
|
if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
|
|
return GDALExtendedDataType(GDT_Unknown);
|
|
}
|
|
for (const auto &comp : components)
|
|
{
|
|
// Check alignment too ?
|
|
if (comp->GetOffset() < nLastOffset)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
|
|
return GDALExtendedDataType(GDT_Unknown);
|
|
}
|
|
nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
|
|
}
|
|
if (nTotalSize < nLastOffset)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
|
|
return GDALExtendedDataType(GDT_Unknown);
|
|
}
|
|
if (nTotalSize == 0 || components.empty())
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
|
|
return GDALExtendedDataType(GDT_Unknown);
|
|
}
|
|
return GDALExtendedDataType(osName, nTotalSize, std::move(components));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Create() */
|
|
/************************************************************************/
|
|
|
|
/** Return a new GDALExtendedDataType of class GEDTC_STRING.
|
|
*
|
|
* This is the same as the C function GDALExtendedDataTypeCreateString().
|
|
*
|
|
* @param nMaxStringLength maximum length of a string in bytes. 0 if
|
|
* unknown/unlimited
|
|
* @param eSubType Subtype.
|
|
*/
|
|
GDALExtendedDataType
|
|
GDALExtendedDataType::CreateString(size_t nMaxStringLength,
|
|
GDALExtendedDataTypeSubType eSubType)
|
|
{
|
|
return GDALExtendedDataType(nMaxStringLength, eSubType);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* operator==() */
|
|
/************************************************************************/
|
|
|
|
/** Equality operator.
|
|
*
|
|
* This is the same as the C function GDALExtendedDataTypeEquals().
|
|
*/
|
|
bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
|
|
{
|
|
if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
|
|
m_nSize != other.m_nSize || m_osName != other.m_osName)
|
|
{
|
|
return false;
|
|
}
|
|
if (m_eClass == GEDTC_NUMERIC)
|
|
{
|
|
return m_eNumericDT == other.m_eNumericDT;
|
|
}
|
|
if (m_eClass == GEDTC_STRING)
|
|
{
|
|
return true;
|
|
}
|
|
CPLAssert(m_eClass == GEDTC_COMPOUND);
|
|
if (m_aoComponents.size() != other.m_aoComponents.size())
|
|
{
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < m_aoComponents.size(); i++)
|
|
{
|
|
if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CanConvertTo() */
|
|
/************************************************************************/
|
|
|
|
/** Return whether this data type can be converted to the other one.
|
|
*
|
|
* This is the same as the C function GDALExtendedDataTypeCanConvertTo().
|
|
*
|
|
* @param other Target data type for the conversion being considered.
|
|
*/
|
|
bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
|
|
{
|
|
if (m_eClass == GEDTC_NUMERIC)
|
|
{
|
|
if (m_eNumericDT == GDT_Unknown)
|
|
return false;
|
|
if (other.m_eClass == GEDTC_NUMERIC &&
|
|
other.m_eNumericDT == GDT_Unknown)
|
|
return false;
|
|
return other.m_eClass == GEDTC_NUMERIC ||
|
|
other.m_eClass == GEDTC_STRING;
|
|
}
|
|
if (m_eClass == GEDTC_STRING)
|
|
{
|
|
return other.m_eClass == m_eClass;
|
|
}
|
|
CPLAssert(m_eClass == GEDTC_COMPOUND);
|
|
if (other.m_eClass != GEDTC_COMPOUND)
|
|
return false;
|
|
std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
|
|
srcComponents;
|
|
for (const auto &srcComp : m_aoComponents)
|
|
{
|
|
srcComponents[srcComp->GetName()] = &srcComp;
|
|
}
|
|
for (const auto &dstComp : other.m_aoComponents)
|
|
{
|
|
auto oIter = srcComponents.find(dstComp->GetName());
|
|
if (oIter == srcComponents.end())
|
|
return false;
|
|
if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* NeedsFreeDynamicMemory() */
|
|
/************************************************************************/
|
|
|
|
/** Return whether the data type holds dynamically allocated memory, that
|
|
* needs to be freed with FreeDynamicMemory().
|
|
*
|
|
*/
|
|
bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
|
|
{
|
|
switch (m_eClass)
|
|
{
|
|
case GEDTC_STRING:
|
|
return true;
|
|
|
|
case GEDTC_NUMERIC:
|
|
return false;
|
|
|
|
case GEDTC_COMPOUND:
|
|
{
|
|
for (const auto &comp : m_aoComponents)
|
|
{
|
|
if (comp->GetType().NeedsFreeDynamicMemory())
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* FreeDynamicMemory() */
|
|
/************************************************************************/
|
|
|
|
/** Release the dynamic memory (strings typically) from a raw value.
|
|
*
|
|
* This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
|
|
*
|
|
* @param pBuffer Raw buffer of a single element of an attribute or array value.
|
|
*/
|
|
void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
|
|
{
|
|
switch (m_eClass)
|
|
{
|
|
case GEDTC_STRING:
|
|
{
|
|
char *pszStr;
|
|
memcpy(&pszStr, pBuffer, sizeof(char *));
|
|
if (pszStr)
|
|
{
|
|
VSIFree(pszStr);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GEDTC_NUMERIC:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case GEDTC_COMPOUND:
|
|
{
|
|
GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
|
|
for (const auto &comp : m_aoComponents)
|
|
{
|
|
comp->GetType().FreeDynamicMemory(pabyBuffer +
|
|
comp->GetOffset());
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GDALEDTComponent() */
|
|
/************************************************************************/
|
|
|
|
GDALEDTComponent::~GDALEDTComponent() = default;
|
|
|
|
/************************************************************************/
|
|
/* GDALEDTComponent() */
|
|
/************************************************************************/
|
|
|
|
/** constructor of a GDALEDTComponent
|
|
*
|
|
* This is the same as the C function GDALEDTComponendCreate()
|
|
*
|
|
* @param name Component name
|
|
* @param offset Offset in byte of the component in the compound data type.
|
|
* In case of nesting of compound data type, this should be
|
|
* the offset to the immediate belonging data type, not to the
|
|
* higher level one.
|
|
* @param type Component data type.
|
|
*/
|
|
GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
|
|
const GDALExtendedDataType &type)
|
|
: m_osName(name), m_nOffset(offset), m_oType(type)
|
|
{
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALEDTComponent() */
|
|
/************************************************************************/
|
|
|
|
/** Copy constructor. */
|
|
GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
|
|
|
|
/************************************************************************/
|
|
/* operator==() */
|
|
/************************************************************************/
|
|
|
|
/** Equality operator.
|
|
*/
|
|
bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
|
|
{
|
|
return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
|
|
m_oType == other.m_oType;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GDALDimension() */
|
|
/************************************************************************/
|
|
|
|
GDALDimension::~GDALDimension() = default;
|
|
|
|
/************************************************************************/
|
|
/* GDALDimension() */
|
|
/************************************************************************/
|
|
|
|
//! @cond Doxygen_Suppress
|
|
/** Constructor.
|
|
*
|
|
* @param osParentName Parent name
|
|
* @param osName name
|
|
* @param osType type. See GetType().
|
|
* @param osDirection direction. See GetDirection().
|
|
* @param nSize size.
|
|
*/
|
|
GDALDimension::GDALDimension(const std::string &osParentName,
|
|
const std::string &osName,
|
|
const std::string &osType,
|
|
const std::string &osDirection, GUInt64 nSize)
|
|
: m_osName(osName),
|
|
m_osFullName(
|
|
!osParentName.empty()
|
|
? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
|
|
: osName),
|
|
m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
|
|
{
|
|
}
|
|
//! @endcond
|
|
|
|
/************************************************************************/
|
|
/* GetIndexingVariable() */
|
|
/************************************************************************/
|
|
|
|
/** Return the variable that is used to index the dimension (if there is one).
|
|
*
|
|
* This is the array, typically one-dimensional, describing the values taken
|
|
* by the dimension.
|
|
*/
|
|
std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetIndexingVariable() */
|
|
/************************************************************************/
|
|
|
|
/** Set the variable that is used to index the dimension.
|
|
*
|
|
* This is the array, typically one-dimensional, describing the values taken
|
|
* by the dimension.
|
|
*
|
|
* Optionally implemented by drivers.
|
|
*
|
|
* Drivers known to implement it: MEM.
|
|
*
|
|
* @param poArray Variable to use to index the dimension.
|
|
* @return true in case of success.
|
|
*/
|
|
bool GDALDimension::SetIndexingVariable(
|
|
CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
|
|
{
|
|
CPLError(CE_Failure, CPLE_NotSupported,
|
|
"SetIndexingVariable() not implemented");
|
|
return false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/************************************************************************/
|
|
/************************************************************************/
|
|
/* C API */
|
|
/************************************************************************/
|
|
/************************************************************************/
|
|
/************************************************************************/
|
|
|
|
struct GDALExtendedDataTypeHS
|
|
{
|
|
std::unique_ptr<GDALExtendedDataType> m_poImpl;
|
|
|
|
explicit GDALExtendedDataTypeHS(GDALExtendedDataType *dt) : m_poImpl(dt)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct GDALEDTComponentHS
|
|
{
|
|
std::unique_ptr<GDALEDTComponent> m_poImpl;
|
|
|
|
explicit GDALEDTComponentHS(const GDALEDTComponent &component)
|
|
: m_poImpl(new GDALEDTComponent(component))
|
|
{
|
|
}
|
|
};
|
|
|
|
struct GDALGroupHS
|
|
{
|
|
std::shared_ptr<GDALGroup> m_poImpl;
|
|
|
|
explicit GDALGroupHS(const std::shared_ptr<GDALGroup> &poGroup)
|
|
: m_poImpl(poGroup)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct GDALMDArrayHS
|
|
{
|
|
std::shared_ptr<GDALMDArray> m_poImpl;
|
|
|
|
explicit GDALMDArrayHS(const std::shared_ptr<GDALMDArray> &poArray)
|
|
: m_poImpl(poArray)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct GDALAttributeHS
|
|
{
|
|
std::shared_ptr<GDALAttribute> m_poImpl;
|
|
|
|
explicit GDALAttributeHS(const std::shared_ptr<GDALAttribute> &poAttr)
|
|
: m_poImpl(poAttr)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct GDALDimensionHS
|
|
{
|
|
std::shared_ptr<GDALDimension> m_poImpl;
|
|
|
|
explicit GDALDimensionHS(const std::shared_ptr<GDALDimension> &poDim)
|
|
: m_poImpl(poDim)
|
|
{
|
|
}
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeCreate() */
|
|
/************************************************************************/
|
|
|
|
/** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::Create()
|
|
*
|
|
* The returned handle should be freed with GDALExtendedDataTypeRelease().
|
|
*
|
|
* @param eType Numeric data type.
|
|
*
|
|
* @return a new GDALExtendedDataTypeH handle, or nullptr.
|
|
*/
|
|
GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
|
|
{
|
|
return new GDALExtendedDataTypeHS(
|
|
new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeCreateString() */
|
|
/************************************************************************/
|
|
|
|
/** Return a new GDALExtendedDataType of class GEDTC_STRING.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::CreateString()
|
|
*
|
|
* The returned handle should be freed with GDALExtendedDataTypeRelease().
|
|
*
|
|
* @return a new GDALExtendedDataTypeH handle, or nullptr.
|
|
*/
|
|
GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
|
|
{
|
|
return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
|
|
GDALExtendedDataType::CreateString(nMaxStringLength)));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeCreateStringEx() */
|
|
/************************************************************************/
|
|
|
|
/** Return a new GDALExtendedDataType of class GEDTC_STRING.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::CreateString()
|
|
*
|
|
* The returned handle should be freed with GDALExtendedDataTypeRelease().
|
|
*
|
|
* @return a new GDALExtendedDataTypeH handle, or nullptr.
|
|
* @since GDAL 3.4
|
|
*/
|
|
GDALExtendedDataTypeH
|
|
GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
|
|
GDALExtendedDataTypeSubType eSubType)
|
|
{
|
|
return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
|
|
GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeCreateCompound() */
|
|
/************************************************************************/
|
|
|
|
/** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::Create(const
|
|
* std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
|
|
*
|
|
* The returned handle should be freed with GDALExtendedDataTypeRelease().
|
|
*
|
|
* @param pszName Type name.
|
|
* @param nTotalSize Total size of the type in bytes.
|
|
* Should be large enough to store all components.
|
|
* @param nComponents Number of components in comps array.
|
|
* @param comps Components.
|
|
* @return a new GDALExtendedDataTypeH handle, or nullptr.
|
|
*/
|
|
GDALExtendedDataTypeH
|
|
GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
|
|
size_t nComponents,
|
|
const GDALEDTComponentH *comps)
|
|
{
|
|
std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
|
|
for (size_t i = 0; i < nComponents; i++)
|
|
{
|
|
compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
|
|
new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
|
|
}
|
|
auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
|
|
std::move(compsCpp));
|
|
if (dt.GetClass() != GEDTC_COMPOUND)
|
|
return nullptr;
|
|
return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeRelease() */
|
|
/************************************************************************/
|
|
|
|
/** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
|
|
*
|
|
* Note: when applied on a object coming from a driver, this does not
|
|
* destroy the object in the file, database, etc...
|
|
*/
|
|
void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
|
|
{
|
|
delete hEDT;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeGetName() */
|
|
/************************************************************************/
|
|
|
|
/** Return type name.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::GetName()
|
|
*/
|
|
const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
|
|
{
|
|
VALIDATE_POINTER1(hEDT, __func__, "");
|
|
return hEDT->m_poImpl->GetName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeGetClass() */
|
|
/************************************************************************/
|
|
|
|
/** Return type class.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::GetClass()
|
|
*/
|
|
GDALExtendedDataTypeClass
|
|
GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
|
|
{
|
|
VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
|
|
return hEDT->m_poImpl->GetClass();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeGetNumericDataType() */
|
|
/************************************************************************/
|
|
|
|
/** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
|
|
*/
|
|
GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
|
|
{
|
|
VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
|
|
return hEDT->m_poImpl->GetNumericDataType();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeGetSize() */
|
|
/************************************************************************/
|
|
|
|
/** Return data type size in bytes.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::GetSize()
|
|
*/
|
|
size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
|
|
{
|
|
VALIDATE_POINTER1(hEDT, __func__, 0);
|
|
return hEDT->m_poImpl->GetSize();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeGetMaxStringLength() */
|
|
/************************************************************************/
|
|
|
|
/** Return the maximum length of a string in bytes.
|
|
*
|
|
* 0 indicates unknown/unlimited string.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
|
|
*/
|
|
size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
|
|
{
|
|
VALIDATE_POINTER1(hEDT, __func__, 0);
|
|
return hEDT->m_poImpl->GetMaxStringLength();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeCanConvertTo() */
|
|
/************************************************************************/
|
|
|
|
/** Return whether this data type can be converted to the other one.
|
|
*
|
|
* This is the same as the C function GDALExtendedDataType::CanConvertTo()
|
|
*
|
|
* @param hSourceEDT Source data type for the conversion being considered.
|
|
* @param hTargetEDT Target data type for the conversion being considered.
|
|
* @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
|
|
*/
|
|
int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
|
|
GDALExtendedDataTypeH hTargetEDT)
|
|
{
|
|
VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
|
|
VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
|
|
return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeEquals() */
|
|
/************************************************************************/
|
|
|
|
/** Return whether this data type is equal to another one.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::operator==()
|
|
*
|
|
* @param hFirstEDT First data type.
|
|
* @param hSecondEDT Second data type.
|
|
* @return TRUE if they are equal. FALSE otherwise.
|
|
*/
|
|
int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
|
|
GDALExtendedDataTypeH hSecondEDT)
|
|
{
|
|
VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
|
|
VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
|
|
return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeGetSubType() */
|
|
/************************************************************************/
|
|
|
|
/** Return the subtype of a type.
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::GetSubType()
|
|
*
|
|
* @param hEDT Data type.
|
|
* @return subtype.
|
|
* @since 3.4
|
|
*/
|
|
GDALExtendedDataTypeSubType
|
|
GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
|
|
{
|
|
VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
|
|
return hEDT->m_poImpl->GetSubType();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeGetComponents() */
|
|
/************************************************************************/
|
|
|
|
/** Return the components of the data type (only valid when GetClass() ==
|
|
* GEDTC_COMPOUND)
|
|
*
|
|
* The returned array and its content must be freed with
|
|
* GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
|
|
* freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
|
|
* individual array members).
|
|
*
|
|
* This is the same as the C++ method GDALExtendedDataType::GetComponents()
|
|
*
|
|
* @param hEDT Data type
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
* @return an array of *pnCount components.
|
|
*/
|
|
GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
|
|
size_t *pnCount)
|
|
{
|
|
VALIDATE_POINTER1(hEDT, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
const auto &components = hEDT->m_poImpl->GetComponents();
|
|
auto ret = static_cast<GDALEDTComponentH *>(
|
|
CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
|
|
for (size_t i = 0; i < components.size(); i++)
|
|
{
|
|
ret[i] = new GDALEDTComponentHS(*components[i].get());
|
|
}
|
|
*pnCount = components.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtendedDataTypeFreeComponents() */
|
|
/************************************************************************/
|
|
|
|
/** Free the return of GDALExtendedDataTypeGetComponents().
|
|
*
|
|
* @param components return value of GDALExtendedDataTypeGetComponents()
|
|
* @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
|
|
*/
|
|
void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
|
|
size_t nCount)
|
|
{
|
|
for (size_t i = 0; i < nCount; i++)
|
|
{
|
|
delete components[i];
|
|
}
|
|
CPLFree(components);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALEDTComponentCreate() */
|
|
/************************************************************************/
|
|
|
|
/** Create a new GDALEDTComponent.
|
|
*
|
|
* The returned value must be freed with GDALEDTComponentRelease().
|
|
*
|
|
* This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
|
|
*/
|
|
GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
|
|
GDALExtendedDataTypeH hType)
|
|
{
|
|
VALIDATE_POINTER1(pszName, __func__, nullptr);
|
|
VALIDATE_POINTER1(hType, __func__, nullptr);
|
|
return new GDALEDTComponentHS(
|
|
GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALEDTComponentRelease() */
|
|
/************************************************************************/
|
|
|
|
/** Release the GDAL in-memory object associated with a GDALEDTComponentH.
|
|
*
|
|
* Note: when applied on a object coming from a driver, this does not
|
|
* destroy the object in the file, database, etc...
|
|
*/
|
|
void GDALEDTComponentRelease(GDALEDTComponentH hComp)
|
|
{
|
|
delete hComp;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALEDTComponentGetName() */
|
|
/************************************************************************/
|
|
|
|
/** Return the name.
|
|
*
|
|
* The returned pointer is valid until hComp is released.
|
|
*
|
|
* This is the same as the C++ method GDALEDTComponent::GetName().
|
|
*/
|
|
const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
|
|
{
|
|
VALIDATE_POINTER1(hComp, __func__, nullptr);
|
|
return hComp->m_poImpl->GetName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALEDTComponentGetOffset() */
|
|
/************************************************************************/
|
|
|
|
/** Return the offset (in bytes) of the component in the compound data type.
|
|
*
|
|
* This is the same as the C++ method GDALEDTComponent::GetOffset().
|
|
*/
|
|
size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
|
|
{
|
|
VALIDATE_POINTER1(hComp, __func__, 0);
|
|
return hComp->m_poImpl->GetOffset();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALEDTComponentGetType() */
|
|
/************************************************************************/
|
|
|
|
/** Return the data type of the component.
|
|
*
|
|
* This is the same as the C++ method GDALEDTComponent::GetType().
|
|
*/
|
|
GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
|
|
{
|
|
VALIDATE_POINTER1(hComp, __func__, nullptr);
|
|
return new GDALExtendedDataTypeHS(
|
|
new GDALExtendedDataType(hComp->m_poImpl->GetType()));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupRelease() */
|
|
/************************************************************************/
|
|
|
|
/** Release the GDAL in-memory object associated with a GDALGroupH.
|
|
*
|
|
* Note: when applied on a object coming from a driver, this does not
|
|
* destroy the object in the file, database, etc...
|
|
*/
|
|
void GDALGroupRelease(GDALGroupH hGroup)
|
|
{
|
|
delete hGroup;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupGetName() */
|
|
/************************************************************************/
|
|
|
|
/** Return the name of the group.
|
|
*
|
|
* The returned pointer is valid until hGroup is released.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::GetName().
|
|
*/
|
|
const char *GDALGroupGetName(GDALGroupH hGroup)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
return hGroup->m_poImpl->GetName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupGetFullName() */
|
|
/************************************************************************/
|
|
|
|
/** Return the full name of the group.
|
|
*
|
|
* The returned pointer is valid until hGroup is released.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::GetFullName().
|
|
*/
|
|
const char *GDALGroupGetFullName(GDALGroupH hGroup)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
return hGroup->m_poImpl->GetFullName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupGetMDArrayNames() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of multidimensional array names contained in this group.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::GetGroupNames().
|
|
*
|
|
* @return the array names, to be freed with CSLDestroy()
|
|
*/
|
|
char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
|
|
CPLStringList res;
|
|
for (const auto &name : names)
|
|
{
|
|
res.AddString(name.c_str());
|
|
}
|
|
return res.StealList();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupOpenMDArray() */
|
|
/************************************************************************/
|
|
|
|
/** Open and return a multidimensional array.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::OpenMDArray().
|
|
*
|
|
* @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
|
|
*/
|
|
GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
|
|
auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
|
|
papszOptions);
|
|
if (!array)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(array);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupOpenMDArrayFromFullname() */
|
|
/************************************************************************/
|
|
|
|
/** Open and return a multidimensional array from its fully qualified name.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
|
|
*
|
|
* @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
|
|
*
|
|
* @since GDAL 3.2
|
|
*/
|
|
GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
|
|
const char *pszFullname,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszFullname, __func__, nullptr);
|
|
auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
|
|
std::string(pszFullname), papszOptions);
|
|
if (!array)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(array);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupResolveMDArray() */
|
|
/************************************************************************/
|
|
|
|
/** Locate an array in a group and its subgroups by name.
|
|
*
|
|
* See GDALGroup::ResolveMDArray() for description of the behavior.
|
|
* @since GDAL 3.2
|
|
*/
|
|
GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
|
|
const char *pszStartingPoint,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszName, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
|
|
auto array = hGroup->m_poImpl->ResolveMDArray(
|
|
std::string(pszName), std::string(pszStartingPoint), papszOptions);
|
|
if (!array)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(array);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupGetGroupNames() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of sub-groups contained in this group.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::GetGroupNames().
|
|
*
|
|
* @return the group names, to be freed with CSLDestroy()
|
|
*/
|
|
char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
|
|
CPLStringList res;
|
|
for (const auto &name : names)
|
|
{
|
|
res.AddString(name.c_str());
|
|
}
|
|
return res.StealList();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupOpenGroup() */
|
|
/************************************************************************/
|
|
|
|
/** Open and return a sub-group.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::OpenGroup().
|
|
*
|
|
* @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
|
|
*/
|
|
GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
|
|
auto subGroup =
|
|
hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
|
|
if (!subGroup)
|
|
return nullptr;
|
|
return new GDALGroupHS(subGroup);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupGetVectorLayerNames() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of layer names contained in this group.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::GetVectorLayerNames().
|
|
*
|
|
* @return the group names, to be freed with CSLDestroy()
|
|
* @since 3.4
|
|
*/
|
|
char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
|
|
CPLStringList res;
|
|
for (const auto &name : names)
|
|
{
|
|
res.AddString(name.c_str());
|
|
}
|
|
return res.StealList();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupOpenVectorLayer() */
|
|
/************************************************************************/
|
|
|
|
/** Open and return a vector layer.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::OpenVectorLayer().
|
|
*
|
|
* Note that the vector layer is owned by its parent GDALDatasetH, and thus
|
|
* the returned handled if only valid while the parent GDALDatasetH is kept
|
|
* opened.
|
|
*
|
|
* @return the vector layer, or nullptr.
|
|
* @since 3.4
|
|
*/
|
|
OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
|
|
const char *pszVectorLayerName,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
|
|
return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
|
|
std::string(pszVectorLayerName), papszOptions));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupOpenMDArrayFromFullname() */
|
|
/************************************************************************/
|
|
|
|
/** Open and return a sub-group from its fully qualified name.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
|
|
*
|
|
* @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
|
|
*
|
|
* @since GDAL 3.2
|
|
*/
|
|
GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
|
|
const char *pszFullname,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszFullname, __func__, nullptr);
|
|
auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
|
|
std::string(pszFullname), papszOptions);
|
|
if (!subGroup)
|
|
return nullptr;
|
|
return new GDALGroupHS(subGroup);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupGetDimensions() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of dimensions contained in this group and used by its
|
|
* arrays.
|
|
*
|
|
* The returned array must be freed with GDALReleaseDimensions(). If only the
|
|
* array itself needs to be freed, CPLFree() should be called (and
|
|
* GDALDimensionRelease() on individual array members).
|
|
*
|
|
* This is the same as the C++ method GDALGroup::GetDimensions().
|
|
*
|
|
* @param hGroup Group.
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
* @param papszOptions Driver specific options determining how dimensions
|
|
* should be retrieved. Pass nullptr for default behavior.
|
|
*
|
|
* @return an array of *pnCount dimensions.
|
|
*/
|
|
GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
|
|
auto ret = static_cast<GDALDimensionH *>(
|
|
CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
ret[i] = new GDALDimensionHS(dims[i]);
|
|
}
|
|
*pnCount = dims.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupGetAttribute() */
|
|
/************************************************************************/
|
|
|
|
/** Return an attribute by its name.
|
|
*
|
|
* This is the same as the C++ method GDALIHasAttribute::GetAttribute()
|
|
*
|
|
* The returned attribute must be freed with GDALAttributeRelease().
|
|
*/
|
|
GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszName, __func__, nullptr);
|
|
auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
|
|
if (attr)
|
|
return new GDALAttributeHS(attr);
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupGetAttributes() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of attributes contained in this group.
|
|
*
|
|
* The returned array must be freed with GDALReleaseAttributes(). If only the
|
|
* array itself needs to be freed, CPLFree() should be called (and
|
|
* GDALAttributeRelease() on individual array members).
|
|
*
|
|
* This is the same as the C++ method GDALGroup::GetAttributes().
|
|
*
|
|
* @param hGroup Group.
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
* @param papszOptions Driver specific options determining how attributes
|
|
* should be retrieved. Pass nullptr for default behavior.
|
|
*
|
|
* @return an array of *pnCount attributes.
|
|
*/
|
|
GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
|
|
auto ret = static_cast<GDALAttributeH *>(
|
|
CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
|
|
for (size_t i = 0; i < attrs.size(); i++)
|
|
{
|
|
ret[i] = new GDALAttributeHS(attrs[i]);
|
|
}
|
|
*pnCount = attrs.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupGetStructuralInfo() */
|
|
/************************************************************************/
|
|
|
|
/** Return structural information on the group.
|
|
*
|
|
* This may be the compression, etc..
|
|
*
|
|
* The return value should not be freed and is valid until GDALGroup is
|
|
* released or this function called again.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::GetStructuralInfo().
|
|
*/
|
|
CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
return hGroup->m_poImpl->GetStructuralInfo();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALReleaseAttributes() */
|
|
/************************************************************************/
|
|
|
|
/** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
|
|
*
|
|
* @param attributes return pointer of above methods
|
|
* @param nCount *pnCount value returned by above methods
|
|
*/
|
|
void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
|
|
{
|
|
for (size_t i = 0; i < nCount; i++)
|
|
{
|
|
delete attributes[i];
|
|
}
|
|
CPLFree(attributes);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupCreateGroup() */
|
|
/************************************************************************/
|
|
|
|
/** Create a sub-group within a group.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::CreateGroup().
|
|
*
|
|
* @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
|
|
*/
|
|
GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
|
|
auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
|
|
papszOptions);
|
|
if (!ret)
|
|
return nullptr;
|
|
return new GDALGroupHS(ret);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupCreateDimension() */
|
|
/************************************************************************/
|
|
|
|
/** Create a dimension within a group.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::CreateDimension().
|
|
*
|
|
* @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
|
|
*/
|
|
GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
|
|
const char *pszType,
|
|
const char *pszDirection, GUInt64 nSize,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszName, __func__, nullptr);
|
|
auto ret = hGroup->m_poImpl->CreateDimension(
|
|
std::string(pszName), std::string(pszType ? pszType : ""),
|
|
std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
|
|
if (!ret)
|
|
return nullptr;
|
|
return new GDALDimensionHS(ret);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupCreateMDArray() */
|
|
/************************************************************************/
|
|
|
|
/** Create a multidimensional array within a group.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::CreateMDArray().
|
|
*
|
|
* @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
|
|
*/
|
|
GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
|
|
size_t nDimensions,
|
|
GDALDimensionH *pahDimensions,
|
|
GDALExtendedDataTypeH hEDT,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszName, __func__, nullptr);
|
|
VALIDATE_POINTER1(hEDT, __func__, nullptr);
|
|
std::vector<std::shared_ptr<GDALDimension>> dims;
|
|
dims.reserve(nDimensions);
|
|
for (size_t i = 0; i < nDimensions; i++)
|
|
dims.push_back(pahDimensions[i]->m_poImpl);
|
|
auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
|
|
*(hEDT->m_poImpl), papszOptions);
|
|
if (!ret)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(ret);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGroupCreateAttribute() */
|
|
/************************************************************************/
|
|
|
|
/** Create a attribute within a group.
|
|
*
|
|
* This is the same as the C++ method GDALGroup::CreateAttribute().
|
|
*
|
|
* @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
|
|
*/
|
|
GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
|
|
size_t nDimensions,
|
|
const GUInt64 *panDimensions,
|
|
GDALExtendedDataTypeH hEDT,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hGroup, __func__, nullptr);
|
|
VALIDATE_POINTER1(hEDT, __func__, nullptr);
|
|
std::vector<GUInt64> dims;
|
|
dims.reserve(nDimensions);
|
|
for (size_t i = 0; i < nDimensions; i++)
|
|
dims.push_back(panDimensions[i]);
|
|
auto ret = hGroup->m_poImpl->CreateAttribute(
|
|
std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
|
|
if (!ret)
|
|
return nullptr;
|
|
return new GDALAttributeHS(ret);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayRelease() */
|
|
/************************************************************************/
|
|
|
|
/** Release the GDAL in-memory object associated with a GDALMDArray.
|
|
*
|
|
* Note: when applied on a object coming from a driver, this does not
|
|
* destroy the object in the file, database, etc...
|
|
*/
|
|
void GDALMDArrayRelease(GDALMDArrayH hMDArray)
|
|
{
|
|
delete hMDArray;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetName() */
|
|
/************************************************************************/
|
|
|
|
/** Return array name.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetName()
|
|
*/
|
|
const char *GDALMDArrayGetName(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
return hArray->m_poImpl->GetName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetFullName() */
|
|
/************************************************************************/
|
|
|
|
/** Return array full name.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetFullName()
|
|
*/
|
|
const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
return hArray->m_poImpl->GetFullName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetName() */
|
|
/************************************************************************/
|
|
|
|
/** Return the total number of values in the array.
|
|
*
|
|
* This is the same as the C++ method
|
|
* GDALAbstractMDArray::GetTotalElementsCount()
|
|
*/
|
|
GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, 0);
|
|
return hArray->m_poImpl->GetTotalElementsCount();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetDimensionCount() */
|
|
/************************************************************************/
|
|
|
|
/** Return the number of dimensions.
|
|
*
|
|
* This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
|
|
*/
|
|
size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, 0);
|
|
return hArray->m_poImpl->GetDimensionCount();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetDimensions() */
|
|
/************************************************************************/
|
|
|
|
/** Return the dimensions of the array
|
|
*
|
|
* The returned array must be freed with GDALReleaseDimensions(). If only the
|
|
* array itself needs to be freed, CPLFree() should be called (and
|
|
* GDALDimensionRelease() on individual array members).
|
|
*
|
|
* This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
|
|
*
|
|
* @param hArray Array.
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
*
|
|
* @return an array of *pnCount dimensions.
|
|
*/
|
|
GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
const auto &dims(hArray->m_poImpl->GetDimensions());
|
|
auto ret = static_cast<GDALDimensionH *>(
|
|
CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
ret[i] = new GDALDimensionHS(dims[i]);
|
|
}
|
|
*pnCount = dims.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALReleaseDimensions() */
|
|
/************************************************************************/
|
|
|
|
/** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
|
|
*
|
|
* @param dims return pointer of above methods
|
|
* @param nCount *pnCount value returned by above methods
|
|
*/
|
|
void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
|
|
{
|
|
for (size_t i = 0; i < nCount; i++)
|
|
{
|
|
delete dims[i];
|
|
}
|
|
CPLFree(dims);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetDataType() */
|
|
/************************************************************************/
|
|
|
|
/** Return the data type
|
|
*
|
|
* The return must be freed with GDALExtendedDataTypeRelease().
|
|
*/
|
|
GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
return new GDALExtendedDataTypeHS(
|
|
new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayRead() */
|
|
/************************************************************************/
|
|
|
|
/** Read part or totality of a multidimensional array.
|
|
*
|
|
* This is the same as the C++ method GDALAbstractMDArray::Read()
|
|
*
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
|
|
const void *pDstBufferAllocStart,
|
|
size_t nDstBufferAllocSize)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
if ((arrayStartIdx == nullptr || count == nullptr) &&
|
|
hArray->m_poImpl->GetDimensionCount() > 0)
|
|
{
|
|
VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
|
|
VALIDATE_POINTER1(count, __func__, FALSE);
|
|
}
|
|
VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
|
|
VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
|
|
// coverity[var_deref_model]
|
|
return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
|
|
*(bufferDataType->m_poImpl), pDstBuffer,
|
|
pDstBufferAllocStart, nDstBufferAllocSize);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayWrite() */
|
|
/************************************************************************/
|
|
|
|
/** Write part or totality of a multidimensional array.
|
|
*
|
|
* This is the same as the C++ method GDALAbstractMDArray::Write()
|
|
*
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
GDALExtendedDataTypeH bufferDataType,
|
|
const void *pSrcBuffer, const void *pSrcBufferAllocStart,
|
|
size_t nSrcBufferAllocSize)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
if ((arrayStartIdx == nullptr || count == nullptr) &&
|
|
hArray->m_poImpl->GetDimensionCount() > 0)
|
|
{
|
|
VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
|
|
VALIDATE_POINTER1(count, __func__, FALSE);
|
|
}
|
|
VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
|
|
VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
|
|
// coverity[var_deref_model]
|
|
return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
|
|
bufferStride, *(bufferDataType->m_poImpl),
|
|
pSrcBuffer, pSrcBufferAllocStart,
|
|
nSrcBufferAllocSize);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayAdviseRead() */
|
|
/************************************************************************/
|
|
|
|
/** Advise driver of upcoming read requests.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::AdviseRead()
|
|
*
|
|
* @return TRUE in case of success.
|
|
*
|
|
* @since GDAL 3.2
|
|
*/
|
|
int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
|
|
const size_t *count)
|
|
{
|
|
return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayAdviseReadEx() */
|
|
/************************************************************************/
|
|
|
|
/** Advise driver of upcoming read requests.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::AdviseRead()
|
|
*
|
|
* @return TRUE in case of success.
|
|
*
|
|
* @since GDAL 3.4
|
|
*/
|
|
int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
|
|
const size_t *count, CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
// coverity[var_deref_model]
|
|
return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetAttribute() */
|
|
/************************************************************************/
|
|
|
|
/** Return an attribute by its name.
|
|
*
|
|
* This is the same as the C++ method GDALIHasAttribute::GetAttribute()
|
|
*
|
|
* The returned attribute must be freed with GDALAttributeRelease().
|
|
*/
|
|
GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszName, __func__, nullptr);
|
|
auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
|
|
if (attr)
|
|
return new GDALAttributeHS(attr);
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetAttributes() */
|
|
/************************************************************************/
|
|
|
|
/** Return the list of attributes contained in this array.
|
|
*
|
|
* The returned array must be freed with GDALReleaseAttributes(). If only the
|
|
* array itself needs to be freed, CPLFree() should be called (and
|
|
* GDALAttributeRelease() on individual array members).
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetAttributes().
|
|
*
|
|
* @param hArray Array.
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
* @param papszOptions Driver specific options determining how attributes
|
|
* should be retrieved. Pass nullptr for default behavior.
|
|
*
|
|
* @return an array of *pnCount attributes.
|
|
*/
|
|
GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
|
|
auto ret = static_cast<GDALAttributeH *>(
|
|
CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
|
|
for (size_t i = 0; i < attrs.size(); i++)
|
|
{
|
|
ret[i] = new GDALAttributeHS(attrs[i]);
|
|
}
|
|
*pnCount = attrs.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayCreateAttribute() */
|
|
/************************************************************************/
|
|
|
|
/** Create a attribute within an array.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::CreateAttribute().
|
|
*
|
|
* @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
|
|
*/
|
|
GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
|
|
const char *pszName,
|
|
size_t nDimensions,
|
|
const GUInt64 *panDimensions,
|
|
GDALExtendedDataTypeH hEDT,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszName, __func__, nullptr);
|
|
VALIDATE_POINTER1(hEDT, __func__, nullptr);
|
|
std::vector<GUInt64> dims;
|
|
dims.reserve(nDimensions);
|
|
for (size_t i = 0; i < nDimensions; i++)
|
|
dims.push_back(panDimensions[i]);
|
|
auto ret = hArray->m_poImpl->CreateAttribute(
|
|
std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
|
|
if (!ret)
|
|
return nullptr;
|
|
return new GDALAttributeHS(ret);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetRawNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
/** Return the nodata value as a "raw" value.
|
|
*
|
|
* The value returned might be nullptr in case of no nodata value. When
|
|
* a nodata value is registered, a non-nullptr will be returned whose size in
|
|
* bytes is GetDataType().GetSize().
|
|
*
|
|
* The returned value should not be modified or freed.
|
|
*
|
|
* This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
|
|
*
|
|
* @return nullptr or a pointer to GetDataType().GetSize() bytes.
|
|
*/
|
|
const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
return hArray->m_poImpl->GetRawNoDataValue();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetNoDataValueAsDouble() */
|
|
/************************************************************************/
|
|
|
|
/** Return the nodata value as a double.
|
|
*
|
|
* The value returned might be nullptr in case of no nodata value. When
|
|
* a nodata value is registered, a non-nullptr will be returned whose size in
|
|
* bytes is GetDataType().GetSize().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
|
|
*
|
|
* @param hArray Array handle.
|
|
* @param pbHasNoDataValue Pointer to a output boolean that will be set to true
|
|
* if a nodata value exists and can be converted to double. Might be nullptr.
|
|
*
|
|
* @return the nodata value as a double. A 0.0 value might also indicate the
|
|
* absence of a nodata value or an error in the conversion (*pbHasNoDataValue
|
|
* will be set to false then).
|
|
*/
|
|
double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
|
|
int *pbHasNoDataValue)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, 0);
|
|
bool bHasNodataValue = false;
|
|
double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
|
|
if (pbHasNoDataValue)
|
|
*pbHasNoDataValue = bHasNodataValue;
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetNoDataValueAsInt64() */
|
|
/************************************************************************/
|
|
|
|
/** Return the nodata value as a Int64.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
|
|
*
|
|
* @param hArray Array handle.
|
|
* @param pbHasNoDataValue Pointer to a output boolean that will be set to true
|
|
* if a nodata value exists and can be converted to Int64. Might be nullptr.
|
|
*
|
|
* @return the nodata value as a Int64.
|
|
* @since GDAL 3.5
|
|
*/
|
|
int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
|
|
int *pbHasNoDataValue)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, 0);
|
|
bool bHasNodataValue = false;
|
|
const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
|
|
if (pbHasNoDataValue)
|
|
*pbHasNoDataValue = bHasNodataValue;
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetNoDataValueAsUInt64() */
|
|
/************************************************************************/
|
|
|
|
/** Return the nodata value as a UInt64.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
|
|
*
|
|
* @param hArray Array handle.
|
|
* @param pbHasNoDataValue Pointer to a output boolean that will be set to true
|
|
* if a nodata value exists and can be converted to UInt64. Might be nullptr.
|
|
*
|
|
* @return the nodata value as a UInt64.
|
|
* @since GDAL 3.5
|
|
*/
|
|
uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
|
|
int *pbHasNoDataValue)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, 0);
|
|
bool bHasNodataValue = false;
|
|
const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
|
|
if (pbHasNoDataValue)
|
|
*pbHasNoDataValue = bHasNodataValue;
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArraySetRawNoDataValue() */
|
|
/************************************************************************/
|
|
|
|
/** Set the nodata value as a "raw" value.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
|
|
* void*).
|
|
*
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetRawNoDataValue(pNoData);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArraySetNoDataValueAsDouble() */
|
|
/************************************************************************/
|
|
|
|
/** Set the nodata value as a double.
|
|
*
|
|
* If the natural data type of the attribute/array is not double, type
|
|
* conversion will occur to the type returned by GetDataType().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
|
|
*
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArraySetNoDataValueAsInt64() */
|
|
/************************************************************************/
|
|
|
|
/** Set the nodata value as a Int64.
|
|
*
|
|
* If the natural data type of the attribute/array is not Int64, type conversion
|
|
* will occur to the type returned by GetDataType().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
|
|
*
|
|
* @return TRUE in case of success.
|
|
* @since GDAL 3.5
|
|
*/
|
|
int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArraySetNoDataValueAsUInt64() */
|
|
/************************************************************************/
|
|
|
|
/** Set the nodata value as a UInt64.
|
|
*
|
|
* If the natural data type of the attribute/array is not UInt64, type
|
|
* conversion will occur to the type returned by GetDataType().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
|
|
*
|
|
* @return TRUE in case of success.
|
|
* @since GDAL 3.5
|
|
*/
|
|
int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
|
|
uint64_t nNoDataValue)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayResize() */
|
|
/************************************************************************/
|
|
|
|
/** Resize an array to new dimensions.
|
|
*
|
|
* Not all drivers may allow this operation, and with restrictions (e.g.
|
|
* for netCDF, this is limited to growing of "unlimited" dimensions)
|
|
*
|
|
* Resizing a dimension used in other arrays will cause those other arrays
|
|
* to be resized.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::Resize().
|
|
*
|
|
* @param hArray Array.
|
|
* @param panNewDimSizes Array of GetDimensionCount() values containing the
|
|
* new size of each indexing dimension.
|
|
* @param papszOptions Options. (Driver specific)
|
|
* @return true in case of success.
|
|
* @since GDAL 3.7
|
|
*/
|
|
bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, false);
|
|
VALIDATE_POINTER1(panNewDimSizes, __func__, false);
|
|
std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
|
|
for (size_t i = 0; i < anNewDimSizes.size(); ++i)
|
|
{
|
|
anNewDimSizes[i] = panNewDimSizes[i];
|
|
}
|
|
return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArraySetScale() */
|
|
/************************************************************************/
|
|
|
|
/** Set the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::SetScale().
|
|
*
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetScale(dfScale);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArraySetScaleEx() */
|
|
/************************************************************************/
|
|
|
|
/** Set the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::SetScale().
|
|
*
|
|
* @return TRUE in case of success.
|
|
* @since GDAL 3.3
|
|
*/
|
|
int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
|
|
GDALDataType eStorageType)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetScale(dfScale, eStorageType);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArraySetOffset() */
|
|
/************************************************************************/
|
|
|
|
/** Set the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::SetOffset().
|
|
*
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetOffset(dfOffset);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArraySetOffsetEx() */
|
|
/************************************************************************/
|
|
|
|
/** Set the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetOffset() + GetOffset()
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::SetOffset().
|
|
*
|
|
* @return TRUE in case of success.
|
|
* @since GDAL 3.3
|
|
*/
|
|
int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
|
|
GDALDataType eStorageType)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetScale() */
|
|
/************************************************************************/
|
|
|
|
/** Get the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetScale().
|
|
*
|
|
* @return the scale value
|
|
*/
|
|
double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, 0.0);
|
|
bool bHasValue = false;
|
|
double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
|
|
if (pbHasValue)
|
|
*pbHasValue = bHasValue;
|
|
return dfRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetScaleEx() */
|
|
/************************************************************************/
|
|
|
|
/** Get the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetScale()
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetScale().
|
|
*
|
|
* @return the scale value
|
|
* @since GDAL 3.3
|
|
*/
|
|
double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
|
|
GDALDataType *peStorageType)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, 0.0);
|
|
bool bHasValue = false;
|
|
double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
|
|
if (pbHasValue)
|
|
*pbHasValue = bHasValue;
|
|
return dfRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetOffset() */
|
|
/************************************************************************/
|
|
|
|
/** Get the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetOffset().
|
|
*
|
|
* @return the scale value
|
|
*/
|
|
double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, 0.0);
|
|
bool bHasValue = false;
|
|
double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
|
|
if (pbHasValue)
|
|
*pbHasValue = bHasValue;
|
|
return dfRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetOffsetEx() */
|
|
/************************************************************************/
|
|
|
|
/** Get the scale value to apply to raw values.
|
|
*
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetOffset().
|
|
*
|
|
* @return the scale value
|
|
* @since GDAL 3.3
|
|
*/
|
|
double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
|
|
GDALDataType *peStorageType)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, 0.0);
|
|
bool bHasValue = false;
|
|
double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
|
|
if (pbHasValue)
|
|
*pbHasValue = bHasValue;
|
|
return dfRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetBlockSize() */
|
|
/************************************************************************/
|
|
|
|
/** Return the "natural" block size of the array along all dimensions.
|
|
*
|
|
* Some drivers might organize the array in tiles/blocks and reading/writing
|
|
* aligned on those tile/block boundaries will be more efficient.
|
|
*
|
|
* The returned number of elements in the vector is the same as
|
|
* GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
|
|
* the natural block size along the considered dimension.
|
|
* "Flat" arrays will typically return a vector of values set to 0.
|
|
*
|
|
* The default implementation will return a vector of values set to 0.
|
|
*
|
|
* This method is used by GetProcessingChunkSize().
|
|
*
|
|
* Pedantic note: the returned type is GUInt64, so in the highly unlikeley
|
|
* theoretical case of a 32-bit platform, this might exceed its size_t
|
|
* allocation capabilities.
|
|
*
|
|
* This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
|
|
*
|
|
* @return the block size, in number of elements along each dimension.
|
|
*/
|
|
GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
auto res = hArray->m_poImpl->GetBlockSize();
|
|
auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
|
|
for (size_t i = 0; i < res.size(); i++)
|
|
{
|
|
ret[i] = res[i];
|
|
}
|
|
*pnCount = res.size();
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* GDALMDArrayGetProcessingChunkSize() */
|
|
/************************************************************************/
|
|
|
|
/** \brief Return an optimal chunk size for read/write operations, given the
|
|
* natural block size and memory constraints specified.
|
|
*
|
|
* This method will use GetBlockSize() to define a chunk whose dimensions are
|
|
* multiple of those returned by GetBlockSize() (unless the block define by
|
|
* GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
|
|
* returned by this method).
|
|
*
|
|
* This is the same as the C++ method
|
|
* GDALAbstractMDArray::GetProcessingChunkSize().
|
|
*
|
|
* @param hArray Array.
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
* @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
|
|
* chunk.
|
|
*
|
|
* @return the chunk size, in number of elements along each dimension.
|
|
*/
|
|
|
|
size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
|
|
size_t nMaxChunkMemory)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
|
|
auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
|
|
for (size_t i = 0; i < res.size(); i++)
|
|
{
|
|
ret[i] = res[i];
|
|
}
|
|
*pnCount = res.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetStructuralInfo() */
|
|
/************************************************************************/
|
|
|
|
/** Return structural information on the array.
|
|
*
|
|
* This may be the compression, etc..
|
|
*
|
|
* The return value should not be freed and is valid until GDALMDArray is
|
|
* released or this function called again.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetStructuralInfo().
|
|
*/
|
|
CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
return hArray->m_poImpl->GetStructuralInfo();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetView() */
|
|
/************************************************************************/
|
|
|
|
/** Return a view of the array using slicing or field access.
|
|
*
|
|
* The returned object should be released with GDALMDArrayRelease().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetView().
|
|
*/
|
|
GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
|
|
auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
|
|
if (!sliced)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(sliced);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayTranspose() */
|
|
/************************************************************************/
|
|
|
|
/** Return a view of the array whose axis have been reordered.
|
|
*
|
|
* The returned object should be released with GDALMDArrayRelease().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::Transpose().
|
|
*/
|
|
GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
|
|
const int *panMapNewAxisToOldAxis)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
|
|
if (nNewAxisCount)
|
|
{
|
|
memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
|
|
nNewAxisCount * sizeof(int));
|
|
}
|
|
auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
|
|
if (!reordered)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(reordered);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetUnscaled() */
|
|
/************************************************************************/
|
|
|
|
/** Return an array that is the unscaled version of the current one.
|
|
*
|
|
* That is each value of the unscaled array will be
|
|
* unscaled_value = raw_value * GetScale() + GetOffset()
|
|
*
|
|
* Starting with GDAL 3.3, the Write() method is implemented and will convert
|
|
* from unscaled values to raw values.
|
|
*
|
|
* The returned object should be released with GDALMDArrayRelease().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetUnscaled().
|
|
*/
|
|
GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
auto unscaled = hArray->m_poImpl->GetUnscaled();
|
|
if (!unscaled)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(unscaled);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetMask() */
|
|
/************************************************************************/
|
|
|
|
/** Return an array that is a mask for the current array
|
|
*
|
|
* This array will be of type Byte, with values set to 0 to indicate invalid
|
|
* pixels of the current array, and values set to 1 to indicate valid pixels.
|
|
*
|
|
* The returned object should be released with GDALMDArrayRelease().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetMask().
|
|
*/
|
|
GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
|
|
if (!unscaled)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(unscaled);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetResampled() */
|
|
/************************************************************************/
|
|
|
|
/** Return an array that is a resampled / reprojected view of the current array
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetResampled().
|
|
*
|
|
* Currently this method can only resample along the last 2 dimensions.
|
|
*
|
|
* The returned object should be released with GDALMDArrayRelease().
|
|
*
|
|
* @since 3.4
|
|
*/
|
|
GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
|
|
const GDALDimensionH *pahNewDims,
|
|
GDALRIOResampleAlg resampleAlg,
|
|
OGRSpatialReferenceH hTargetSRS,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
|
|
std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
|
|
for (size_t i = 0; i < nNewDimCount; ++i)
|
|
{
|
|
if (pahNewDims[i])
|
|
apoNewDims[i] = pahNewDims[i]->m_poImpl;
|
|
}
|
|
auto poNewArray = hArray->m_poImpl->GetResampled(
|
|
apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
|
|
papszOptions);
|
|
if (!poNewArray)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(poNewArray);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArraySetUnit() */
|
|
/************************************************************************/
|
|
|
|
/** Set the variable unit.
|
|
*
|
|
* Values should conform as much as possible with those allowed by
|
|
* the NetCDF CF conventions:
|
|
* http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
|
|
* but others might be returned.
|
|
*
|
|
* Few examples are "meter", "degrees", "second", ...
|
|
* Empty value means unknown.
|
|
*
|
|
* This is the same as the C function GDALMDArraySetUnit()
|
|
*
|
|
* @param hArray array.
|
|
* @param pszUnit unit name.
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetUnit() */
|
|
/************************************************************************/
|
|
|
|
/** Return the array unit.
|
|
*
|
|
* Values should conform as much as possible with those allowed by
|
|
* the NetCDF CF conventions:
|
|
* http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
|
|
* but others might be returned.
|
|
*
|
|
* Few examples are "meter", "degrees", "second", ...
|
|
* Empty value means unknown.
|
|
*
|
|
* The return value should not be freed and is valid until GDALMDArray is
|
|
* released or this function called again.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetUnit().
|
|
*/
|
|
const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
return hArray->m_poImpl->GetUnit().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetSpatialRef() */
|
|
/************************************************************************/
|
|
|
|
/** Assign a spatial reference system object to the array.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::SetSpatialRef().
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->SetSpatialRef(
|
|
OGRSpatialReference::FromHandle(hSRS));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetSpatialRef() */
|
|
/************************************************************************/
|
|
|
|
/** Return the spatial reference system object associated with the array.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetSpatialRef().
|
|
*
|
|
* The returned object must be freed with OSRDestroySpatialReference().
|
|
*/
|
|
OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
auto poSRS = hArray->m_poImpl->GetSpatialRef();
|
|
return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetStatistics() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Fetch statistics.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetStatistics().
|
|
*
|
|
* @since GDAL 3.2
|
|
*/
|
|
|
|
CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
|
|
int bApproxOK, int bForce, double *pdfMin,
|
|
double *pdfMax, double *pdfMean,
|
|
double *pdfStdDev, GUInt64 *pnValidCount,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, CE_Failure);
|
|
return hArray->m_poImpl->GetStatistics(
|
|
CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
|
|
pdfStdDev, pnValidCount, pfnProgress, pProgressData);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayComputeStatistics() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Compute statistics.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::ComputeStatistics().
|
|
*
|
|
* @since GDAL 3.2
|
|
*/
|
|
|
|
int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
|
|
int bApproxOK, double *pdfMin, double *pdfMax,
|
|
double *pdfMean, double *pdfStdDev,
|
|
GUInt64 *pnValidCount,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->ComputeStatistics(
|
|
CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
|
|
pnValidCount, pfnProgress, pProgressData);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetCoordinateVariables() */
|
|
/************************************************************************/
|
|
|
|
/** Return coordinate variables.
|
|
*
|
|
* The returned array must be freed with GDALReleaseArrays(). If only the array
|
|
* itself needs to be freed, CPLFree() should be called (and
|
|
* GDALMDArrayRelease() on individual array members).
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
|
|
*
|
|
* @param hArray Array.
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
*
|
|
* @return an array of *pnCount arrays.
|
|
* @since 3.4
|
|
*/
|
|
GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
|
|
size_t *pnCount)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
|
|
auto ret = static_cast<GDALMDArrayH *>(
|
|
CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
|
|
for (size_t i = 0; i < coordinates.size(); i++)
|
|
{
|
|
ret[i] = new GDALMDArrayHS(coordinates[i]);
|
|
}
|
|
*pnCount = coordinates.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayGetGridded() */
|
|
/************************************************************************/
|
|
|
|
/** Return a gridded array from scattered point data, that is from an array
|
|
* whose last dimension is the indexing variable of X and Y arrays.
|
|
*
|
|
* The returned object should be released with GDALMDArrayRelease().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::GetGridded().
|
|
*
|
|
* @since GDAL 3.7
|
|
*/
|
|
GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
|
|
const char *pszGridOptions,
|
|
GDALMDArrayH hXArray, GDALMDArrayH hYArray,
|
|
CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
|
|
auto gridded = hArray->m_poImpl->GetGridded(
|
|
pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
|
|
hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
|
|
if (!gridded)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(gridded);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALReleaseArrays() */
|
|
/************************************************************************/
|
|
|
|
/** Free the return of GDALMDArrayGetCoordinateVariables()
|
|
*
|
|
* @param arrays return pointer of above methods
|
|
* @param nCount *pnCount value returned by above methods
|
|
*/
|
|
void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
|
|
{
|
|
for (size_t i = 0; i < nCount; i++)
|
|
{
|
|
delete arrays[i];
|
|
}
|
|
CPLFree(arrays);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayCache() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Cache the content of the array into an auxiliary filename.
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::Cache().
|
|
*
|
|
* @since GDAL 3.4
|
|
*/
|
|
|
|
int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, FALSE);
|
|
return hArray->m_poImpl->Cache(papszOptions);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeRelease() */
|
|
/************************************************************************/
|
|
|
|
/** Release the GDAL in-memory object associated with a GDALAttribute.
|
|
*
|
|
* Note: when applied on a object coming from a driver, this does not
|
|
* destroy the object in the file, database, etc...
|
|
*/
|
|
void GDALAttributeRelease(GDALAttributeH hAttr)
|
|
{
|
|
delete hAttr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeGetName() */
|
|
/************************************************************************/
|
|
|
|
/** Return the name of the attribute.
|
|
*
|
|
* The returned pointer is valid until hAttr is released.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::GetName().
|
|
*/
|
|
const char *GDALAttributeGetName(GDALAttributeH hAttr)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, nullptr);
|
|
return hAttr->m_poImpl->GetName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeGetFullName() */
|
|
/************************************************************************/
|
|
|
|
/** Return the full name of the attribute.
|
|
*
|
|
* The returned pointer is valid until hAttr is released.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::GetFullName().
|
|
*/
|
|
const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, nullptr);
|
|
return hAttr->m_poImpl->GetFullName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeGetTotalElementsCount() */
|
|
/************************************************************************/
|
|
|
|
/** Return the total number of values in the attribute.
|
|
*
|
|
* This is the same as the C++ method
|
|
* GDALAbstractMDArray::GetTotalElementsCount()
|
|
*/
|
|
GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, 0);
|
|
return hAttr->m_poImpl->GetTotalElementsCount();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeGetDimensionCount() */
|
|
/************************************************************************/
|
|
|
|
/** Return the number of dimensions.
|
|
*
|
|
* This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
|
|
*/
|
|
size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, 0);
|
|
return hAttr->m_poImpl->GetDimensionCount();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeGetDimensionsSize() */
|
|
/************************************************************************/
|
|
|
|
/** Return the dimension sizes of the attribute.
|
|
*
|
|
* The returned array must be freed with CPLFree()
|
|
*
|
|
* @param hAttr Attribute.
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
*
|
|
* @return an array of *pnCount values.
|
|
*/
|
|
GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
const auto &dims = hAttr->m_poImpl->GetDimensions();
|
|
auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
|
|
for (size_t i = 0; i < dims.size(); i++)
|
|
{
|
|
ret[i] = dims[i]->GetSize();
|
|
}
|
|
*pnCount = dims.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeGetDataType() */
|
|
/************************************************************************/
|
|
|
|
/** Return the data type
|
|
*
|
|
* The return must be freed with GDALExtendedDataTypeRelease().
|
|
*/
|
|
GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, nullptr);
|
|
return new GDALExtendedDataTypeHS(
|
|
new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeReadAsRaw() */
|
|
/************************************************************************/
|
|
|
|
/** Return the raw value of an attribute.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::ReadAsRaw().
|
|
*
|
|
* The returned buffer must be freed with GDALAttributeFreeRawResult()
|
|
*
|
|
* @param hAttr Attribute.
|
|
* @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
|
|
*
|
|
* @return a buffer of *pnSize bytes.
|
|
*/
|
|
GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnSize, __func__, nullptr);
|
|
auto res(hAttr->m_poImpl->ReadAsRaw());
|
|
*pnSize = res.size();
|
|
auto ret = res.StealData();
|
|
if (!ret)
|
|
{
|
|
*pnSize = 0;
|
|
return nullptr;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeFreeRawResult() */
|
|
/************************************************************************/
|
|
|
|
/** Free the return of GDALAttributeAsRaw()
|
|
*/
|
|
void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
|
|
CPL_UNUSED size_t nSize)
|
|
{
|
|
VALIDATE_POINTER0(hAttr, __func__);
|
|
if (raw)
|
|
{
|
|
const auto dt(hAttr->m_poImpl->GetDataType());
|
|
const auto nDTSize(dt.GetSize());
|
|
GByte *pabyPtr = raw;
|
|
const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
|
|
CPLAssert(nSize == nDTSize * nEltCount);
|
|
for (size_t i = 0; i < nEltCount; ++i)
|
|
{
|
|
dt.FreeDynamicMemory(pabyPtr);
|
|
pabyPtr += nDTSize;
|
|
}
|
|
CPLFree(raw);
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeReadAsString() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as a string.
|
|
*
|
|
* The returned string should not be freed, and its lifetime does not
|
|
* excess a next call to ReadAsString() on the same object, or the deletion
|
|
* of the object itself.
|
|
*
|
|
* This function will only return the first element if there are several.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::ReadAsString()
|
|
*
|
|
* @return a string, or nullptr.
|
|
*/
|
|
const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, nullptr);
|
|
return hAttr->m_poImpl->ReadAsString();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeReadAsInt() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as a integer.
|
|
*
|
|
* This function will only return the first element if there are several.
|
|
*
|
|
* It can fail if its value can be converted to integer.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::ReadAsInt()
|
|
*
|
|
* @return a integer, or INT_MIN in case of error.
|
|
*/
|
|
int GDALAttributeReadAsInt(GDALAttributeH hAttr)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, 0);
|
|
return hAttr->m_poImpl->ReadAsInt();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeReadAsDouble() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as a double.
|
|
*
|
|
* This function will only return the first element if there are several.
|
|
*
|
|
* It can fail if its value can be converted to double.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::ReadAsDoubl()
|
|
*
|
|
* @return a double value.
|
|
*/
|
|
double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, 0);
|
|
return hAttr->m_poImpl->ReadAsDouble();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeReadAsStringArray() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as an array of strings.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::ReadAsStringArray()
|
|
*
|
|
* The return value must be freed with CSLDestroy().
|
|
*/
|
|
char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, nullptr);
|
|
return hAttr->m_poImpl->ReadAsStringArray().StealList();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeReadAsIntArray() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as an array of integers.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::ReadAsIntArray()
|
|
*
|
|
* @param hAttr Attribute
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
* @return array to be freed with CPLFree(), or nullptr.
|
|
*/
|
|
int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
*pnCount = 0;
|
|
auto tmp(hAttr->m_poImpl->ReadAsIntArray());
|
|
if (tmp.empty())
|
|
return nullptr;
|
|
auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
|
|
if (!ret)
|
|
return nullptr;
|
|
memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
|
|
*pnCount = tmp.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeReadAsDoubleArray() */
|
|
/************************************************************************/
|
|
|
|
/** Return the value of an attribute as an array of doubles.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
|
|
*
|
|
* @param hAttr Attribute
|
|
* @param pnCount Pointer to the number of values returned. Must NOT be NULL.
|
|
* @return array to be freed with CPLFree(), or nullptr.
|
|
*/
|
|
double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, nullptr);
|
|
VALIDATE_POINTER1(pnCount, __func__, nullptr);
|
|
*pnCount = 0;
|
|
auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
|
|
if (tmp.empty())
|
|
return nullptr;
|
|
auto ret =
|
|
static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
|
|
if (!ret)
|
|
return nullptr;
|
|
memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
|
|
*pnCount = tmp.size();
|
|
return ret;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeWriteRaw() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from raw values expressed in GetDataType()
|
|
*
|
|
* The values should be provided in the type of GetDataType() and there should
|
|
* be exactly GetTotalElementsCount() of them.
|
|
* If GetDataType() is a string, each value should be a char* pointer.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
|
|
*
|
|
* @param hAttr Attribute
|
|
* @param pabyValue Buffer of nLen bytes.
|
|
* @param nLength Size of pabyValue in bytes. Should be equal to
|
|
* GetTotalElementsCount() * GetDataType().GetSize()
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
|
|
size_t nLength)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, FALSE);
|
|
return hAttr->m_poImpl->Write(pabyValue, nLength);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeWriteString() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from a string value.
|
|
*
|
|
* Type conversion will be performed if needed. If the attribute contains
|
|
* multiple values, only the first one will be updated.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::Write(const char*)
|
|
*
|
|
* @param hAttr Attribute
|
|
* @param pszVal Pointer to a string.
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, FALSE);
|
|
return hAttr->m_poImpl->Write(pszVal);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeWriteInt() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from a integer value.
|
|
*
|
|
* Type conversion will be performed if needed. If the attribute contains
|
|
* multiple values, only the first one will be updated.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::WriteInt()
|
|
*
|
|
* @param hAttr Attribute
|
|
* @param nVal Value.
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, FALSE);
|
|
return hAttr->m_poImpl->WriteInt(nVal);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeWriteDouble() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from a double value.
|
|
*
|
|
* Type conversion will be performed if needed. If the attribute contains
|
|
* multiple values, only the first one will be updated.
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::Write(double);
|
|
*
|
|
* @param hAttr Attribute
|
|
* @param dfVal Value.
|
|
*
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, FALSE);
|
|
return hAttr->m_poImpl->Write(dfVal);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeWriteStringArray() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from an array of strings.
|
|
*
|
|
* Type conversion will be performed if needed.
|
|
*
|
|
* Exactly GetTotalElementsCount() strings must be provided
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::Write(CSLConstList)
|
|
*
|
|
* @param hAttr Attribute
|
|
* @param papszValues Array of strings.
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
|
|
CSLConstList papszValues)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, FALSE);
|
|
return hAttr->m_poImpl->Write(papszValues);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALAttributeWriteDoubleArray() */
|
|
/************************************************************************/
|
|
|
|
/** Write an attribute from an array of double.
|
|
*
|
|
* Type conversion will be performed if needed.
|
|
*
|
|
* Exactly GetTotalElementsCount() strings must be provided
|
|
*
|
|
* This is the same as the C++ method GDALAttribute::Write(const double *,
|
|
* size_t)
|
|
*
|
|
* @param hAttr Attribute
|
|
* @param padfValues Array of double.
|
|
* @param nCount Should be equal to GetTotalElementsCount().
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
|
|
const double *padfValues, size_t nCount)
|
|
{
|
|
VALIDATE_POINTER1(hAttr, __func__, FALSE);
|
|
return hAttr->m_poImpl->Write(padfValues, nCount);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDimensionRelease() */
|
|
/************************************************************************/
|
|
|
|
/** Release the GDAL in-memory object associated with a GDALDimension.
|
|
*
|
|
* Note: when applied on a object coming from a driver, this does not
|
|
* destroy the object in the file, database, etc...
|
|
*/
|
|
void GDALDimensionRelease(GDALDimensionH hDim)
|
|
{
|
|
delete hDim;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDimensionGetName() */
|
|
/************************************************************************/
|
|
|
|
/** Return dimension name.
|
|
*
|
|
* This is the same as the C++ method GDALDimension::GetName()
|
|
*/
|
|
const char *GDALDimensionGetName(GDALDimensionH hDim)
|
|
{
|
|
VALIDATE_POINTER1(hDim, __func__, nullptr);
|
|
return hDim->m_poImpl->GetName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDimensionGetFullName() */
|
|
/************************************************************************/
|
|
|
|
/** Return dimension full name.
|
|
*
|
|
* This is the same as the C++ method GDALDimension::GetFullName()
|
|
*/
|
|
const char *GDALDimensionGetFullName(GDALDimensionH hDim)
|
|
{
|
|
VALIDATE_POINTER1(hDim, __func__, nullptr);
|
|
return hDim->m_poImpl->GetFullName().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDimensionGetType() */
|
|
/************************************************************************/
|
|
|
|
/** Return dimension type.
|
|
*
|
|
* This is the same as the C++ method GDALDimension::GetType()
|
|
*/
|
|
const char *GDALDimensionGetType(GDALDimensionH hDim)
|
|
{
|
|
VALIDATE_POINTER1(hDim, __func__, nullptr);
|
|
return hDim->m_poImpl->GetType().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDimensionGetDirection() */
|
|
/************************************************************************/
|
|
|
|
/** Return dimension direction.
|
|
*
|
|
* This is the same as the C++ method GDALDimension::GetDirection()
|
|
*/
|
|
const char *GDALDimensionGetDirection(GDALDimensionH hDim)
|
|
{
|
|
VALIDATE_POINTER1(hDim, __func__, nullptr);
|
|
return hDim->m_poImpl->GetDirection().c_str();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDimensionGetSize() */
|
|
/************************************************************************/
|
|
|
|
/** Return the size, that is the number of values along the dimension.
|
|
*
|
|
* This is the same as the C++ method GDALDimension::GetSize()
|
|
*/
|
|
GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
|
|
{
|
|
VALIDATE_POINTER1(hDim, __func__, 0);
|
|
return hDim->m_poImpl->GetSize();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDimensionGetIndexingVariable() */
|
|
/************************************************************************/
|
|
|
|
/** Return the variable that is used to index the dimension (if there is one).
|
|
*
|
|
* This is the array, typically one-dimensional, describing the values taken
|
|
* by the dimension.
|
|
*
|
|
* The returned value should be freed with GDALMDArrayRelease().
|
|
*
|
|
* This is the same as the C++ method GDALDimension::GetIndexingVariable()
|
|
*/
|
|
GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
|
|
{
|
|
VALIDATE_POINTER1(hDim, __func__, nullptr);
|
|
auto var(hDim->m_poImpl->GetIndexingVariable());
|
|
if (!var)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(var);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDimensionSetIndexingVariable() */
|
|
/************************************************************************/
|
|
|
|
/** Set the variable that is used to index the dimension.
|
|
*
|
|
* This is the array, typically one-dimensional, describing the values taken
|
|
* by the dimension.
|
|
*
|
|
* This is the same as the C++ method GDALDimension::SetIndexingVariable()
|
|
*
|
|
* @return TRUE in case of success.
|
|
*/
|
|
int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
|
|
{
|
|
VALIDATE_POINTER1(hDim, __func__, FALSE);
|
|
return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
|
|
: nullptr);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDatasetGetRootGroup() */
|
|
/************************************************************************/
|
|
|
|
/** Return the root GDALGroup of this dataset.
|
|
*
|
|
* Only valid for multidimensional datasets.
|
|
*
|
|
* The returned value must be freed with GDALGroupRelease().
|
|
*
|
|
* This is the same as the C++ method GDALDataset::GetRootGroup().
|
|
*
|
|
* @since GDAL 3.1
|
|
*/
|
|
GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
|
|
{
|
|
VALIDATE_POINTER1(hDS, __func__, nullptr);
|
|
auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
|
|
return poGroup ? new GDALGroupHS(poGroup) : nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterBandAsMDArray() */
|
|
/************************************************************************/
|
|
|
|
/** Return a view of this raster band as a 2D multidimensional GDALMDArray.
|
|
*
|
|
* The band must be linked to a GDALDataset. If this dataset is not already
|
|
* marked as shared, it will be, so that the returned array holds a reference
|
|
* to it.
|
|
*
|
|
* If the dataset has a geotransform attached, the X and Y dimensions of the
|
|
* returned array will have an associated indexing variable.
|
|
*
|
|
* The returned pointer must be released with GDALMDArrayRelease().
|
|
*
|
|
* This is the same as the C++ method GDALRasterBand::AsMDArray().
|
|
*
|
|
* @return a new array, or NULL.
|
|
*
|
|
* @since GDAL 3.1
|
|
*/
|
|
GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
|
|
{
|
|
VALIDATE_POINTER1(hBand, __func__, nullptr);
|
|
auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
|
|
if (!poArray)
|
|
return nullptr;
|
|
return new GDALMDArrayHS(poArray);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALMDArrayAsClassicDataset() */
|
|
/************************************************************************/
|
|
|
|
/** Return a view of this array as a "classic" GDALDataset (ie 2D)
|
|
*
|
|
* Only 2D or more arrays are supported.
|
|
*
|
|
* In the case of > 2D arrays, additional dimensions will be represented as
|
|
* raster bands.
|
|
*
|
|
* The "reverse" method is GDALRasterBand::AsMDArray().
|
|
*
|
|
* This is the same as the C++ method GDALMDArray::AsClassicDataset().
|
|
*
|
|
* @param hArray Array.
|
|
* @param iXDim Index of the dimension that will be used as the X/width axis.
|
|
* @param iYDim Index of the dimension that will be used as the Y/height axis.
|
|
* @return a new GDALDataset that must be freed with GDALClose(), or nullptr
|
|
*/
|
|
GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
|
|
size_t iYDim)
|
|
{
|
|
VALIDATE_POINTER1(hArray, __func__, nullptr);
|
|
return GDALDataset::ToHandle(
|
|
hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
|
|
}
|
|
|
|
//! @cond Doxygen_Suppress
|
|
|
|
GDALAttributeString::GDALAttributeString(const std::string &osParentName,
|
|
const std::string &osName,
|
|
const std::string &osValue,
|
|
GDALExtendedDataTypeSubType eSubType)
|
|
: GDALAbstractMDArray(osParentName, osName),
|
|
GDALAttribute(osParentName, osName),
|
|
m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
|
|
{
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<GDALDimension>> &
|
|
GDALAttributeString::GetDimensions() const
|
|
{
|
|
return m_dims;
|
|
}
|
|
|
|
const GDALExtendedDataType &GDALAttributeString::GetDataType() const
|
|
{
|
|
return m_dt;
|
|
}
|
|
|
|
bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
|
|
const GPtrDiff_t *,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
if (bufferDataType.GetClass() != GEDTC_STRING)
|
|
return false;
|
|
char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
|
|
if (!pszStr)
|
|
return false;
|
|
memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
|
|
*static_cast<char **>(pDstBuffer) = pszStr;
|
|
return true;
|
|
}
|
|
|
|
GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
|
|
const std::string &osName,
|
|
double dfValue)
|
|
: GDALAbstractMDArray(osParentName, osName),
|
|
GDALAttribute(osParentName, osName),
|
|
m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
|
|
{
|
|
}
|
|
|
|
GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
|
|
const std::string &osName,
|
|
int nValue)
|
|
: GDALAbstractMDArray(osParentName, osName),
|
|
GDALAttribute(osParentName, osName),
|
|
m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
|
|
{
|
|
}
|
|
|
|
GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
|
|
const std::string &osName,
|
|
const std::vector<GUInt32> &anValues)
|
|
: GDALAbstractMDArray(osParentName, osName),
|
|
GDALAttribute(osParentName, osName),
|
|
m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
|
|
{
|
|
m_dims.push_back(std::make_shared<GDALDimension>(
|
|
std::string(), "dim0", std::string(), std::string(),
|
|
m_anValuesUInt32.size()));
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<GDALDimension>> &
|
|
GDALAttributeNumeric::GetDimensions() const
|
|
{
|
|
return m_dims;
|
|
}
|
|
|
|
const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
|
|
{
|
|
return m_dt;
|
|
}
|
|
|
|
bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
|
|
const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride,
|
|
const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
if (m_dims.empty())
|
|
{
|
|
if (m_dt.GetNumericDataType() == GDT_Float64)
|
|
GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
|
|
bufferDataType);
|
|
else
|
|
{
|
|
CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
|
|
GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
|
|
bufferDataType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
|
|
GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
|
|
for (size_t i = 0; i < count[0]; ++i)
|
|
{
|
|
GDALExtendedDataType::CopyValue(
|
|
&m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
|
|
i * arrayStep[0])],
|
|
m_dt, pabyDstBuffer, bufferDataType);
|
|
pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
|
|
const std::string &osParentName, const std::string &osName,
|
|
const std::shared_ptr<GDALDimension> &poDim, double dfStart,
|
|
double dfIncrement, double dfOffsetInIncrement)
|
|
: GDALAbstractMDArray(osParentName, osName),
|
|
GDALMDArray(osParentName, osName), m_dfStart(dfStart),
|
|
m_dfIncrement(dfIncrement),
|
|
m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
|
|
{
|
|
}
|
|
|
|
std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
|
|
const std::string &osParentName, const std::string &osName,
|
|
const std::shared_ptr<GDALDimension> &poDim, double dfStart,
|
|
double dfIncrement, double dfOffsetInIncrement)
|
|
{
|
|
auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
|
|
osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
|
|
poArray->SetSelf(poArray);
|
|
return poArray;
|
|
}
|
|
|
|
const std::vector<std::shared_ptr<GDALDimension>> &
|
|
GDALMDArrayRegularlySpaced::GetDimensions() const
|
|
{
|
|
return m_dims;
|
|
}
|
|
|
|
const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
|
|
{
|
|
return m_dt;
|
|
}
|
|
|
|
std::vector<std::shared_ptr<GDALAttribute>>
|
|
GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
|
|
{
|
|
return m_attributes;
|
|
}
|
|
|
|
void GDALMDArrayRegularlySpaced::AddAttribute(
|
|
const std::shared_ptr<GDALAttribute> &poAttr)
|
|
{
|
|
m_attributes.emplace_back(poAttr);
|
|
}
|
|
|
|
bool GDALMDArrayRegularlySpaced::IRead(
|
|
const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
|
|
const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
|
|
void *pDstBuffer) const
|
|
{
|
|
GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
|
|
for (size_t i = 0; i < count[0]; i++)
|
|
{
|
|
const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
|
|
m_dfOffsetInIncrement) *
|
|
m_dfIncrement;
|
|
GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
|
|
bufferDataType);
|
|
pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
|
|
const std::string &osParentName, const std::string &osName,
|
|
const std::string &osType, const std::string &osDirection, GUInt64 nSize)
|
|
: GDALDimension(osParentName, osName, osType, osDirection, nSize)
|
|
{
|
|
}
|
|
|
|
std::shared_ptr<GDALMDArray>
|
|
GDALDimensionWeakIndexingVar::GetIndexingVariable() const
|
|
{
|
|
return m_poIndexingVariable.lock();
|
|
}
|
|
|
|
// cppcheck-suppress passedByValue
|
|
bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
|
|
std::shared_ptr<GDALMDArray> poIndexingVariable)
|
|
{
|
|
m_poIndexingVariable = poIndexingVariable;
|
|
return true;
|
|
}
|
|
|
|
void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
|
|
{
|
|
m_nSize = nNewSize;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMultiDim::Private */
|
|
/************************************************************************/
|
|
|
|
struct GDALPamMultiDim::Private
|
|
{
|
|
std::string m_osFilename{};
|
|
std::string m_osPamFilename{};
|
|
|
|
struct Statistics
|
|
{
|
|
bool bHasStats = false;
|
|
bool bApproxStats = false;
|
|
double dfMin = 0;
|
|
double dfMax = 0;
|
|
double dfMean = 0;
|
|
double dfStdDev = 0;
|
|
GUInt64 nValidCount = 0;
|
|
};
|
|
|
|
struct ArrayInfo
|
|
{
|
|
std::shared_ptr<OGRSpatialReference> poSRS{};
|
|
// cppcheck-suppress unusedStructMember
|
|
Statistics stats{};
|
|
};
|
|
|
|
std::map<std::string, ArrayInfo> m_oMapArray{};
|
|
std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
|
|
bool m_bDirty = false;
|
|
bool m_bLoaded = false;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMultiDim */
|
|
/************************************************************************/
|
|
|
|
GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
|
|
: d(new Private())
|
|
{
|
|
d->m_osFilename = osFilename;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMultiDim::~GDALPamMultiDim() */
|
|
/************************************************************************/
|
|
|
|
GDALPamMultiDim::~GDALPamMultiDim()
|
|
{
|
|
if (d->m_bDirty)
|
|
Save();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMultiDim::Load() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamMultiDim::Load()
|
|
{
|
|
if (d->m_bLoaded)
|
|
return;
|
|
d->m_bLoaded = true;
|
|
|
|
const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
|
|
d->m_osPamFilename =
|
|
pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
|
|
CPLXMLTreeCloser oTree(nullptr);
|
|
{
|
|
CPLErrorStateBackuper oStateBackuper;
|
|
CPLErrorHandlerPusher oErrorHandlerPusher(CPLQuietErrorHandler);
|
|
oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
|
|
}
|
|
if (!oTree)
|
|
{
|
|
return;
|
|
}
|
|
const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
|
|
if (!poPAMMultiDim)
|
|
return;
|
|
for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
|
|
psIter = psIter->psNext)
|
|
{
|
|
if (psIter->eType == CXT_Element &&
|
|
strcmp(psIter->pszValue, "Array") == 0)
|
|
{
|
|
const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
|
|
if (!pszName)
|
|
continue;
|
|
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
/* Check for an SRS node. */
|
|
/* --------------------------------------------------------------------
|
|
*/
|
|
const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
|
|
if (psSRSNode)
|
|
{
|
|
std::shared_ptr<OGRSpatialReference> poSRS =
|
|
std::make_shared<OGRSpatialReference>();
|
|
poSRS->SetFromUserInput(
|
|
CPLGetXMLValue(psSRSNode, nullptr, ""),
|
|
OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
|
|
const char *pszMapping = CPLGetXMLValue(
|
|
psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
|
|
if (pszMapping)
|
|
{
|
|
char **papszTokens =
|
|
CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
|
|
std::vector<int> anMapping;
|
|
for (int i = 0; papszTokens && papszTokens[i]; i++)
|
|
{
|
|
anMapping.push_back(atoi(papszTokens[i]));
|
|
}
|
|
CSLDestroy(papszTokens);
|
|
poSRS->SetDataAxisToSRSAxisMapping(anMapping);
|
|
}
|
|
else
|
|
{
|
|
poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
|
|
}
|
|
|
|
const char *pszCoordinateEpoch =
|
|
CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
|
|
if (pszCoordinateEpoch)
|
|
poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
|
|
|
|
d->m_oMapArray[pszName].poSRS = poSRS;
|
|
}
|
|
|
|
const CPLXMLNode *psStatistics =
|
|
CPLGetXMLNode(psIter, "Statistics");
|
|
if (psStatistics)
|
|
{
|
|
Private::Statistics sStats;
|
|
sStats.bHasStats = true;
|
|
sStats.bApproxStats = CPLTestBool(
|
|
CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
|
|
sStats.dfMin =
|
|
CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
|
|
sStats.dfMax =
|
|
CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
|
|
sStats.dfMean =
|
|
CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
|
|
sStats.dfStdDev =
|
|
CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
|
|
sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
|
|
CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
|
|
d->m_oMapArray[pszName].stats = sStats;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CPLXMLNode *psNextBackup = psIter->psNext;
|
|
psIter->psNext = nullptr;
|
|
d->m_apoOtherNodes.emplace_back(
|
|
CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
|
|
psIter->psNext = psNextBackup;
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMultiDim::Save() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamMultiDim::Save()
|
|
{
|
|
CPLXMLTreeCloser oTree(
|
|
CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
|
|
for (const auto &poOtherNode : d->m_apoOtherNodes)
|
|
{
|
|
CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
|
|
}
|
|
for (const auto &kv : d->m_oMapArray)
|
|
{
|
|
CPLXMLNode *psArrayNode =
|
|
CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
|
|
CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.c_str());
|
|
if (kv.second.poSRS)
|
|
{
|
|
char *pszWKT = nullptr;
|
|
{
|
|
CPLErrorStateBackuper oErrorStateBackuper;
|
|
CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
|
|
const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
|
|
kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
|
|
}
|
|
CPLXMLNode *psSRSNode =
|
|
CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
|
|
CPLFree(pszWKT);
|
|
const auto &mapping =
|
|
kv.second.poSRS->GetDataAxisToSRSAxisMapping();
|
|
CPLString osMapping;
|
|
for (size_t i = 0; i < mapping.size(); ++i)
|
|
{
|
|
if (!osMapping.empty())
|
|
osMapping += ",";
|
|
osMapping += CPLSPrintf("%d", mapping[i]);
|
|
}
|
|
CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
|
|
osMapping.c_str());
|
|
|
|
const double dfCoordinateEpoch =
|
|
kv.second.poSRS->GetCoordinateEpoch();
|
|
if (dfCoordinateEpoch > 0)
|
|
{
|
|
std::string osCoordinateEpoch =
|
|
CPLSPrintf("%f", dfCoordinateEpoch);
|
|
if (osCoordinateEpoch.find('.') != std::string::npos)
|
|
{
|
|
while (osCoordinateEpoch.back() == '0')
|
|
osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
|
|
}
|
|
CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
|
|
osCoordinateEpoch.c_str());
|
|
}
|
|
}
|
|
|
|
if (kv.second.stats.bHasStats)
|
|
{
|
|
CPLXMLNode *psMDArray =
|
|
CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
|
|
CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
|
|
kv.second.stats.bApproxStats ? "1"
|
|
: "0");
|
|
CPLCreateXMLElementAndValue(
|
|
psMDArray, "Minimum",
|
|
CPLSPrintf("%.18g", kv.second.stats.dfMin));
|
|
CPLCreateXMLElementAndValue(
|
|
psMDArray, "Maximum",
|
|
CPLSPrintf("%.18g", kv.second.stats.dfMax));
|
|
CPLCreateXMLElementAndValue(
|
|
psMDArray, "Mean", CPLSPrintf("%.18g", kv.second.stats.dfMean));
|
|
CPLCreateXMLElementAndValue(
|
|
psMDArray, "StdDev",
|
|
CPLSPrintf("%.18g", kv.second.stats.dfStdDev));
|
|
CPLCreateXMLElementAndValue(
|
|
psMDArray, "ValidSampleCount",
|
|
CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
|
|
}
|
|
}
|
|
|
|
std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
|
|
CPLInstallErrorHandlerAccumulator(aoErrors);
|
|
const int bSaved =
|
|
CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
|
|
CPLUninstallErrorHandlerAccumulator();
|
|
|
|
const char *pszNewPam = nullptr;
|
|
if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
|
|
((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
|
|
{
|
|
CPLErrorReset();
|
|
CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
|
|
}
|
|
else
|
|
{
|
|
for (const auto &oError : aoErrors)
|
|
{
|
|
CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMultiDim::GetSpatialRef() */
|
|
/************************************************************************/
|
|
|
|
std::shared_ptr<OGRSpatialReference>
|
|
GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName)
|
|
{
|
|
Load();
|
|
auto oIter = d->m_oMapArray.find(osArrayFullName);
|
|
if (oIter != d->m_oMapArray.end())
|
|
return oIter->second.poSRS;
|
|
return nullptr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMultiDim::SetSpatialRef() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
|
|
const OGRSpatialReference *poSRS)
|
|
{
|
|
Load();
|
|
d->m_bDirty = true;
|
|
if (poSRS && !poSRS->IsEmpty())
|
|
d->m_oMapArray[osArrayFullName].poSRS.reset(poSRS->Clone());
|
|
else
|
|
d->m_oMapArray[osArrayFullName].poSRS.reset();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetStatistics() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
|
|
bool bApproxOK, double *pdfMin,
|
|
double *pdfMax, double *pdfMean,
|
|
double *pdfStdDev, GUInt64 *pnValidCount)
|
|
{
|
|
Load();
|
|
auto oIter = d->m_oMapArray.find(osArrayFullName);
|
|
if (oIter == d->m_oMapArray.end())
|
|
return CE_Failure;
|
|
const auto &stats = oIter->second.stats;
|
|
if (!stats.bHasStats)
|
|
return CE_Failure;
|
|
if (!bApproxOK && stats.bApproxStats)
|
|
return CE_Failure;
|
|
if (pdfMin)
|
|
*pdfMin = stats.dfMin;
|
|
if (pdfMax)
|
|
*pdfMax = stats.dfMax;
|
|
if (pdfMean)
|
|
*pdfMean = stats.dfMean;
|
|
if (pdfStdDev)
|
|
*pdfStdDev = stats.dfStdDev;
|
|
if (pnValidCount)
|
|
*pnValidCount = stats.nValidCount;
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetStatistics() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
|
|
bool bApproxStats, double dfMin,
|
|
double dfMax, double dfMean,
|
|
double dfStdDev, GUInt64 nValidCount)
|
|
{
|
|
Load();
|
|
d->m_bDirty = true;
|
|
auto &stats = d->m_oMapArray[osArrayFullName].stats;
|
|
stats.bHasStats = true;
|
|
stats.bApproxStats = bApproxStats;
|
|
stats.dfMin = dfMin;
|
|
stats.dfMax = dfMax;
|
|
stats.dfMean = dfMean;
|
|
stats.dfStdDev = dfStdDev;
|
|
stats.nValidCount = nValidCount;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ClearStatistics() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName)
|
|
{
|
|
Load();
|
|
d->m_bDirty = true;
|
|
d->m_oMapArray[osArrayFullName].stats.bHasStats = false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ClearStatistics() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamMultiDim::ClearStatistics()
|
|
{
|
|
Load();
|
|
d->m_bDirty = true;
|
|
for (auto &kv : d->m_oMapArray)
|
|
kv.second.stats.bHasStats = false;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMDArray */
|
|
/************************************************************************/
|
|
|
|
GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
|
|
const std::string &osName,
|
|
const std::shared_ptr<GDALPamMultiDim> &poPam)
|
|
:
|
|
#if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
|
|
GDALAbstractMDArray(osParentName, osName),
|
|
#endif
|
|
GDALMDArray(osParentName, osName), m_poPam(poPam)
|
|
{
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMDArray::SetSpatialRef() */
|
|
/************************************************************************/
|
|
|
|
bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
|
|
{
|
|
if (!m_poPam)
|
|
return false;
|
|
m_poPam->SetSpatialRef(GetFullName(), poSRS);
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPamMDArray::GetSpatialRef() */
|
|
/************************************************************************/
|
|
|
|
std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
|
|
{
|
|
if (!m_poPam)
|
|
return nullptr;
|
|
return m_poPam->GetSpatialRef(GetFullName());
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetStatistics() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
|
|
double *pdfMin, double *pdfMax,
|
|
double *pdfMean, double *pdfStdDev,
|
|
GUInt64 *pnValidCount,
|
|
GDALProgressFunc pfnProgress,
|
|
void *pProgressData)
|
|
{
|
|
if (m_poPam &&
|
|
m_poPam->GetStatistics(GetFullName(), bApproxOK, pdfMin, pdfMax,
|
|
pdfMean, pdfStdDev, pnValidCount) == CE_None)
|
|
{
|
|
return CE_None;
|
|
}
|
|
if (!bForce)
|
|
return CE_Warning;
|
|
|
|
return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
|
|
pdfMean, pdfStdDev, pnValidCount,
|
|
pfnProgress, pProgressData);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* SetStatistics() */
|
|
/************************************************************************/
|
|
|
|
bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
|
|
double dfMax, double dfMean, double dfStdDev,
|
|
GUInt64 nValidCount)
|
|
{
|
|
if (!m_poPam)
|
|
return false;
|
|
m_poPam->SetStatistics(GetFullName(), bApproxStats, dfMin, dfMax, dfMean,
|
|
dfStdDev, nValidCount);
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ClearStatistics() */
|
|
/************************************************************************/
|
|
|
|
void GDALPamMDArray::ClearStatistics()
|
|
{
|
|
if (!m_poPam)
|
|
return;
|
|
m_poPam->ClearStatistics(GetFullName());
|
|
}
|
|
|
|
//! @endcond
|