Skip to content

Commit

Permalink
ZARR multiidm: implement Rename() for dimension
Browse files Browse the repository at this point in the history
  • Loading branch information
rouault committed May 3, 2023
1 parent abca7d5 commit 555b3d1
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 10 deletions.
149 changes: 149 additions & 0 deletions autotest/gdrivers/zarr_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3943,3 +3943,152 @@ def reopen_after_rename():

finally:
gdal.RmdirRecursive(filename)


###############################################################################


@gdaltest.enable_exceptions()
@pytest.mark.parametrize("create_z_metadata", [True, False])
def test_zarr_multidim_rename_dim_at_creation(create_z_metadata):

drv = gdal.GetDriverByName("ZARR")
filename = "/vsimem/test.zarr"

def create():
ds = drv.CreateMultiDimensional(
filename,
options=["CREATE_ZMETADATA=" + ("YES" if create_z_metadata else "NO")],
)
rg = ds.GetRootGroup()
dim = rg.CreateDimension("dim", None, None, 2)
other_dim = rg.CreateDimension("other_dim", None, None, 2)
var = rg.CreateMDArray(
"var", [dim, other_dim], gdal.ExtendedDataType.Create(gdal.GDT_Int16)
)

# Empty name
with pytest.raises(Exception):
dim.Rename("")

# Existing name
with pytest.raises(Exception):
dim.Rename("other_dim")
assert dim.GetName() == "dim"
assert dim.GetFullName() == "/dim"

dim.Rename("dim_renamed")
assert dim.GetName() == "dim_renamed"
assert dim.GetFullName() == "/dim_renamed"

assert set(x.GetName() for x in rg.GetDimensions()) == {
"dim_renamed",
"other_dim",
}

assert [x.GetName() for x in var.GetDimensions()] == [
"dim_renamed",
"other_dim",
]

def reopen():
ds = gdal.OpenEx(filename, gdal.OF_MULTIDIM_RASTER)
rg = ds.GetRootGroup()

assert set(x.GetName() for x in rg.GetDimensions()) == {
"dim_renamed",
"other_dim",
}

# Read-only
with pytest.raises(Exception):
rg.GetDimensions()[0].Rename("dim_renamed2")

assert set(x.GetName() for x in rg.GetDimensions()) == {
"dim_renamed",
"other_dim",
}

try:
create()
reopen()
finally:
gdal.RmdirRecursive(filename)


###############################################################################


@gdaltest.enable_exceptions()
@pytest.mark.parametrize("create_z_metadata", [True, False])
def test_zarr_multidim_rename_dim_after_reopening(create_z_metadata):

drv = gdal.GetDriverByName("ZARR")
filename = "/vsimem/test.zarr"

def create():
ds = drv.CreateMultiDimensional(
filename,
options=["CREATE_ZMETADATA=" + ("YES" if create_z_metadata else "NO")],
)
rg = ds.GetRootGroup()
dim = rg.CreateDimension("dim", None, None, 2)
other_dim = rg.CreateDimension("other_dim", None, None, 2)
rg.CreateMDArray(
"var", [dim, other_dim], gdal.ExtendedDataType.Create(gdal.GDT_Int16)
)

def rename():
ds = gdal.OpenEx(filename, gdal.OF_MULTIDIM_RASTER | gdal.OF_UPDATE)
rg = ds.GetRootGroup()
dim = list(filter(lambda dim: dim.GetName() == "dim", rg.GetDimensions()))[0]

# Empty name
with pytest.raises(Exception):
dim.Rename("")

# Existing name
with pytest.raises(Exception):
dim.Rename("other_dim")
assert dim.GetName() == "dim"
assert dim.GetFullName() == "/dim"

dim.Rename("dim_renamed")
assert dim.GetName() == "dim_renamed"
assert dim.GetFullName() == "/dim_renamed"

assert set(x.GetName() for x in rg.GetDimensions()) == {
"dim_renamed",
"other_dim",
}

def reopen_after_rename():
ds = gdal.OpenEx(filename, gdal.OF_MULTIDIM_RASTER)
rg = ds.GetRootGroup()

assert set(x.GetName() for x in rg.GetDimensions()) == {
"dim_renamed",
"other_dim",
}

# Read-only
with pytest.raises(Exception):
rg.GetDimensions()[0].Rename("dim_renamed2")

assert set(x.GetName() for x in rg.GetDimensions()) == {
"dim_renamed",
"other_dim",
}

var = rg.OpenMDArray("var")
assert [x.GetName() for x in var.GetDimensions()] == [
"dim_renamed",
"other_dim",
]

try:
create()
rename()
reopen_after_rename()
finally:
gdal.RmdirRecursive(filename)
39 changes: 37 additions & 2 deletions frmts/zarr/zarr.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@ class ZarrSharedResource

~ZarrSharedResource();

bool IsUpdatable() const
{
return m_bUpdatable;
}

void EnableZMetadata()
{
m_bZMetadataEnabled = true;
Expand Down Expand Up @@ -320,6 +325,11 @@ class ZarrGroupBase CPL_NON_FINAL : public GDALGroup
{
}

protected:
friend class ZarrDimension;
bool RenameDimension(const std::string &osOldName,
const std::string &osNewName);

public:
~ZarrGroupBase() override;

Expand Down Expand Up @@ -528,17 +538,42 @@ class ZarrGroupV3 final : public ZarrGroupBase

class ZarrDimension final : public GDALDimensionWeakIndexingVar
{
const bool m_bUpdatable;
std::weak_ptr<ZarrGroupBase> m_poParentGroup;
bool m_bModified = false;
bool m_bXArrayDim = false;

public:
ZarrDimension(const std::string &osParentName, const std::string &osName,
ZarrDimension(const std::shared_ptr<ZarrSharedResource> &poSharedResource,
const std::weak_ptr<ZarrGroupBase> &poParentGroup,
const std::string &osParentName, const std::string &osName,
const std::string &osType, const std::string &osDirection,
GUInt64 nSize)
: GDALDimensionWeakIndexingVar(osParentName, osName, osType,
osDirection, nSize)
osDirection, nSize),
m_bUpdatable(poSharedResource->IsUpdatable()),
m_poParentGroup(poParentGroup)
{
}

bool Rename(const std::string &osNewName) override;

void ParentRenamed(const std::string &osNewParentFullName) override;

bool IsModified() const
{
return m_bModified;
}

void SetXArrayDimension()
{
m_bXArrayDim = true;
}

bool IsXArrayDimension() const
{
return m_bXArrayDim;
}
};

/************************************************************************/
Expand Down
20 changes: 14 additions & 6 deletions frmts/zarr/zarr_array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,17 @@ void ZarrArray::Flush()
}

CPLJSONArray j_ARRAY_DIMENSIONS;
bool bDimensionsModified = false;
if (!m_aoDims.empty())
{
for (const auto &poDim : m_aoDims)
{
if (dynamic_cast<const ZarrArray *>(
poDim->GetIndexingVariable().get()) != nullptr)
const auto poZarrDim =
dynamic_cast<const ZarrDimension *>(poDim.get());
if (poZarrDim && poZarrDim->IsXArrayDimension())
{
if (poZarrDim->IsModified())
bDimensionsModified = true;
j_ARRAY_DIMENSIONS.Add(poDim->GetName());
}
else
Expand All @@ -237,7 +241,7 @@ void ZarrArray::Flush()
}

CPLJSONObject oAttrs;
if (m_oAttrGroup.IsModified() ||
if (m_oAttrGroup.IsModified() || bDimensionsModified ||
(m_bNew && j_ARRAY_DIMENSIONS.Size() != 0) || m_bUnitModified ||
m_bOffsetModified || m_bScaleModified || m_bSRSModified)
{
Expand Down Expand Up @@ -3331,8 +3335,8 @@ ZarrGroupBase::LoadArray(const std::string &osArrayName,
return nullptr;
}
aoDims.emplace_back(std::make_shared<ZarrDimension>(
std::string(), CPLSPrintf("dim%d", i), std::string(), std::string(),
nSize));
m_poSharedResource, m_pSelf, std::string(), CPLSPrintf("dim%d", i),
std::string(), std::string(), nSize));
}

const auto GetDimensionTypeDirection =
Expand Down Expand Up @@ -3498,7 +3502,9 @@ ZarrGroupBase::LoadArray(const std::string &osArrayName,
}

auto poDimLocal = std::make_shared<ZarrDimension>(
GetFullName(), osDimName, osType, osDirection, poDim->GetSize());
m_poSharedResource, m_pSelf, GetFullName(), osDimName, osType,
osDirection, poDim->GetSize());
poDimLocal->SetXArrayDimension();
m_oMapDimensions[osDimName] = poDimLocal;
poDim = poDimLocal;
return true;
Expand Down Expand Up @@ -3600,6 +3606,8 @@ ZarrGroupBase::LoadArray(const std::string &osArrayName,
{
auto poDimLocal =
std::make_shared<ZarrDimension>(
m_poSharedResource,
poDimParentGroup->m_pSelf,
poDimParentGroup->GetFullName(),
poDim->GetName(), osType,
osDirection, poDim->GetSize());
Expand Down
67 changes: 66 additions & 1 deletion frmts/zarr/zarr_group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,35 @@ std::shared_ptr<GDALDimension> ZarrGroupBase::CreateDimension(
"A dimension with same name already exists");
return nullptr;
}
auto newDim(std::make_shared<ZarrDimension>(GetFullName(), osName, osType,
auto newDim(std::make_shared<ZarrDimension>(m_poSharedResource, m_pSelf,
GetFullName(), osName, osType,
osDirection, nSize));
newDim->SetXArrayDimension();
m_oMapDimensions[osName] = newDim;
return newDim;
}

/************************************************************************/
/* RenameDimension() */
/************************************************************************/

bool ZarrGroupBase::RenameDimension(const std::string &osOldName,
const std::string &osNewName)
{
if (m_oMapDimensions.find(osNewName) != m_oMapDimensions.end())
{
CPLError(CE_Failure, CPLE_AppDefined,
"A dimension with same name already exists");
return false;
}
auto oIter = m_oMapDimensions.find(osOldName);
auto poDim = oIter->second;
CPLAssert(oIter != m_oMapDimensions.end());
m_oMapDimensions.erase(oIter);
m_oMapDimensions[osNewName] = poDim;
return true;
}

/************************************************************************/
/* ZarrGroupBase::UpdateDimensionSize() */
/************************************************************************/
Expand Down Expand Up @@ -2295,6 +2318,48 @@ void ZarrSharedResource::UpdateDimensionSize(
poRG.reset();
}

/************************************************************************/
/* Rename() */
/************************************************************************/

bool ZarrDimension::Rename(const std::string &osNewName)
{
if (!m_bUpdatable)
{
CPLError(CE_Failure, CPLE_NotSupported,
"Dataset not open in update mode");
return false;
}
if (!IsXArrayDimension())
{
CPLError(CE_Failure, CPLE_NotSupported,
"Cannot rename an implicit dimension "
"(that is one listed in _ARRAY_DIMENSIONS attribute)");
return false;
}
if (!ZarrGroupBase::IsValidObjectName(osNewName))
{
CPLError(CE_Failure, CPLE_NotSupported, "Invalid dimension name");
return false;
}

if (auto poParentGroup = m_poParentGroup.lock())
{
if (!poParentGroup->RenameDimension(m_osName, osNewName))
{
return false;
}
}

m_osFullName.resize(m_osFullName.size() - m_osName.size());
m_osFullName += osNewName;
m_osName = osNewName;

m_bModified = true;

return true;
}

/************************************************************************/
/* ParentRenamed() */
/************************************************************************/
Expand Down
2 changes: 1 addition & 1 deletion gcore/gdalmultidim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8894,7 +8894,7 @@ bool GDALDimension::SetIndexingVariable(
*
* This is not implemented by all drivers.
*
* Drivers known to implement it: MEM, netCDF.
* Drivers known to implement it: MEM, netCDF, ZARR.
*
* This is the same as the C function GDALDimensionRename().
*
Expand Down

0 comments on commit 555b3d1

Please sign in to comment.