Skip to content

Commit

Permalink
GDALMDArray::AsClassicDataset(): allow to set band IMAGERY metadata d…
Browse files Browse the repository at this point in the history
…omain

Add a BAND_IMAGERY_METADATA options to papszOptions:

JSON serialized object defining which arrays of the
poRootGroup, indexed by non-X and Y dimensions,
should be mapped as band metadata items in the
band IMAGERY domain.
The object currently accepts 2 members:
- "CENTRAL_WAVELENGTH_UM": Central Wavelength in
  micrometers.
- "FWHM_UM": Full-width half-maximum
  in micrometers.

The value of each member should be an object with the
following members:
- "array": (required) full name of a band parameter
  array.
  Such array must be a one dimensional array, and its
  dimension must be one of the dimensions of the
  array on which the method is called
  (excluding the X and Y dimensons).
- "unit": (optional) unit of the values pointed in
  the above array.
  Can be a literal string or a string of the form
  "${attribute_name}" to point to an attribute for
  the array.
  Accepted values are "um", "micrometer"
  (with UK vs US spelling, singular or plural), "nm",
  "nanometer" (with UK vs US spelling, singular or
  plural)
  If not provided, micrometer is assumed.

Example for EMIT datasets:
```json
{
   "CENTRAL_WAVELENGTH_UM": {
       "array": "/sensor_band_parameters/wavelengths",
       "unit": "${units}"
   },
   "FWHM_UM": {
       "array": "/sensor_band_parameters/fwhm",
       "unit": "${units}"
   }
}
```
  • Loading branch information
rouault committed Jan 3, 2025
1 parent 3fc05d4 commit d4779d4
Show file tree
Hide file tree
Showing 2 changed files with 396 additions and 18 deletions.
130 changes: 130 additions & 0 deletions autotest/gcore/multidim.py
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,136 @@ def test_multidim_asclassicsubdataset_band_metadata():
}


@gdaltest.enable_exceptions()
def test_multidim_asclassicsubdataset_band_imagery_metadata():

drv = gdal.GetDriverByName("MEM")
mem_ds = drv.CreateMultiDimensional("myds")
rg = mem_ds.GetRootGroup()

dimOther = rg.CreateDimension("other", None, None, 2)

other = rg.CreateMDArray(
"other", [dimOther], gdal.ExtendedDataType.Create(gdal.GDT_Float64)
)
other.Write(array.array("d", [10.5, 20]))

fwhm = rg.CreateMDArray(
"fwhm", [dimOther], gdal.ExtendedDataType.Create(gdal.GDT_Float64)
)
fwhm.Write(array.array("d", [3.5, 4.5]))

string_attr = other.CreateAttribute(
"string_attr", [], gdal.ExtendedDataType.CreateString()
)
assert string_attr.Write("nm") == gdal.CE_None

dimX = rg.CreateDimension("X", None, None, 2)
X = rg.CreateMDArray("X", [dimX], gdal.ExtendedDataType.Create(gdal.GDT_Float64))
X.Write(array.array("d", [10, 20]))
dimX.SetIndexingVariable(X)
dimY = rg.CreateDimension("Y", None, None, 2)

ar = rg.CreateMDArray(
"ar", [dimOther, dimY, dimX], gdal.ExtendedDataType.Create(gdal.GDT_Float64)
)

with pytest.raises(
Exception,
match="Root group should be provided when BAND_IMAGERY_METADATA is set",
):
ar.AsClassicDataset(2, 1, None, ["BAND_IMAGERY_METADATA={}"])

with pytest.raises(
Exception, match="Invalid JSON content for BAND_IMAGERY_METADATA"
):
ar.AsClassicDataset(2, 1, rg, ["BAND_IMAGERY_METADATA=invalid"])

with pytest.raises(
Exception, match="Value of BAND_IMAGERY_METADATA should be an object"
):
ar.AsClassicDataset(2, 1, rg, ["BAND_IMAGERY_METADATA=false"])

band_metadata = {"CENTRAL_WAVELENGTH_UM": {}}
with pytest.raises(
Exception,
match=r'BAND_IMAGERY_METADATA\["CENTRAL_WAVELENGTH_UM"\]\["array"\] is missing',
):
ds = ar.AsClassicDataset(
2, 1, rg, ["BAND_IMAGERY_METADATA=" + json.dumps(band_metadata)]
)

band_metadata = {"CENTRAL_WAVELENGTH_UM": {"array": "/i/do/not/exist"}}
with pytest.raises(Exception, match="Array /i/do/not/exist cannot be found"):
ds = ar.AsClassicDataset(
2, 1, rg, ["BAND_IMAGERY_METADATA=" + json.dumps(band_metadata)]
)

band_metadata = {"CENTRAL_WAVELENGTH_UM": {"array": "/ar"}}
with pytest.raises(Exception, match="Array /ar is not a 1D array"):
ds = ar.AsClassicDataset(
2, 1, rg, ["BAND_IMAGERY_METADATA=" + json.dumps(band_metadata)]
)

band_metadata = {"CENTRAL_WAVELENGTH_UM": {"array": "/X"}}
with pytest.raises(
Exception,
match='Dimension "X" of array "/X" is not a non-X/Y dimension of array "ar"',
):
ds = ar.AsClassicDataset(
2, 1, rg, ["BAND_IMAGERY_METADATA=" + json.dumps(band_metadata)]
)

band_metadata = {"CENTRAL_WAVELENGTH_UM": {"array": "/other", "unit": "${invalid"}}
with pytest.raises(
Exception,
match=r'Value of BAND_IMAGERY_METADATA\["CENTRAL_WAVELENGTH_UM"\]\["unit"\] = \${invalid is invalid',
):
ds = ar.AsClassicDataset(
2, 1, rg, ["BAND_IMAGERY_METADATA=" + json.dumps(band_metadata)]
)

band_metadata = {"CENTRAL_WAVELENGTH_UM": {"array": "/other", "unit": "${invalid}"}}
with pytest.raises(
Exception,
match=r'Value of BAND_IMAGERY_METADATA\["CENTRAL_WAVELENGTH_UM"\]\["unit"\] = \${invalid} is invalid: invalid is not an attribute of \/other',
):
ds = ar.AsClassicDataset(
2, 1, rg, ["BAND_IMAGERY_METADATA=" + json.dumps(band_metadata)]
)

band_metadata = {"CENTRAL_WAVELENGTH_UM": {"array": "/other", "unit": "unhandled"}}
with pytest.raises(
Exception,
match=r'Unhandled value for BAND_IMAGERY_METADATA\["CENTRAL_WAVELENGTH_UM"\]\["unit"\] = unhandled',
):
ds = ar.AsClassicDataset(
2, 1, rg, ["BAND_IMAGERY_METADATA=" + json.dumps(band_metadata)]
)

band_metadata = {"ignored": {}}
with gdal.quiet_errors():
ar.AsClassicDataset(
2, 1, rg, ["BAND_IMAGERY_METADATA=" + json.dumps(band_metadata)]
)

band_metadata = {
"CENTRAL_WAVELENGTH_UM": {"array": "/other", "unit": "${string_attr}"},
"FWHM_UM": {"array": "/fwhm"},
}
ds = ar.AsClassicDataset(
2, 1, rg, ["BAND_IMAGERY_METADATA=" + json.dumps(band_metadata)]
)
assert ds.GetRasterBand(1).GetMetadata("IMAGERY") == {
"CENTRAL_WAVELENGTH_UM": "0.0105",
"FWHM_UM": "3.5",
}
assert ds.GetRasterBand(2).GetMetadata("IMAGERY") == {
"CENTRAL_WAVELENGTH_UM": "0.02",
"FWHM_UM": "4.5",
}


@gdaltest.enable_exceptions()
def test_multidim_SubsetDimensionFromSelection():

Expand Down
Loading

0 comments on commit d4779d4

Please sign in to comment.