#!/usr/bin/env pytest ############################################################################### # $Id$ # # Project: GDAL/OGR Test Suite # Purpose: Test multidimensional support in HDF4 driver # Author: Even Rouault # ############################################################################### # Copyright (c) 2019, Even Rouault # # 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. ############################################################################### import struct import gdaltest import pytest from osgeo import gdal pytestmark = pytest.mark.require_driver("HDF4") ############################################################################### @pytest.fixture(autouse=True, scope="module") def module_disable_exceptions(): with gdaltest.disable_exceptions(): yield ############################################################################### # Test reading HDFEOS SWATH products def test_hdf4multidim_hdfeos_swath(): gdaltest.download_or_skip( "https://gamma.hdfgroup.org/ftp/pub/outgoing/NASAHDFfiles2/eosweb/hdf4/hdfeos2-swath-wo-dimmaps/AMSR_E_L2_Ocean_B01_200206182340_A.hdf", "AMSR_E_L2_Ocean_B01_200206182340_A.hdf", ) ds = gdal.OpenEx( "tmp/cache/AMSR_E_L2_Ocean_B01_200206182340_A.hdf", gdal.OF_MULTIDIM_RASTER ) assert ds rg = ds.GetRootGroup() assert rg assert rg.GetGroupNames() == ["swaths"] attrs = rg.GetAttributes() assert attrs attr = rg.GetAttribute("HDFEOSVersion") assert attr.Read() == "HDFEOS_V2.7.2" swaths = rg.OpenGroup("swaths") assert swaths assert not rg.OpenGroup("foo") assert swaths.GetGroupNames() == ["Swath1"] swath1 = swaths.OpenGroup("Swath1") assert swath1 assert not swaths.OpenGroup("foo") attrs = swath1.GetAttributes() assert len(attrs) == 3 attr = swath1.GetAttribute("SoftwareRevisionDate") assert attr.Read()[0:-1] == "November 7, 2003" attr = swath1.GetAttribute("SoftwareBuildNumber") assert attr.Read() == 1 dims = swath1.GetDimensions() assert len(dims) == 4 dim = next((x for x in dims if x.GetName() == "DataXtrack_lo"), None) assert dim assert dim.GetFullName() == "/swaths/Swath1/DataXtrack_lo" assert dim.GetSize() == 196 assert swath1.GetGroupNames() == ["Data Fields", "Geolocation Fields"] assert not swath1.OpenGroup("foo") datafields = swath1.OpenGroup("Data Fields") assert datafields assert len(datafields.GetMDArrayNames()) == 10 assert not datafields.OpenMDArray("foo") array = datafields.OpenMDArray("High_res_cloud") assert array dims = array.GetDimensions() assert len(dims) == 2 assert dims[0].GetFullName() == "/swaths/Swath1/DataTrack_lo" assert array.GetDataType().GetNumericDataType() == gdal.GDT_Int16 attr = array.GetAttribute("Scale") assert attr.Read() == 9.999999747378752e-05 got_data = array.Read(array_start_idx=[13, 0], count=[3, 2]) assert len(got_data) == 3 * 2 * 2 assert struct.unpack("h" * 6, got_data) == (0, 0, 17318, 17317, 17318, 17317) got_data = array.Read( array_start_idx=[13, 0], count=[3, 2], buffer_datatype=gdal.ExtendedDataType.Create(gdal.GDT_Int32), ) assert len(got_data) == 3 * 2 * 4 assert struct.unpack("i" * 6, got_data) == (0, 0, 17318, 17317, 17318, 17317) got_data = array.Read(array_start_idx=[13, 0], count=[3, 2], array_step=[2, 1]) assert len(got_data) == 3 * 2 * 2 assert struct.unpack("h" * 6, got_data) == (0, 0, 17318, 17317, 17317, 17317) got_data = array.Read(array_start_idx=[15, 1], count=[3, 2], array_step=[-1, -1]) assert len(got_data) == 3 * 2 * 2 assert struct.unpack("h" * 6, got_data) == (17317, 17318, 17317, 17318, 0, 0) got_data = array.Read(array_start_idx=[13, 0], count=[3, 2], buffer_stride=[1, 3]) assert len(got_data) == 3 * 2 * 2 assert struct.unpack("h" * 6, got_data) == (0, 17318, 17318, 0, 17317, 17317) ds = gdal.OpenEx( "tmp/cache/AMSR_E_L2_Ocean_B01_200206182340_A.hdf", gdal.OF_MULTIDIM_RASTER, open_options=["LIST_SDS=YES"], ) rg = ds.GetRootGroup() assert rg.GetGroupNames() == ["swaths", "scientific_datasets"] ############################################################################### # Test reading HDFEOS GRID products def test_hdf4multidim_hdfeos_grid(): gdaltest.download_or_skip( "http://download.osgeo.org/gdal/data/hdf4/MOD09A1.A2010041.h06v03.005.2010051001103.hdf", "MOD09A1.A2010041.h06v03.005.2010051001103.hdf", ) ds = gdal.OpenEx( "tmp/cache/MOD09A1.A2010041.h06v03.005.2010051001103.hdf", gdal.OF_MULTIDIM_RASTER, ) assert ds rg = ds.GetRootGroup() assert rg.GetGroupNames() == ["eos_grids"] attrs = rg.GetAttributes() assert attrs attr = rg.GetAttribute("HDFEOSVersion") assert attr.Read() == "HDFEOS_V2.9" eos_grids = rg.OpenGroup("eos_grids") assert eos_grids assert not rg.OpenGroup("foo") assert eos_grids.GetGroupNames() == ["MOD_Grid_500m_Surface_Reflectance"] MOD_Grid_500m_Surface_Reflectance = eos_grids.OpenGroup( "MOD_Grid_500m_Surface_Reflectance" ) assert MOD_Grid_500m_Surface_Reflectance assert not eos_grids.OpenGroup("foo") attrs = MOD_Grid_500m_Surface_Reflectance.GetAttributes() assert len(attrs) == 0 assert MOD_Grid_500m_Surface_Reflectance.GetGroupNames() == ["Data Fields"] assert not MOD_Grid_500m_Surface_Reflectance.OpenGroup("foo") datafields = MOD_Grid_500m_Surface_Reflectance.OpenGroup("Data Fields") assert datafields assert len(datafields.GetMDArrayNames()) == 13 assert not datafields.OpenMDArray("foo") array = datafields.OpenMDArray("sur_refl_b01") assert array dims = array.GetDimensions() assert len(dims) == 2 assert dims[0].GetFullName() == "/eos_grids/MOD_Grid_500m_Surface_Reflectance/YDim" assert dims[0].GetSize() == 2400 assert dims[1].GetFullName() == "/eos_grids/MOD_Grid_500m_Surface_Reflectance/XDim" assert dims[1].GetSize() == 2400 assert array.GetDataType().GetNumericDataType() == gdal.GDT_Int16 assert array.GetNoDataValueAsDouble() == -28672.0 assert array.GetOffset() == 0 assert array.GetScale() == 0.0001 assert array.GetUnit() == "reflectance" attr = array.GetAttribute("valid_range") assert attr.Read() == (-100, 16000) assert array.GetSpatialRef() got_data = array.Read(array_start_idx=[2398, 2398], count=[2, 2]) assert len(got_data) == 2 * 2 * 2 assert struct.unpack("h" * 4, got_data) == (-24, 0, -15, -22) dims = MOD_Grid_500m_Surface_Reflectance.GetDimensions() assert len(dims) == 2 assert dims[0].GetName() == "YDim" assert dims[0].GetIndexingVariable() assert dims[1].GetName() == "XDim" assert dims[1].GetIndexingVariable() assert MOD_Grid_500m_Surface_Reflectance.GetMDArrayNames() == ["YDim", "XDim"] XDim = MOD_Grid_500m_Surface_Reflectance.OpenMDArray("XDim") assert XDim YDim = MOD_Grid_500m_Surface_Reflectance.OpenMDArray("YDim") assert YDim assert not MOD_Grid_500m_Surface_Reflectance.OpenMDArray("foo") ds = gdal.OpenEx( "tmp/cache/MOD09A1.A2010041.h06v03.005.2010051001103.hdf", gdal.OF_MULTIDIM_RASTER, open_options=["LIST_SDS=YES"], ) rg = ds.GetRootGroup() assert rg.GetGroupNames() == ["eos_grids", "scientific_datasets"] ############################################################################### # Test reading GDAL SDS 2D def test_hdf4multidim_gdal_sds_2d(): ds = gdal.OpenEx("data/byte_2.hdf", gdal.OF_MULTIDIM_RASTER) assert ds rg = ds.GetRootGroup() assert rg.GetGroupNames() is None assert rg.OpenGroup("scientific_datasets") is None assert rg.GetMDArrayNames() == ["Band0", "X", "Y"] dims = rg.GetDimensions() assert len(dims) == 2 array = rg.OpenMDArray("Band0") assert array dims = array.GetDimensions() assert len(dims) == 2 assert dims[0].GetFullName() == "/Y" assert dims[1].GetFullName() == "/X" assert array.GetDataType().GetNumericDataType() == gdal.GDT_Byte got_data = array.Read(array_start_idx=[0, 0], count=[2, 2]) assert len(got_data) == 2 * 2 assert struct.unpack("B" * 4, got_data) == (107, 123, 115, 132) assert array.GetSpatialRef() assert array.GetUnit() == "" assert not array.GetOffset() assert not array.GetScale() assert not array.GetNoDataValueAsDouble() X = dims[0].GetIndexingVariable() assert X assert struct.unpack("d" * 2, X.Read(array_start_idx=[0], count=[2])) == ( 3751290.0, 3751230.0, ) Y = dims[1].GetIndexingVariable() assert Y assert struct.unpack("d" * 2, Y.Read(array_start_idx=[0], count=[2])) == ( 440750.0, 440810.0, ) ############################################################################### # Test reading GDAL SDS 3D def test_hdf4multidim_gdal_sds_3d(): ds = gdal.OpenEx("data/byte_3.hdf", gdal.OF_MULTIDIM_RASTER) assert ds rg = ds.GetRootGroup() assert rg.GetGroupNames() is None assert rg.OpenGroup("scientific_datasets") is None assert rg.GetMDArrayNames() == ["3-dimensional Scientific Dataset", "X", "Y"] dims = rg.GetDimensions() assert len(dims) == 3 array = rg.OpenMDArray("3-dimensional Scientific Dataset") assert array dims = array.GetDimensions() assert len(dims) == 3 assert dims[0].GetFullName() == "/Y" assert dims[1].GetFullName() == "/X" assert dims[2].GetFullName() == "/Band" assert array.GetDataType().GetNumericDataType() == gdal.GDT_Byte got_data = array.Read(array_start_idx=[0, 0, 0], count=[2, 2, 1]) assert len(got_data) == 2 * 2 assert struct.unpack("B" * 4, got_data) == (107, 123, 115, 132) got_data = array.Transpose([2, 1, 0]).Read( array_start_idx=[0, 0, 0], count=[1, 2, 2] ) assert len(got_data) == 2 * 2 assert struct.unpack("B" * 4, got_data) == (107, 115, 123, 132) assert array.GetSpatialRef() X = dims[0].GetIndexingVariable() assert X assert struct.unpack("d" * 2, X.Read(array_start_idx=[0], count=[2])) == ( 3751290.0, 3751230.0, ) assert rg.OpenMDArray("X") Y = dims[1].GetIndexingVariable() assert Y assert struct.unpack("d" * 2, Y.Read(array_start_idx=[0], count=[2])) == ( 440750.0, 440810.0, ) assert rg.OpenMDArray("Y") ############################################################################### # Test reading a simple SDS product def test_hdf4multidim_sds(): # Generated with # https://support.hdfgroup.org/ftp/HDF/HDF_Current/src/unpacked/mfhdf/examples/SD_create_sds.c # + https://support.hdfgroup.org/ftp/HDF/HDF_Current/src/unpacked/mfhdf/examples/SD_set_get_dim_info.c # + https://support.hdfgroup.org/ftp/HDF/HDF_Current/src/unpacked/mfhdf/examples/SD_set_attr.c ds = gdal.OpenEx("data/SDS.hdf", gdal.OF_MULTIDIM_RASTER) assert ds rg = ds.GetRootGroup() assert rg.GetGroupNames() == ["scientific_datasets"] subg = rg.OpenGroup("scientific_datasets") assert subg attrs = rg.GetAttributes() assert len(attrs) == 1 assert attrs[0].GetName() == "File_contents" assert attrs[0].Read() == "Storm_track_data" assert not rg.OpenGroup("foo") assert not rg.GetMDArrayNames() assert not rg.OpenMDArray("foo") assert not subg.GetGroupNames() assert not subg.OpenGroup("foo") assert subg.GetMDArrayNames() == ["SDStemplate", "Y_Axis", "X_Axis"] dims = subg.GetDimensions() assert len(dims) == 2 array = subg.OpenMDArray("SDStemplate") assert array dims = array.GetDimensions() assert len(dims) == 2 assert dims[0].GetFullName() == "/scientific_datasets/Y_Axis" assert dims[0].GetIndexingVariable() assert dims[1].GetFullName() == "/scientific_datasets/X_Axis" assert dims[1].GetIndexingVariable() attrs = array.GetAttributes() assert len(attrs) == 1 assert attrs[0].GetName() == "Valid_range" attr = array.GetAttribute("Valid_range") assert attr assert attr.Read() == (2, 10) assert array.GetUnit() == "" assert not array.GetSpatialRef() ############################################################################### # Test reading a SDS product with unlimited dimension def test_hdf4multidim_sds_unlimited_dim(): # Generated with # hhttps://support.hdfgroup.org/ftp/HDF/HDF_Current/src/unpacked/mfhdf/examples/SD_unlimited_sds.c ds = gdal.OpenEx("data/SDSUNLIMITED.hdf", gdal.OF_MULTIDIM_RASTER) assert ds rg = ds.GetRootGroup() assert rg.GetGroupNames() == ["scientific_datasets"] subg = rg.OpenGroup("scientific_datasets") assert subg dims = subg.GetDimensions() assert len(dims) == 2 assert dims[0].GetName() == "fakeDim0" assert dims[0].GetSize() == 11 array = subg.OpenMDArray("AppendableData") dims = array.GetDimensions() assert len(dims) == 2 assert dims[0].GetName() == "fakeDim0" assert dims[0].GetSize() == 11 assert len(array.Read()) == 11 * 10 * 4 ############################################################################### # Test reading a 'random' SDS product def test_hdf4multidim_sds_read_world(): gdaltest.download_or_skip( "http://download.osgeo.org/gdal/data/hdf4/A2004259075000.L2_LAC_SST.hdf", "A2004259075000.L2_LAC_SST.hdf", ) ds = gdal.OpenEx("tmp/cache/A2004259075000.L2_LAC_SST.hdf", gdal.OF_MULTIDIM_RASTER) assert ds rg = ds.GetRootGroup() assert rg.GetGroupNames() == ["scientific_datasets"] subg = rg.OpenGroup("scientific_datasets") assert subg assert not rg.OpenGroup("foo") assert not rg.GetMDArrayNames() assert not rg.OpenMDArray("foo") assert not subg.GetGroupNames() assert not subg.OpenGroup("foo") assert subg.GetMDArrayNames() == ["sst"] dims = subg.GetDimensions() assert len(dims) == 2 sst = subg.OpenMDArray("sst") assert sst dims = sst.GetDimensions() assert len(dims) == 2 assert dims[0].GetFullName() == "/scientific_datasets/fakeDim0" assert dims[1].GetFullName() == "/scientific_datasets/fakeDim1" attrs = sst.GetAttributes() assert len(attrs) == 5 attr = sst.GetAttribute("long_name") assert attr assert attr.Read() == "Sea Surface Temperature" assert sst.GetUnit() == "degrees-C" assert not sst.GetSpatialRef() ############################################################################### # Test reading a SDS product with indexed dimensions def test_hdf4multidim_sds_read_world_with_indexing_variable(): gdaltest.download_or_skip( "https://download.osgeo.org/gdal/data/hdf4/REANALYSIS_1999217.hdf", "REANALYSIS_1999217.hdf", ) ds = gdal.OpenEx("tmp/cache/REANALYSIS_1999217.hdf", gdal.OF_MULTIDIM_RASTER) assert ds rg = ds.GetRootGroup() assert rg.GetGroupNames() == ["scientific_datasets"] subg = rg.OpenGroup("scientific_datasets") assert subg dims = subg.GetDimensions() assert len(dims) == 3 assert dims[0].GetFullName() == "/scientific_datasets/lat" assert dims[0].GetIndexingVariable() assert dims[1].GetFullName() == "/scientific_datasets/lon" assert dims[1].GetIndexingVariable() assert dims[2].GetFullName() == "/scientific_datasets/time" assert not dims[2].GetIndexingVariable() assert subg.GetMDArrayNames() == ["lat", "lon", "slp", "pr_wtr", "air"] slp = subg.OpenMDArray("slp") assert slp assert slp.GetUnit() == "Pascals" assert slp.GetOffset() == 119765.0 assert slp.GetScale() == 1.0 ############################################################################### # Test reading a GR dataset def test_hdf4multidim_gr(): # Generated by https://support.hdfgroup.org/ftp/HDF/HDF_Current/src/unpacked/hdf/examples/GR_create_and_write_image.c ds = gdal.OpenEx("data/General_RImages.hdf", gdal.OF_MULTIDIM_RASTER) assert ds rg = ds.GetRootGroup() assert rg.GetGroupNames() == ["general_rasters"] subg = rg.OpenGroup("general_rasters") assert subg attrs = subg.GetAttributes() assert len(attrs) == 2 attr = subg.GetAttribute("File Attribute 1") assert attr.Read() == "Contents of First FILE Attribute" assert not subg.GetGroupNames() assert not subg.OpenGroup("foo") assert subg.GetMDArrayNames() == ["Image Array 1"] array = subg.OpenMDArray("Image Array 1") assert not subg.OpenMDArray("foo") dims = array.GetDimensions() assert len(dims) == 3 assert dims[0].GetName() == "y" assert dims[0].GetSize() == 10 assert dims[1].GetName() == "x" assert dims[1].GetSize() == 5 assert dims[2].GetName() == "bands" assert dims[2].GetSize() == 2 got_data = array.Read(array_start_idx=[1, 2, 0], count=[3, 2, 2]) assert len(got_data) == 3 * 2 * 2 * 2 assert struct.unpack("h" * 12, got_data) == (4, 5, 5, 6, 6, 7, 5, 6, 6, 7, 7, 8) got_data = array.Read(array_start_idx=[1, 2, 0], count=[3, 2, 1]) assert len(got_data) == 3 * 2 * 1 * 2 assert struct.unpack("h" * 6, got_data) == (4, 5, 6, 5, 6, 7) got_data = array.Read(array_start_idx=[1, 2, 1], count=[3, 2, 1]) assert len(got_data) == 3 * 2 * 1 * 2 assert struct.unpack("h" * 6, got_data) == (5, 6, 7, 6, 7, 8) got_data = array.Read( array_start_idx=[1, 2, 1], count=[3, 2, 2], array_step=[1, 1, -1] ) assert len(got_data) == 3 * 2 * 2 * 2 assert struct.unpack("h" * 12, got_data) == (5, 4, 6, 5, 7, 6, 6, 5, 7, 6, 8, 7) attrs = array.GetAttributes() assert len(attrs) == 2 attr = array.GetAttribute("Image Attribute 1") assert attr.Read() == "Contents of IMAGE's First Attribute" attr = array.GetAttribute("Image Attribute 2") assert attr.Read() == (1, 2, 3, 4, 5, 6) ############################################################################### # Test reading a GR dataset with a palette def test_hdf4multidim_gr_palette(): # Generated by https://support.hdfgroup.org/ftp/HDF/HDF_Current/src/unpacked/hdf/examples/GR_write_palette.c def get_lut(): ds = gdal.OpenEx("data/Image_with_Palette.hdf", gdal.OF_MULTIDIM_RASTER) assert ds rg = ds.GetRootGroup() assert rg.GetGroupNames() == ["general_rasters"] subg = rg.OpenGroup("general_rasters") array = subg.OpenMDArray("Image with Palette") assert array lut = array.GetAttribute("lut") return lut lut = get_lut() assert lut got = lut.Read() assert len(got) == 3 * 256 assert got[0] == 0 assert got[1] == 1 assert got[2] == 2 assert got[255 * 3 + 0] == 255 assert got[255 * 3 + 1] == 0 assert got[255 * 3 + 2] == 1