Skip to content

Commit

Permalink
Add GDALCreateRasterAttributeTableFromMDArrays() to return a virtual …
Browse files Browse the repository at this point in the history
…Raster Attribute Table from several GDALMDArray's
  • Loading branch information
rouault committed Nov 14, 2023
1 parent ec439b3 commit ac436f6
Show file tree
Hide file tree
Showing 9 changed files with 797 additions and 59 deletions.
172 changes: 172 additions & 0 deletions autotest/gcore/multidim.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,3 +968,175 @@ def test_multidim_SubsetDimensionFromSelection():
assert indexed_twice_by_band_subset.Read(
array_start_idx=[1, 0, 0], array_step=[-1, 1, 1]
) == array.array("d", [12.0, 14.0, 15.0, 17.0, 0.0, 2.0, 3.0, 5.0])


@gdaltest.enable_exceptions()
def test_multidim_CreateRasterAttributeTableFromMDArrays():

drv = gdal.GetDriverByName("MEM")
mem_ds = drv.CreateMultiDimensional("myds")
rg = mem_ds.GetRootGroup()
dim = rg.CreateDimension("dim", None, None, 2)
other_dim = rg.CreateDimension("other_dim", None, None, 2)
ar_double = rg.CreateMDArray(
"ar_double", [dim], gdal.ExtendedDataType.Create(gdal.GDT_Float64)
)
ar_int = rg.CreateMDArray(
"ar_int", [dim], gdal.ExtendedDataType.Create(gdal.GDT_Int32)
)
ar_string = rg.CreateMDArray(
"ar_string", [dim], gdal.ExtendedDataType.CreateString()
)
ar_other_dim = rg.CreateMDArray(
"ar_other_dim", [other_dim], gdal.ExtendedDataType.Create(gdal.GDT_Float64)
)
ar_2D = rg.CreateMDArray(
"ar_2D", [dim, dim], gdal.ExtendedDataType.Create(gdal.GDT_Float64)
)

with pytest.raises(Exception, match="apoArrays should not be empty"):
gdal.CreateRasterAttributeTableFromMDArrays(gdal.GRTT_ATHEMATIC, [])
with pytest.raises(Exception, match="object of wrong GDALMDArrayHS"):
gdal.CreateRasterAttributeTableFromMDArrays(gdal.GRTT_ATHEMATIC, [None])
with pytest.raises(Exception, match="not a sequence"):
gdal.CreateRasterAttributeTableFromMDArrays(gdal.GRTT_ATHEMATIC, 1)
with pytest.raises(Exception, match="bject of wrong GDALMDArrayHS"):
gdal.CreateRasterAttributeTableFromMDArrays(gdal.GRTT_ATHEMATIC, [dim])
with pytest.raises(Exception, match=r"apoArrays\[0\] has a dimension count != 1"):
gdal.CreateRasterAttributeTableFromMDArrays(gdal.GRTT_ATHEMATIC, [ar_2D])
with pytest.raises(
Exception,
match=r"apoArrays\[1\] does not have the same dimension has apoArrays\[0\]",
):
gdal.CreateRasterAttributeTableFromMDArrays(
gdal.GRTT_ATHEMATIC, [ar_double, ar_other_dim]
)
with pytest.raises(Exception, match="nUsages != nArrays"):
gdal.CreateRasterAttributeTableFromMDArrays(
gdal.GRTT_ATHEMATIC, [ar_double], [gdal.GFU_Generic, gdal.GFU_Generic]
)
with pytest.raises(Exception, match="not a sequence"):
gdal.CreateRasterAttributeTableFromMDArrays(gdal.GRTT_ATHEMATIC, [ar_double], 1)
with pytest.raises(Exception, match="not a valid GDALRATFieldUsage"):
gdal.CreateRasterAttributeTableFromMDArrays(
gdal.GRTT_ATHEMATIC, [ar_double], [None]
)
with pytest.raises(Exception, match="not a valid GDALRATFieldUsage"):
gdal.CreateRasterAttributeTableFromMDArrays(
gdal.GRTT_ATHEMATIC, [ar_double], [-1]
)
with pytest.raises(Exception, match="not a valid GDALRATFieldUsage"):
gdal.CreateRasterAttributeTableFromMDArrays(
gdal.GRTT_ATHEMATIC, [ar_double], [gdal.GFU_MaxCount]
)

rat = gdal.CreateRasterAttributeTableFromMDArrays(
gdal.GRTT_ATHEMATIC, [ar_double, ar_int, ar_string]
)
assert rat.GetTableType() == gdal.GRTT_ATHEMATIC
assert rat.GetColumnCount() == 3
assert rat.GetRowCount() == 2
assert rat.Clone().GetColumnCount() == 3
assert rat.GetNameOfCol(-1) is None
assert rat.GetNameOfCol(rat.GetColumnCount()) is None
assert rat.GetNameOfCol(0) == "ar_double"
assert rat.GetUsageOfCol(-1) == gdal.GFU_Generic
assert rat.GetUsageOfCol(0) == gdal.GFU_Generic
assert rat.GetUsageOfCol(rat.GetColumnCount()) == gdal.GFU_Generic
assert rat.GetTypeOfCol(-1) == gdal.GFT_Integer
assert rat.GetTypeOfCol(rat.GetColumnCount()) == gdal.GFT_Integer
assert rat.GetTypeOfCol(0) == gdal.GFT_Real
assert rat.GetTypeOfCol(1) == gdal.GFT_Integer
assert rat.GetTypeOfCol(2) == gdal.GFT_String

ar_double.Write([0.5, 1.5])

icol = 0
assert rat.GetValueAsDouble(-1, icol) == 0
assert rat.GetValueAsDouble(0, icol) == 0.5
assert rat.GetValueAsDouble(1, icol) == 1.5
assert rat.GetValueAsDouble(2, icol) == 0

assert rat.ReadValuesIOAsDouble(icol, 0, 2) == [0.5, 1.5]
with pytest.raises(Exception, match="Invalid iStartRow/iLength"):
rat.ReadValuesIOAsDouble(icol, -1, 1)
with pytest.raises(Exception, match="Invalid iStartRow/iLength"):
rat.ReadValuesIOAsDouble(icol, 0, rat.GetRowCount() + 1)
with pytest.raises(Exception, match="invalid length"):
rat.ReadValuesIOAsDouble(icol, 0, -1)
with pytest.raises(Exception, match="Invalid iField"):
rat.ReadValuesIOAsDouble(-1, 0, 1)
with pytest.raises(Exception, match="Invalid iField"):
rat.ReadValuesIOAsDouble(rat.GetColumnCount(), 0, 1)

ar_int.Write([1, 2])

icol = 1
assert rat.GetValueAsInt(-1, icol) == 0
assert rat.GetValueAsInt(0, icol) == 1
assert rat.GetValueAsInt(1, icol) == 2
assert rat.GetValueAsInt(2, icol) == 0

assert rat.ReadValuesIOAsInteger(icol, 0, 2) == [1, 2]
with pytest.raises(Exception, match="Invalid iStartRow/iLength"):
rat.ReadValuesIOAsInteger(icol, -1, 1)
with pytest.raises(Exception, match="Invalid iStartRow/iLength"):
rat.ReadValuesIOAsInteger(icol, 0, rat.GetRowCount() + 1)
with pytest.raises(Exception, match="invalid length"):
rat.ReadValuesIOAsInteger(icol, 0, -1)
with pytest.raises(Exception, match="Invalid iField"):
rat.ReadValuesIOAsInteger(-1, 0, 1)
with pytest.raises(Exception, match="Invalid iField"):
rat.ReadValuesIOAsInteger(rat.GetColumnCount(), 0, 1)

ar_string.Write(["foo", "bar"])

icol = 2
assert rat.GetValueAsString(-1, icol) is None
assert rat.GetValueAsString(0, icol) == "foo"
assert rat.GetValueAsString(1, icol) == "bar"
assert rat.GetValueAsString(2, icol) is None

assert rat.ReadValuesIOAsString(icol, 0, 2) == ["foo", "bar"]
with pytest.raises(Exception, match="Invalid iStartRow/iLength"):
rat.ReadValuesIOAsString(icol, -1, 1)
with pytest.raises(Exception, match="Invalid iStartRow/iLength"):
rat.ReadValuesIOAsString(icol, 0, rat.GetRowCount() + 1)
with pytest.raises(Exception, match="invalid length"):
rat.ReadValuesIOAsString(icol, 0, -1)
with pytest.raises(Exception, match="Invalid iField"):
rat.ReadValuesIOAsString(-1, 0, 1)
with pytest.raises(Exception, match="Invalid iField"):
rat.ReadValuesIOAsString(rat.GetColumnCount(), 0, 1)

with pytest.raises(
Exception,
match=r"GDALRasterAttributeTableFromMDArrays::SetValue\(\): not supported",
):
rat.SetValueAsString(0, 0, "foo")
with pytest.raises(
Exception,
match=r"GDALRasterAttributeTableFromMDArrays::SetValue\(\): not supported",
):
rat.SetValueAsInt(0, 0, 0)
with pytest.raises(
Exception,
match=r"GDALRasterAttributeTableFromMDArrays::SetValue\(\): not supported",
):
rat.SetValueAsDouble(0, 0, 0.5)
assert rat.ChangesAreWrittenToFile() == False
with pytest.raises(
Exception,
match=r"GDALRasterAttributeTableFromMDArrays::SetTableType\(\): not supported",
):
rat.SetTableType(gdal.GRTT_ATHEMATIC)
rat.RemoveStatistics()

rat = gdal.CreateRasterAttributeTableFromMDArrays(
gdal.GRTT_ATHEMATIC,
[ar_double, ar_int, ar_string],
[gdal.GFU_Generic, gdal.GFU_PixelCount, gdal.GFU_Generic],
)
assert rat.GetUsageOfCol(1) == gdal.GFU_PixelCount
assert rat.GetColOfUsage(gdal.GFU_PixelCount) == 1
assert rat.GetColOfUsage(gdal.GFU_Min) == -1
1 change: 1 addition & 0 deletions gcore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ add_library(
gdalmultidim_gridded.cpp
gdalmultidim_gltorthorectification.cpp
gdalmultidim_subsetdimension.cpp
gdalmultidim_rat.cpp
gdalpython.cpp
gdalpythondriverloader.cpp
tilematrixset.cpp
Expand Down
4 changes: 4 additions & 0 deletions gcore/gdal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2347,6 +2347,10 @@ void CPL_DLL GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount);
int CPL_DLL GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions);
bool CPL_DLL GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName);

GDALRasterAttributeTableH CPL_DLL GDALCreateRasterAttributeTableFromMDArrays(
GDALRATTableType eTableType, int nArrays, const GDALMDArrayH *ahArrays,
const GDALRATFieldUsage *paeUsages);

void CPL_DLL GDALAttributeRelease(GDALAttributeH hAttr);
void CPL_DLL GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount);
const char CPL_DLL *GDALAttributeGetName(GDALAttributeH hAttr);
Expand Down
5 changes: 5 additions & 0 deletions gcore/gdal_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -3857,6 +3857,11 @@ std::string CPL_DLL GDALGetCompressionFormatForJPEG(VSILFILE *fp);
std::string CPL_DLL GDALGetCompressionFormatForJPEG(const void *pBuffer,
size_t nBufferSize);

GDALRasterAttributeTable CPL_DLL *GDALCreateRasterAttributeTableFromMDArrays(
GDALRATTableType eTableType,
const std::vector<std::shared_ptr<GDALMDArray>> &apoArrays,
const std::vector<GDALRATFieldUsage> &aeUsages);

//! @endcond

#endif /* ndef GDAL_PRIV_H_INCLUDED */
60 changes: 1 addition & 59 deletions gcore/gdalmultidim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include "cpl_safemaths.hpp"
#include "memmultidim.h"
#include "ogrsf_frmts.h"
#include "gdalmultidim_priv.h"

#if defined(__clang__) || defined(_MSC_VER)
#define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
Expand Down Expand Up @@ -10237,65 +10238,6 @@ void GDALDimension::ParentDeleted()
/************************************************************************/
/************************************************************************/

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() */
/************************************************************************/
Expand Down
95 changes: 95 additions & 0 deletions gcore/gdalmultidim_priv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/******************************************************************************
* Name: gdalmultidim_priv.h
* Project: GDAL Core
* Purpose: GDAL private header for multidimensional support
* Author: Even Rouault <even.rouault at spatialys.com>
*
******************************************************************************
* Copyright (c) 2023, 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.
****************************************************************************/

#ifndef GDALMULTIDIM_PRIV_INCLUDED
#define GDALMULTIDIM_PRIV_INCLUDED

#include "gdal_priv.h"

// For 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)
{
}
};

#endif // GDALMULTIDIM_PRIV_INCLUDED
Loading

0 comments on commit ac436f6

Please sign in to comment.