LCOV - code coverage report
Current view: top level - frmts/hdf5 - bagdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2457 2836 86.6 %
Date: 2024-05-03 15:49:35 Functions: 102 106 96.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 5 (HDF5)
       4             :  * Purpose:  Read BAG datasets.
       5             :  * Author:   Frank Warmerdam <warmerdam@pobox.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2009-2018, Even Rouault <even dot rouault at spatialys dot com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "hdf5dataset.h"
      32             : #include "hdf5drivercore.h"
      33             : #include "gh5_convenience.h"
      34             : 
      35             : #include "cpl_mem_cache.h"
      36             : #include "cpl_string.h"
      37             : #include "cpl_time.h"
      38             : #include "gdal_alg.h"
      39             : #include "gdal_frmts.h"
      40             : #include "gdal_pam.h"
      41             : #include "gdal_priv.h"
      42             : #include "gdal_rat.h"
      43             : #include "iso19115_srs.h"
      44             : #include "ogr_core.h"
      45             : #include "ogr_spatialref.h"
      46             : #include "ogrsf_frmts.h"
      47             : #include "rat.h"
      48             : 
      49             : #include <cassert>
      50             : #include <algorithm>
      51             : #include <limits>
      52             : #include <map>
      53             : #include <utility>
      54             : #include <set>
      55             : 
      56             : struct BAGRefinementGrid
      57             : {
      58             :     unsigned nIndex = 0;
      59             :     unsigned nWidth = 0;
      60             :     unsigned nHeight = 0;
      61             :     float fResX = 0.0f;
      62             :     float fResY = 0.0f;
      63             :     float fSWX = 0.0f;  // offset from (bottom left corner of) the south west
      64             :                         // low resolution grid, in center-pixel convention
      65             :     float fSWY = 0.0f;  // offset from (bottom left corner of) the south west
      66             :                         // low resolution grid, in center-pixel convention
      67             : };
      68             : 
      69             : constexpr float fDEFAULT_NODATA = 1000000.0f;
      70             : 
      71             : /************************************************************************/
      72             : /*                                h5check()                             */
      73             : /************************************************************************/
      74             : 
      75             : #ifdef DEBUG
      76        1198 : template <class T> static T h5check(T ret, const char *filename, int line)
      77             : {
      78        1198 :     if (ret < 0)
      79             :     {
      80           0 :         CPLError(CE_Failure, CPLE_AppDefined, "HDF5 API failed at %s:%d",
      81             :                  filename, line);
      82             :     }
      83        1198 :     return ret;
      84             : }
      85             : 
      86             : #define H5_CHECK(x) h5check(x, __FILE__, __LINE__)
      87             : #else
      88             : #define H5_CHECK(x) (x)
      89             : #endif
      90             : 
      91             : /************************************************************************/
      92             : /* ==================================================================== */
      93             : /*                               BAGDataset                             */
      94             : /* ==================================================================== */
      95             : /************************************************************************/
      96             : class BAGDataset final : public GDALPamDataset
      97             : {
      98             :     friend class BAGRasterBand;
      99             :     friend class BAGSuperGridBand;
     100             :     friend class BAGBaseBand;
     101             :     friend class BAGResampledBand;
     102             :     friend class BAGGeorefMDSuperGridBand;
     103             :     friend class BAGInterpolatedBand;
     104             : 
     105             :     bool m_bReportVertCRS = true;
     106             : 
     107             :     enum class Population
     108             :     {
     109             :         MAX,
     110             :         MIN,
     111             :         MEAN,
     112             :         COUNT
     113             :     };
     114             :     Population m_ePopulation = Population::MAX;
     115             :     bool m_bMask = false;
     116             : 
     117             :     bool m_bIsChild = false;
     118             :     std::vector<std::unique_ptr<BAGDataset>> m_apoOverviewDS{};
     119             : 
     120             :     std::shared_ptr<GDAL::HDF5SharedResources> m_poSharedResources{};
     121             :     std::shared_ptr<GDALGroup> m_poRootGroup{};
     122             : 
     123             :     std::unique_ptr<OGRLayer> m_poTrackingListLayer{};
     124             : 
     125             :     OGRSpatialReference m_oSRS{};
     126             :     double adfGeoTransform[6] = {0, 1, 0, 0, 0, 1};
     127             : 
     128             :     int m_nLowResWidth = 0;
     129             :     int m_nLowResHeight = 0;
     130             : 
     131             :     double m_dfLowResMinX = 0.0;
     132             :     double m_dfLowResMinY = 0.0;
     133             :     double m_dfLowResMaxX = 0.0;
     134             :     double m_dfLowResMaxY = 0.0;
     135             : 
     136             :     void LoadMetadata();
     137             : 
     138             :     char *pszXMLMetadata = nullptr;
     139             :     char *apszMDList[2]{};
     140             : 
     141             :     int m_nChunkXSizeVarresMD = 0;
     142             :     int m_nChunkYSizeVarresMD = 0;
     143             :     void GetVarresMetadataChunkSizes(int &nChunkXSize, int &nChunkYSize);
     144             : 
     145             :     unsigned m_nChunkSizeVarresRefinement = 0;
     146             :     void GetVarresRefinementChunkSize(unsigned &nChunkSize);
     147             : 
     148             :     bool ReadVarresMetadataValue(int y, int x, hid_t memspace,
     149             :                                  BAGRefinementGrid *rgrid, int height,
     150             :                                  int width);
     151             : 
     152             :     bool LookForRefinementGrids(CSLConstList l_papszOpenOptions, int nY,
     153             :                                 int nX);
     154             : 
     155             :     hid_t m_hVarresMetadata = -1;
     156             :     hid_t m_hVarresMetadataDataType = -1;
     157             :     hid_t m_hVarresMetadataDataspace = -1;
     158             :     hid_t m_hVarresMetadataNative = -1;
     159             :     std::map<int, BAGRefinementGrid> m_oMapRefinemendGrids;
     160             : 
     161             :     CPLStringList m_aosSubdatasets;
     162             : 
     163             :     hid_t m_hVarresRefinements = -1;
     164             :     hid_t m_hVarresRefinementsDataType = -1;
     165             :     hid_t m_hVarresRefinementsDataspace = -1;
     166             :     hid_t m_hVarresRefinementsNative = -1;
     167             :     unsigned m_nRefinementsSize = 0;
     168             : 
     169             :     unsigned m_nSuperGridRefinementStartIndex = 0;
     170             : 
     171             :     lru11::Cache<unsigned, std::vector<float>> m_oCacheRefinementValues;
     172             :     const float *GetRefinementValues(unsigned nRefinementIndex);
     173             : 
     174             :     bool GetMeanSupergridsResolution(double &dfResX, double &dfResY);
     175             : 
     176             :     double m_dfResFilterMin = 0;
     177             :     double m_dfResFilterMax = std::numeric_limits<double>::infinity();
     178             : 
     179             :     void InitOverviewDS(BAGDataset *poParentDS, int nXSize, int nYSize);
     180             : 
     181             :     bool m_bMetadataWritten = false;
     182             :     CPLStringList m_aosCreationOptions{};
     183             :     bool WriteMetadataIfNeeded();
     184             : 
     185             :     bool OpenRaster(GDALOpenInfo *poOpenInfo, const CPLString &osFilename,
     186             :                     bool bOpenSuperGrid, int nX, int nY, bool bIsSubdataset,
     187             :                     const CPLString &osGeorefMetadataLayer,
     188             :                     CPLString &outOsSubDsName);
     189             :     bool OpenVector();
     190             : 
     191         153 :     inline hid_t GetHDF5Handle()
     192             :     {
     193         153 :         return m_poSharedResources->m_hHDF5;
     194             :     }
     195             : 
     196             :   public:
     197             :     BAGDataset();
     198             :     BAGDataset(BAGDataset *poParentDS, int nOvrFactor);
     199             :     BAGDataset(BAGDataset *poParentDS, int nXSize, int nYSize);
     200             :     virtual ~BAGDataset();
     201             : 
     202             :     virtual CPLErr GetGeoTransform(double *) override;
     203             :     const OGRSpatialReference *GetSpatialRef() const override;
     204             : 
     205             :     CPLErr SetGeoTransform(double *padfGeoTransform) override;
     206             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
     207             : 
     208             :     virtual char **GetMetadataDomainList() override;
     209             :     virtual char **GetMetadata(const char *pszDomain = "") override;
     210             : 
     211         138 :     int GetLayerCount() override
     212             :     {
     213         138 :         return m_poTrackingListLayer ? 1 : 0;
     214             :     }
     215             : 
     216             :     OGRLayer *GetLayer(int idx) override;
     217             : 
     218             :     static GDALDataset *Open(GDALOpenInfo *);
     219             :     static GDALDataset *OpenForCreate(GDALOpenInfo *, int nXSizeIn,
     220             :                                       int nYSizeIn, int nBandsIn,
     221             :                                       CSLConstList papszCreationOptions);
     222             :     static GDALDataset *CreateCopy(const char *pszFilename,
     223             :                                    GDALDataset *poSrcDS, int bStrict,
     224             :                                    char **papszOptions,
     225             :                                    GDALProgressFunc pfnProgress,
     226             :                                    void *pProgressData);
     227             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     228             :                                int nBandsIn, GDALDataType eType,
     229             :                                char **papszOptions);
     230             : 
     231             :     OGRErr ParseWKTFromXML(const char *pszISOXML);
     232             : };
     233             : 
     234             : /************************************************************************/
     235             : /*                              BAGCreator                              */
     236             : /************************************************************************/
     237             : 
     238             : class BAGCreator
     239             : {
     240             :     hid_t m_hdf5 = -1;
     241             :     hid_t m_bagRoot = -1;
     242             : 
     243             :     bool CreateBase(const char *pszFilename, char **papszOptions);
     244             :     bool CreateTrackingListDataset();
     245             :     bool CreateElevationOrUncertainty(
     246             :         GDALDataset *poSrcDS, int nBand, const char *pszDSName,
     247             :         const char *pszMaxAttrName, const char *pszMinAttrName,
     248             :         char **papszOptions, GDALProgressFunc pfnProgress, void *pProgressData);
     249             :     bool Close();
     250             : 
     251             :   public:
     252          74 :     BAGCreator() = default;
     253             :     ~BAGCreator();
     254             : 
     255             :     static bool SubstituteVariables(CPLXMLNode *psNode, char **papszDict);
     256             :     static CPLString GenerateMetadata(int nXSize, int nYSize,
     257             :                                       const double *padfGeoTransform,
     258             :                                       const OGRSpatialReference *poSRS,
     259             :                                       char **papszOptions);
     260             :     static bool CreateAndWriteMetadata(hid_t hdf5,
     261             :                                        const CPLString &osXMLMetadata);
     262             : 
     263             :     bool Create(const char *pszFilename, GDALDataset *poSrcDS,
     264             :                 char **papszOptions, GDALProgressFunc pfnProgress,
     265             :                 void *pProgressData);
     266             : 
     267             :     bool Create(const char *pszFilename, int nBands, GDALDataType eType,
     268             :                 char **papszOptions);
     269             : };
     270             : 
     271             : /************************************************************************/
     272             : /* ==================================================================== */
     273             : /*                               BAGRasterBand                          */
     274             : /* ==================================================================== */
     275             : /************************************************************************/
     276             : class BAGRasterBand final : public GDALPamRasterBand
     277             : {
     278             :     friend class BAGDataset;
     279             : 
     280             :     hid_t m_hDatasetID = -1;
     281             :     hid_t m_hNative = -1;
     282             :     hid_t m_hDataspace = -1;
     283             : 
     284             :     bool m_bMinMaxSet = false;
     285             :     double m_dfMinimum = std::numeric_limits<double>::max();
     286             :     double m_dfMaximum = -std::numeric_limits<double>::max();
     287             : 
     288             :     bool m_bHasNoData = false;
     289             :     float m_fNoDataValue = std::numeric_limits<float>::quiet_NaN();
     290             : 
     291             :     bool CreateDatasetIfNeeded();
     292             :     void FinalizeDataset();
     293             : 
     294             :   public:
     295             :     BAGRasterBand(BAGDataset *, int);
     296             :     virtual ~BAGRasterBand();
     297             : 
     298             :     bool Initialize(hid_t hDataset, const char *pszName);
     299             : 
     300             :     virtual CPLErr IReadBlock(int, int, void *) override;
     301             :     virtual CPLErr IWriteBlock(int, int, void *) override;
     302             :     virtual double GetNoDataValue(int *) override;
     303             :     virtual CPLErr SetNoDataValue(double dfNoData) override;
     304             : 
     305             :     virtual double GetMinimum(int *pbSuccess = nullptr) override;
     306             :     virtual double GetMaximum(int *pbSuccess = nullptr) override;
     307             : };
     308             : 
     309             : /************************************************************************/
     310             : /* ==================================================================== */
     311             : /*                              BAGBaseBand                             */
     312             : /* ==================================================================== */
     313             : /************************************************************************/
     314             : 
     315             : class BAGBaseBand CPL_NON_FINAL : public GDALRasterBand
     316             : {
     317             :   protected:
     318             :     bool m_bHasNoData = false;
     319             :     float m_fNoDataValue = std::numeric_limits<float>::quiet_NaN();
     320             : 
     321             :   public:
     322         114 :     BAGBaseBand() = default;
     323         114 :     ~BAGBaseBand() = default;
     324             : 
     325             :     double GetNoDataValue(int *) override;
     326             : 
     327             :     int GetOverviewCount() override;
     328             :     GDALRasterBand *GetOverview(int) override;
     329             : };
     330             : 
     331             : /************************************************************************/
     332             : /*                           GetNoDataValue()                           */
     333             : /************************************************************************/
     334             : 
     335          53 : double BAGBaseBand::GetNoDataValue(int *pbSuccess)
     336             : 
     337             : {
     338          53 :     if (pbSuccess)
     339          37 :         *pbSuccess = m_bHasNoData;
     340          53 :     if (m_bHasNoData)
     341          52 :         return m_fNoDataValue;
     342             : 
     343           1 :     return GDALRasterBand::GetNoDataValue(pbSuccess);
     344             : }
     345             : 
     346             : /************************************************************************/
     347             : /*                          GetOverviewCount()                          */
     348             : /************************************************************************/
     349             : 
     350          53 : int BAGBaseBand::GetOverviewCount()
     351             : {
     352          53 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
     353          53 :     return static_cast<int>(poGDS->m_apoOverviewDS.size());
     354             : }
     355             : 
     356             : /************************************************************************/
     357             : /*                            GetOverview()                             */
     358             : /************************************************************************/
     359             : 
     360          23 : GDALRasterBand *BAGBaseBand::GetOverview(int i)
     361             : 
     362             : {
     363          23 :     if (i < 0 || i >= GetOverviewCount())
     364           2 :         return nullptr;
     365          21 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
     366          21 :     return poGDS->m_apoOverviewDS[i]->GetRasterBand(nBand);
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /* ==================================================================== */
     371             : /*                             BAGSuperGridBand                         */
     372             : /* ==================================================================== */
     373             : /************************************************************************/
     374             : 
     375          28 : class BAGSuperGridBand final : public BAGBaseBand
     376             : {
     377             :     friend class BAGDataset;
     378             : 
     379             :   public:
     380             :     BAGSuperGridBand(BAGDataset *, int, bool bHasNoData, float fNoDataValue);
     381             :     virtual ~BAGSuperGridBand();
     382             : 
     383             :     CPLErr IReadBlock(int, int, void *) override;
     384             : };
     385             : 
     386             : /************************************************************************/
     387             : /* ==================================================================== */
     388             : /*                             BAGResampledBand                         */
     389             : /* ==================================================================== */
     390             : /************************************************************************/
     391             : 
     392         180 : class BAGResampledBand final : public BAGBaseBand
     393             : {
     394             :     friend class BAGDataset;
     395             : 
     396             :     bool m_bMinMaxSet = false;
     397             :     double m_dfMinimum = 0.0;
     398             :     double m_dfMaximum = 0.0;
     399             : 
     400             :   public:
     401             :     BAGResampledBand(BAGDataset *, int nBandIn, bool bHasNoData,
     402             :                      float fNoDataValue, bool bInitializeMinMax);
     403             :     virtual ~BAGResampledBand();
     404             : 
     405             :     void InitializeMinMax();
     406             : 
     407             :     CPLErr IReadBlock(int, int, void *) override;
     408             : 
     409             :     double GetMinimum(int *pbSuccess = nullptr) override;
     410             :     double GetMaximum(int *pbSuccess = nullptr) override;
     411             : };
     412             : 
     413             : /************************************************************************/
     414             : /* ==================================================================== */
     415             : /*                          BAGInterpolatedBand                         */
     416             : /* ==================================================================== */
     417             : /************************************************************************/
     418             : 
     419          20 : class BAGInterpolatedBand final : public BAGBaseBand
     420             : {
     421             :     friend class BAGDataset;
     422             : 
     423             :     bool m_bMinMaxSet = false;
     424             :     double m_dfMinimum = 0.0;
     425             :     double m_dfMaximum = 0.0;
     426             : 
     427             :     void LoadClosestRefinedNodes(
     428             :         double dfX, double dfY, int iXRefinedGrid, int iYRefinedGrid,
     429             :         const std::vector<BAGRefinementGrid> &rgrids, int nLowResMinIdxX,
     430             :         int nLowResMinIdxY, int nCountLowResX, int nCountLowResY,
     431             :         double dfLowResMinX, double dfLowResMinY, double dfLowResResX,
     432             :         double dfLowResResY, std::vector<double> &adfX,
     433             :         std::vector<double> &adfY, std::vector<float> &afDepth,
     434             :         std::vector<float> &afUncrt);
     435             : 
     436             :   public:
     437             :     BAGInterpolatedBand(BAGDataset *, int nBandIn, bool bHasNoData,
     438             :                         float fNoDataValue, bool bInitializeMinMax);
     439             :     virtual ~BAGInterpolatedBand();
     440             : 
     441             :     void InitializeMinMax();
     442             : 
     443             :     CPLErr IReadBlock(int, int, void *) override;
     444             : 
     445             :     double GetMinimum(int *pbSuccess = nullptr) override;
     446             :     double GetMaximum(int *pbSuccess = nullptr) override;
     447             : };
     448             : 
     449             : /************************************************************************/
     450             : /*                           BAGRasterBand()                            */
     451             : /************************************************************************/
     452         153 : BAGRasterBand::BAGRasterBand(BAGDataset *poDSIn, int nBandIn)
     453             : {
     454         153 :     poDS = poDSIn;
     455         153 :     nBand = nBandIn;
     456         153 : }
     457             : 
     458             : /************************************************************************/
     459             : /*                           ~BAGRasterBand()                           */
     460             : /************************************************************************/
     461             : 
     462         306 : BAGRasterBand::~BAGRasterBand()
     463             : {
     464             :     HDF5_GLOBAL_LOCK();
     465             : 
     466         153 :     if (eAccess == GA_Update)
     467             :     {
     468           8 :         CreateDatasetIfNeeded();
     469           8 :         FinalizeDataset();
     470             :     }
     471             : 
     472         153 :     if (m_hDataspace > 0)
     473         152 :         H5Sclose(m_hDataspace);
     474             : 
     475         153 :     if (m_hNative > 0)
     476         152 :         H5Tclose(m_hNative);
     477             : 
     478         153 :     if (m_hDatasetID > 0)
     479         152 :         H5Dclose(m_hDatasetID);
     480         306 : }
     481             : 
     482             : /************************************************************************/
     483             : /*                             Initialize()                             */
     484             : /************************************************************************/
     485             : 
     486         144 : bool BAGRasterBand::Initialize(hid_t hDatasetIDIn, const char *pszName)
     487             : 
     488             : {
     489         144 :     GDALRasterBand::SetDescription(pszName);
     490             : 
     491         144 :     m_hDatasetID = hDatasetIDIn;
     492             : 
     493         144 :     const hid_t datatype = H5Dget_type(m_hDatasetID);
     494         144 :     m_hDataspace = H5Dget_space(m_hDatasetID);
     495         144 :     const int n_dims = H5Sget_simple_extent_ndims(m_hDataspace);
     496         144 :     m_hNative = H5Tget_native_type(datatype, H5T_DIR_ASCEND);
     497             : 
     498         144 :     eDataType = GH5_GetDataType(m_hNative);
     499             : 
     500         144 :     if (n_dims == 2)
     501             :     {
     502         144 :         hsize_t dims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
     503         144 :         hsize_t maxdims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
     504             : 
     505         144 :         H5Sget_simple_extent_dims(m_hDataspace, dims, maxdims);
     506             : 
     507         144 :         nRasterXSize = static_cast<int>(dims[1]);
     508         144 :         nRasterYSize = static_cast<int>(dims[0]);
     509             :     }
     510             :     else
     511             :     {
     512           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Dataset not of rank 2.");
     513           0 :         return false;
     514             :     }
     515             : 
     516         144 :     nBlockXSize = nRasterXSize;
     517         144 :     nBlockYSize = 1;
     518             : 
     519             :     // Check for chunksize, and use it as blocksize for optimized reading.
     520         144 :     const hid_t listid = H5Dget_create_plist(hDatasetIDIn);
     521         144 :     if (listid > 0)
     522             :     {
     523         144 :         if (H5Pget_layout(listid) == H5D_CHUNKED)
     524             :         {
     525          28 :             hsize_t panChunkDims[3] = {static_cast<hsize_t>(0),
     526             :                                        static_cast<hsize_t>(0),
     527             :                                        static_cast<hsize_t>(0)};
     528          28 :             const int nDimSize = H5Pget_chunk(listid, 3, panChunkDims);
     529          28 :             nBlockXSize = static_cast<int>(panChunkDims[nDimSize - 1]);
     530          28 :             nBlockYSize = static_cast<int>(panChunkDims[nDimSize - 2]);
     531             :         }
     532             : 
     533         144 :         H5D_fill_value_t fillType = H5D_FILL_VALUE_UNDEFINED;
     534         288 :         if (H5Pfill_value_defined(listid, &fillType) >= 0 &&
     535         144 :             fillType == H5D_FILL_VALUE_USER_DEFINED)
     536             :         {
     537         141 :             float fNoDataValue = 0.0f;
     538         141 :             if (H5Pget_fill_value(listid, H5T_NATIVE_FLOAT, &fNoDataValue) >= 0)
     539             :             {
     540         141 :                 m_bHasNoData = true;
     541         141 :                 m_fNoDataValue = fNoDataValue;
     542             :             }
     543             :         }
     544             : 
     545         144 :         int nfilters = H5Pget_nfilters(listid);
     546             : 
     547         144 :         char name[120] = {};
     548         144 :         size_t cd_nelmts = 20;
     549         144 :         unsigned int cd_values[20] = {};
     550         144 :         unsigned int flags = 0;
     551         172 :         for (int i = 0; i < nfilters; i++)
     552             :         {
     553          28 :             const H5Z_filter_t filter = H5Pget_filter(
     554             :                 listid, i, &flags, &cd_nelmts, cd_values, sizeof(name), name);
     555          28 :             if (filter == H5Z_FILTER_DEFLATE)
     556          28 :                 poDS->GDALDataset::SetMetadataItem("COMPRESSION", "DEFLATE",
     557             :                                                    "IMAGE_STRUCTURE");
     558           0 :             else if (filter == H5Z_FILTER_NBIT)
     559           0 :                 poDS->GDALDataset::SetMetadataItem("COMPRESSION", "NBIT",
     560             :                                                    "IMAGE_STRUCTURE");
     561           0 :             else if (filter == H5Z_FILTER_SCALEOFFSET)
     562           0 :                 poDS->GDALDataset::SetMetadataItem("COMPRESSION", "SCALEOFFSET",
     563             :                                                    "IMAGE_STRUCTURE");
     564           0 :             else if (filter == H5Z_FILTER_SZIP)
     565           0 :                 poDS->GDALDataset::SetMetadataItem("COMPRESSION", "SZIP",
     566             :                                                    "IMAGE_STRUCTURE");
     567             :         }
     568             : 
     569         144 :         H5Pclose(listid);
     570             :     }
     571             : 
     572             :     // Load min/max information.
     573         387 :     if (EQUAL(pszName, "elevation") &&
     574          99 :         GH5_FetchAttribute(hDatasetIDIn, "Maximum Elevation Value",
     575         243 :                            m_dfMaximum) &&
     576          87 :         GH5_FetchAttribute(hDatasetIDIn, "Minimum Elevation Value",
     577          87 :                            m_dfMinimum))
     578             :     {
     579          87 :         m_bMinMaxSet = true;
     580             :     }
     581         157 :     else if (EQUAL(pszName, "uncertainty") &&
     582          43 :              GH5_FetchAttribute(hDatasetIDIn, "Maximum Uncertainty Value",
     583         100 :                                 m_dfMaximum) &&
     584          38 :              GH5_FetchAttribute(hDatasetIDIn, "Minimum Uncertainty Value",
     585          38 :                                 m_dfMinimum))
     586             :     {
     587             :         // Some products where uncertainty band is completely set to nodata
     588             :         // wrongly declare minimum and maximum to 0.0.
     589          38 :         if (m_dfMinimum != 0.0 || m_dfMaximum != 0.0)
     590          36 :             m_bMinMaxSet = true;
     591             :     }
     592          40 :     else if (EQUAL(pszName, "nominal_elevation") &&
     593          21 :              GH5_FetchAttribute(hDatasetIDIn, "max_value", m_dfMaximum) &&
     594           2 :              GH5_FetchAttribute(hDatasetIDIn, "min_value", m_dfMinimum))
     595             :     {
     596           2 :         m_bMinMaxSet = true;
     597             :     }
     598             : 
     599         144 :     return true;
     600             : }
     601             : 
     602             : /************************************************************************/
     603             : /*                         CreateDatasetIfNeeded()                      */
     604             : /************************************************************************/
     605             : 
     606         283 : bool BAGRasterBand::CreateDatasetIfNeeded()
     607             : {
     608         283 :     if (m_hDatasetID > 0 || eAccess == GA_ReadOnly)
     609         275 :         return true;
     610             : 
     611           8 :     hsize_t dims[2] = {static_cast<hsize_t>(nRasterYSize),
     612           8 :                        static_cast<hsize_t>(nRasterXSize)};
     613             : 
     614           8 :     m_hDataspace = H5_CHECK(H5Screate_simple(2, dims, nullptr));
     615           8 :     if (m_hDataspace < 0)
     616           0 :         return false;
     617             : 
     618           8 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
     619           8 :     bool bDeflate = EQUAL(
     620             :         poGDS->m_aosCreationOptions.FetchNameValueDef("COMPRESS", "DEFLATE"),
     621             :         "DEFLATE");
     622             :     int nCompressionLevel =
     623           8 :         atoi(poGDS->m_aosCreationOptions.FetchNameValueDef("ZLEVEL", "6"));
     624             : 
     625           8 :     bool ret = false;
     626           8 :     hid_t hDataType = -1;
     627           8 :     hid_t hParams = -1;
     628             :     do
     629             :     {
     630           8 :         hDataType = H5_CHECK(H5Tcopy(H5T_NATIVE_FLOAT));
     631           8 :         if (hDataType < 0)
     632           0 :             break;
     633             : 
     634           8 :         if (H5_CHECK(H5Tset_order(hDataType, H5T_ORDER_LE)) < 0)
     635           0 :             break;
     636             : 
     637           8 :         hParams = H5_CHECK(H5Pcreate(H5P_DATASET_CREATE));
     638           8 :         if (hParams < 0)
     639           0 :             break;
     640             : 
     641           8 :         if (H5_CHECK(H5Pset_fill_time(hParams, H5D_FILL_TIME_ALLOC)) < 0)
     642           0 :             break;
     643             : 
     644           8 :         if (H5_CHECK(H5Pset_fill_value(hParams, hDataType, &m_fNoDataValue)) <
     645             :             0)
     646           0 :             break;
     647             : 
     648           8 :         if (H5_CHECK(H5Pset_layout(hParams, H5D_CHUNKED)) < 0)
     649           0 :             break;
     650           8 :         hsize_t chunk_size[2] = {static_cast<hsize_t>(nBlockYSize),
     651           8 :                                  static_cast<hsize_t>(nBlockXSize)};
     652           8 :         if (H5_CHECK(H5Pset_chunk(hParams, 2, chunk_size)) < 0)
     653           0 :             break;
     654             : 
     655           8 :         if (bDeflate)
     656             :         {
     657           8 :             if (H5_CHECK(H5Pset_deflate(hParams, nCompressionLevel)) < 0)
     658           0 :                 break;
     659             :         }
     660             : 
     661           8 :         m_hDatasetID = H5_CHECK(H5Dcreate(poGDS->GetHDF5Handle(),
     662             :                                           nBand == 1 ? "/BAG_root/elevation"
     663             :                                                      : "/BAG_root/uncertainty",
     664             :                                           hDataType, m_hDataspace, hParams));
     665           8 :         if (m_hDatasetID < 0)
     666           0 :             break;
     667             : 
     668           8 :         ret = true;
     669             :     } while (false);
     670             : 
     671           8 :     if (hParams >= 0)
     672           8 :         H5_CHECK(H5Pclose(hParams));
     673           8 :     if (hDataType > 0)
     674           8 :         H5_CHECK(H5Tclose(hDataType));
     675             : 
     676           8 :     m_hNative = H5_CHECK(H5Tcopy(H5T_NATIVE_FLOAT));
     677             : 
     678           8 :     return ret;
     679             : }
     680             : 
     681             : /************************************************************************/
     682             : /*                         FinalizeDataset()                            */
     683             : /************************************************************************/
     684             : 
     685           8 : void BAGRasterBand::FinalizeDataset()
     686             : {
     687           8 :     if (m_dfMinimum > m_dfMaximum)
     688           3 :         return;
     689             : 
     690           5 :     const char *pszMaxAttrName =
     691           5 :         nBand == 1 ? "Maximum Elevation Value" : "Maximum Uncertainty Value";
     692           5 :     const char *pszMinAttrName =
     693           5 :         nBand == 1 ? "Minimum Elevation Value" : "Minimum Uncertainty Value";
     694             : 
     695           5 :     if (!GH5_CreateAttribute(m_hDatasetID, pszMaxAttrName, m_hNative))
     696           0 :         return;
     697             : 
     698           5 :     if (!GH5_CreateAttribute(m_hDatasetID, pszMinAttrName, m_hNative))
     699           0 :         return;
     700             : 
     701           5 :     if (!GH5_WriteAttribute(m_hDatasetID, pszMaxAttrName, m_dfMaximum))
     702           0 :         return;
     703             : 
     704           5 :     if (!GH5_WriteAttribute(m_hDatasetID, pszMinAttrName, m_dfMinimum))
     705           0 :         return;
     706             : }
     707             : 
     708             : /************************************************************************/
     709             : /*                             GetMinimum()                             */
     710             : /************************************************************************/
     711             : 
     712           8 : double BAGRasterBand::GetMinimum(int *pbSuccess)
     713             : 
     714             : {
     715           8 :     if (m_bMinMaxSet)
     716             :     {
     717           8 :         if (pbSuccess)
     718           8 :             *pbSuccess = TRUE;
     719           8 :         return m_dfMinimum;
     720             :     }
     721             : 
     722           0 :     return GDALRasterBand::GetMinimum(pbSuccess);
     723             : }
     724             : 
     725             : /************************************************************************/
     726             : /*                             GetMaximum()                             */
     727             : /************************************************************************/
     728             : 
     729           8 : double BAGRasterBand::GetMaximum(int *pbSuccess)
     730             : 
     731             : {
     732           8 :     if (m_bMinMaxSet)
     733             :     {
     734           8 :         if (pbSuccess)
     735           8 :             *pbSuccess = TRUE;
     736           8 :         return m_dfMaximum;
     737             :     }
     738             : 
     739           0 :     return GDALRasterBand::GetMaximum(pbSuccess);
     740             : }
     741             : 
     742             : /************************************************************************/
     743             : /*                           GetNoDataValue()                           */
     744             : /************************************************************************/
     745         102 : double BAGRasterBand::GetNoDataValue(int *pbSuccess)
     746             : 
     747             : {
     748         102 :     if (pbSuccess)
     749          92 :         *pbSuccess = m_bHasNoData;
     750         102 :     if (m_bHasNoData)
     751         102 :         return m_fNoDataValue;
     752             : 
     753           0 :     return GDALPamRasterBand::GetNoDataValue(pbSuccess);
     754             : }
     755             : 
     756             : /************************************************************************/
     757             : /*                           SetNoDataValue()                           */
     758             : /************************************************************************/
     759             : 
     760           4 : CPLErr BAGRasterBand::SetNoDataValue(double dfNoData)
     761             : {
     762           4 :     if (eAccess == GA_ReadOnly)
     763           0 :         return GDALPamRasterBand::SetNoDataValue(dfNoData);
     764             : 
     765           4 :     if (m_hDatasetID > 0)
     766             :     {
     767           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     768             :                  "Setting the nodata value after grid values have been written "
     769             :                  "is not supported");
     770           0 :         return CE_Failure;
     771             :     }
     772             : 
     773           4 :     m_bHasNoData = true;
     774           4 :     m_fNoDataValue = static_cast<float>(dfNoData);
     775           4 :     return CE_None;
     776             : }
     777             : 
     778             : /************************************************************************/
     779             : /*                             IReadBlock()                             */
     780             : /************************************************************************/
     781         252 : CPLErr BAGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     782             : {
     783             :     HDF5_GLOBAL_LOCK();
     784             : 
     785         252 :     if (!CreateDatasetIfNeeded())
     786           0 :         return CE_Failure;
     787             : 
     788         252 :     const int nXOff = nBlockXOff * nBlockXSize;
     789             :     H5OFFSET_TYPE offset[2] = {
     790         252 :         static_cast<H5OFFSET_TYPE>(
     791         252 :             std::max(0, nRasterYSize - (nBlockYOff + 1) * nBlockYSize)),
     792         252 :         static_cast<H5OFFSET_TYPE>(nXOff)};
     793             : 
     794         252 :     const int nSizeOfData = static_cast<int>(H5Tget_size(m_hNative));
     795         252 :     memset(pImage, 0,
     796         252 :            static_cast<size_t>(nBlockXSize) * nBlockYSize * nSizeOfData);
     797             : 
     798             :     //  Blocksize may not be a multiple of imagesize.
     799         252 :     hsize_t count[3] = {
     800         252 :         std::min(static_cast<hsize_t>(nBlockYSize), GetYSize() - offset[0]),
     801         252 :         std::min(static_cast<hsize_t>(nBlockXSize), GetXSize() - offset[1]),
     802         252 :         static_cast<hsize_t>(0)};
     803             : 
     804         252 :     if (nRasterYSize - (nBlockYOff + 1) * nBlockYSize < 0)
     805             :     {
     806           0 :         count[0] +=
     807           0 :             (nRasterYSize - static_cast<hsize_t>(nBlockYOff + 1) * nBlockYSize);
     808             :     }
     809             : 
     810             :     // Select block from file space.
     811             :     {
     812         252 :         const herr_t status = H5Sselect_hyperslab(
     813             :             m_hDataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr);
     814         252 :         if (status < 0)
     815           0 :             return CE_Failure;
     816             :     }
     817             : 
     818             :     // Create memory space to receive the data.
     819         252 :     hsize_t col_dims[2] = {static_cast<hsize_t>(nBlockYSize),
     820         252 :                            static_cast<hsize_t>(nBlockXSize)};
     821         252 :     const int rank = 2;
     822         252 :     const hid_t memspace = H5Screate_simple(rank, col_dims, nullptr);
     823         252 :     H5OFFSET_TYPE mem_offset[2] = {0, 0};
     824         252 :     const herr_t status = H5Sselect_hyperslab(
     825             :         memspace, H5S_SELECT_SET, mem_offset, nullptr, count, nullptr);
     826         252 :     if (status < 0)
     827             :     {
     828           0 :         H5Sclose(memspace);
     829           0 :         return CE_Failure;
     830             :     }
     831             : 
     832         252 :     const herr_t status_read = H5Dread(m_hDatasetID, m_hNative, memspace,
     833             :                                        m_hDataspace, H5P_DEFAULT, pImage);
     834             : 
     835         252 :     H5Sclose(memspace);
     836             : 
     837             :     // Y flip the data.
     838         252 :     const int nLinesToFlip = static_cast<int>(count[0]);
     839         252 :     const int nLineSize = nSizeOfData * nBlockXSize;
     840         252 :     GByte *const pabyTemp = static_cast<GByte *>(CPLMalloc(nLineSize));
     841         252 :     GByte *const pbyImage = static_cast<GByte *>(pImage);
     842             : 
     843         316 :     for (int iY = 0; iY < nLinesToFlip / 2; iY++)
     844             :     {
     845          64 :         memcpy(pabyTemp, pbyImage + iY * nLineSize, nLineSize);
     846          64 :         memcpy(pbyImage + iY * nLineSize,
     847          64 :                pbyImage + (nLinesToFlip - iY - 1) * nLineSize, nLineSize);
     848          64 :         memcpy(pbyImage + (nLinesToFlip - iY - 1) * nLineSize, pabyTemp,
     849             :                nLineSize);
     850             :     }
     851             : 
     852         252 :     CPLFree(pabyTemp);
     853             : 
     854             :     // Return success or failure.
     855         252 :     if (status_read < 0)
     856             :     {
     857           0 :         CPLError(CE_Failure, CPLE_AppDefined, "H5Dread() failed for block.");
     858           0 :         return CE_Failure;
     859             :     }
     860             : 
     861         252 :     return CE_None;
     862             : }
     863             : 
     864             : /************************************************************************/
     865             : /*                            IWriteBlock()                             */
     866             : /************************************************************************/
     867             : 
     868          15 : CPLErr BAGRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     869             : {
     870             :     HDF5_GLOBAL_LOCK();
     871             : 
     872          15 :     if (!CreateDatasetIfNeeded())
     873           0 :         return CE_Failure;
     874             : 
     875          15 :     const int nXOff = nBlockXOff * nBlockXSize;
     876          15 :     H5OFFSET_TYPE offset[3] = {
     877          15 :         static_cast<H5OFFSET_TYPE>(
     878          15 :             std::max(0, nRasterYSize - (nBlockYOff + 1) * nBlockYSize)),
     879          15 :         static_cast<H5OFFSET_TYPE>(nXOff)};
     880             : 
     881             :     //  Blocksize may not be a multiple of imagesize.
     882          15 :     hsize_t count[3] = {
     883          15 :         std::min(static_cast<hsize_t>(nBlockYSize), GetYSize() - offset[0]),
     884          15 :         std::min(static_cast<hsize_t>(nBlockXSize), GetXSize() - offset[1])};
     885             : 
     886          15 :     if (nRasterYSize - (nBlockYOff + 1) * nBlockYSize < 0)
     887             :     {
     888           0 :         count[0] +=
     889           0 :             (nRasterYSize - static_cast<hsize_t>(nBlockYOff + 1) * nBlockYSize);
     890             :     }
     891             : 
     892             :     // Select block from file space.
     893             :     {
     894          15 :         const herr_t status = H5Sselect_hyperslab(
     895             :             m_hDataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr);
     896          15 :         if (status < 0)
     897           0 :             return CE_Failure;
     898             :     }
     899             : 
     900             :     // Create memory space to receive the data.
     901          15 :     hsize_t col_dims[2] = {static_cast<hsize_t>(nBlockYSize),
     902          15 :                            static_cast<hsize_t>(nBlockXSize)};
     903          15 :     const int rank = 2;
     904          15 :     const hid_t memspace = H5Screate_simple(rank, col_dims, nullptr);
     905          15 :     H5OFFSET_TYPE mem_offset[2] = {0, 0};
     906          15 :     const herr_t status = H5Sselect_hyperslab(
     907             :         memspace, H5S_SELECT_SET, mem_offset, nullptr, count, nullptr);
     908          15 :     if (status < 0)
     909             :     {
     910           0 :         H5Sclose(memspace);
     911           0 :         return CE_Failure;
     912             :     }
     913             : 
     914             :     // Y flip the data.
     915          15 :     const int nLinesToFlip = static_cast<int>(count[0]);
     916          15 :     const int nSizeOfData = static_cast<int>(H5Tget_size(m_hNative));
     917          15 :     const int nLineSize = nSizeOfData * nBlockXSize;
     918             :     GByte *const pabyTemp = static_cast<GByte *>(
     919          15 :         CPLMalloc(static_cast<size_t>(nLineSize) * nLinesToFlip));
     920          15 :     GByte *const pbyImage = static_cast<GByte *>(pImage);
     921             : 
     922          65 :     for (int iY = 0; iY < nLinesToFlip; iY++)
     923             :     {
     924          50 :         memcpy(pabyTemp + iY * nLineSize,
     925          50 :                pbyImage + (nLinesToFlip - iY - 1) * nLineSize, nLineSize);
     926         504 :         for (int iX = 0; iX < static_cast<int>(count[1]); iX++)
     927             :         {
     928             :             float f;
     929         454 :             GDALCopyWords(pabyTemp + iY * nLineSize + iX * nSizeOfData,
     930             :                           eDataType, 0, &f, GDT_Float32, 0, 1);
     931         454 :             if (!m_bHasNoData || m_fNoDataValue != f)
     932             :             {
     933         452 :                 m_dfMinimum = std::min(m_dfMinimum, static_cast<double>(f));
     934         452 :                 m_dfMaximum = std::max(m_dfMaximum, static_cast<double>(f));
     935             :             }
     936             :         }
     937             :     }
     938             : 
     939          15 :     const herr_t status_write = H5Dwrite(m_hDatasetID, m_hNative, memspace,
     940             :                                          m_hDataspace, H5P_DEFAULT, pabyTemp);
     941             : 
     942          15 :     H5Sclose(memspace);
     943             : 
     944          15 :     CPLFree(pabyTemp);
     945             : 
     946             :     // Return success or failure.
     947          15 :     if (status_write < 0)
     948             :     {
     949           0 :         CPLError(CE_Failure, CPLE_AppDefined, "H5Dwrite() failed for block.");
     950           0 :         return CE_Failure;
     951             :     }
     952             : 
     953          15 :     return CE_None;
     954             : }
     955             : 
     956             : /************************************************************************/
     957             : /*                           BAGSuperGridBand()                         */
     958             : /************************************************************************/
     959             : 
     960          14 : BAGSuperGridBand::BAGSuperGridBand(BAGDataset *poDSIn, int nBandIn,
     961          14 :                                    bool bHasNoData, float fNoDataValue)
     962             : {
     963          14 :     poDS = poDSIn;
     964          14 :     nBand = nBandIn;
     965          14 :     nRasterXSize = poDS->GetRasterXSize();
     966          14 :     nRasterYSize = poDS->GetRasterYSize();
     967          14 :     nBlockXSize = nRasterXSize;
     968          14 :     nBlockYSize = 1;
     969          14 :     eDataType = GDT_Float32;
     970          14 :     GDALRasterBand::SetDescription(nBand == 1 ? "elevation" : "uncertainty");
     971          14 :     m_bHasNoData = bHasNoData;
     972          14 :     m_fNoDataValue = fNoDataValue;
     973          14 : }
     974             : 
     975             : /************************************************************************/
     976             : /*                          ~BAGSuperGridBand()                         */
     977             : /************************************************************************/
     978             : 
     979             : BAGSuperGridBand::~BAGSuperGridBand() = default;
     980             : 
     981             : /************************************************************************/
     982             : /*                             IReadBlock()                             */
     983             : /************************************************************************/
     984             : 
     985           8 : CPLErr BAGSuperGridBand::IReadBlock(int, int nBlockYOff, void *pImage)
     986             : {
     987             :     HDF5_GLOBAL_LOCK();
     988             : 
     989           8 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
     990           8 :     H5OFFSET_TYPE offset[2] = {
     991             :         static_cast<H5OFFSET_TYPE>(0),
     992             :         static_cast<H5OFFSET_TYPE>(
     993           8 :             poGDS->m_nSuperGridRefinementStartIndex +
     994           8 :             static_cast<H5OFFSET_TYPE>(nRasterYSize - 1 - nBlockYOff) *
     995           8 :                 nBlockXSize)};
     996           8 :     hsize_t count[2] = {1, static_cast<hsize_t>(nBlockXSize)};
     997             :     {
     998           8 :         herr_t status = H5Sselect_hyperslab(
     999             :             poGDS->m_hVarresRefinementsDataspace, H5S_SELECT_SET, offset,
    1000             :             nullptr, count, nullptr);
    1001           8 :         if (status < 0)
    1002           0 :             return CE_Failure;
    1003             :     }
    1004             : 
    1005             :     // Create memory space to receive the data.
    1006           8 :     const hid_t memspace = H5Screate_simple(2, count, nullptr);
    1007           8 :     H5OFFSET_TYPE mem_offset[2] = {0, 0};
    1008             :     {
    1009           8 :         const herr_t status = H5Sselect_hyperslab(
    1010             :             memspace, H5S_SELECT_SET, mem_offset, nullptr, count, nullptr);
    1011           8 :         if (status < 0)
    1012             :         {
    1013           0 :             H5Sclose(memspace);
    1014           0 :             return CE_Failure;
    1015             :         }
    1016             :     }
    1017             : 
    1018           8 :     float *afBuffer = new float[2 * nBlockXSize];
    1019             :     {
    1020           8 :         const herr_t status = H5Dread(
    1021             :             poGDS->m_hVarresRefinements, poGDS->m_hVarresRefinementsNative,
    1022             :             memspace, poGDS->m_hVarresRefinementsDataspace, H5P_DEFAULT,
    1023             :             afBuffer);
    1024           8 :         if (status < 0)
    1025             :         {
    1026           0 :             H5Sclose(memspace);
    1027           0 :             delete[] afBuffer;
    1028           0 :             return CE_Failure;
    1029             :         }
    1030             :     }
    1031             : 
    1032           8 :     GDALCopyWords(afBuffer + nBand - 1, GDT_Float32, 2 * sizeof(float), pImage,
    1033             :                   GDT_Float32, sizeof(float), nBlockXSize);
    1034             : 
    1035           8 :     H5Sclose(memspace);
    1036           8 :     delete[] afBuffer;
    1037           8 :     return CE_None;
    1038             : }
    1039             : 
    1040             : /************************************************************************/
    1041             : /*                           BAGResampledBand()                         */
    1042             : /************************************************************************/
    1043             : 
    1044          90 : BAGResampledBand::BAGResampledBand(BAGDataset *poDSIn, int nBandIn,
    1045             :                                    bool bHasNoData, float fNoDataValue,
    1046          90 :                                    bool bInitializeMinMax)
    1047             : {
    1048          90 :     poDS = poDSIn;
    1049          90 :     nBand = nBandIn;
    1050          90 :     nRasterXSize = poDS->GetRasterXSize();
    1051          90 :     nRasterYSize = poDS->GetRasterYSize();
    1052             :     // Mostly for autotest purposes
    1053             :     const int nBlockSize =
    1054          90 :         std::max(1, atoi(CPLGetConfigOption("GDAL_BAG_BLOCK_SIZE", "256")));
    1055          90 :     nBlockXSize = std::min(nBlockSize, poDS->GetRasterXSize());
    1056          90 :     nBlockYSize = std::min(nBlockSize, poDS->GetRasterYSize());
    1057          90 :     if (poDSIn->m_bMask)
    1058             :     {
    1059           1 :         eDataType = GDT_Byte;
    1060             :     }
    1061          89 :     else if (poDSIn->m_ePopulation == BAGDataset::Population::COUNT)
    1062             :     {
    1063           1 :         eDataType = GDT_UInt32;
    1064           1 :         GDALRasterBand::SetDescription("count");
    1065             :     }
    1066             :     else
    1067             :     {
    1068          88 :         m_bHasNoData = true;
    1069          88 :         m_fNoDataValue = bHasNoData ? fNoDataValue : fDEFAULT_NODATA;
    1070          88 :         eDataType = GDT_Float32;
    1071          88 :         GDALRasterBand::SetDescription(nBand == 1 ? "elevation"
    1072             :                                                   : "uncertainty");
    1073             :     }
    1074          90 :     if (bInitializeMinMax)
    1075             :     {
    1076          36 :         InitializeMinMax();
    1077             :     }
    1078          90 : }
    1079             : 
    1080             : /************************************************************************/
    1081             : /*                          ~BAGResampledBand()                         */
    1082             : /************************************************************************/
    1083             : 
    1084             : BAGResampledBand::~BAGResampledBand() = default;
    1085             : 
    1086             : /************************************************************************/
    1087             : /*                           InitializeMinMax()                         */
    1088             : /************************************************************************/
    1089             : 
    1090          36 : void BAGResampledBand::InitializeMinMax()
    1091             : {
    1092          36 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
    1093          90 :     if (nBand == 1 &&
    1094          18 :         GH5_FetchAttribute(poGDS->m_hVarresRefinements, "max_depth",
    1095          54 :                            m_dfMaximum) &&
    1096          18 :         GH5_FetchAttribute(poGDS->m_hVarresRefinements, "min_depth",
    1097          18 :                            m_dfMinimum))
    1098             :     {
    1099          18 :         m_bMinMaxSet = true;
    1100             :     }
    1101          54 :     else if (nBand == 2 &&
    1102          18 :              GH5_FetchAttribute(poGDS->m_hVarresRefinements, "max_uncrt",
    1103          36 :                                 m_dfMaximum) &&
    1104          18 :              GH5_FetchAttribute(poGDS->m_hVarresRefinements, "min_uncrt",
    1105          18 :                                 m_dfMinimum))
    1106             :     {
    1107          18 :         m_bMinMaxSet = true;
    1108             :     }
    1109          36 : }
    1110             : 
    1111             : /************************************************************************/
    1112             : /*                             GetMinimum()                             */
    1113             : /************************************************************************/
    1114             : 
    1115           2 : double BAGResampledBand::GetMinimum(int *pbSuccess)
    1116             : 
    1117             : {
    1118           2 :     if (m_bMinMaxSet)
    1119             :     {
    1120           2 :         if (pbSuccess)
    1121           2 :             *pbSuccess = TRUE;
    1122           2 :         return m_dfMinimum;
    1123             :     }
    1124             : 
    1125           0 :     return GDALRasterBand::GetMinimum(pbSuccess);
    1126             : }
    1127             : 
    1128             : /************************************************************************/
    1129             : /*                             GetMaximum()                             */
    1130             : /************************************************************************/
    1131             : 
    1132           2 : double BAGResampledBand::GetMaximum(int *pbSuccess)
    1133             : 
    1134             : {
    1135           2 :     if (m_bMinMaxSet)
    1136             :     {
    1137           2 :         if (pbSuccess)
    1138           2 :             *pbSuccess = TRUE;
    1139           2 :         return m_dfMaximum;
    1140             :     }
    1141             : 
    1142           0 :     return GDALRasterBand::GetMaximum(pbSuccess);
    1143             : }
    1144             : 
    1145             : /************************************************************************/
    1146             : /*                             IReadBlock()                             */
    1147             : /************************************************************************/
    1148             : 
    1149         289 : CPLErr BAGResampledBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1150             :                                     void *pImage)
    1151             : {
    1152             :     HDF5_GLOBAL_LOCK();
    1153             : 
    1154         289 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
    1155             : #ifdef DEBUG_VERBOSE
    1156             :     CPLDebug(
    1157             :         "BAG",
    1158             :         "IReadBlock: nRasterXSize=%d, nBlockXOff=%d, nBlockYOff=%d, nBand=%d",
    1159             :         nRasterXSize, nBlockXOff, nBlockYOff, nBand);
    1160             : #endif
    1161             : 
    1162         289 :     const float fNoDataValue = m_fNoDataValue;
    1163         289 :     const float fNoSuperGridValue = m_fNoDataValue;
    1164         289 :     float *depthsPtr = nullptr;
    1165         289 :     float *uncrtPtr = nullptr;
    1166             : 
    1167         289 :     GDALRasterBlock *poBlock = nullptr;
    1168         289 :     if (poGDS->nBands == 2)
    1169             :     {
    1170         287 :         if (nBand == 1)
    1171             :         {
    1172         287 :             depthsPtr = static_cast<float *>(pImage);
    1173         287 :             poBlock = poGDS->GetRasterBand(2)->GetLockedBlockRef(
    1174             :                 nBlockXOff, nBlockYOff, TRUE);
    1175         287 :             if (poBlock)
    1176             :             {
    1177         287 :                 uncrtPtr = static_cast<float *>(poBlock->GetDataRef());
    1178             :             }
    1179             :             else
    1180             :             {
    1181           0 :                 return CE_Failure;
    1182             :             }
    1183             :         }
    1184             :         else
    1185             :         {
    1186           0 :             uncrtPtr = static_cast<float *>(pImage);
    1187           0 :             poBlock = poGDS->GetRasterBand(1)->GetLockedBlockRef(
    1188             :                 nBlockXOff, nBlockYOff, TRUE);
    1189           0 :             if (poBlock)
    1190             :             {
    1191           0 :                 depthsPtr = static_cast<float *>(poBlock->GetDataRef());
    1192             :             }
    1193             :             else
    1194             :             {
    1195           0 :                 return CE_Failure;
    1196             :             }
    1197             :         }
    1198             :     }
    1199             : 
    1200         289 :     if (depthsPtr)
    1201             :     {
    1202         287 :         GDALCopyWords(&fNoSuperGridValue, GDT_Float32, 0, depthsPtr,
    1203             :                       GDT_Float32, static_cast<int>(sizeof(float)),
    1204         287 :                       nBlockXSize * nBlockYSize);
    1205             :     }
    1206             : 
    1207         289 :     if (uncrtPtr)
    1208             :     {
    1209         287 :         GDALCopyWords(&fNoSuperGridValue, GDT_Float32, 0, uncrtPtr, GDT_Float32,
    1210             :                       static_cast<int>(sizeof(float)),
    1211         287 :                       nBlockXSize * nBlockYSize);
    1212             :     }
    1213             : 
    1214         578 :     std::vector<int> counts;
    1215         289 :     if (poGDS->m_bMask)
    1216             :     {
    1217           1 :         CPLAssert(pImage);  // to make CLang Static Analyzer happy
    1218           1 :         memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
    1219             :     }
    1220         288 :     else if (poGDS->m_ePopulation == BAGDataset::Population::MEAN)
    1221             :     {
    1222           1 :         counts.resize(static_cast<size_t>(nBlockXSize) * nBlockYSize);
    1223             :     }
    1224         287 :     else if (poGDS->m_ePopulation == BAGDataset::Population::COUNT)
    1225             :     {
    1226           1 :         CPLAssert(pImage);  // to make CLang Static Analyzer happy
    1227           1 :         memset(pImage, 0,
    1228           1 :                static_cast<size_t>(nBlockXSize) * nBlockYSize *
    1229           1 :                    GDALGetDataTypeSizeBytes(eDataType));
    1230             :     }
    1231             : 
    1232             :     const int nReqCountX =
    1233         289 :         std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
    1234             :     const int nReqCountY =
    1235         289 :         std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
    1236             :     // Compute extent of block in georeferenced coordinates
    1237         289 :     double dfBlockMinX = poGDS->adfGeoTransform[0] +
    1238         289 :                          nBlockXOff * nBlockXSize * poGDS->adfGeoTransform[1];
    1239         289 :     double dfBlockMaxX = dfBlockMinX + nReqCountX * poGDS->adfGeoTransform[1];
    1240         289 :     double dfBlockMaxY = poGDS->adfGeoTransform[3] +
    1241         289 :                          nBlockYOff * nBlockYSize * poGDS->adfGeoTransform[5];
    1242         289 :     double dfBlockMinY = dfBlockMaxY + nReqCountY * poGDS->adfGeoTransform[5];
    1243             : 
    1244             :     // Compute min/max indices of intersecting supergrids (origin bottom-left)
    1245         289 :     const double dfLowResResX =
    1246         289 :         (poGDS->m_dfLowResMaxX - poGDS->m_dfLowResMinX) / poGDS->m_nLowResWidth;
    1247         289 :     const double dfLowResResY =
    1248         289 :         (poGDS->m_dfLowResMaxY - poGDS->m_dfLowResMinY) /
    1249         289 :         poGDS->m_nLowResHeight;
    1250             :     int nLowResMinIdxX =
    1251         578 :         std::max(0, static_cast<int>((dfBlockMinX - poGDS->m_dfLowResMinX) /
    1252         289 :                                      dfLowResResX));
    1253             :     int nLowResMinIdxY =
    1254         578 :         std::max(0, static_cast<int>((dfBlockMinY - poGDS->m_dfLowResMinY) /
    1255         289 :                                      dfLowResResY));
    1256             :     int nLowResMaxIdxX = std::min(
    1257         578 :         poGDS->m_nLowResWidth - 1,
    1258         289 :         static_cast<int>((dfBlockMaxX - poGDS->m_dfLowResMinX) / dfLowResResX));
    1259             :     int nLowResMaxIdxY = std::min(
    1260         578 :         poGDS->m_nLowResHeight - 1,
    1261         289 :         static_cast<int>((dfBlockMaxY - poGDS->m_dfLowResMinY) / dfLowResResY));
    1262             : 
    1263             :     // Create memory space to receive the data.
    1264         289 :     const int nCountLowResX = nLowResMaxIdxX - nLowResMinIdxX + 1;
    1265         289 :     const int nCountLowResY = nLowResMaxIdxY - nLowResMinIdxY + 1;
    1266         289 :     hsize_t countVarresMD[2] = {static_cast<hsize_t>(nCountLowResY),
    1267         289 :                                 static_cast<hsize_t>(nCountLowResX)};
    1268         289 :     const hid_t memspaceVarresMD = H5Screate_simple(2, countVarresMD, nullptr);
    1269         289 :     H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
    1270             :                                    static_cast<H5OFFSET_TYPE>(0)};
    1271         289 :     if (H5Sselect_hyperslab(memspaceVarresMD, H5S_SELECT_SET, mem_offset,
    1272         289 :                             nullptr, countVarresMD, nullptr) < 0)
    1273             :     {
    1274           0 :         H5Sclose(memspaceVarresMD);
    1275           0 :         if (poBlock != nullptr)
    1276             :         {
    1277           0 :             poBlock->DropLock();
    1278           0 :             poBlock = nullptr;
    1279             :         }
    1280           0 :         return CE_Failure;
    1281             :     }
    1282             : 
    1283         289 :     std::vector<BAGRefinementGrid> rgrids(static_cast<size_t>(nCountLowResY) *
    1284         578 :                                           nCountLowResX);
    1285         289 :     if (!(poGDS->ReadVarresMetadataValue(nLowResMinIdxY, nLowResMinIdxX,
    1286             :                                          memspaceVarresMD, rgrids.data(),
    1287             :                                          nCountLowResY, nCountLowResX)))
    1288             :     {
    1289           0 :         H5Sclose(memspaceVarresMD);
    1290           0 :         if (poBlock != nullptr)
    1291             :         {
    1292           0 :             poBlock->DropLock();
    1293           0 :             poBlock = nullptr;
    1294             :         }
    1295           0 :         return CE_Failure;
    1296             :     }
    1297             : 
    1298         289 :     H5Sclose(memspaceVarresMD);
    1299             : 
    1300         289 :     CPLErr eErr = CE_None;
    1301         722 :     for (int y = nLowResMinIdxY; y <= nLowResMaxIdxY; y++)
    1302             :     {
    1303        1336 :         for (int x = nLowResMinIdxX; x <= nLowResMaxIdxX; x++)
    1304             :         {
    1305         903 :             const auto &rgrid = rgrids[(y - nLowResMinIdxY) * nCountLowResX +
    1306         903 :                                        (x - nLowResMinIdxX)];
    1307         903 :             if (rgrid.nWidth == 0)
    1308             :             {
    1309          80 :                 continue;
    1310             :             }
    1311         903 :             const float gridRes = std::max(rgrid.fResX, rgrid.fResY);
    1312         903 :             if (!(gridRes > poGDS->m_dfResFilterMin &&
    1313         851 :                   gridRes <= poGDS->m_dfResFilterMax))
    1314             :             {
    1315          80 :                 continue;
    1316             :             }
    1317             : 
    1318             :             // Super grid bounding box with pixel-center convention
    1319         823 :             const double dfMinX =
    1320         823 :                 poGDS->m_dfLowResMinX + x * dfLowResResX + rgrid.fSWX;
    1321         823 :             const double dfMaxX =
    1322         823 :                 dfMinX + (rgrid.nWidth - 1) * static_cast<double>(rgrid.fResX);
    1323         823 :             const double dfMinY =
    1324         823 :                 poGDS->m_dfLowResMinY + y * dfLowResResY + rgrid.fSWY;
    1325         823 :             const double dfMaxY =
    1326         823 :                 dfMinY + (rgrid.nHeight - 1) * static_cast<double>(rgrid.fResY);
    1327             : 
    1328             :             // Intersection of super grid with block
    1329         823 :             const double dfInterMinX = std::max(dfBlockMinX, dfMinX);
    1330         823 :             const double dfInterMinY = std::max(dfBlockMinY, dfMinY);
    1331         823 :             const double dfInterMaxX = std::min(dfBlockMaxX, dfMaxX);
    1332         823 :             const double dfInterMaxY = std::min(dfBlockMaxY, dfMaxY);
    1333             : 
    1334             :             // Min/max indices in the super grid
    1335             :             const int nMinSrcX = std::max(
    1336         823 :                 0, static_cast<int>((dfInterMinX - dfMinX) / rgrid.fResX));
    1337             :             const int nMinSrcY = std::max(
    1338         823 :                 0, static_cast<int>((dfInterMinY - dfMinY) / rgrid.fResY));
    1339             :             // Need to use ceil due to numerical imprecision
    1340             :             const int nMaxSrcX =
    1341        1646 :                 std::min(static_cast<int>(rgrid.nWidth) - 1,
    1342        1646 :                          static_cast<int>(
    1343         823 :                              std::ceil((dfInterMaxX - dfMinX) / rgrid.fResX)));
    1344             :             const int nMaxSrcY =
    1345        1646 :                 std::min(static_cast<int>(rgrid.nHeight) - 1,
    1346        1646 :                          static_cast<int>(
    1347         823 :                              std::ceil((dfInterMaxY - dfMinY) / rgrid.fResY)));
    1348             : #ifdef DEBUG_VERBOSE
    1349             :             CPLDebug(
    1350             :                 "BAG",
    1351             :                 "y = %d, x = %d, minx = %d, miny = %d, maxx = %d, maxy = %d", y,
    1352             :                 x, nMinSrcX, nMinSrcY, nMaxSrcX, nMaxSrcY);
    1353             : #endif
    1354         823 :             const double dfCstX =
    1355         823 :                 (dfMinX - dfBlockMinX) / poGDS->adfGeoTransform[1];
    1356         823 :             const double dfMulX = rgrid.fResX / poGDS->adfGeoTransform[1];
    1357             : 
    1358        3709 :             for (int super_y = nMinSrcY; super_y <= nMaxSrcY; super_y++)
    1359             :             {
    1360        2886 :                 const double dfSrcY =
    1361        2886 :                     dfMinY + super_y * static_cast<double>(rgrid.fResY);
    1362        2886 :                 const int nTargetY = static_cast<int>(std::floor(
    1363        2886 :                     (dfBlockMaxY - dfSrcY) / -poGDS->adfGeoTransform[5]));
    1364        2886 :                 if (!(nTargetY >= 0 && nTargetY < nReqCountY))
    1365             :                 {
    1366         752 :                     continue;
    1367             :                 }
    1368             : 
    1369        2134 :                 const unsigned nTargetIdxBase = nTargetY * nBlockXSize;
    1370        2134 :                 const unsigned nRefinementIndexase =
    1371        2134 :                     rgrid.nIndex + super_y * rgrid.nWidth;
    1372             : 
    1373       11648 :                 for (int super_x = nMinSrcX; super_x <= nMaxSrcX; super_x++)
    1374             :                 {
    1375             :                     /*
    1376             :                     const double dfSrcX = dfMinX + super_x * rgrid.fResX;
    1377             :                     const int nTargetX = static_cast<int>(std::floor(
    1378             :                         (dfSrcX - dfBlockMinX) / poGDS->adfGeoTransform[1]));
    1379             :                     */
    1380        9514 :                     const int nTargetX =
    1381        9514 :                         static_cast<int>(std::floor(dfCstX + super_x * dfMulX));
    1382        9514 :                     if (!(nTargetX >= 0 && nTargetX < nReqCountX))
    1383             :                     {
    1384        1260 :                         continue;
    1385             :                     }
    1386             : 
    1387        8254 :                     const unsigned nTargetIdx = nTargetIdxBase + nTargetX;
    1388        8254 :                     if (poGDS->m_bMask)
    1389             :                     {
    1390         502 :                         static_cast<GByte *>(pImage)[nTargetIdx] = 255;
    1391         502 :                         continue;
    1392             :                     }
    1393             : 
    1394        7752 :                     if (poGDS->m_ePopulation == BAGDataset::Population::COUNT)
    1395             :                     {
    1396         556 :                         static_cast<GUInt32 *>(pImage)[nTargetIdx]++;
    1397         556 :                         continue;
    1398             :                     }
    1399             : 
    1400        7196 :                     CPLAssert(depthsPtr);
    1401        7196 :                     CPLAssert(uncrtPtr);
    1402             : 
    1403        7196 :                     const unsigned nRefinementIndex =
    1404        7196 :                         nRefinementIndexase + super_x;
    1405             :                     const auto *pafRefValues =
    1406        7196 :                         poGDS->GetRefinementValues(nRefinementIndex);
    1407        7196 :                     if (!pafRefValues)
    1408             :                     {
    1409           0 :                         eErr = CE_Failure;
    1410           0 :                         goto end;
    1411             :                     }
    1412             : 
    1413        7196 :                     float depth = pafRefValues[0];
    1414        7196 :                     if (depth == fNoDataValue)
    1415             :                     {
    1416           0 :                         if (depthsPtr[nTargetIdx] == fNoSuperGridValue)
    1417             :                         {
    1418           0 :                             depthsPtr[nTargetIdx] = fNoDataValue;
    1419             :                         }
    1420           0 :                         continue;
    1421             :                     }
    1422             : 
    1423        7196 :                     if (poGDS->m_ePopulation == BAGDataset::Population::MEAN)
    1424             :                     {
    1425             : 
    1426         556 :                         if (counts[nTargetIdx] == 0)
    1427             :                         {
    1428         133 :                             depthsPtr[nTargetIdx] = depth;
    1429             :                         }
    1430             :                         else
    1431             :                         {
    1432         423 :                             depthsPtr[nTargetIdx] += depth;
    1433             :                         }
    1434         556 :                         counts[nTargetIdx]++;
    1435             : 
    1436         556 :                         auto uncrt = pafRefValues[1];
    1437         556 :                         auto &target_uncrt_ptr = uncrtPtr[nTargetIdx];
    1438         556 :                         if (uncrt > target_uncrt_ptr ||
    1439         408 :                             target_uncrt_ptr == fNoDataValue)
    1440             :                         {
    1441         281 :                             target_uncrt_ptr = uncrt;
    1442             :                         }
    1443             :                     }
    1444        6640 :                     else if ((poGDS->m_ePopulation ==
    1445        6084 :                                   BAGDataset::Population::MAX &&
    1446        6084 :                               depth > depthsPtr[nTargetIdx]) ||
    1447        5371 :                              (poGDS->m_ePopulation ==
    1448         556 :                                   BAGDataset::Population::MIN &&
    1449         556 :                               depth < depthsPtr[nTargetIdx]) ||
    1450        5145 :                              depthsPtr[nTargetIdx] == fNoDataValue ||
    1451        2172 :                              depthsPtr[nTargetIdx] == fNoSuperGridValue)
    1452             :                     {
    1453        4468 :                         depthsPtr[nTargetIdx] = depth;
    1454        4468 :                         uncrtPtr[nTargetIdx] = pafRefValues[1];
    1455             :                     }
    1456             :                 }
    1457             :             }
    1458             :         }
    1459             :     }
    1460             : 
    1461         289 :     if (poGDS->m_ePopulation == BAGDataset::Population::MEAN && depthsPtr)
    1462             :     {
    1463         151 :         for (int i = 0; i < nBlockXSize * nBlockYSize; i++)
    1464             :         {
    1465         150 :             if (counts[i])
    1466             :             {
    1467         133 :                 depthsPtr[i] /= counts[i];
    1468             :             }
    1469             :         }
    1470             :     }
    1471             : 
    1472         288 : end:
    1473         289 :     if (poBlock != nullptr)
    1474             :     {
    1475         287 :         poBlock->DropLock();
    1476         287 :         poBlock = nullptr;
    1477             :     }
    1478             : 
    1479         289 :     return eErr;
    1480             : }
    1481             : 
    1482             : /************************************************************************/
    1483             : /*                        BAGInterpolatedBand()                         */
    1484             : /************************************************************************/
    1485             : 
    1486          10 : BAGInterpolatedBand::BAGInterpolatedBand(BAGDataset *poDSIn, int nBandIn,
    1487             :                                          bool bHasNoData, float fNoDataValue,
    1488          10 :                                          bool bInitializeMinMax)
    1489             : {
    1490          10 :     poDS = poDSIn;
    1491          10 :     nBand = nBandIn;
    1492          10 :     nRasterXSize = poDS->GetRasterXSize();
    1493          10 :     nRasterYSize = poDS->GetRasterYSize();
    1494             :     // Mostly for autotest purposes
    1495             :     const int nBlockSize =
    1496          10 :         std::max(1, atoi(CPLGetConfigOption("GDAL_BAG_BLOCK_SIZE", "256")));
    1497          10 :     nBlockXSize = std::min(nBlockSize, poDS->GetRasterXSize());
    1498          10 :     nBlockYSize = std::min(nBlockSize, poDS->GetRasterYSize());
    1499             : 
    1500          10 :     m_bHasNoData = true;
    1501          10 :     m_fNoDataValue = bHasNoData ? fNoDataValue : fDEFAULT_NODATA;
    1502          10 :     eDataType = GDT_Float32;
    1503          10 :     GDALRasterBand::SetDescription(nBand == 1 ? "elevation" : "uncertainty");
    1504          10 :     if (bInitializeMinMax)
    1505             :     {
    1506           2 :         InitializeMinMax();
    1507             :     }
    1508          10 : }
    1509             : 
    1510             : /************************************************************************/
    1511             : /*                        ~BAGInterpolatedBand()                        */
    1512             : /************************************************************************/
    1513             : 
    1514             : BAGInterpolatedBand::~BAGInterpolatedBand() = default;
    1515             : 
    1516             : /************************************************************************/
    1517             : /*                           InitializeMinMax()                         */
    1518             : /************************************************************************/
    1519             : 
    1520           2 : void BAGInterpolatedBand::InitializeMinMax()
    1521             : {
    1522           2 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
    1523           5 :     if (nBand == 1 &&
    1524           1 :         GH5_FetchAttribute(poGDS->m_hVarresRefinements, "max_depth",
    1525           3 :                            m_dfMaximum) &&
    1526           0 :         GH5_FetchAttribute(poGDS->m_hVarresRefinements, "min_depth",
    1527           0 :                            m_dfMinimum))
    1528             :     {
    1529           0 :         m_bMinMaxSet = true;
    1530             :     }
    1531           5 :     else if (nBand == 2 &&
    1532           1 :              GH5_FetchAttribute(poGDS->m_hVarresRefinements, "max_uncrt",
    1533           3 :                                 m_dfMaximum) &&
    1534           0 :              GH5_FetchAttribute(poGDS->m_hVarresRefinements, "min_uncrt",
    1535           0 :                                 m_dfMinimum))
    1536             :     {
    1537           0 :         m_bMinMaxSet = true;
    1538             :     }
    1539           2 : }
    1540             : 
    1541             : /************************************************************************/
    1542             : /*                             GetMinimum()                             */
    1543             : /************************************************************************/
    1544             : 
    1545           2 : double BAGInterpolatedBand::GetMinimum(int *pbSuccess)
    1546             : 
    1547             : {
    1548           2 :     if (m_bMinMaxSet)
    1549             :     {
    1550           0 :         if (pbSuccess)
    1551           0 :             *pbSuccess = TRUE;
    1552           0 :         return m_dfMinimum;
    1553             :     }
    1554             : 
    1555           2 :     return GDALRasterBand::GetMinimum(pbSuccess);
    1556             : }
    1557             : 
    1558             : /************************************************************************/
    1559             : /*                             GetMaximum()                             */
    1560             : /************************************************************************/
    1561             : 
    1562           2 : double BAGInterpolatedBand::GetMaximum(int *pbSuccess)
    1563             : 
    1564             : {
    1565           2 :     if (m_bMinMaxSet)
    1566             :     {
    1567           0 :         if (pbSuccess)
    1568           0 :             *pbSuccess = TRUE;
    1569           0 :         return m_dfMaximum;
    1570             :     }
    1571             : 
    1572           2 :     return GDALRasterBand::GetMaximum(pbSuccess);
    1573             : }
    1574             : 
    1575             : /************************************************************************/
    1576             : /*                     BarycentricInterpolation()                       */
    1577             : /************************************************************************/
    1578             : 
    1579         203 : static bool BarycentricInterpolation(double dfX, double dfY,
    1580             :                                      const double adfX[3], const double adfY[3],
    1581             :                                      double &dfCoord0, double &dfCoord1,
    1582             :                                      double &dfCoord2)
    1583             : {
    1584             :     /* See https://en.wikipedia.org/wiki/Barycentric_coordinate_system */
    1585         203 :     const double dfDenom = (adfY[1] - adfY[2]) * (adfX[0] - adfX[2]) +
    1586         203 :                            (adfX[2] - adfX[1]) * (adfY[0] - adfY[2]);
    1587         203 :     if (fabs(dfDenom) < 1e-5)
    1588             :     {
    1589             :         // Degenerate triangle
    1590           0 :         return false;
    1591             :     }
    1592         203 :     const double dfTerm0X = adfY[1] - adfY[2];
    1593         203 :     const double dfTerm0Y = adfX[2] - adfX[1];
    1594         203 :     const double dfTerm1X = adfY[2] - adfY[0];
    1595         203 :     const double dfTerm1Y = adfX[0] - adfX[2];
    1596         203 :     const double dfDiffX = dfX - adfX[2];
    1597         203 :     const double dfDiffY = dfY - adfY[2];
    1598         203 :     dfCoord0 = (dfTerm0X * dfDiffX + dfTerm0Y * dfDiffY) / dfDenom;
    1599         203 :     dfCoord1 = (dfTerm1X * dfDiffX + dfTerm1Y * dfDiffY) / dfDenom;
    1600         203 :     dfCoord2 = 1 - dfCoord0 - dfCoord1;
    1601         203 :     return (dfCoord0 >= 0 && dfCoord0 <= 1 && dfCoord1 >= 0 && dfCoord1 <= 1 &&
    1602         406 :             dfCoord2 >= 0 && dfCoord2 <= 1);
    1603             : }
    1604             : 
    1605             : /************************************************************************/
    1606             : /*                               SQ()                                   */
    1607             : /************************************************************************/
    1608             : 
    1609      205823 : static inline double SQ(double v)
    1610             : {
    1611      205823 :     return v * v;
    1612             : }
    1613             : 
    1614             : /************************************************************************/
    1615             : /*                             IReadBlock()                             */
    1616             : /************************************************************************/
    1617             : 
    1618           5 : CPLErr BAGInterpolatedBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    1619             :                                        void *pImage)
    1620             : {
    1621             :     HDF5_GLOBAL_LOCK();
    1622             : 
    1623           5 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
    1624             : #ifdef DEBUG_VERBOSE
    1625             :     CPLDebug(
    1626             :         "BAG",
    1627             :         "IReadBlock: nRasterXSize=%d, nBlockXOff=%d, nBlockYOff=%d, nBand=%d",
    1628             :         nRasterXSize, nBlockXOff, nBlockYOff, nBand);
    1629             : #endif
    1630             : 
    1631           5 :     float *depthsPtr = nullptr;
    1632           5 :     float *uncrtPtr = nullptr;
    1633             : 
    1634           5 :     GDALRasterBlock *poBlock = nullptr;
    1635           5 :     if (nBand == 1)
    1636             :     {
    1637           5 :         depthsPtr = static_cast<float *>(pImage);
    1638           5 :         poBlock = poGDS->GetRasterBand(2)->GetLockedBlockRef(nBlockXOff,
    1639             :                                                              nBlockYOff, TRUE);
    1640           5 :         if (poBlock)
    1641             :         {
    1642           5 :             uncrtPtr = static_cast<float *>(poBlock->GetDataRef());
    1643             :         }
    1644             :     }
    1645             :     else
    1646             :     {
    1647           0 :         uncrtPtr = static_cast<float *>(pImage);
    1648           0 :         poBlock = poGDS->GetRasterBand(1)->GetLockedBlockRef(nBlockXOff,
    1649             :                                                              nBlockYOff, TRUE);
    1650           0 :         if (poBlock)
    1651             :         {
    1652           0 :             depthsPtr = static_cast<float *>(poBlock->GetDataRef());
    1653             :         }
    1654             :     }
    1655             : 
    1656           5 :     if (!depthsPtr || !uncrtPtr)
    1657           0 :         return CE_Failure;
    1658             : 
    1659           5 :     GDALCopyWords(&m_fNoDataValue, GDT_Float32, 0, depthsPtr, GDT_Float32,
    1660           5 :                   static_cast<int>(sizeof(float)), nBlockXSize * nBlockYSize);
    1661             : 
    1662           5 :     GDALCopyWords(&m_fNoDataValue, GDT_Float32, 0, uncrtPtr, GDT_Float32,
    1663           5 :                   static_cast<int>(sizeof(float)), nBlockXSize * nBlockYSize);
    1664             : 
    1665             :     const int nReqCountX =
    1666           5 :         std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
    1667             :     const int nReqCountY =
    1668           5 :         std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
    1669             :     // Compute extent of block in georeferenced coordinates
    1670           5 :     const double dfBlockMinX =
    1671           5 :         poGDS->adfGeoTransform[0] +
    1672           5 :         nBlockXOff * nBlockXSize * poGDS->adfGeoTransform[1];
    1673           5 :     const double dfBlockMaxX =
    1674           5 :         dfBlockMinX + nReqCountX * poGDS->adfGeoTransform[1];
    1675           5 :     const double dfBlockMaxY =
    1676           5 :         poGDS->adfGeoTransform[3] +
    1677           5 :         nBlockYOff * nBlockYSize * poGDS->adfGeoTransform[5];
    1678           5 :     const double dfBlockMinY =
    1679           5 :         dfBlockMaxY + nReqCountY * poGDS->adfGeoTransform[5];
    1680             : 
    1681             :     // Compute min/max indices of intersecting supergrids (origin bottom-left)
    1682             :     // We add a margin of (dfLowResResX, dfLowResResY) to be able to
    1683             :     // properly interpolate if the edges of a GDAL block align with the edge
    1684             :     // of a low-res cell
    1685           5 :     const double dfLowResResX =
    1686           5 :         (poGDS->m_dfLowResMaxX - poGDS->m_dfLowResMinX) / poGDS->m_nLowResWidth;
    1687           5 :     const double dfLowResResY =
    1688           5 :         (poGDS->m_dfLowResMaxY - poGDS->m_dfLowResMinY) /
    1689           5 :         poGDS->m_nLowResHeight;
    1690             :     const int nLowResMinIdxX = std::max(
    1691          10 :         0,
    1692          10 :         static_cast<int>((dfBlockMinX - poGDS->m_dfLowResMinX - dfLowResResX) /
    1693           5 :                          dfLowResResX));
    1694             :     const int nLowResMinIdxY = std::max(
    1695          10 :         0,
    1696          10 :         static_cast<int>((dfBlockMinY - poGDS->m_dfLowResMinY - dfLowResResY) /
    1697           5 :                          dfLowResResY));
    1698             :     const int nLowResMaxIdxX = std::min(
    1699          10 :         poGDS->m_nLowResWidth - 1,
    1700          10 :         static_cast<int>((dfBlockMaxX - poGDS->m_dfLowResMinX + dfLowResResX) /
    1701           5 :                          dfLowResResX));
    1702             :     const int nLowResMaxIdxY = std::min(
    1703          10 :         poGDS->m_nLowResHeight - 1,
    1704          10 :         static_cast<int>((dfBlockMaxY - poGDS->m_dfLowResMinY + dfLowResResY) /
    1705           5 :                          dfLowResResY));
    1706           5 :     if (nLowResMinIdxX >= poGDS->m_nLowResWidth ||
    1707           5 :         nLowResMinIdxY >= poGDS->m_nLowResHeight || nLowResMaxIdxX < 0 ||
    1708             :         nLowResMaxIdxY < 0)
    1709             :     {
    1710           0 :         return CE_None;
    1711             :     }
    1712             : 
    1713             :     // Create memory space to receive the data.
    1714           5 :     const int nCountLowResX = nLowResMaxIdxX - nLowResMinIdxX + 1;
    1715           5 :     const int nCountLowResY = nLowResMaxIdxY - nLowResMinIdxY + 1;
    1716           5 :     hsize_t countVarresMD[2] = {static_cast<hsize_t>(nCountLowResY),
    1717           5 :                                 static_cast<hsize_t>(nCountLowResX)};
    1718           5 :     const hid_t memspaceVarresMD = H5Screate_simple(2, countVarresMD, nullptr);
    1719           5 :     H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
    1720             :                                    static_cast<H5OFFSET_TYPE>(0)};
    1721           5 :     if (H5Sselect_hyperslab(memspaceVarresMD, H5S_SELECT_SET, mem_offset,
    1722           5 :                             nullptr, countVarresMD, nullptr) < 0)
    1723             :     {
    1724           0 :         H5Sclose(memspaceVarresMD);
    1725           0 :         if (poBlock != nullptr)
    1726             :         {
    1727           0 :             poBlock->DropLock();
    1728           0 :             poBlock = nullptr;
    1729             :         }
    1730           0 :         return CE_Failure;
    1731             :     }
    1732             : 
    1733           5 :     std::vector<BAGRefinementGrid> rgrids(static_cast<size_t>(nCountLowResY) *
    1734          10 :                                           nCountLowResX);
    1735           5 :     if (!(poGDS->ReadVarresMetadataValue(nLowResMinIdxY, nLowResMinIdxX,
    1736             :                                          memspaceVarresMD, rgrids.data(),
    1737             :                                          nCountLowResY, nCountLowResX)))
    1738             :     {
    1739           0 :         H5Sclose(memspaceVarresMD);
    1740           0 :         if (poBlock != nullptr)
    1741             :         {
    1742           0 :             poBlock->DropLock();
    1743           0 :             poBlock = nullptr;
    1744             :         }
    1745           0 :         return CE_Failure;
    1746             :     }
    1747             : 
    1748           5 :     H5Sclose(memspaceVarresMD);
    1749             : 
    1750           5 :     CPLErr eErr = CE_None;
    1751           5 :     const double dfLowResMinX = poGDS->m_dfLowResMinX;
    1752           5 :     const double dfLowResMinY = poGDS->m_dfLowResMinY;
    1753             : 
    1754             :     // georeferenced (X,Y) coordinates of the source nodes from the refinement
    1755             :     //grids
    1756          10 :     std::vector<double> adfX, adfY;
    1757             :     // Depth and uncertainty values from the source nodes from the refinement
    1758             :     //grids
    1759          10 :     std::vector<float> afDepth, afUncrt;
    1760             :     // Work variable to sort adfX, adfY, afDepth, afUncrt w.r.t to their
    1761             :     // distance with the (dfX, dfY) point to be interpolated
    1762           5 :     std::vector<int> anIndices;
    1763             : 
    1764             :     // Maximum distance of candidate source nodes to the point to be
    1765             :     // interpolated
    1766           5 :     const double dfMaxDistance = 0.5 * std::max(dfLowResResX, dfLowResResY);
    1767             : 
    1768         253 :     for (int y = 0; y < nReqCountY; ++y)
    1769             :     {
    1770             :         // Y georeference ordinate of the center of the cell to interpolate
    1771         248 :         const double dfY = dfBlockMaxY + (y + 0.5) * poGDS->adfGeoTransform[5];
    1772             :         // Y index of the corresponding refinement grid
    1773         248 :         const int iYRefinedGrid =
    1774         248 :             static_cast<int>(floor((dfY - dfLowResMinY) / dfLowResResY));
    1775         248 :         if (iYRefinedGrid < nLowResMinIdxY || iYRefinedGrid > nLowResMaxIdxY)
    1776           0 :             continue;
    1777       30928 :         for (int x = 0; x < nReqCountX; ++x)
    1778             :         {
    1779             :             // X georeference ordinate of the center of the cell to interpolate
    1780       30680 :             const double dfX =
    1781       30680 :                 dfBlockMinX + (x + 0.5) * poGDS->adfGeoTransform[1];
    1782             :             // X index of the corresponding refinement grid
    1783       30680 :             const int iXRefinedGrid =
    1784       30680 :                 static_cast<int>((dfX - dfLowResMinX) / dfLowResResX);
    1785       30680 :             if (iXRefinedGrid < nLowResMinIdxX ||
    1786             :                 iXRefinedGrid > nLowResMaxIdxX)
    1787           0 :                 continue;
    1788             : 
    1789             :             // Correspond refinement grid
    1790             :             const auto &rgrid =
    1791       30680 :                 rgrids[(iYRefinedGrid - nLowResMinIdxY) * nCountLowResX +
    1792       30680 :                        (iXRefinedGrid - nLowResMinIdxX)];
    1793       30680 :             if (rgrid.nWidth == 0)
    1794             :             {
    1795           0 :                 continue;
    1796             :             }
    1797       30680 :             const float gridRes = std::max(rgrid.fResX, rgrid.fResY);
    1798       30680 :             if (!(gridRes > poGDS->m_dfResFilterMin &&
    1799       30680 :                   gridRes <= poGDS->m_dfResFilterMax))
    1800             :             {
    1801           0 :                 continue;
    1802             :             }
    1803             : 
    1804             :             // (dfMinRefinedX, dfMinRefinedY) is the georeferenced coordinate
    1805             :             // of the bottom-left corner of the refinement grid
    1806       30680 :             const double dfMinRefinedX =
    1807       30680 :                 dfLowResMinX + iXRefinedGrid * dfLowResResX + rgrid.fSWX;
    1808       30680 :             const double dfMinRefinedY =
    1809       30680 :                 dfLowResMinY + iYRefinedGrid * dfLowResResY + rgrid.fSWY;
    1810             : 
    1811             :             // (iXInRefinedGrid, iYInRefinedGrid) is the index of the cell
    1812             :             // within the refinement grid into which (dfX, dfY) falls into.
    1813       30680 :             const int iXInRefinedGrid =
    1814       30680 :                 static_cast<int>(floor((dfX - dfMinRefinedX) / rgrid.fResX));
    1815       30680 :             const int iYInRefinedGrid =
    1816       30680 :                 static_cast<int>(floor((dfY - dfMinRefinedY) / rgrid.fResY));
    1817             : 
    1818       30680 :             if (iXInRefinedGrid >= 0 &&
    1819       30680 :                 iXInRefinedGrid < static_cast<int>(rgrid.nWidth) - 1 &&
    1820        2088 :                 iYInRefinedGrid >= 0 &&
    1821        2088 :                 iYInRefinedGrid < static_cast<int>(rgrid.nHeight) - 1)
    1822             :             {
    1823             :                 // The point to interpolate is fully within a single refinement
    1824             :                 // grid
    1825         126 :                 const unsigned nRefinementIndexBase =
    1826         126 :                     rgrid.nIndex + iYInRefinedGrid * rgrid.nWidth +
    1827         126 :                     iXInRefinedGrid;
    1828             :                 float d[2][2];
    1829             :                 float u[2][2];
    1830         126 :                 int nCountNoData = 0;
    1831             : 
    1832             :                 // Load the depth and uncertainty values of the 4 nodes of
    1833             :                 // the refinement grid surrounding the center of the target
    1834             :                 // cell.
    1835         378 :                 for (int j = 0; j < 2; ++j)
    1836             :                 {
    1837         756 :                     for (int i = 0; i < 2; ++i)
    1838             :                     {
    1839         504 :                         const unsigned nRefinementIndex =
    1840         504 :                             nRefinementIndexBase + j * rgrid.nWidth + i;
    1841             :                         const auto pafRefValues =
    1842         504 :                             poGDS->GetRefinementValues(nRefinementIndex);
    1843         504 :                         if (!pafRefValues)
    1844             :                         {
    1845           0 :                             eErr = CE_Failure;
    1846           0 :                             goto end;
    1847             :                         }
    1848         504 :                         d[j][i] = pafRefValues[0];
    1849         504 :                         u[j][i] = pafRefValues[1];
    1850         504 :                         if (d[j][i] == m_fNoDataValue)
    1851         121 :                             ++nCountNoData;
    1852             :                     }
    1853             :                 }
    1854             : 
    1855             :                 // Compute the relative distance of the point to be
    1856             :                 // interpolated compared to the closest bottom-left most node
    1857             :                 // of the refinement grid.
    1858             :                 // (alphaX,alphaY)=(0,0): point to be interpolated matches the
    1859             :                 // closest bottom-left most node
    1860             :                 // (alphaX,alphaY)=(1,1): point to be interpolated matches the
    1861             :                 // closest top-right most node
    1862             :                 const double alphaX =
    1863         126 :                     fmod(dfX - dfMinRefinedX, rgrid.fResX) / rgrid.fResX;
    1864             :                 const double alphaY =
    1865         126 :                     fmod(dfY - dfMinRefinedY, rgrid.fResY) / rgrid.fResY;
    1866         126 :                 if (nCountNoData == 0)
    1867             :                 {
    1868             :                     // If the 4 nodes of the supergrid around the point
    1869             :                     // to be interpolated are valid, do bilinear interpolation
    1870             : #define BILINEAR_INTERP(var)                                                   \
    1871             :     ((1 - alphaY) * ((1 - alphaX) * var[0][0] + alphaX * var[0][1]) +          \
    1872             :      alphaY * ((1 - alphaX) * var[1][0] + alphaX * var[1][1]))
    1873             : 
    1874           5 :                     depthsPtr[y * nBlockXSize + x] =
    1875           5 :                         static_cast<float>(BILINEAR_INTERP(d));
    1876           5 :                     uncrtPtr[y * nBlockXSize + x] =
    1877           5 :                         static_cast<float>(BILINEAR_INTERP(u));
    1878             :                 }
    1879         121 :                 else if (nCountNoData == 1)
    1880             :                 {
    1881             :                     // If only one of the 4 nodes is at nodata, determine if the
    1882             :                     // point to be interpolated is within the triangle formed
    1883             :                     // by the remaining 3 valid nodes.
    1884             :                     // If so, do barycentric interpolation
    1885         121 :                     adfX.resize(3);
    1886         121 :                     adfY.resize(3);
    1887         121 :                     afDepth.resize(3);
    1888         121 :                     afUncrt.resize(3);
    1889             : 
    1890         121 :                     int idx = 0;
    1891         363 :                     for (int j = 0; j < 2; ++j)
    1892             :                     {
    1893         726 :                         for (int i = 0; i < 2; ++i)
    1894             :                         {
    1895         484 :                             if (d[j][i] != m_fNoDataValue)
    1896             :                             {
    1897         363 :                                 CPLAssert(idx < 3);
    1898         363 :                                 adfX[idx] = i;
    1899         363 :                                 adfY[idx] = j;
    1900         363 :                                 afDepth[idx] = d[j][i];
    1901         363 :                                 afUncrt[idx] = u[j][i];
    1902         363 :                                 ++idx;
    1903             :                             }
    1904             :                         }
    1905             :                     }
    1906         121 :                     CPLAssert(idx == 3);
    1907             :                     double dfCoord0;
    1908             :                     double dfCoord1;
    1909             :                     double dfCoord2;
    1910         121 :                     if (BarycentricInterpolation(alphaX, alphaY, adfX.data(),
    1911         121 :                                                  adfY.data(), dfCoord0,
    1912             :                                                  dfCoord1, dfCoord2))
    1913             :                     {
    1914             :                         // Inside triangle
    1915          98 :                         depthsPtr[y * nBlockXSize + x] = static_cast<float>(
    1916          98 :                             dfCoord0 * afDepth[0] + dfCoord1 * afDepth[1] +
    1917          98 :                             dfCoord2 * afDepth[2]);
    1918          98 :                         uncrtPtr[y * nBlockXSize + x] = static_cast<float>(
    1919          98 :                             dfCoord0 * afUncrt[0] + dfCoord1 * afUncrt[1] +
    1920          98 :                             dfCoord2 * afUncrt[2]);
    1921             :                     }
    1922         126 :                 }
    1923             :                 // else: 2 or more nodes invalid. Target point is set at nodata
    1924             :             }
    1925             :             else
    1926             :             {
    1927             :                 // Point to interpolate is on an edge or corner of the
    1928             :                 // refinement grid
    1929       30554 :                 adfX.clear();
    1930       30554 :                 adfY.clear();
    1931       30554 :                 afDepth.clear();
    1932       30554 :                 afUncrt.clear();
    1933             : 
    1934             :                 const auto LoadValues =
    1935       92778 :                     [this, dfX, dfY, &rgrids, nLowResMinIdxX, nLowResMinIdxY,
    1936             :                      nCountLowResX, nCountLowResY, dfLowResMinX, dfLowResMinY,
    1937             :                      dfLowResResX, dfLowResResY, &adfX, &adfY, &afDepth,
    1938       92778 :                      &afUncrt](int iX, int iY)
    1939             :                 {
    1940       92778 :                     LoadClosestRefinedNodes(
    1941             :                         dfX, dfY, iX, iY, rgrids, nLowResMinIdxX,
    1942             :                         nLowResMinIdxY, nCountLowResX, nCountLowResY,
    1943             :                         dfLowResMinX, dfLowResMinY, dfLowResResX, dfLowResResY,
    1944             :                         adfX, adfY, afDepth, afUncrt);
    1945      123332 :                 };
    1946             : 
    1947             :                 // Load values of the closest point to the point to be
    1948             :                 // interpolated in the current refinement grid
    1949       30554 :                 LoadValues(iXRefinedGrid, iYRefinedGrid);
    1950             : 
    1951       30554 :                 const bool bAtLeft = iXInRefinedGrid < 0 && iXRefinedGrid > 0;
    1952       30554 :                 const bool bAtRight =
    1953       59146 :                     iXInRefinedGrid >= static_cast<int>(rgrid.nWidth) - 1 &&
    1954       28592 :                     iXRefinedGrid + 1 < poGDS->m_nLowResWidth;
    1955       30554 :                 const bool bAtBottom = iYInRefinedGrid < 0 && iYRefinedGrid > 0;
    1956       30554 :                 const bool bAtTop =
    1957       59396 :                     iYInRefinedGrid >= static_cast<int>(rgrid.nHeight) - 1 &&
    1958       28842 :                     iYRefinedGrid + 1 < poGDS->m_nLowResHeight;
    1959       30554 :                 const int nXShift = bAtLeft ? -1 : bAtRight ? 1 : 0;
    1960       30554 :                 const int nYShift = bAtBottom ? -1 : bAtTop ? 1 : 0;
    1961             : 
    1962             :                 // Load values of the closest point to the point to be
    1963             :                 // interpolated in the surrounding refinement grids.
    1964       30554 :                 if (nXShift)
    1965             :                 {
    1966       23824 :                     LoadValues(iXRefinedGrid + nXShift, iYRefinedGrid);
    1967       23824 :                     if (nYShift)
    1968             :                     {
    1969       16778 :                         LoadValues(iXRefinedGrid + nXShift,
    1970             :                                    iYRefinedGrid + nYShift);
    1971             :                     }
    1972             :                 }
    1973       30554 :                 if (nYShift)
    1974             :                 {
    1975       21622 :                     LoadValues(iXRefinedGrid, iYRefinedGrid + nYShift);
    1976             :                 }
    1977             : 
    1978             :                 // Filter out candidate source points that are away from
    1979             :                 // target point of more than dfMaxDistance
    1980       30554 :                 size_t j = 0;
    1981      129888 :                 for (size_t i = 0; i < adfX.size(); ++i)
    1982             :                 {
    1983       99334 :                     if (SQ(adfX[i] - dfX) + SQ(adfY[i] - dfY) <=
    1984       99334 :                         dfMaxDistance * dfMaxDistance)
    1985             :                     {
    1986       28244 :                         adfX[j] = adfX[i];
    1987       28244 :                         adfY[j] = adfY[i];
    1988       28244 :                         afDepth[j] = afDepth[i];
    1989       28244 :                         afUncrt[j] = afUncrt[i];
    1990       28244 :                         ++j;
    1991             :                     }
    1992             :                 }
    1993       30554 :                 adfX.resize(j);
    1994       30554 :                 adfY.resize(j);
    1995       30554 :                 afDepth.resize(j);
    1996       30554 :                 afUncrt.resize(j);
    1997             : 
    1998             :                 // Now interpolate the target point from the source values
    1999       30554 :                 bool bTryIDW = false;
    2000       30554 :                 if (adfX.size() >= 3)
    2001             :                 {
    2002             :                     // If there are at least 3 source nodes, sort them
    2003             :                     // by increasing distance w.r.t the point to be interpolated
    2004         319 :                     anIndices.clear();
    2005        1571 :                     for (size_t i = 0; i < adfX.size(); ++i)
    2006        1252 :                         anIndices.push_back(static_cast<int>(i));
    2007             :                     // Sort nodes by increasing distance w.r.t (dfX, dfY)
    2008         319 :                     std::sort(
    2009             :                         anIndices.begin(), anIndices.end(),
    2010       14240 :                         [&adfX, &adfY, dfX, dfY](int i1, int i2)
    2011             :                         {
    2012        1780 :                             return SQ(adfX[i1] - dfX) + SQ(adfY[i1] - dfY) <
    2013        1780 :                                    SQ(adfX[i2] - dfX) + SQ(adfY[i2] - dfY);
    2014             :                         });
    2015             :                     double adfXSorted[3];
    2016             :                     double adfYSorted[3];
    2017             :                     float afDepthSorted[3];
    2018             :                     float afUncrtSorted[3];
    2019        1276 :                     for (int i = 0; i < 3; ++i)
    2020             :                     {
    2021         957 :                         adfXSorted[i] = adfX[anIndices[i]];
    2022         957 :                         adfYSorted[i] = adfY[anIndices[i]];
    2023         957 :                         afDepthSorted[i] = afDepth[anIndices[i]];
    2024         957 :                         afUncrtSorted[i] = afUncrt[anIndices[i]];
    2025             :                     }
    2026             :                     double dfCoord0;
    2027             :                     double dfCoord1;
    2028             :                     double dfCoord2;
    2029             :                     // Perform barycentric interpolation with those 3
    2030             :                     // points if they are all valid, and if the point to
    2031             :                     // be interpolated falls into it.
    2032         319 :                     if (afDepthSorted[0] != m_fNoDataValue &&
    2033         230 :                         afDepthSorted[1] != m_fNoDataValue &&
    2034         174 :                         afDepthSorted[2] != m_fNoDataValue)
    2035             :                     {
    2036          82 :                         if (BarycentricInterpolation(dfX, dfY, adfXSorted,
    2037             :                                                      adfYSorted, dfCoord0,
    2038             :                                                      dfCoord1, dfCoord2))
    2039             :                         {
    2040             :                             // Inside triangle
    2041          77 :                             depthsPtr[y * nBlockXSize + x] =
    2042          77 :                                 static_cast<float>(dfCoord0 * afDepthSorted[0] +
    2043          77 :                                                    dfCoord1 * afDepthSorted[1] +
    2044          77 :                                                    dfCoord2 * afDepthSorted[2]);
    2045          77 :                             uncrtPtr[y * nBlockXSize + x] =
    2046          77 :                                 static_cast<float>(dfCoord0 * afUncrtSorted[0] +
    2047          77 :                                                    dfCoord1 * afUncrtSorted[1] +
    2048          77 :                                                    dfCoord2 * afUncrtSorted[2]);
    2049             :                         }
    2050             :                         else
    2051             :                         {
    2052             :                             // Attempt inverse distance weighting in the
    2053             :                             // cases where the point to be interpolated doesn't
    2054             :                             // fall within the triangle formed by the 3
    2055             :                             // closes points.
    2056           5 :                             bTryIDW = true;
    2057             :                         }
    2058             :                     }
    2059             :                 }
    2060       30554 :                 if (bTryIDW)
    2061             :                 {
    2062             :                     // Do inverse distance weighting a a fallback.
    2063             : 
    2064           5 :                     int nCountValid = 0;
    2065           5 :                     double dfTotalDepth = 0;
    2066           5 :                     double dfTotalUncrt = 0;
    2067           5 :                     double dfTotalWeight = 0;
    2068             :                     // Epsilon value to add to weights to avoid potential
    2069             :                     // divergence to infinity if a source node is too close
    2070             :                     // to the target point
    2071           5 :                     const double EPS = SQ(std::min(poGDS->adfGeoTransform[1],
    2072           5 :                                                    -poGDS->adfGeoTransform[5]) /
    2073             :                                           10);
    2074          20 :                     for (size_t i = 0; i < adfX.size(); ++i)
    2075             :                     {
    2076          15 :                         if (afDepth[i] != m_fNoDataValue)
    2077             :                         {
    2078          15 :                             nCountValid++;
    2079             :                             double dfSqrDistance =
    2080          15 :                                 SQ(adfX[i] - dfX) + SQ(adfY[i] - dfY) + EPS;
    2081          15 :                             double dfWeight = 1. / dfSqrDistance;
    2082          15 :                             dfTotalDepth += dfWeight * afDepth[i];
    2083          15 :                             dfTotalUncrt += dfWeight * afUncrt[i];
    2084          15 :                             dfTotalWeight += dfWeight;
    2085             :                         }
    2086             :                     }
    2087           5 :                     if (nCountValid >= 3)
    2088             :                     {
    2089           5 :                         depthsPtr[y * nBlockXSize + x] =
    2090           5 :                             static_cast<float>(dfTotalDepth / dfTotalWeight);
    2091           5 :                         uncrtPtr[y * nBlockXSize + x] =
    2092           5 :                             static_cast<float>(dfTotalUncrt / dfTotalWeight);
    2093             :                     }
    2094             :                 }
    2095             :             }
    2096             :         }
    2097             :     }
    2098           5 : end:
    2099           5 :     if (poBlock != nullptr)
    2100             :     {
    2101           5 :         poBlock->DropLock();
    2102           5 :         poBlock = nullptr;
    2103             :     }
    2104             : 
    2105           5 :     return eErr;
    2106             : }
    2107             : 
    2108             : /************************************************************************/
    2109             : /*                   LoadClosestRefinedNodes()                          */
    2110             : /************************************************************************/
    2111             : 
    2112       92778 : void BAGInterpolatedBand::LoadClosestRefinedNodes(
    2113             :     double dfX, double dfY, int iXRefinedGrid, int iYRefinedGrid,
    2114             :     const std::vector<BAGRefinementGrid> &rgrids, int nLowResMinIdxX,
    2115             :     int nLowResMinIdxY, int nCountLowResX, int nCountLowResY,
    2116             :     double dfLowResMinX, double dfLowResMinY, double dfLowResResX,
    2117             :     double dfLowResResY, std::vector<double> &adfX, std::vector<double> &adfY,
    2118             :     std::vector<float> &afDepth, std::vector<float> &afUncrt)
    2119             : {
    2120       92778 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
    2121       92778 :     CPLAssert(iXRefinedGrid >= nLowResMinIdxX);
    2122       92778 :     CPLAssert(iXRefinedGrid < nLowResMinIdxX + nCountLowResX);
    2123       92778 :     CPLAssert(iYRefinedGrid >= nLowResMinIdxY);
    2124       92778 :     CPLAssert(iYRefinedGrid < nLowResMinIdxY + nCountLowResY);
    2125       92778 :     CPL_IGNORE_RET_VAL(nCountLowResY);
    2126             :     const auto &rgrid =
    2127       92778 :         rgrids[(iYRefinedGrid - nLowResMinIdxY) * nCountLowResX +
    2128       92778 :                (iXRefinedGrid - nLowResMinIdxX)];
    2129       92778 :     if (rgrid.nWidth == 0)
    2130             :     {
    2131           0 :         return;
    2132             :     }
    2133       92778 :     const float gridRes = std::max(rgrid.fResX, rgrid.fResY);
    2134       92778 :     if (!(gridRes > poGDS->m_dfResFilterMin &&
    2135       92778 :           gridRes <= poGDS->m_dfResFilterMax))
    2136             :     {
    2137           0 :         return;
    2138             :     }
    2139             : 
    2140       92778 :     const double dfMinRefinedX =
    2141       92778 :         dfLowResMinX + iXRefinedGrid * dfLowResResX + rgrid.fSWX;
    2142       92778 :     const double dfMinRefinedY =
    2143       92778 :         dfLowResMinY + iYRefinedGrid * dfLowResResY + rgrid.fSWY;
    2144       92778 :     const int iXInRefinedGrid =
    2145       92778 :         static_cast<int>(floor((dfX - dfMinRefinedX) / rgrid.fResX));
    2146       92778 :     const int iYInRefinedGrid =
    2147       92778 :         static_cast<int>(floor((dfY - dfMinRefinedY) / rgrid.fResY));
    2148             : 
    2149       99334 :     const auto LoadValues = [poGDS, dfMinRefinedX, dfMinRefinedY, &rgrid, &adfX,
    2150             :                              &adfY, &afDepth,
    2151      496670 :                              &afUncrt](int iXAdjusted, int iYAdjusted)
    2152             :     {
    2153       99334 :         const unsigned nRefinementIndex =
    2154       99334 :             rgrid.nIndex + iYAdjusted * rgrid.nWidth + iXAdjusted;
    2155       99334 :         const auto pafRefValues = poGDS->GetRefinementValues(nRefinementIndex);
    2156       99334 :         if (pafRefValues)
    2157             :         {
    2158           0 :             adfX.push_back(dfMinRefinedX +
    2159       99334 :                            iXAdjusted * static_cast<double>(rgrid.fResX));
    2160      198668 :             adfY.push_back(dfMinRefinedY +
    2161       99334 :                            iYAdjusted * static_cast<double>(rgrid.fResY));
    2162       99334 :             afDepth.push_back(pafRefValues[0]);
    2163       99334 :             afUncrt.push_back(pafRefValues[1]);
    2164             :         }
    2165       99334 :     };
    2166             : 
    2167             :     const int iXAdjusted = std::max(
    2168       92778 :         0, std::min(iXInRefinedGrid, static_cast<int>(rgrid.nWidth) - 1));
    2169             :     const int iYAdjusted = std::max(
    2170       92778 :         0, std::min(iYInRefinedGrid, static_cast<int>(rgrid.nHeight) - 1));
    2171       92778 :     LoadValues(iXAdjusted, iYAdjusted);
    2172       92778 :     if (iYInRefinedGrid >= 0 &&
    2173       54378 :         iYInRefinedGrid < static_cast<int>(rgrid.nHeight) - 1)
    2174        3145 :         LoadValues(iXAdjusted, iYInRefinedGrid + 1);
    2175       92778 :     if (iXInRefinedGrid >= 0 &&
    2176       52176 :         iXInRefinedGrid < static_cast<int>(rgrid.nWidth) - 1)
    2177        3411 :         LoadValues(iXInRefinedGrid + 1, iYAdjusted);
    2178             : }
    2179             : 
    2180             : /************************************************************************/
    2181             : /* ==================================================================== */
    2182             : /*                        BAGGeorefMDBandBase                           */
    2183             : /* ==================================================================== */
    2184             : /************************************************************************/
    2185             : 
    2186             : class BAGGeorefMDBandBase CPL_NON_FINAL : public GDALPamRasterBand
    2187             : {
    2188             :   protected:
    2189             :     std::shared_ptr<GDALMDArray> m_poKeys;
    2190             :     std::unique_ptr<GDALRasterBand> m_poElevBand;
    2191             :     std::unique_ptr<GDALRasterAttributeTable> m_poRAT{};
    2192             : 
    2193           6 :     BAGGeorefMDBandBase(const std::shared_ptr<GDALMDArray> &poValues,
    2194             :                         const std::shared_ptr<GDALMDArray> &poKeys,
    2195             :                         GDALRasterBand *poElevBand)
    2196           6 :         : m_poKeys(poKeys), m_poElevBand(poElevBand),
    2197           6 :           m_poRAT(HDF5CreateRAT(poValues, false))
    2198             :     {
    2199           6 :     }
    2200             : 
    2201             :     CPLErr IReadBlockFromElevBand(int nBlockXOff, int nBlockYOff, void *pImage);
    2202             : 
    2203             :   public:
    2204           1 :     GDALRasterAttributeTable *GetDefaultRAT() override
    2205             :     {
    2206           1 :         return m_poRAT.get();
    2207             :     }
    2208             : 
    2209             :     double GetNoDataValue(int *pbSuccess) override;
    2210             : };
    2211             : 
    2212             : /************************************************************************/
    2213             : /*                         GetNoDataValue()                             */
    2214             : /************************************************************************/
    2215             : 
    2216           1 : double BAGGeorefMDBandBase::GetNoDataValue(int *pbSuccess)
    2217             : {
    2218           1 :     if (pbSuccess)
    2219           1 :         *pbSuccess = TRUE;
    2220           1 :     return 0;
    2221             : }
    2222             : 
    2223             : /************************************************************************/
    2224             : /*                   IReadBlockFromElevBand()                           */
    2225             : /************************************************************************/
    2226             : 
    2227           8 : CPLErr BAGGeorefMDBandBase::IReadBlockFromElevBand(int nBlockXOff,
    2228             :                                                    int nBlockYOff, void *pImage)
    2229             : {
    2230          16 :     std::vector<float> afData(static_cast<size_t>(nBlockXSize) * nBlockYSize);
    2231           8 :     const int nXOff = nBlockXOff * nBlockXSize;
    2232           8 :     const int nReqXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
    2233           8 :     const int nYOff = nBlockYOff * nBlockYSize;
    2234           8 :     const int nReqYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
    2235          16 :     if (m_poElevBand->RasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
    2236           8 :                                &afData[0], nReqXSize, nReqYSize, GDT_Float32, 4,
    2237          16 :                                nBlockXSize * 4, nullptr) != CE_None)
    2238             :     {
    2239           0 :         return CE_Failure;
    2240             :     }
    2241           8 :     int bHasNoData = FALSE;
    2242             :     const float fNoDataValue =
    2243           8 :         static_cast<float>(m_poElevBand->GetNoDataValue(&bHasNoData));
    2244           8 :     GByte *const pbyImage = static_cast<GByte *>(pImage);
    2245          16 :     for (int y = 0; y < nReqYSize; y++)
    2246             :     {
    2247          40 :         for (int x = 0; x < nReqXSize; x++)
    2248             :         {
    2249          32 :             pbyImage[y * nBlockXSize + x] =
    2250          32 :                 (afData[y * nBlockXSize + x] == fNoDataValue ||
    2251          29 :                  CPLIsNan(afData[y * nBlockXSize + x]))
    2252          61 :                     ? 0
    2253             :                     : 1;
    2254             :         }
    2255             :     }
    2256             : 
    2257           8 :     return CE_None;
    2258             : }
    2259             : 
    2260             : /************************************************************************/
    2261             : /* ==================================================================== */
    2262             : /*                             BAGGeorefMDBand                          */
    2263             : /* ==================================================================== */
    2264             : /************************************************************************/
    2265             : 
    2266             : class BAGGeorefMDBand final : public BAGGeorefMDBandBase
    2267             : {
    2268             :   public:
    2269             :     BAGGeorefMDBand(const std::shared_ptr<GDALMDArray> &poValues,
    2270             :                     const std::shared_ptr<GDALMDArray> &poKeys,
    2271             :                     GDALRasterBand *poElevBand);
    2272             : 
    2273             :     CPLErr IReadBlock(int, int, void *) override;
    2274             : };
    2275             : 
    2276             : /************************************************************************/
    2277             : /*                         BAGGeorefMDBand()                            */
    2278             : /************************************************************************/
    2279             : 
    2280           2 : BAGGeorefMDBand::BAGGeorefMDBand(const std::shared_ptr<GDALMDArray> &poValues,
    2281             :                                  const std::shared_ptr<GDALMDArray> &poKeys,
    2282           2 :                                  GDALRasterBand *poElevBand)
    2283           2 :     : BAGGeorefMDBandBase(poValues, poKeys, poElevBand)
    2284             : {
    2285           2 :     nRasterXSize = poElevBand->GetXSize();
    2286           2 :     nRasterYSize = poElevBand->GetYSize();
    2287           2 :     if (poKeys)
    2288             :     {
    2289           2 :         auto blockSize = poKeys->GetBlockSize();
    2290           1 :         CPLAssert(blockSize.size() == 2);
    2291           1 :         nBlockYSize = static_cast<int>(blockSize[0]);
    2292           1 :         nBlockXSize = static_cast<int>(blockSize[1]);
    2293           1 :         eDataType = poKeys->GetDataType().GetNumericDataType();
    2294           1 :         if (nBlockXSize == 0 || nBlockYSize == 0)
    2295             :         {
    2296           0 :             nBlockXSize = nRasterXSize;
    2297           0 :             nBlockYSize = 1;
    2298             :         }
    2299             :     }
    2300             :     else
    2301             :     {
    2302           1 :         eDataType = GDT_Byte;
    2303           1 :         m_poElevBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2304             :     }
    2305             : 
    2306             :     // For testing purposes
    2307             :     const char *pszBlockXSize =
    2308           2 :         CPLGetConfigOption("BAG_GEOREF_MD_BLOCKXSIZE", nullptr);
    2309           2 :     if (pszBlockXSize)
    2310           0 :         nBlockXSize = atoi(pszBlockXSize);
    2311             :     const char *pszBlockYSize =
    2312           2 :         CPLGetConfigOption("BAG_GEOREF_MD_BLOCKYSIZE", nullptr);
    2313           2 :     if (pszBlockYSize)
    2314           0 :         nBlockYSize = atoi(pszBlockYSize);
    2315           2 : }
    2316             : 
    2317             : /************************************************************************/
    2318             : /*                             IReadBlock()                             */
    2319             : /************************************************************************/
    2320             : 
    2321           8 : CPLErr BAGGeorefMDBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
    2322             : {
    2323             :     HDF5_GLOBAL_LOCK();
    2324             : 
    2325           8 :     if (m_poKeys)
    2326             :     {
    2327             :         const GUInt64 arrayStartIdx[2] = {
    2328           4 :             static_cast<GUInt64>(
    2329           4 :                 std::max(0, nRasterYSize - (nBlockYOff + 1) * nBlockYSize)),
    2330           4 :             static_cast<GUInt64>(nBlockXOff) * nBlockXSize};
    2331             :         size_t count[2] = {
    2332           8 :             std::min(static_cast<size_t>(nBlockYSize),
    2333           4 :                      static_cast<size_t>(GetYSize() - arrayStartIdx[0])),
    2334           8 :             std::min(static_cast<size_t>(nBlockXSize),
    2335           4 :                      static_cast<size_t>(GetXSize() - arrayStartIdx[1]))};
    2336           4 :         if (nRasterYSize - (nBlockYOff + 1) * nBlockYSize < 0)
    2337             :         {
    2338           0 :             count[0] += (nRasterYSize - (nBlockYOff + 1) * nBlockYSize);
    2339             :         }
    2340           4 :         const GInt64 arrayStep[2] = {1, 1};
    2341           4 :         const GPtrDiff_t bufferStride[2] = {nBlockXSize, 1};
    2342             : 
    2343           8 :         if (!m_poKeys->Read(arrayStartIdx, count, arrayStep, bufferStride,
    2344           4 :                             m_poKeys->GetDataType(), pImage))
    2345             :         {
    2346           0 :             return CE_Failure;
    2347             :         }
    2348             : 
    2349             :         // Y flip the data.
    2350           4 :         const int nLinesToFlip = static_cast<int>(count[0]);
    2351           4 :         if (nLinesToFlip > 1)
    2352             :         {
    2353             :             const int nLineSize =
    2354           4 :                 GDALGetDataTypeSizeBytes(eDataType) * nBlockXSize;
    2355           4 :             GByte *const pabyTemp = static_cast<GByte *>(CPLMalloc(nLineSize));
    2356           4 :             GByte *const pbyImage = static_cast<GByte *>(pImage);
    2357             : 
    2358           8 :             for (int iY = 0; iY < nLinesToFlip / 2; iY++)
    2359             :             {
    2360           4 :                 memcpy(pabyTemp, pbyImage + iY * nLineSize, nLineSize);
    2361           4 :                 memcpy(pbyImage + iY * nLineSize,
    2362           4 :                        pbyImage + (nLinesToFlip - iY - 1) * nLineSize,
    2363             :                        nLineSize);
    2364           4 :                 memcpy(pbyImage + (nLinesToFlip - iY - 1) * nLineSize, pabyTemp,
    2365             :                        nLineSize);
    2366             :             }
    2367             : 
    2368           4 :             CPLFree(pabyTemp);
    2369             :         }
    2370           4 :         return CE_None;
    2371             :     }
    2372             :     else
    2373             :     {
    2374           4 :         return IReadBlockFromElevBand(nBlockXOff, nBlockYOff, pImage);
    2375             :     }
    2376             : }
    2377             : 
    2378             : /************************************************************************/
    2379             : /* ==================================================================== */
    2380             : /*                    BAGGeorefMDSuperGridBand                          */
    2381             : /* ==================================================================== */
    2382             : /************************************************************************/
    2383             : 
    2384             : class BAGGeorefMDSuperGridBand final : public BAGGeorefMDBandBase
    2385             : {
    2386             :   public:
    2387             :     BAGGeorefMDSuperGridBand(const std::shared_ptr<GDALMDArray> &poValues,
    2388             :                              const std::shared_ptr<GDALMDArray> &poKeys,
    2389             :                              GDALRasterBand *poElevBand);
    2390             : 
    2391             :     CPLErr IReadBlock(int, int, void *) override;
    2392             : };
    2393             : 
    2394             : /************************************************************************/
    2395             : /*                     BAGGeorefMDSuperGridBand()                       */
    2396             : /************************************************************************/
    2397             : 
    2398           4 : BAGGeorefMDSuperGridBand::BAGGeorefMDSuperGridBand(
    2399             :     const std::shared_ptr<GDALMDArray> &poValues,
    2400           4 :     const std::shared_ptr<GDALMDArray> &poKeys, GDALRasterBand *poElevBand)
    2401           4 :     : BAGGeorefMDBandBase(poValues, poKeys, poElevBand)
    2402             : {
    2403           4 :     nRasterXSize = poElevBand->GetXSize();
    2404           4 :     nRasterYSize = poElevBand->GetYSize();
    2405           4 :     if (poKeys)
    2406             :     {
    2407           2 :         nBlockYSize = 1;
    2408           2 :         nBlockXSize = nRasterXSize;
    2409           2 :         eDataType = poKeys->GetDataType().GetNumericDataType();
    2410             :     }
    2411             :     else
    2412             :     {
    2413           2 :         eDataType = GDT_Byte;
    2414           2 :         m_poElevBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2415             :     }
    2416           4 : }
    2417             : 
    2418             : /************************************************************************/
    2419             : /*                             IReadBlock()                             */
    2420             : /************************************************************************/
    2421             : 
    2422           8 : CPLErr BAGGeorefMDSuperGridBand::IReadBlock(int nBlockXOff, int nBlockYOff,
    2423             :                                             void *pImage)
    2424             : {
    2425           8 :     BAGDataset *poGDS = cpl::down_cast<BAGDataset *>(poDS);
    2426           8 :     if (m_poKeys)
    2427             :     {
    2428           4 :         const GUInt64 arrayStartIdx[2] = {
    2429           4 :             0, poGDS->m_nSuperGridRefinementStartIndex +
    2430           4 :                    static_cast<GUInt64>(nRasterYSize - 1 - nBlockYOff) *
    2431           4 :                        nBlockXSize};
    2432           4 :         size_t count[2] = {1, static_cast<size_t>(nBlockXSize)};
    2433           4 :         const GInt64 arrayStep[2] = {1, 1};
    2434           4 :         const GPtrDiff_t bufferStride[2] = {nBlockXSize, 1};
    2435             : 
    2436           8 :         if (!m_poKeys->Read(arrayStartIdx, count, arrayStep, bufferStride,
    2437           4 :                             m_poKeys->GetDataType(), pImage))
    2438             :         {
    2439           0 :             return CE_Failure;
    2440             :         }
    2441           4 :         return CE_None;
    2442             :     }
    2443             :     else
    2444             :     {
    2445           4 :         return IReadBlockFromElevBand(nBlockXOff, nBlockYOff, pImage);
    2446             :     }
    2447             : }
    2448             : 
    2449             : /************************************************************************/
    2450             : /* ==================================================================== */
    2451             : /*                              BAGDataset                              */
    2452             : /* ==================================================================== */
    2453             : /************************************************************************/
    2454             : 
    2455             : /************************************************************************/
    2456             : /*                             BAGDataset()                             */
    2457             : /************************************************************************/
    2458             : 
    2459          88 : BAGDataset::BAGDataset()
    2460             : {
    2461          88 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2462          88 : }
    2463             : 
    2464          28 : BAGDataset::BAGDataset(BAGDataset *poParentDS, int nOvrFactor)
    2465             : {
    2466          28 :     const int nXSize = poParentDS->nRasterXSize / nOvrFactor;
    2467          28 :     const int nYSize = poParentDS->nRasterYSize / nOvrFactor;
    2468          28 :     InitOverviewDS(poParentDS, nXSize, nYSize);
    2469          28 : }
    2470             : 
    2471          17 : BAGDataset::BAGDataset(BAGDataset *poParentDS, int nXSize, int nYSize)
    2472             : {
    2473          17 :     InitOverviewDS(poParentDS, nXSize, nYSize);
    2474          17 : }
    2475             : 
    2476          45 : void BAGDataset::InitOverviewDS(BAGDataset *poParentDS, int nXSize, int nYSize)
    2477             : {
    2478          45 :     m_ePopulation = poParentDS->m_ePopulation;
    2479          45 :     m_bMask = poParentDS->m_bMask;
    2480          45 :     m_bIsChild = true;
    2481             :     // m_apoOverviewDS
    2482          45 :     m_poSharedResources = poParentDS->m_poSharedResources;
    2483          45 :     m_poRootGroup = poParentDS->m_poRootGroup;
    2484          45 :     m_oSRS = poParentDS->m_oSRS;
    2485          45 :     nRasterXSize = nXSize;
    2486          45 :     nRasterYSize = nYSize;
    2487          45 :     adfGeoTransform[0] = poParentDS->adfGeoTransform[0];
    2488          45 :     adfGeoTransform[1] = poParentDS->adfGeoTransform[1] *
    2489          45 :                          poParentDS->nRasterXSize / nRasterXSize;
    2490          45 :     adfGeoTransform[2] = poParentDS->adfGeoTransform[2];
    2491          45 :     adfGeoTransform[3] = poParentDS->adfGeoTransform[3];
    2492          45 :     adfGeoTransform[4] = poParentDS->adfGeoTransform[4];
    2493          45 :     adfGeoTransform[5] = poParentDS->adfGeoTransform[5] *
    2494          45 :                          poParentDS->nRasterYSize / nRasterYSize;
    2495          45 :     m_nLowResWidth = poParentDS->m_nLowResWidth;
    2496          45 :     m_nLowResHeight = poParentDS->m_nLowResHeight;
    2497          45 :     m_dfLowResMinX = poParentDS->m_dfLowResMinX;
    2498          45 :     m_dfLowResMinY = poParentDS->m_dfLowResMinY;
    2499          45 :     m_dfLowResMaxX = poParentDS->m_dfLowResMaxX;
    2500          45 :     m_dfLowResMaxY = poParentDS->m_dfLowResMaxY;
    2501             :     // char        *pszXMLMetadata = nullptr;
    2502             :     // char        *apszMDList[2]{};
    2503          45 :     m_nChunkXSizeVarresMD = poParentDS->m_nChunkXSizeVarresMD;
    2504          45 :     m_nChunkYSizeVarresMD = poParentDS->m_nChunkYSizeVarresMD;
    2505          45 :     m_nChunkSizeVarresRefinement = poParentDS->m_nChunkSizeVarresRefinement;
    2506             : 
    2507          45 :     m_hVarresMetadata = poParentDS->m_hVarresMetadata;
    2508          45 :     m_hVarresMetadataDataType = poParentDS->m_hVarresMetadataDataType;
    2509          45 :     m_hVarresMetadataDataspace = poParentDS->m_hVarresMetadataDataspace;
    2510          45 :     m_hVarresMetadataNative = poParentDS->m_hVarresMetadataNative;
    2511             :     // m_oMapRefinemendGrids;
    2512             : 
    2513             :     // m_aosSubdatasets;
    2514             : 
    2515          45 :     m_hVarresRefinements = poParentDS->m_hVarresRefinements;
    2516          45 :     m_hVarresRefinementsDataType = poParentDS->m_hVarresRefinementsDataType;
    2517          45 :     m_hVarresRefinementsDataspace = poParentDS->m_hVarresRefinementsDataspace;
    2518          45 :     m_hVarresRefinementsNative = poParentDS->m_hVarresRefinementsNative;
    2519          45 :     m_nRefinementsSize = poParentDS->m_nRefinementsSize;
    2520             : 
    2521          45 :     m_nSuperGridRefinementStartIndex =
    2522          45 :         poParentDS->m_nSuperGridRefinementStartIndex;
    2523          45 :     m_dfResFilterMin = poParentDS->m_dfResFilterMin;
    2524          45 :     m_dfResFilterMax = poParentDS->m_dfResFilterMax;
    2525             : 
    2526          45 :     if (poParentDS->GetRasterCount() > 1)
    2527             :     {
    2528          45 :         GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    2529             :     }
    2530          45 : }
    2531             : 
    2532             : /************************************************************************/
    2533             : /*                            ~BAGDataset()                             */
    2534             : /************************************************************************/
    2535         266 : BAGDataset::~BAGDataset()
    2536             : {
    2537         133 :     if (eAccess == GA_Update && nBands == 1)
    2538             :     {
    2539           2 :         auto poFirstBand = cpl::down_cast<BAGRasterBand *>(GetRasterBand(1));
    2540           2 :         auto poBand = new BAGRasterBand(this, 2);
    2541           2 :         poBand->nBlockXSize = poFirstBand->nBlockXSize;
    2542           2 :         poBand->nBlockYSize = poFirstBand->nBlockYSize;
    2543           2 :         poBand->eDataType = GDT_Float32;
    2544           2 :         poBand->m_bHasNoData = true;
    2545           2 :         poBand->m_fNoDataValue = poFirstBand->m_fNoDataValue;
    2546           2 :         SetBand(2, poBand);
    2547             :     }
    2548             : 
    2549         133 :     if (eAccess == GA_Update)
    2550             :     {
    2551          12 :         for (int i = 0; i < nBands; i++)
    2552             :         {
    2553             :             cpl::down_cast<BAGRasterBand *>(GetRasterBand(i + 1))
    2554           8 :                 ->CreateDatasetIfNeeded();
    2555             :         }
    2556             :     }
    2557             : 
    2558         133 :     FlushCache(true);
    2559             : 
    2560         133 :     m_apoOverviewDS.clear();
    2561         133 :     if (!m_bIsChild)
    2562             :     {
    2563          88 :         if (m_hVarresMetadataDataType >= 0)
    2564          63 :             H5Tclose(m_hVarresMetadataDataType);
    2565             : 
    2566          88 :         if (m_hVarresMetadataDataspace >= 0)
    2567          63 :             H5Sclose(m_hVarresMetadataDataspace);
    2568             : 
    2569          88 :         if (m_hVarresMetadataNative >= 0)
    2570          63 :             H5Tclose(m_hVarresMetadataNative);
    2571             : 
    2572          88 :         if (m_hVarresMetadata >= 0)
    2573          63 :             H5Dclose(m_hVarresMetadata);
    2574             : 
    2575          88 :         if (m_hVarresRefinementsDataType >= 0)
    2576          63 :             H5Tclose(m_hVarresRefinementsDataType);
    2577             : 
    2578          88 :         if (m_hVarresRefinementsDataspace >= 0)
    2579          63 :             H5Sclose(m_hVarresRefinementsDataspace);
    2580             : 
    2581          88 :         if (m_hVarresRefinementsNative >= 0)
    2582          63 :             H5Tclose(m_hVarresRefinementsNative);
    2583             : 
    2584          88 :         if (m_hVarresRefinements >= 0)
    2585          63 :             H5Dclose(m_hVarresRefinements);
    2586             : 
    2587          88 :         CPLFree(pszXMLMetadata);
    2588             :     }
    2589         266 : }
    2590             : 
    2591             : /************************************************************************/
    2592             : /*                          GH5DopenNoWarning()                         */
    2593             : /************************************************************************/
    2594             : 
    2595          83 : static hid_t GH5DopenNoWarning(hid_t hHDF5, const char *pszDatasetName)
    2596             : {
    2597             :     hid_t hDataset;
    2598             : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
    2599             : #pragma GCC diagnostic push
    2600             : #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
    2601             : #endif
    2602          83 :     H5E_BEGIN_TRY
    2603             :     {
    2604          83 :         hDataset = H5Dopen(hHDF5, pszDatasetName);
    2605             :     }
    2606          83 :     H5E_END_TRY;
    2607             : 
    2608             : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
    2609             : #pragma GCC diagnostic pop
    2610             : #endif
    2611          83 :     return hDataset;
    2612             : }
    2613             : 
    2614             : /************************************************************************/
    2615             : /*                                Open()                                */
    2616             : /************************************************************************/
    2617             : 
    2618          97 : GDALDataset *BAGDataset::Open(GDALOpenInfo *poOpenInfo)
    2619             : 
    2620             : {
    2621             :     // Confirm that this appears to be a BAG file.
    2622          97 :     if (!BAGDatasetIdentify(poOpenInfo))
    2623          12 :         return nullptr;
    2624             : 
    2625             :     HDF5_GLOBAL_LOCK();
    2626             : 
    2627          85 :     if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
    2628             :     {
    2629           0 :         return HDF5Dataset::OpenMultiDim(poOpenInfo);
    2630             :     }
    2631             : 
    2632             :     // Confirm the requested access is supported.
    2633          85 :     if (poOpenInfo->eAccess == GA_Update)
    2634             :     {
    2635           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2636             :                  "The BAG driver does not support update access.");
    2637           0 :         return nullptr;
    2638             :     }
    2639             : 
    2640          85 :     bool bOpenSuperGrid = false;
    2641          85 :     int nX = -1;
    2642          85 :     int nY = -1;
    2643         170 :     CPLString osFilename(poOpenInfo->pszFilename);
    2644         170 :     CPLString osGeorefMetadataLayer;
    2645          85 :     bool bIsSubdataset = false;
    2646          85 :     if (STARTS_WITH(poOpenInfo->pszFilename, "BAG:"))
    2647             :     {
    2648          16 :         bIsSubdataset = true;
    2649             :         char **papszTokens =
    2650          16 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":",
    2651             :                                CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES);
    2652             : 
    2653          17 :         if (CSLCount(papszTokens) == 3 &&
    2654           1 :             EQUAL(papszTokens[2], "bathymetry_coverage"))
    2655             :         {
    2656           1 :             osFilename = papszTokens[1];
    2657             :         }
    2658          18 :         else if (CSLCount(papszTokens) == 4 &&
    2659           3 :                  EQUAL(papszTokens[2], "georef_metadata"))
    2660             :         {
    2661           3 :             osFilename = papszTokens[1];
    2662           3 :             osGeorefMetadataLayer = papszTokens[3];
    2663             :         }
    2664          16 :         else if (CSLCount(papszTokens) == 6 &&
    2665           4 :                  EQUAL(papszTokens[2], "georef_metadata"))
    2666             :         {
    2667           4 :             osFilename = papszTokens[1];
    2668           4 :             osGeorefMetadataLayer = papszTokens[3];
    2669           4 :             bOpenSuperGrid = true;
    2670           4 :             nY = atoi(papszTokens[4]);
    2671           4 :             nX = atoi(papszTokens[5]);
    2672             :         }
    2673             :         else
    2674             :         {
    2675           8 :             if (CSLCount(papszTokens) != 5)
    2676             :             {
    2677           0 :                 CSLDestroy(papszTokens);
    2678           0 :                 return nullptr;
    2679             :             }
    2680           8 :             bOpenSuperGrid = true;
    2681           8 :             osFilename = papszTokens[1];
    2682           8 :             nY = atoi(papszTokens[3]);
    2683           8 :             nX = atoi(papszTokens[4]);
    2684             :         }
    2685          16 :         if (bOpenSuperGrid)
    2686             :         {
    2687             : 
    2688          12 :             if (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX") !=
    2689          11 :                     nullptr ||
    2690          11 :                 CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY") !=
    2691          11 :                     nullptr ||
    2692          11 :                 CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX") !=
    2693          11 :                     nullptr ||
    2694          11 :                 CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY") !=
    2695          23 :                     nullptr ||
    2696          11 :                 CSLFetchNameValue(poOpenInfo->papszOpenOptions,
    2697             :                                   "SUPERGRIDS_INDICES") != nullptr)
    2698             :             {
    2699           1 :                 CPLError(
    2700             :                     CE_Warning, CPLE_AppDefined,
    2701             :                     "Open options MINX/MINY/MAXX/MAXY/SUPERGRIDS_INDICES are "
    2702             :                     "ignored when opening a supergrid");
    2703             :             }
    2704             :         }
    2705          16 :         CSLDestroy(papszTokens);
    2706             :     }
    2707             : 
    2708             :     // Open the file as an HDF5 file.
    2709          85 :     hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
    2710          85 :     H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
    2711          85 :     hid_t hHDF5 = H5Fopen(
    2712             :         osFilename,
    2713          85 :         /*poOpenInfo->eAccess == GA_Update ? H5F_ACC_RDWR : */ H5F_ACC_RDONLY,
    2714             :         fapl);
    2715          85 :     H5Pclose(fapl);
    2716          85 :     if (hHDF5 < 0)
    2717           1 :         return nullptr;
    2718             : 
    2719             :     // Confirm it is a BAG dataset by checking for the
    2720             :     // BAG_root/Bag Version attribute.
    2721          84 :     const hid_t hBagRoot = H5Gopen(hHDF5, "/BAG_root");
    2722             :     const hid_t hVersion =
    2723          84 :         hBagRoot >= 0 ? H5Aopen_name(hBagRoot, "Bag Version") : -1;
    2724             : 
    2725          84 :     if (hVersion < 0)
    2726             :     {
    2727           0 :         if (hBagRoot >= 0)
    2728           0 :             H5Gclose(hBagRoot);
    2729           0 :         H5Fclose(hHDF5);
    2730           0 :         return nullptr;
    2731             :     }
    2732          84 :     H5Aclose(hVersion);
    2733             : 
    2734         168 :     auto poSharedResources = GDAL::HDF5SharedResources::Create(osFilename);
    2735          84 :     poSharedResources->m_hHDF5 = hHDF5;
    2736             : 
    2737         168 :     auto poRootGroup = HDF5Dataset::OpenGroup(poSharedResources);
    2738          84 :     if (poRootGroup == nullptr)
    2739           0 :         return nullptr;
    2740             : 
    2741             :     // Create a corresponding dataset.
    2742          84 :     BAGDataset *const poDS = new BAGDataset();
    2743             : 
    2744          84 :     poDS->eAccess = poOpenInfo->eAccess;
    2745          84 :     poDS->m_poRootGroup = std::move(poRootGroup);
    2746          84 :     poDS->m_poSharedResources = std::move(poSharedResources);
    2747             : 
    2748             :     // Extract version as metadata.
    2749         168 :     CPLString osVersion;
    2750             : 
    2751          84 :     if (GH5_FetchAttribute(hBagRoot, "Bag Version", osVersion))
    2752          84 :         poDS->GDALDataset::SetMetadataItem("BagVersion", osVersion);
    2753             : 
    2754          84 :     H5Gclose(hBagRoot);
    2755             : 
    2756         168 :     CPLString osSubDsName;
    2757          84 :     if (poOpenInfo->nOpenFlags & GDAL_OF_RASTER)
    2758             :     {
    2759          83 :         if (poDS->OpenRaster(poOpenInfo, osFilename, bOpenSuperGrid, nX, nY,
    2760             :                              bIsSubdataset, osGeorefMetadataLayer, osSubDsName))
    2761             :         {
    2762          76 :             if (!osSubDsName.empty())
    2763             :             {
    2764           2 :                 delete poDS;
    2765           4 :                 GDALOpenInfo oOpenInfo(osSubDsName, GA_ReadOnly);
    2766           2 :                 oOpenInfo.nOpenFlags = poOpenInfo->nOpenFlags;
    2767           2 :                 return Open(&oOpenInfo);
    2768             :             }
    2769             :         }
    2770             :         else
    2771             :         {
    2772           7 :             delete poDS;
    2773           7 :             return nullptr;
    2774             :         }
    2775             :     }
    2776             : 
    2777          75 :     if (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
    2778             :     {
    2779          47 :         if (!poDS->OpenVector() &&
    2780           1 :             (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0)
    2781             :         {
    2782           0 :             delete poDS;
    2783           0 :             return nullptr;
    2784             :         }
    2785             :     }
    2786             : 
    2787          75 :     return poDS;
    2788             : }
    2789             : 
    2790             : /************************************************************************/
    2791             : /*                          OpenRaster()                                */
    2792             : /************************************************************************/
    2793             : 
    2794          83 : bool BAGDataset::OpenRaster(GDALOpenInfo *poOpenInfo,
    2795             :                             const CPLString &osFilename, bool bOpenSuperGrid,
    2796             :                             int nX, int nY, bool bIsSubdataset,
    2797             :                             const CPLString &osGeorefMetadataLayer,
    2798             :                             CPLString &outOsSubDsName)
    2799             : {
    2800             :     const char *pszMode =
    2801          83 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "MODE", "AUTO");
    2802          83 :     const bool bLowResGrid = EQUAL(pszMode, "LOW_RES_GRID");
    2803          84 :     if (bLowResGrid &&
    2804           1 :         (CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX") != nullptr ||
    2805           0 :          CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY") != nullptr ||
    2806           0 :          CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX") != nullptr ||
    2807           0 :          CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY") != nullptr ||
    2808           0 :          CSLFetchNameValue(poOpenInfo->papszOpenOptions,
    2809             :                            "SUPERGRIDS_INDICES") != nullptr))
    2810             :     {
    2811           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    2812             :                  "Open options MINX/MINY/MAXX/MAXY/SUPERGRIDS_INDICES are "
    2813             :                  "ignored when opening the low resolution grid");
    2814             :     }
    2815             : 
    2816             :     const bool bListSubDS =
    2817         165 :         !bLowResGrid &&
    2818          82 :         (EQUAL(pszMode, "LIST_SUPERGRIDS") ||
    2819          77 :          CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX") != nullptr ||
    2820          75 :          CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY") != nullptr ||
    2821          74 :          CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX") != nullptr ||
    2822          74 :          CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY") != nullptr ||
    2823          74 :          CSLFetchNameValue(poOpenInfo->papszOpenOptions,
    2824          83 :                            "SUPERGRIDS_INDICES") != nullptr);
    2825          83 :     const bool bResampledGrid = EQUAL(pszMode, "RESAMPLED_GRID");
    2826          83 :     const bool bInterpolate = EQUAL(pszMode, "INTERPOLATED");
    2827             : 
    2828             :     const char *pszNoDataValue =
    2829          83 :         CSLFetchNameValue(poOpenInfo->papszOpenOptions, "NODATA_VALUE");
    2830          83 :     bool bHasNoData = pszNoDataValue != nullptr;
    2831             :     float fNoDataValue =
    2832          83 :         bHasNoData ? static_cast<float>(CPLAtof(pszNoDataValue)) : 0.0f;
    2833             : 
    2834             :     // Fetch the elevation dataset and attach as a band.
    2835          83 :     int nNextBand = 1;
    2836             : 
    2837          83 :     BAGRasterBand *poElevBand = new BAGRasterBand(this, nNextBand);
    2838             : 
    2839             :     {
    2840             :         const hid_t hElevation =
    2841          83 :             H5Dopen(GetHDF5Handle(), "/BAG_root/elevation");
    2842          83 :         if (hElevation < 0 || !poElevBand->Initialize(hElevation, "elevation"))
    2843             :         {
    2844           1 :             delete poElevBand;
    2845           1 :             return false;
    2846             :         }
    2847             :     }
    2848             : 
    2849          82 :     m_nLowResWidth = poElevBand->nRasterXSize;
    2850          82 :     m_nLowResHeight = poElevBand->nRasterYSize;
    2851             : 
    2852          82 :     if (bOpenSuperGrid || bListSubDS || bResampledGrid || bInterpolate)
    2853             :     {
    2854          53 :         if (!bHasNoData)
    2855             :         {
    2856          53 :             int nHasNoData = FALSE;
    2857          53 :             double dfNoData = poElevBand->GetNoDataValue(&nHasNoData);
    2858          53 :             if (nHasNoData)
    2859             :             {
    2860          53 :                 bHasNoData = true;
    2861          53 :                 fNoDataValue = static_cast<float>(dfNoData);
    2862             :             }
    2863             :         }
    2864          53 :         delete poElevBand;
    2865          53 :         nRasterXSize = 0;
    2866          53 :         nRasterYSize = 0;
    2867             :     }
    2868          29 :     else if (!osGeorefMetadataLayer.empty())
    2869             :     {
    2870           3 :         auto poGeoref_metadataLayer = m_poRootGroup->OpenGroupFromFullname(
    2871           3 :             "/BAG_root/georef_metadata/" + osGeorefMetadataLayer, nullptr);
    2872           3 :         if (poGeoref_metadataLayer == nullptr)
    2873             :         {
    2874           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2875             :                      "Cannot find georef_metadata layer %s",
    2876             :                      osGeorefMetadataLayer.c_str());
    2877           1 :             delete poElevBand;
    2878           1 :             return false;
    2879             :         }
    2880             : 
    2881           4 :         auto poKeys = poGeoref_metadataLayer->OpenMDArray("keys");
    2882           2 :         if (poKeys != nullptr)
    2883             :         {
    2884           1 :             auto poDims = poKeys->GetDimensions();
    2885           1 :             if (poDims.size() != 2 ||
    2886           1 :                 poDims[0]->GetSize() !=
    2887           3 :                     static_cast<size_t>(poElevBand->nRasterYSize) ||
    2888           1 :                 poDims[1]->GetSize() !=
    2889           1 :                     static_cast<size_t>(poElevBand->nRasterXSize))
    2890             :             {
    2891           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2892             :                          "Wrong dimensions for %s/keys",
    2893             :                          osGeorefMetadataLayer.c_str());
    2894           0 :                 delete poElevBand;
    2895           0 :                 return false;
    2896             :             }
    2897           2 :             if (poKeys->GetDataType().GetClass() != GEDTC_NUMERIC ||
    2898           1 :                 !GDALDataTypeIsInteger(
    2899           1 :                     poKeys->GetDataType().GetNumericDataType()))
    2900             :             {
    2901           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2902             :                          "Only integer data type supported for %s/keys",
    2903             :                          osGeorefMetadataLayer.c_str());
    2904           0 :                 delete poElevBand;
    2905           0 :                 return false;
    2906             :             }
    2907             :         }
    2908             : 
    2909           4 :         auto poValues = poGeoref_metadataLayer->OpenMDArray("values");
    2910           2 :         if (poValues == nullptr)
    2911             :         {
    2912           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2913             :                      "Cannot find array values of georef_metadata layer %s",
    2914             :                      osGeorefMetadataLayer.c_str());
    2915           0 :             delete poElevBand;
    2916           0 :             return false;
    2917             :         }
    2918           2 :         if (poValues->GetDimensionCount() != 1)
    2919             :         {
    2920           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2921             :                      "Wrong dimensions for %s/values",
    2922             :                      osGeorefMetadataLayer.c_str());
    2923           0 :             delete poElevBand;
    2924           0 :             return false;
    2925             :         }
    2926           2 :         if (poValues->GetDataType().GetClass() != GEDTC_COMPOUND)
    2927             :         {
    2928           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2929             :                      "Only compound data type supported for %s/values",
    2930             :                      osGeorefMetadataLayer.c_str());
    2931           0 :             delete poElevBand;
    2932           0 :             return false;
    2933             :         }
    2934             : 
    2935           2 :         nRasterXSize = poElevBand->nRasterXSize;
    2936           2 :         nRasterYSize = poElevBand->nRasterYSize;
    2937           2 :         SetBand(1, new BAGGeorefMDBand(poValues, poKeys, poElevBand));
    2938             :     }
    2939             :     else
    2940             :     {
    2941          26 :         nRasterXSize = poElevBand->nRasterXSize;
    2942          26 :         nRasterYSize = poElevBand->nRasterYSize;
    2943             : 
    2944          26 :         SetBand(nNextBand++, poElevBand);
    2945             : 
    2946             :         // Try to do the same for the uncertainty band.
    2947             :         const hid_t hUncertainty =
    2948          26 :             H5Dopen(GetHDF5Handle(), "/BAG_root/uncertainty");
    2949          26 :         BAGRasterBand *poUBand = new BAGRasterBand(this, nNextBand);
    2950             : 
    2951          52 :         if (hUncertainty >= 0 &&
    2952          26 :             poUBand->Initialize(hUncertainty, "uncertainty"))
    2953             :         {
    2954          26 :             SetBand(nNextBand++, poUBand);
    2955             :         }
    2956             :         else
    2957             :         {
    2958           0 :             delete poUBand;
    2959             :         }
    2960             : 
    2961             :         // Load other root datasets (such as nominal_elevation)
    2962          78 :         auto poBAG_root = m_poRootGroup->OpenGroup("BAG_root", nullptr);
    2963          26 :         if (poBAG_root)
    2964             :         {
    2965          52 :             const auto arrayNames = poBAG_root->GetMDArrayNames(nullptr);
    2966         153 :             for (const auto &arrayName : arrayNames)
    2967             :             {
    2968         127 :                 if (arrayName != "elevation" && arrayName != "uncertainty")
    2969             :                 {
    2970         150 :                     auto poArray = poBAG_root->OpenMDArray(arrayName, nullptr);
    2971         150 :                     if (poArray && poArray->GetDimensions().size() == 2 &&
    2972          18 :                         poArray->GetDimensions()[0]->GetSize() ==
    2973          18 :                             static_cast<unsigned>(nRasterYSize) &&
    2974          10 :                         poArray->GetDimensions()[1]->GetSize() ==
    2975         160 :                             static_cast<unsigned>(nRasterXSize) &&
    2976          10 :                         poArray->GetDataType().GetClass() == GEDTC_NUMERIC)
    2977             :                     {
    2978           2 :                         hid_t hBandId = GH5DopenNoWarning(
    2979             :                             GetHDF5Handle(),
    2980           4 :                             ("/BAG_root/" + arrayName).c_str());
    2981             :                         BAGRasterBand *const poBand =
    2982           2 :                             new BAGRasterBand(this, nNextBand);
    2983           4 :                         if (hBandId >= 0 &&
    2984           2 :                             poBand->Initialize(hBandId, arrayName.c_str()))
    2985             :                         {
    2986           2 :                             SetBand(nNextBand++, poBand);
    2987             :                         }
    2988             :                         else
    2989             :                         {
    2990           0 :                             delete poBand;
    2991             :                         }
    2992             :                     }
    2993             :                 }
    2994             :             }
    2995             :         }
    2996             :     }
    2997             : 
    2998          81 :     SetDescription(poOpenInfo->pszFilename);
    2999             : 
    3000          81 :     m_bReportVertCRS = CPLTestBool(CSLFetchNameValueDef(
    3001          81 :         poOpenInfo->papszOpenOptions, "REPORT_VERTCRS", "YES"));
    3002             : 
    3003             :     // Load the XML metadata.
    3004          81 :     LoadMetadata();
    3005             : 
    3006          81 :     if (bResampledGrid)
    3007             :     {
    3008          25 :         m_bMask = CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
    3009             :                                                    "SUPERGRIDS_MASK", "NO"));
    3010             :     }
    3011             : 
    3012          81 :     if (!m_bMask)
    3013             :     {
    3014          80 :         GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
    3015             :     }
    3016             : 
    3017             :     // Load for refinements grids for variable resolution datasets
    3018          81 :     bool bHasRefinementGrids = false;
    3019          81 :     if (bOpenSuperGrid || bListSubDS || bResampledGrid || bInterpolate)
    3020             :     {
    3021             :         bHasRefinementGrids =
    3022          53 :             LookForRefinementGrids(poOpenInfo->papszOpenOptions, nY, nX);
    3023          55 :         if (!bOpenSuperGrid && m_aosSubdatasets.size() == 2 &&
    3024           2 :             EQUAL(pszMode, "AUTO"))
    3025             :         {
    3026             :             outOsSubDsName =
    3027           2 :                 CSLFetchNameValueDef(m_aosSubdatasets, "SUBDATASET_1_NAME", "");
    3028           2 :             return true;
    3029             :         }
    3030             :     }
    3031             :     else
    3032             :     {
    3033          28 :         if (LookForRefinementGrids(poOpenInfo->papszOpenOptions, 0, 0))
    3034             :         {
    3035          10 :             GDALDataset::SetMetadataItem("HAS_SUPERGRIDS", "TRUE");
    3036             :         }
    3037          28 :         m_aosSubdatasets.Clear();
    3038             :     }
    3039             : 
    3040          79 :     if (!bIsSubdataset && osGeorefMetadataLayer.empty())
    3041             :     {
    3042          65 :         auto poGeoref_metadata = m_poRootGroup->OpenGroupFromFullname(
    3043         195 :             "/BAG_root/georef_metadata", nullptr);
    3044          65 :         if (poGeoref_metadata)
    3045             :         {
    3046           4 :             const auto groupNames = poGeoref_metadata->GetGroupNames(nullptr);
    3047           2 :             if (!groupNames.empty())
    3048             :             {
    3049           2 :                 if (m_aosSubdatasets.empty())
    3050             :                 {
    3051           1 :                     const int nIdx = 1;
    3052             :                     m_aosSubdatasets.AddNameValue(
    3053             :                         CPLSPrintf("SUBDATASET_%d_NAME", nIdx),
    3054             :                         CPLSPrintf("BAG:\"%s\":bathymetry_coverage",
    3055           1 :                                    GetDescription()));
    3056             :                     m_aosSubdatasets.AddNameValue(
    3057             :                         CPLSPrintf("SUBDATASET_%d_DESC", nIdx),
    3058           1 :                         "Bathymetry gridded data");
    3059             :                 }
    3060           6 :                 for (const auto &groupName : groupNames)
    3061             :                 {
    3062           4 :                     const int nIdx = m_aosSubdatasets.size() / 2 + 1;
    3063             :                     m_aosSubdatasets.AddNameValue(
    3064             :                         CPLSPrintf("SUBDATASET_%d_NAME", nIdx),
    3065             :                         CPLSPrintf("BAG:\"%s\":georef_metadata:%s",
    3066           4 :                                    GetDescription(), groupName.c_str()));
    3067             :                     m_aosSubdatasets.AddNameValue(
    3068             :                         CPLSPrintf("SUBDATASET_%d_DESC", nIdx),
    3069             :                         CPLSPrintf("Georeferenced metadata %s",
    3070           4 :                                    groupName.c_str()));
    3071             :                 }
    3072             :             }
    3073             :         }
    3074             :     }
    3075             : 
    3076          79 :     double dfMinResX = 0.0;
    3077          79 :     double dfMinResY = 0.0;
    3078          79 :     double dfMaxResX = 0.0;
    3079          79 :     double dfMaxResY = 0.0;
    3080          79 :     if (m_hVarresMetadata >= 0)
    3081             :     {
    3082          61 :         if (!GH5_FetchAttribute(m_hVarresMetadata, "min_resolution_x",
    3083         122 :                                 dfMinResX) ||
    3084          61 :             !GH5_FetchAttribute(m_hVarresMetadata, "min_resolution_y",
    3085             :                                 dfMinResY))
    3086             :         {
    3087           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3088             :                      "Cannot get min_resolution_x and/or min_resolution_y");
    3089           0 :             return false;
    3090             :         }
    3091             : 
    3092          61 :         if (!GH5_FetchAttribute(m_hVarresMetadata, "max_resolution_x",
    3093         122 :                                 dfMaxResX) ||
    3094          61 :             !GH5_FetchAttribute(m_hVarresMetadata, "max_resolution_y",
    3095             :                                 dfMaxResY))
    3096             :         {
    3097           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3098             :                      "Cannot get max_resolution_x and/or max_resolution_y");
    3099           0 :             return false;
    3100             :         }
    3101             : 
    3102          61 :         if (!bOpenSuperGrid && !bResampledGrid && !bInterpolate)
    3103             :         {
    3104          24 :             GDALDataset::SetMetadataItem("MIN_RESOLUTION_X",
    3105             :                                          CPLSPrintf("%f", dfMinResX));
    3106          24 :             GDALDataset::SetMetadataItem("MIN_RESOLUTION_Y",
    3107             :                                          CPLSPrintf("%f", dfMinResY));
    3108          24 :             GDALDataset::SetMetadataItem("MAX_RESOLUTION_X",
    3109             :                                          CPLSPrintf("%f", dfMaxResX));
    3110          24 :             GDALDataset::SetMetadataItem("MAX_RESOLUTION_Y",
    3111             :                                          CPLSPrintf("%f", dfMaxResY));
    3112             :         }
    3113             :     }
    3114             : 
    3115          79 :     if (bResampledGrid || bInterpolate)
    3116             :     {
    3117          26 :         if (!bHasRefinementGrids)
    3118             :         {
    3119           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3120             :                      "No supergrids available. "
    3121             :                      "%s mode not available",
    3122             :                      pszMode);
    3123           3 :             return false;
    3124             :         }
    3125             : 
    3126          52 :         const char *pszValuePopStrategy = CSLFetchNameValueDef(
    3127          26 :             poOpenInfo->papszOpenOptions, "VALUE_POPULATION", "MAX");
    3128          26 :         if (EQUAL(pszValuePopStrategy, "MIN"))
    3129             :         {
    3130           1 :             m_ePopulation = BAGDataset::Population::MIN;
    3131             :         }
    3132          25 :         else if (EQUAL(pszValuePopStrategy, "MEAN"))
    3133             :         {
    3134           1 :             m_ePopulation = BAGDataset::Population::MEAN;
    3135             :         }
    3136          24 :         else if (EQUAL(pszValuePopStrategy, "MAX"))
    3137             :         {
    3138          23 :             m_ePopulation = BAGDataset::Population::MAX;
    3139             :         }
    3140             :         else
    3141             :         {
    3142           1 :             m_ePopulation = BAGDataset::Population::COUNT;
    3143           1 :             bHasNoData = false;
    3144           1 :             fNoDataValue = 0;
    3145             :         }
    3146             : 
    3147             :         const char *pszResX =
    3148          26 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "RESX");
    3149             :         const char *pszResY =
    3150          26 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "RESY");
    3151          52 :         const char *pszResStrategy = CSLFetchNameValueDef(
    3152          26 :             poOpenInfo->papszOpenOptions, "RES_STRATEGY", "AUTO");
    3153          26 :         double dfDefaultResX = 0.0;
    3154          26 :         double dfDefaultResY = 0.0;
    3155             : 
    3156             :         const char *pszResFilterMin =
    3157          26 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "RES_FILTER_MIN");
    3158             :         const char *pszResFilterMax =
    3159          26 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "RES_FILTER_MAX");
    3160             : 
    3161          26 :         double dfResFilterMin = 0;
    3162          26 :         if (pszResFilterMin != nullptr)
    3163             :         {
    3164           6 :             dfResFilterMin = CPLAtof(pszResFilterMin);
    3165           6 :             const double dfMaxRes = std::min(dfMaxResX, dfMaxResY);
    3166           6 :             if (dfResFilterMin >= dfMaxRes)
    3167             :             {
    3168           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3169             :                          "Cannot specified RES_FILTER_MIN >= %g", dfMaxRes);
    3170           1 :                 return false;
    3171             :             }
    3172           5 :             GDALDataset::SetMetadataItem("RES_FILTER_MIN",
    3173             :                                          CPLSPrintf("%g", dfResFilterMin));
    3174             :         }
    3175             : 
    3176          25 :         double dfResFilterMax = std::numeric_limits<double>::infinity();
    3177          25 :         if (pszResFilterMax != nullptr)
    3178             :         {
    3179           6 :             dfResFilterMax = CPLAtof(pszResFilterMax);
    3180           6 :             const double dfMinRes = std::min(dfMinResX, dfMinResY);
    3181           6 :             if (dfResFilterMax < dfMinRes)
    3182             :             {
    3183           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3184             :                          "Cannot specified RES_FILTER_MAX < %g", dfMinRes);
    3185           2 :                 return false;
    3186             :             }
    3187           4 :             GDALDataset::SetMetadataItem("RES_FILTER_MAX",
    3188             :                                          CPLSPrintf("%g", dfResFilterMax));
    3189             :         }
    3190             : 
    3191          23 :         if (dfResFilterMin >= dfResFilterMax)
    3192             :         {
    3193           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3194             :                      "Cannot specified RES_FILTER_MIN >= RES_FILTER_MAX");
    3195           0 :             return false;
    3196             :         }
    3197             : 
    3198          23 :         if (EQUAL(pszResStrategy, "AUTO") &&
    3199          12 :             (pszResFilterMin || pszResFilterMax))
    3200             :         {
    3201           5 :             if (pszResFilterMax)
    3202             :             {
    3203           4 :                 dfDefaultResX = dfResFilterMax;
    3204           4 :                 dfDefaultResY = dfResFilterMax;
    3205             :             }
    3206             :             else
    3207             :             {
    3208           1 :                 dfDefaultResX = dfMaxResX;
    3209           1 :                 dfDefaultResY = dfMaxResY;
    3210             :             }
    3211             :         }
    3212          18 :         else if (EQUAL(pszResStrategy, "AUTO") || EQUAL(pszResStrategy, "MIN"))
    3213             :         {
    3214          12 :             dfDefaultResX = dfMinResX;
    3215          12 :             dfDefaultResY = dfMinResY;
    3216             :         }
    3217           6 :         else if (EQUAL(pszResStrategy, "MAX"))
    3218             :         {
    3219           1 :             dfDefaultResX = dfMaxResX;
    3220           1 :             dfDefaultResY = dfMaxResY;
    3221             :         }
    3222           5 :         else if (EQUAL(pszResStrategy, "MEAN"))
    3223             :         {
    3224           5 :             if (!GetMeanSupergridsResolution(dfDefaultResX, dfDefaultResY))
    3225             :             {
    3226           0 :                 return false;
    3227             :             }
    3228             :         }
    3229             : 
    3230             :         const char *pszMinX =
    3231          23 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINX");
    3232             :         const char *pszMinY =
    3233          23 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MINY");
    3234             :         const char *pszMaxX =
    3235          23 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXX");
    3236             :         const char *pszMaxY =
    3237          23 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, "MAXY");
    3238             : 
    3239          23 :         double dfMinX = m_dfLowResMinX;
    3240          23 :         double dfMinY = m_dfLowResMinY;
    3241          23 :         double dfMaxX = m_dfLowResMaxX;
    3242          23 :         double dfMaxY = m_dfLowResMaxY;
    3243          23 :         double dfResX = dfDefaultResX;
    3244          23 :         double dfResY = dfDefaultResY;
    3245          23 :         if (pszMinX)
    3246           1 :             dfMinX = CPLAtof(pszMinX);
    3247          23 :         if (pszMinY)
    3248           1 :             dfMinY = CPLAtof(pszMinY);
    3249          23 :         if (pszMaxX)
    3250           1 :             dfMaxX = CPLAtof(pszMaxX);
    3251          23 :         if (pszMaxY)
    3252           1 :             dfMaxY = CPLAtof(pszMaxY);
    3253          23 :         if (pszResX)
    3254           1 :             dfResX = CPLAtof(pszResX);
    3255          23 :         if (pszResY)
    3256           1 :             dfResY = CPLAtof(pszResY);
    3257             : 
    3258          23 :         if (dfResX <= 0.0 || dfResY <= 0.0)
    3259             :         {
    3260           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    3261             :                      "Invalid resolution: %f x %f", dfResX, dfResY);
    3262           0 :             return false;
    3263             :         }
    3264          23 :         const double dfRasterXSize = (dfMaxX - dfMinX) / dfResX;
    3265          23 :         const double dfRasterYSize = (dfMaxY - dfMinY) / dfResY;
    3266          23 :         if (dfRasterXSize <= 1 || dfRasterYSize <= 1 ||
    3267          23 :             dfRasterXSize > INT_MAX || dfRasterYSize > INT_MAX)
    3268             :         {
    3269           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Invalid raster dimension");
    3270           0 :             return false;
    3271             :         }
    3272          23 :         nRasterXSize = static_cast<int>(dfRasterXSize + 0.5);
    3273          23 :         nRasterYSize = static_cast<int>(dfRasterYSize + 0.5);
    3274          23 :         adfGeoTransform[0] = dfMinX;
    3275          23 :         adfGeoTransform[1] = dfResX;
    3276          23 :         adfGeoTransform[3] = dfMaxY;
    3277          23 :         adfGeoTransform[5] = -dfResY;
    3278          23 :         if (pszMaxY == nullptr || pszMinY != nullptr)
    3279             :         {
    3280             :             // if the constraint is not given by MAXY, we may need to tweak
    3281             :             // adfGeoTransform[3] / maxy, so that we get the requested MINY
    3282             :             // value
    3283          22 :             adfGeoTransform[3] += dfMinY - (dfMaxY - nRasterYSize * dfResY);
    3284             :         }
    3285             : 
    3286          23 :         const double dfMinRes = std::min(dfMinResX, dfMinResY);
    3287          23 :         if (dfResFilterMin > dfMinRes)
    3288             :         {
    3289           3 :             m_dfResFilterMin = dfResFilterMin;
    3290             :         }
    3291          23 :         m_dfResFilterMax = dfResFilterMax;
    3292             : 
    3293             :         // Use min/max BAG refinement metadata items only if the
    3294             :         // GDAL dataset bounding box is equal or larger to the BAG dataset
    3295          23 :         const bool bInitializeMinMax =
    3296          22 :             (!m_bMask && m_ePopulation != BAGDataset::Population::COUNT &&
    3297          21 :              dfMinX <= m_dfLowResMinX && dfMinY <= m_dfLowResMinY &&
    3298          45 :              dfMaxX >= m_dfLowResMaxX && dfMaxY >= m_dfLowResMaxY);
    3299             : 
    3300          23 :         if (bInterpolate)
    3301             :         {
    3302             :             // Depth
    3303           1 :             SetBand(1,
    3304             :                     new BAGInterpolatedBand(this, 1, bHasNoData, fNoDataValue,
    3305           1 :                                             bInitializeMinMax));
    3306             : 
    3307             :             // Uncertainty
    3308           1 :             SetBand(2,
    3309             :                     new BAGInterpolatedBand(this, 2, bHasNoData, fNoDataValue,
    3310           1 :                                             bInitializeMinMax));
    3311             :         }
    3312          22 :         else if (m_bMask || m_ePopulation == BAGDataset::Population::COUNT)
    3313             :         {
    3314           2 :             SetBand(1, new BAGResampledBand(this, 1, false, 0.0f, false));
    3315             :         }
    3316             :         else
    3317             :         {
    3318          20 :             SetBand(1, new BAGResampledBand(this, 1, bHasNoData, fNoDataValue,
    3319          20 :                                             bInitializeMinMax));
    3320             : 
    3321          20 :             SetBand(2, new BAGResampledBand(this, 2, bHasNoData, fNoDataValue,
    3322          20 :                                             bInitializeMinMax));
    3323             :         }
    3324             : 
    3325          23 :         if (GetRasterCount() > 1)
    3326             :         {
    3327          21 :             GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
    3328             :                                          "IMAGE_STRUCTURE");
    3329             :         }
    3330             : 
    3331          42 :         const bool bCanUseLowResAsOvr = nRasterXSize > m_nLowResWidth &&
    3332          42 :                                         nRasterYSize > m_nLowResHeight &&
    3333          19 :                                         GetRasterCount() > 1;
    3334             :         const int nMinOvrSize =
    3335          23 :             bCanUseLowResAsOvr ? 1 + std::min(m_nLowResWidth, m_nLowResHeight)
    3336          23 :                                : 256;
    3337          51 :         for (int nOvrFactor = 2; nRasterXSize / nOvrFactor >= nMinOvrSize &&
    3338          32 :                                  nRasterYSize / nOvrFactor >= nMinOvrSize;
    3339          28 :              nOvrFactor *= 2)
    3340             :         {
    3341          56 :             auto poOvrDS = std::make_unique<BAGDataset>(this, nOvrFactor);
    3342             : 
    3343          84 :             for (int i = 1; i <= GetRasterCount(); i++)
    3344             :             {
    3345          56 :                 if (bInterpolate)
    3346             :                 {
    3347          16 :                     poOvrDS->SetBand(
    3348           8 :                         i, new BAGInterpolatedBand(poOvrDS.get(), i, bHasNoData,
    3349           8 :                                                    fNoDataValue, false));
    3350             :                 }
    3351             :                 else
    3352             :                 {
    3353          96 :                     poOvrDS->SetBand(
    3354          48 :                         i, new BAGResampledBand(poOvrDS.get(), i, bHasNoData,
    3355          48 :                                                 fNoDataValue, false));
    3356             :                 }
    3357             :             }
    3358             : 
    3359          28 :             m_apoOverviewDS.emplace_back(std::move(poOvrDS));
    3360             :         }
    3361             : 
    3362             :         // Use the low resolution grid as the last overview level
    3363          23 :         if (bCanUseLowResAsOvr)
    3364             :         {
    3365          17 :             auto poOvrDS = std::make_unique<BAGDataset>(this, m_nLowResWidth,
    3366          17 :                                                         m_nLowResHeight);
    3367             : 
    3368          17 :             poElevBand = new BAGRasterBand(poOvrDS.get(), 1);
    3369             :             const hid_t hElevation =
    3370          17 :                 H5Dopen(GetHDF5Handle(), "/BAG_root/elevation");
    3371          34 :             if (hElevation < 0 ||
    3372          17 :                 !poElevBand->Initialize(hElevation, "elevation"))
    3373             :             {
    3374           0 :                 delete poElevBand;
    3375           0 :                 return false;
    3376             :             }
    3377          17 :             poOvrDS->SetBand(1, poElevBand);
    3378             : 
    3379             :             const hid_t hUncertainty =
    3380          17 :                 H5Dopen(GetHDF5Handle(), "/BAG_root/uncertainty");
    3381          17 :             BAGRasterBand *poUBand = new BAGRasterBand(poOvrDS.get(), 2);
    3382          34 :             if (hUncertainty < 0 ||
    3383          17 :                 !poUBand->Initialize(hUncertainty, "uncertainty"))
    3384             :             {
    3385           0 :                 delete poUBand;
    3386           0 :                 return false;
    3387             :             }
    3388          17 :             poOvrDS->SetBand(2, poUBand);
    3389             : 
    3390          17 :             m_apoOverviewDS.emplace_back(std::move(poOvrDS));
    3391          23 :         }
    3392             :     }
    3393          53 :     else if (bOpenSuperGrid)
    3394             :     {
    3395          11 :         auto oIter = m_oMapRefinemendGrids.find(nY * m_nLowResWidth + nX);
    3396          11 :         if (oIter == m_oMapRefinemendGrids.end())
    3397             :         {
    3398           2 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid subdataset");
    3399           2 :             return false;
    3400             :         }
    3401             : 
    3402           9 :         m_aosSubdatasets.Clear();
    3403           9 :         const auto &pSuperGrid = oIter->second;
    3404           9 :         nRasterXSize = static_cast<int>(pSuperGrid.nWidth);
    3405           9 :         nRasterYSize = static_cast<int>(pSuperGrid.nHeight);
    3406             : 
    3407             :         // Convert from pixel-center convention to corner-pixel convention
    3408           9 :         const double dfMinX = adfGeoTransform[0] + nX * adfGeoTransform[1] +
    3409           9 :                               pSuperGrid.fSWX - pSuperGrid.fResX / 2;
    3410           9 :         const double dfMinY =
    3411           9 :             adfGeoTransform[3] + m_nLowResHeight * adfGeoTransform[5] +
    3412           9 :             nY * -adfGeoTransform[5] + pSuperGrid.fSWY - pSuperGrid.fResY / 2;
    3413           9 :         const double dfMaxY =
    3414           9 :             dfMinY + pSuperGrid.nHeight * static_cast<double>(pSuperGrid.fResY);
    3415             : 
    3416           9 :         adfGeoTransform[0] = dfMinX;
    3417           9 :         adfGeoTransform[1] = pSuperGrid.fResX;
    3418           9 :         adfGeoTransform[3] = dfMaxY;
    3419           9 :         adfGeoTransform[5] = -pSuperGrid.fResY;
    3420           9 :         m_nSuperGridRefinementStartIndex = pSuperGrid.nIndex;
    3421             : 
    3422           9 :         if (!osGeorefMetadataLayer.empty())
    3423             :         {
    3424           4 :             auto poGeoref_metadataLayer = m_poRootGroup->OpenGroupFromFullname(
    3425           4 :                 "/BAG_root/georef_metadata/" + osGeorefMetadataLayer, nullptr);
    3426           4 :             if (poGeoref_metadataLayer == nullptr)
    3427             :             {
    3428           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3429             :                          "Cannot find georef_metadata layer %s",
    3430             :                          osGeorefMetadataLayer.c_str());
    3431           0 :                 return false;
    3432             :             }
    3433             : 
    3434           8 :             auto poKeys = poGeoref_metadataLayer->OpenMDArray("varres_keys");
    3435           4 :             if (poKeys != nullptr)
    3436             :             {
    3437           2 :                 auto poDims = poKeys->GetDimensions();
    3438           4 :                 if (poDims.size() != 2 || poDims[0]->GetSize() != 1 ||
    3439           2 :                     poDims[1]->GetSize() != m_nRefinementsSize)
    3440             :                 {
    3441           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3442             :                              "Wrong dimensions for %s/varres_keys",
    3443             :                              osGeorefMetadataLayer.c_str());
    3444           0 :                     return false;
    3445             :                 }
    3446           4 :                 if (poKeys->GetDataType().GetClass() != GEDTC_NUMERIC ||
    3447           2 :                     !GDALDataTypeIsInteger(
    3448           2 :                         poKeys->GetDataType().GetNumericDataType()))
    3449             :                 {
    3450           0 :                     CPLError(
    3451             :                         CE_Failure, CPLE_AppDefined,
    3452             :                         "Only integer data type supported for %s/varres_keys",
    3453             :                         osGeorefMetadataLayer.c_str());
    3454           0 :                     return false;
    3455             :                 }
    3456             :             }
    3457             : 
    3458           8 :             auto poValues = poGeoref_metadataLayer->OpenMDArray("values");
    3459           4 :             if (poValues == nullptr)
    3460             :             {
    3461           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3462             :                          "Cannot find array values of georef_metadata layer %s",
    3463             :                          osGeorefMetadataLayer.c_str());
    3464           0 :                 return false;
    3465             :             }
    3466           4 :             if (poValues->GetDimensionCount() != 1)
    3467             :             {
    3468           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3469             :                          "Wrong dimensions for %s/values",
    3470             :                          osGeorefMetadataLayer.c_str());
    3471           0 :                 return false;
    3472             :             }
    3473           4 :             if (poValues->GetDataType().GetClass() != GEDTC_COMPOUND)
    3474             :             {
    3475           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3476             :                          "Only compound data type supported for %s/values",
    3477             :                          osGeorefMetadataLayer.c_str());
    3478           0 :                 return false;
    3479             :             }
    3480           4 :             SetBand(1, new BAGGeorefMDSuperGridBand(
    3481             :                            poValues, poKeys,
    3482           4 :                            new BAGSuperGridBand(this, 1, bHasNoData,
    3483           4 :                                                 fNoDataValue)));
    3484             :         }
    3485             :         else
    3486             :         {
    3487          15 :             for (int i = 0; i < 2; i++)
    3488             :             {
    3489          10 :                 SetBand(i + 1, new BAGSuperGridBand(this, i + 1, bHasNoData,
    3490          10 :                                                     fNoDataValue));
    3491             :             }
    3492             : 
    3493           5 :             GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
    3494             :                                          "IMAGE_STRUCTURE");
    3495             :         }
    3496             : 
    3497           9 :         SetPhysicalFilename(osFilename);
    3498             : 
    3499           9 :         m_oMapRefinemendGrids.clear();
    3500             :     }
    3501             : 
    3502             :     // Setup/check for pam .aux.xml.
    3503          74 :     TryLoadXML();
    3504             : 
    3505             :     // Setup overviews.
    3506          74 :     oOvManager.Initialize(this, poOpenInfo->pszFilename);
    3507             : 
    3508          74 :     return true;
    3509             : }
    3510             : 
    3511             : /************************************************************************/
    3512             : /*                            GetLayer()                                */
    3513             : /************************************************************************/
    3514             : 
    3515          47 : OGRLayer *BAGDataset::GetLayer(int idx)
    3516             : {
    3517          47 :     if (idx != 0)
    3518           1 :         return nullptr;
    3519          46 :     return m_poTrackingListLayer.get();
    3520             : }
    3521             : 
    3522             : /************************************************************************/
    3523             : /*                        BAGTrackingListLayer                          */
    3524             : /************************************************************************/
    3525             : 
    3526             : class BAGTrackingListLayer final
    3527             :     : public OGRLayer,
    3528             :       public OGRGetNextFeatureThroughRaw<BAGTrackingListLayer>
    3529             : {
    3530             :     std::shared_ptr<GDALMDArray> m_poArray{};
    3531             :     OGRFeatureDefn *m_poFeatureDefn = nullptr;
    3532             :     int m_nIdx = 0;
    3533             : 
    3534             :     OGRFeature *GetNextRawFeature();
    3535             : 
    3536             :   public:
    3537             :     explicit BAGTrackingListLayer(const std::shared_ptr<GDALMDArray> &poArray);
    3538             :     ~BAGTrackingListLayer();
    3539             : 
    3540           0 :     OGRFeatureDefn *GetLayerDefn() override
    3541             :     {
    3542           0 :         return m_poFeatureDefn;
    3543             :     }
    3544             : 
    3545             :     void ResetReading() override;
    3546           5 :     DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(BAGTrackingListLayer)
    3547             : 
    3548           0 :     int TestCapability(const char *) override
    3549             :     {
    3550           0 :         return false;
    3551             :     }
    3552             : };
    3553             : 
    3554             : /************************************************************************/
    3555             : /*                       BAGTrackingListLayer()                         */
    3556             : /************************************************************************/
    3557             : 
    3558          45 : BAGTrackingListLayer::BAGTrackingListLayer(
    3559          45 :     const std::shared_ptr<GDALMDArray> &poArray)
    3560          45 :     : m_poArray(poArray)
    3561             : {
    3562          45 :     m_poFeatureDefn = new OGRFeatureDefn("tracking_list");
    3563          45 :     SetDescription(m_poFeatureDefn->GetName());
    3564          45 :     m_poFeatureDefn->Reference();
    3565          45 :     m_poFeatureDefn->SetGeomType(wkbNone);
    3566             : 
    3567          45 :     const auto &poComponents = poArray->GetDataType().GetComponents();
    3568         315 :     for (const auto &poComponent : poComponents)
    3569             :     {
    3570         270 :         if (poComponent->GetType().GetClass() == GEDTC_NUMERIC)
    3571             :         {
    3572             :             OGRFieldType eType;
    3573         270 :             if (GDALDataTypeIsInteger(
    3574         540 :                     poComponent->GetType().GetNumericDataType()))
    3575         180 :                 eType = OFTInteger;
    3576             :             else
    3577          90 :                 eType = OFTReal;
    3578         540 :             OGRFieldDefn oFieldDefn(poComponent->GetName().c_str(), eType);
    3579         270 :             m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
    3580             :         }
    3581             :     }
    3582          45 : }
    3583             : 
    3584             : /************************************************************************/
    3585             : /*                      ~BAGTrackingListLayer()                         */
    3586             : /************************************************************************/
    3587             : 
    3588          90 : BAGTrackingListLayer::~BAGTrackingListLayer()
    3589             : {
    3590          45 :     m_poFeatureDefn->Release();
    3591          90 : }
    3592             : 
    3593             : /************************************************************************/
    3594             : /*                            ResetReading()                            */
    3595             : /************************************************************************/
    3596             : 
    3597           3 : void BAGTrackingListLayer::ResetReading()
    3598             : {
    3599           3 :     m_nIdx = 0;
    3600           3 : }
    3601             : 
    3602             : /************************************************************************/
    3603             : /*                          GetNextRawFeature()                         */
    3604             : /************************************************************************/
    3605             : 
    3606           5 : OGRFeature *BAGTrackingListLayer::GetNextRawFeature()
    3607             : {
    3608           5 :     if (static_cast<GUInt64>(m_nIdx) >=
    3609           5 :         m_poArray->GetDimensions()[0]->GetSize())
    3610           1 :         return nullptr;
    3611             : 
    3612           4 :     const auto &oDataType = m_poArray->GetDataType();
    3613           4 :     std::vector<GByte> abyRow(oDataType.GetSize());
    3614             : 
    3615           4 :     const GUInt64 arrayStartIdx = static_cast<GUInt64>(m_nIdx);
    3616           4 :     const size_t count = 1;
    3617           4 :     const GInt64 arrayStep = 0;
    3618           4 :     const GPtrDiff_t bufferStride = 0;
    3619           8 :     m_poArray->Read(&arrayStartIdx, &count, &arrayStep, &bufferStride,
    3620           4 :                     oDataType, &abyRow[0]);
    3621           4 :     int iCol = 0;
    3622           4 :     auto poFeature = new OGRFeature(m_poFeatureDefn);
    3623           4 :     poFeature->SetFID(m_nIdx);
    3624           4 :     m_nIdx++;
    3625             : 
    3626           4 :     const auto &poComponents = oDataType.GetComponents();
    3627          28 :     for (const auto &poComponent : poComponents)
    3628             :     {
    3629          24 :         if (poComponent->GetType().GetClass() == GEDTC_NUMERIC)
    3630             :         {
    3631          24 :             if (GDALDataTypeIsInteger(
    3632          48 :                     poComponent->GetType().GetNumericDataType()))
    3633             :             {
    3634          16 :                 int nValue = 0;
    3635          16 :                 GDALCopyWords(&abyRow[poComponent->GetOffset()],
    3636          16 :                               poComponent->GetType().GetNumericDataType(), 0,
    3637             :                               &nValue, GDT_Int32, 0, 1);
    3638          16 :                 poFeature->SetField(iCol, nValue);
    3639             :             }
    3640             :             else
    3641             :             {
    3642           8 :                 double dfValue = 0;
    3643           8 :                 GDALCopyWords(&abyRow[poComponent->GetOffset()],
    3644           8 :                               poComponent->GetType().GetNumericDataType(), 0,
    3645             :                               &dfValue, GDT_Float64, 0, 1);
    3646           8 :                 poFeature->SetField(iCol, dfValue);
    3647             :             }
    3648          24 :             iCol++;
    3649             :         }
    3650             :     }
    3651             : 
    3652           4 :     return poFeature;
    3653             : }
    3654             : 
    3655             : /************************************************************************/
    3656             : /*                          OpenVector()                                */
    3657             : /************************************************************************/
    3658             : 
    3659          46 : bool BAGDataset::OpenVector()
    3660             : {
    3661             :     auto poTrackingList =
    3662         138 :         m_poRootGroup->OpenMDArrayFromFullname("/BAG_root/tracking_list");
    3663          46 :     if (!poTrackingList)
    3664           1 :         return false;
    3665          45 :     if (poTrackingList->GetDimensions().size() != 1)
    3666           0 :         return false;
    3667          45 :     if (poTrackingList->GetDataType().GetClass() != GEDTC_COMPOUND)
    3668           0 :         return false;
    3669             : 
    3670          45 :     m_poTrackingListLayer.reset(new BAGTrackingListLayer(poTrackingList));
    3671          45 :     return true;
    3672             : }
    3673             : 
    3674             : /************************************************************************/
    3675             : /*                          OpenForCreate()                             */
    3676             : /************************************************************************/
    3677             : 
    3678           4 : GDALDataset *BAGDataset::OpenForCreate(GDALOpenInfo *poOpenInfo, int nXSizeIn,
    3679             :                                        int nYSizeIn, int nBandsIn,
    3680             :                                        CSLConstList papszCreationOptions)
    3681             : {
    3682           8 :     CPLString osFilename(poOpenInfo->pszFilename);
    3683             : 
    3684             :     // Open the file as an HDF5 file.
    3685           4 :     hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
    3686           4 :     H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
    3687           4 :     hid_t hHDF5 = H5Fopen(osFilename, H5F_ACC_RDWR, fapl);
    3688           4 :     H5Pclose(fapl);
    3689           4 :     if (hHDF5 < 0)
    3690           0 :         return nullptr;
    3691             : 
    3692           8 :     auto poSharedResources = GDAL::HDF5SharedResources::Create(osFilename);
    3693           4 :     poSharedResources->m_hHDF5 = hHDF5;
    3694             : 
    3695           8 :     auto poRootGroup = HDF5Dataset::OpenGroup(poSharedResources);
    3696           4 :     if (poRootGroup == nullptr)
    3697           0 :         return nullptr;
    3698             : 
    3699             :     // Create a corresponding dataset.
    3700           4 :     BAGDataset *const poDS = new BAGDataset();
    3701             : 
    3702           4 :     poDS->eAccess = poOpenInfo->eAccess;
    3703           4 :     poDS->m_poRootGroup = std::move(poRootGroup);
    3704           4 :     poDS->m_poSharedResources = std::move(poSharedResources);
    3705           4 :     poDS->m_aosCreationOptions = papszCreationOptions;
    3706             : 
    3707           4 :     poDS->nRasterXSize = nXSizeIn;
    3708           4 :     poDS->nRasterYSize = nYSizeIn;
    3709             : 
    3710           4 :     const int nBlockSize = std::min(
    3711           8 :         4096,
    3712           4 :         atoi(CSLFetchNameValueDef(papszCreationOptions, "BLOCK_SIZE", "100")));
    3713           4 :     const int nBlockXSize = std::min(poDS->nRasterXSize, nBlockSize);
    3714           4 :     const int nBlockYSize = std::min(poDS->nRasterYSize, nBlockSize);
    3715             : 
    3716          10 :     for (int i = 0; i < nBandsIn; i++)
    3717             :     {
    3718           6 :         auto poBand = new BAGRasterBand(poDS, i + 1);
    3719           6 :         poBand->nBlockXSize = nBlockXSize;
    3720           6 :         poBand->nBlockYSize = nBlockYSize;
    3721           6 :         poBand->eDataType = GDT_Float32;
    3722           6 :         poBand->m_bHasNoData = true;
    3723           6 :         poBand->m_fNoDataValue = fDEFAULT_NODATA;
    3724           6 :         poBand->GDALRasterBand::SetDescription(i == 0 ? "elevation"
    3725             :                                                       : "uncertainty");
    3726           6 :         poDS->SetBand(i + 1, poBand);
    3727             :     }
    3728             : 
    3729           4 :     poDS->SetDescription(poOpenInfo->pszFilename);
    3730             : 
    3731           4 :     poDS->m_bReportVertCRS = CPLTestBool(CSLFetchNameValueDef(
    3732           4 :         poOpenInfo->papszOpenOptions, "REPORT_VERTCRS", "YES"));
    3733             : 
    3734           4 :     poDS->GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
    3735             : 
    3736             :     // Setup/check for pam .aux.xml.
    3737           4 :     poDS->TryLoadXML();
    3738             : 
    3739             :     // Setup overviews.
    3740           4 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    3741             : 
    3742           4 :     return poDS;
    3743             : }
    3744             : 
    3745             : /************************************************************************/
    3746             : /*                      GetMeanSupergridsResolution()                   */
    3747             : /************************************************************************/
    3748             : 
    3749           5 : bool BAGDataset::GetMeanSupergridsResolution(double &dfResX, double &dfResY)
    3750             : {
    3751           5 :     const int nChunkXSize = m_nChunkXSizeVarresMD;
    3752           5 :     const int nChunkYSize = m_nChunkYSizeVarresMD;
    3753             : 
    3754           5 :     dfResX = 0.0;
    3755           5 :     dfResY = 0.0;
    3756           5 :     int nValidSuperGrids = 0;
    3757           5 :     std::vector<BAGRefinementGrid> rgrids(static_cast<size_t>(nChunkXSize) *
    3758          10 :                                           nChunkYSize);
    3759           5 :     const int county = (m_nLowResHeight + nChunkYSize - 1) / nChunkYSize;
    3760           5 :     const int countx = (m_nLowResWidth + nChunkXSize - 1) / nChunkXSize;
    3761          10 :     for (int y = 0; y < county; y++)
    3762             :     {
    3763             :         const int nReqCountY =
    3764           5 :             std::min(nChunkYSize, m_nLowResHeight - y * nChunkYSize);
    3765          10 :         for (int x = 0; x < countx; x++)
    3766             :         {
    3767             :             const int nReqCountX =
    3768           5 :                 std::min(nChunkXSize, m_nLowResWidth - x * nChunkXSize);
    3769             : 
    3770             :             // Create memory space to receive the data.
    3771           5 :             hsize_t count[2] = {static_cast<hsize_t>(nReqCountY),
    3772           5 :                                 static_cast<hsize_t>(nReqCountX)};
    3773           5 :             const hid_t memspace = H5Screate_simple(2, count, nullptr);
    3774           5 :             H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
    3775             :                                            static_cast<H5OFFSET_TYPE>(0)};
    3776           5 :             if (H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mem_offset,
    3777           5 :                                     nullptr, count, nullptr) < 0)
    3778             :             {
    3779           0 :                 H5Sclose(memspace);
    3780           0 :                 return false;
    3781             :             }
    3782             : 
    3783           5 :             if (ReadVarresMetadataValue(y * nChunkYSize, x * nChunkXSize,
    3784             :                                         memspace, rgrids.data(), nReqCountY,
    3785             :                                         nReqCountX))
    3786             :             {
    3787         125 :                 for (int i = 0; i < nReqCountX * nReqCountY; i++)
    3788             :                 {
    3789         120 :                     if (rgrids[i].nWidth > 0)
    3790             :                     {
    3791         120 :                         dfResX += rgrids[i].fResX;
    3792         120 :                         dfResY += rgrids[i].fResY;
    3793         120 :                         nValidSuperGrids++;
    3794             :                     }
    3795             :                 }
    3796             :             }
    3797           5 :             H5Sclose(memspace);
    3798             :         }
    3799             :     }
    3800             : 
    3801           5 :     if (nValidSuperGrids == 0)
    3802             :     {
    3803           0 :         CPLError(CE_Failure, CPLE_AppDefined, "No valid supergrids");
    3804           0 :         return false;
    3805             :     }
    3806             : 
    3807           5 :     dfResX /= nValidSuperGrids;
    3808           5 :     dfResY /= nValidSuperGrids;
    3809           5 :     return true;
    3810             : }
    3811             : 
    3812             : /************************************************************************/
    3813             : /*                      GetVarresMetadataChunkSizes()                   */
    3814             : /************************************************************************/
    3815             : 
    3816          63 : void BAGDataset::GetVarresMetadataChunkSizes(int &nChunkXSize, int &nChunkYSize)
    3817             : {
    3818          63 :     const hid_t listid = H5Dget_create_plist(m_hVarresMetadata);
    3819          63 :     nChunkXSize = m_nLowResWidth;
    3820          63 :     nChunkYSize = std::max(
    3821          63 :         1, std::min(10 * 1024 * 1024 / m_nLowResWidth, m_nLowResHeight));
    3822          63 :     if (listid > 0)
    3823             :     {
    3824          63 :         if (H5Pget_layout(listid) == H5D_CHUNKED)
    3825             :         {
    3826           0 :             hsize_t panChunkDims[2] = {0, 0};
    3827           0 :             const int nDimSize = H5Pget_chunk(listid, 2, panChunkDims);
    3828           0 :             CPL_IGNORE_RET_VAL(nDimSize);
    3829           0 :             CPLAssert(nDimSize == 2);
    3830           0 :             nChunkXSize = static_cast<int>(panChunkDims[1]);
    3831           0 :             nChunkYSize = static_cast<int>(panChunkDims[0]);
    3832             :         }
    3833             : 
    3834          63 :         H5Pclose(listid);
    3835             :     }
    3836          63 : }
    3837             : 
    3838             : /************************************************************************/
    3839             : /*                     GetVarresRefinementChunkSize()                   */
    3840             : /************************************************************************/
    3841             : 
    3842          63 : void BAGDataset::GetVarresRefinementChunkSize(unsigned &nChunkSize)
    3843             : {
    3844          63 :     const hid_t listid = H5Dget_create_plist(m_hVarresRefinements);
    3845          63 :     nChunkSize = 1024;
    3846          63 :     if (listid > 0)
    3847             :     {
    3848          63 :         if (H5Pget_layout(listid) == H5D_CHUNKED)
    3849             :         {
    3850          53 :             hsize_t panChunkDims[2] = {0, 0};
    3851          53 :             const int nDimSize = H5Pget_chunk(listid, 2, panChunkDims);
    3852          53 :             CPL_IGNORE_RET_VAL(nDimSize);
    3853          53 :             CPLAssert(nDimSize == 2);
    3854          53 :             nChunkSize = static_cast<unsigned>(panChunkDims[1]);
    3855             :         }
    3856             : 
    3857          63 :         H5Pclose(listid);
    3858             :     }
    3859          63 : }
    3860             : 
    3861             : /************************************************************************/
    3862             : /*                        GetRefinementValues()                         */
    3863             : /************************************************************************/
    3864             : 
    3865      107034 : const float *BAGDataset::GetRefinementValues(unsigned nRefinementIndex)
    3866             : {
    3867      107034 :     unsigned nStartIndex = (nRefinementIndex / m_nChunkSizeVarresRefinement) *
    3868      107034 :                            m_nChunkSizeVarresRefinement;
    3869      107034 :     const auto vPtr = m_oCacheRefinementValues.getPtr(nStartIndex);
    3870      107034 :     if (vPtr)
    3871      107013 :         return vPtr->data() + 2 * (nRefinementIndex - nStartIndex);
    3872             : 
    3873             :     const unsigned nCachedRefinementCount = std::min(
    3874          21 :         m_nChunkSizeVarresRefinement, m_nRefinementsSize - nStartIndex);
    3875          42 :     std::vector<float> values(2 * nCachedRefinementCount);
    3876             : 
    3877          21 :     hsize_t countVarresRefinements[2] = {
    3878          21 :         static_cast<hsize_t>(1), static_cast<hsize_t>(nCachedRefinementCount)};
    3879             :     const hid_t memspaceVarresRefinements =
    3880          21 :         H5Screate_simple(2, countVarresRefinements, nullptr);
    3881          21 :     H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
    3882             :                                    static_cast<H5OFFSET_TYPE>(0)};
    3883          21 :     if (H5Sselect_hyperslab(memspaceVarresRefinements, H5S_SELECT_SET,
    3884             :                             mem_offset, nullptr, countVarresRefinements,
    3885          21 :                             nullptr) < 0)
    3886             :     {
    3887           0 :         H5Sclose(memspaceVarresRefinements);
    3888           0 :         return nullptr;
    3889             :     }
    3890             : 
    3891          21 :     H5OFFSET_TYPE offsetRefinement[2] = {
    3892          21 :         static_cast<H5OFFSET_TYPE>(0), static_cast<H5OFFSET_TYPE>(nStartIndex)};
    3893          21 :     if (H5Sselect_hyperslab(m_hVarresRefinementsDataspace, H5S_SELECT_SET,
    3894             :                             offsetRefinement, nullptr, countVarresRefinements,
    3895          21 :                             nullptr) < 0)
    3896             :     {
    3897           0 :         H5Sclose(memspaceVarresRefinements);
    3898           0 :         return nullptr;
    3899             :     }
    3900          21 :     if (H5Dread(m_hVarresRefinements, m_hVarresRefinementsNative,
    3901             :                 memspaceVarresRefinements, m_hVarresRefinementsDataspace,
    3902          42 :                 H5P_DEFAULT, values.data()) < 0)
    3903             :     {
    3904           0 :         H5Sclose(memspaceVarresRefinements);
    3905           0 :         return nullptr;
    3906             :     }
    3907          21 :     H5Sclose(memspaceVarresRefinements);
    3908             :     const auto &vRef =
    3909          21 :         m_oCacheRefinementValues.insert(nStartIndex, std::move(values));
    3910          21 :     return vRef.data() + 2 * (nRefinementIndex - nStartIndex);
    3911             : }
    3912             : 
    3913             : /************************************************************************/
    3914             : /*                        ReadVarresMetadataValue()                     */
    3915             : /************************************************************************/
    3916             : 
    3917         336 : bool BAGDataset::ReadVarresMetadataValue(int y, int x, hid_t memspace,
    3918             :                                          BAGRefinementGrid *rgrid, int height,
    3919             :                                          int width)
    3920             : {
    3921         336 :     constexpr int metadata_elt_size = 3 * 4 + 4 * 4;  // 3 uint and 4 float
    3922         336 :     std::vector<char> buffer(static_cast<size_t>(metadata_elt_size) * height *
    3923         672 :                              width);
    3924             : 
    3925         336 :     hsize_t count[2] = {static_cast<hsize_t>(height),
    3926         336 :                         static_cast<hsize_t>(width)};
    3927         336 :     H5OFFSET_TYPE offset[2] = {static_cast<H5OFFSET_TYPE>(y),
    3928         336 :                                static_cast<H5OFFSET_TYPE>(x)};
    3929         336 :     if (H5Sselect_hyperslab(m_hVarresMetadataDataspace, H5S_SELECT_SET, offset,
    3930         336 :                             nullptr, count, nullptr) < 0)
    3931             :     {
    3932           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3933             :                  "ReadVarresMetadataValue(): H5Sselect_hyperslab() failed");
    3934           0 :         return false;
    3935             :     }
    3936             : 
    3937         336 :     if (H5Dread(m_hVarresMetadata, m_hVarresMetadataNative, memspace,
    3938         672 :                 m_hVarresMetadataDataspace, H5P_DEFAULT, buffer.data()) < 0)
    3939             :     {
    3940           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3941             :                  "ReadVarresMetadataValue(): H5Dread() failed");
    3942           0 :         return false;
    3943             :     }
    3944             : 
    3945        2319 :     for (int i = 0; i < width * height; i++)
    3946             :     {
    3947        1983 :         const char *src_ptr = buffer.data() + metadata_elt_size * i;
    3948        1983 :         memcpy(&rgrid[i].nIndex, src_ptr, 4);
    3949        1983 :         memcpy(&rgrid[i].nWidth, src_ptr + 4, 4);
    3950        1983 :         memcpy(&rgrid[i].nHeight, src_ptr + 8, 4);
    3951        1983 :         memcpy(&rgrid[i].fResX, src_ptr + 12, 4);
    3952        1983 :         memcpy(&rgrid[i].fResY, src_ptr + 16, 4);
    3953        1983 :         memcpy(&rgrid[i].fSWX, src_ptr + 20, 4);
    3954        1983 :         memcpy(&rgrid[i].fSWY, src_ptr + 24, 4);
    3955             :     }
    3956         336 :     return true;
    3957             : }
    3958             : 
    3959             : /************************************************************************/
    3960             : /*                        LookForRefinementGrids()                      */
    3961             : /************************************************************************/
    3962             : 
    3963          81 : bool BAGDataset::LookForRefinementGrids(CSLConstList l_papszOpenOptions,
    3964             :                                         int nYSubDS, int nXSubDS)
    3965             : 
    3966             : {
    3967          81 :     m_hVarresMetadata = GH5DopenNoWarning(m_poSharedResources->m_hHDF5,
    3968             :                                           "/BAG_root/varres_metadata");
    3969          81 :     if (m_hVarresMetadata < 0)
    3970          18 :         return false;
    3971          63 :     m_hVarresRefinements =
    3972          63 :         H5Dopen(m_poSharedResources->m_hHDF5, "/BAG_root/varres_refinements");
    3973          63 :     if (m_hVarresRefinements < 0)
    3974           0 :         return false;
    3975             : 
    3976          63 :     m_hVarresMetadataDataType = H5Dget_type(m_hVarresMetadata);
    3977          63 :     if (H5Tget_class(m_hVarresMetadataDataType) != H5T_COMPOUND)
    3978             :     {
    3979           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3980             :                  "m_hVarresMetadataDataType is not compound");
    3981           0 :         return false;
    3982             :     }
    3983             : 
    3984             :     const struct
    3985             :     {
    3986             :         const char *pszName;
    3987             :         hid_t eType;
    3988          63 :     } asMetadataFields[] = {
    3989          63 :         {"index", H5T_NATIVE_UINT},         {"dimensions_x", H5T_NATIVE_UINT},
    3990         126 :         {"dimensions_y", H5T_NATIVE_UINT},  {"resolution_x", H5T_NATIVE_FLOAT},
    3991         126 :         {"resolution_y", H5T_NATIVE_FLOAT}, {"sw_corner_x", H5T_NATIVE_FLOAT},
    3992          63 :         {"sw_corner_y", H5T_NATIVE_FLOAT},
    3993          63 :     };
    3994             : 
    3995          63 :     if (H5Tget_nmembers(m_hVarresMetadataDataType) !=
    3996             :         CPL_ARRAYSIZE(asMetadataFields))
    3997             :     {
    3998           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3999             :                  "m_hVarresMetadataDataType has not %u members",
    4000             :                  static_cast<unsigned>(CPL_ARRAYSIZE(asMetadataFields)));
    4001           0 :         return false;
    4002             :     }
    4003             : 
    4004         504 :     for (unsigned i = 0; i < CPL_ARRAYSIZE(asMetadataFields); i++)
    4005             :     {
    4006         441 :         char *pszName = H5Tget_member_name(m_hVarresMetadataDataType, i);
    4007         441 :         if (strcmp(pszName, asMetadataFields[i].pszName) != 0)
    4008             :         {
    4009           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    4010             :                      "asMetadataFields[%u].pszName = %s instead of %s",
    4011             :                      static_cast<unsigned>(i), pszName,
    4012           0 :                      asMetadataFields[i].pszName);
    4013           0 :             H5free_memory(pszName);
    4014           0 :             return false;
    4015             :         }
    4016         441 :         H5free_memory(pszName);
    4017         441 :         const hid_t type = H5Tget_member_type(m_hVarresMetadataDataType, i);
    4018         441 :         const hid_t hNativeType = H5Tget_native_type(type, H5T_DIR_DEFAULT);
    4019         441 :         bool bTypeOK = H5Tequal(asMetadataFields[i].eType, hNativeType) > 0;
    4020         441 :         H5Tclose(hNativeType);
    4021         441 :         H5Tclose(type);
    4022         441 :         if (!bTypeOK)
    4023             :         {
    4024           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    4025             :                      "asMetadataFields[%u].eType is not of expected type", i);
    4026           0 :             return false;
    4027             :         }
    4028             :     }
    4029             : 
    4030          63 :     m_hVarresMetadataDataspace = H5Dget_space(m_hVarresMetadata);
    4031          63 :     if (H5Sget_simple_extent_ndims(m_hVarresMetadataDataspace) != 2)
    4032             :     {
    4033           0 :         CPLDebug("BAG",
    4034             :                  "H5Sget_simple_extent_ndims(m_hVarresMetadataDataspace) != 2");
    4035           0 :         return false;
    4036             :     }
    4037             : 
    4038             :     {
    4039          63 :         hsize_t dims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
    4040          63 :         hsize_t maxdims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
    4041             : 
    4042          63 :         H5Sget_simple_extent_dims(m_hVarresMetadataDataspace, dims, maxdims);
    4043          63 :         if (dims[0] != static_cast<hsize_t>(m_nLowResHeight) ||
    4044          63 :             dims[1] != static_cast<hsize_t>(m_nLowResWidth))
    4045             :         {
    4046           0 :             CPLDebug("BAG", "Unexpected dimension for m_hVarresMetadata");
    4047           0 :             return false;
    4048             :         }
    4049             :     }
    4050             : 
    4051             :     // We could potentially go beyond but we'd need to make sure that
    4052             :     // m_oMapRefinemendGrids is indexed by a int64_t
    4053          63 :     if (m_nLowResWidth > std::numeric_limits<int>::max() / m_nLowResHeight)
    4054             :     {
    4055           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too many refinement grids");
    4056           0 :         return false;
    4057             :     }
    4058             : 
    4059          63 :     m_hVarresMetadataNative =
    4060          63 :         H5Tget_native_type(m_hVarresMetadataDataType, H5T_DIR_ASCEND);
    4061             : 
    4062          63 :     m_hVarresRefinementsDataType = H5Dget_type(m_hVarresRefinements);
    4063          63 :     if (H5Tget_class(m_hVarresRefinementsDataType) != H5T_COMPOUND)
    4064             :     {
    4065           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    4066             :                  "m_hVarresRefinementsDataType is not compound");
    4067           0 :         return false;
    4068             :     }
    4069             : 
    4070             :     const struct
    4071             :     {
    4072             :         const char *pszName;
    4073             :         hid_t eType;
    4074          63 :     } asRefinementsFields[] = {
    4075          63 :         {"depth", H5T_NATIVE_FLOAT},
    4076          63 :         {"depth_uncrt", H5T_NATIVE_FLOAT},
    4077          63 :     };
    4078             : 
    4079          63 :     if (H5Tget_nmembers(m_hVarresRefinementsDataType) !=
    4080             :         CPL_ARRAYSIZE(asRefinementsFields))
    4081             :     {
    4082           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    4083             :                  "m_hVarresRefinementsDataType has not %u members",
    4084             :                  static_cast<unsigned>(CPL_ARRAYSIZE(asRefinementsFields)));
    4085           0 :         return false;
    4086             :     }
    4087             : 
    4088         189 :     for (unsigned i = 0; i < CPL_ARRAYSIZE(asRefinementsFields); i++)
    4089             :     {
    4090         126 :         char *pszName = H5Tget_member_name(m_hVarresRefinementsDataType, i);
    4091         126 :         if (strcmp(pszName, asRefinementsFields[i].pszName) != 0)
    4092             :         {
    4093           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    4094             :                      "asRefinementsFields[%u].pszName = %s instead of %s",
    4095             :                      static_cast<unsigned>(i), pszName,
    4096           0 :                      asRefinementsFields[i].pszName);
    4097           0 :             H5free_memory(pszName);
    4098           0 :             return false;
    4099             :         }
    4100         126 :         H5free_memory(pszName);
    4101         126 :         const hid_t type = H5Tget_member_type(m_hVarresRefinementsDataType, i);
    4102         126 :         const hid_t hNativeType = H5Tget_native_type(type, H5T_DIR_DEFAULT);
    4103         126 :         bool bTypeOK = H5Tequal(asRefinementsFields[i].eType, hNativeType) > 0;
    4104         126 :         H5Tclose(hNativeType);
    4105         126 :         H5Tclose(type);
    4106         126 :         if (!bTypeOK)
    4107             :         {
    4108           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    4109             :                      "asRefinementsFields[%u].eType is not of expected type",
    4110             :                      i);
    4111           0 :             return false;
    4112             :         }
    4113             :     }
    4114             : 
    4115          63 :     m_hVarresRefinementsDataspace = H5Dget_space(m_hVarresRefinements);
    4116          63 :     if (H5Sget_simple_extent_ndims(m_hVarresRefinementsDataspace) != 2)
    4117             :     {
    4118           0 :         CPLDebug(
    4119             :             "BAG",
    4120             :             "H5Sget_simple_extent_ndims(m_hVarresRefinementsDataspace) != 2");
    4121           0 :         return false;
    4122             :     }
    4123             : 
    4124          63 :     m_hVarresRefinementsNative =
    4125          63 :         H5Tget_native_type(m_hVarresRefinementsDataType, H5T_DIR_ASCEND);
    4126             : 
    4127             :     hsize_t nRefinementsSize;
    4128             :     {
    4129          63 :         hsize_t dims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
    4130          63 :         hsize_t maxdims[2] = {static_cast<hsize_t>(0), static_cast<hsize_t>(0)};
    4131             : 
    4132          63 :         H5Sget_simple_extent_dims(m_hVarresRefinementsDataspace, dims, maxdims);
    4133          63 :         if (dims[0] != 1)
    4134             :         {
    4135           0 :             CPLDebug("BAG", "Unexpected dimension for m_hVarresRefinements");
    4136           0 :             return false;
    4137             :         }
    4138          63 :         nRefinementsSize = dims[1];
    4139          63 :         m_nRefinementsSize = static_cast<unsigned>(nRefinementsSize);
    4140          63 :         CPLDebug("BAG", "m_nRefinementsSize = %u", m_nRefinementsSize);
    4141             :     }
    4142             : 
    4143          63 :     GetVarresMetadataChunkSizes(m_nChunkXSizeVarresMD, m_nChunkYSizeVarresMD);
    4144          63 :     CPLDebug("BAG", "m_nChunkXSizeVarresMD = %d, m_nChunkYSizeVarresMD = %d",
    4145             :              m_nChunkXSizeVarresMD, m_nChunkYSizeVarresMD);
    4146          63 :     GetVarresRefinementChunkSize(m_nChunkSizeVarresRefinement);
    4147          63 :     CPLDebug("BAG", "m_nChunkSizeVarresRefinement = %u",
    4148             :              m_nChunkSizeVarresRefinement);
    4149             : 
    4150          63 :     const char *pszMode = CSLFetchNameValueDef(l_papszOpenOptions, "MODE", "");
    4151          63 :     if (EQUAL(pszMode, "RESAMPLED_GRID") || EQUAL(pszMode, "INTERPOLATED"))
    4152             :     {
    4153          26 :         return true;
    4154             :     }
    4155             : 
    4156             :     const char *pszSUPERGRIDS =
    4157          37 :         CSLFetchNameValue(l_papszOpenOptions, "SUPERGRIDS_INDICES");
    4158             : 
    4159             :     struct yx
    4160             :     {
    4161             :         int y;
    4162             :         int x;
    4163             : 
    4164          14 :         yx(int yin, int xin) : y(yin), x(xin)
    4165             :         {
    4166          14 :         }
    4167             : 
    4168          30 :         bool operator<(const yx &other) const
    4169             :         {
    4170          30 :             return y < other.y || (y == other.y && x < other.x);
    4171             :         }
    4172             :     };
    4173             : 
    4174          74 :     std::set<yx> oSupergrids;
    4175          37 :     int nMinX = 0;
    4176          37 :     int nMinY = 0;
    4177          37 :     int nMaxX = m_nLowResWidth - 1;
    4178          37 :     int nMaxY = m_nLowResHeight - 1;
    4179          37 :     if (nYSubDS >= 0 && nXSubDS >= 0)
    4180             :     {
    4181          21 :         nMinX = nXSubDS;
    4182          21 :         nMaxX = nXSubDS;
    4183          21 :         nMinY = nYSubDS;
    4184          21 :         nMaxY = nYSubDS;
    4185             :     }
    4186          16 :     else if (pszSUPERGRIDS)
    4187             :     {
    4188          11 :         char chExpectedChar = '(';
    4189          11 :         bool bNextIsY = false;
    4190          11 :         bool bNextIsX = false;
    4191          11 :         bool bHasY = false;
    4192          11 :         bool bHasX = false;
    4193          11 :         int nY = 0;
    4194          11 :         int i = 0;
    4195          46 :         for (; pszSUPERGRIDS[i]; i++)
    4196             :         {
    4197          39 :             if (chExpectedChar && pszSUPERGRIDS[i] != chExpectedChar)
    4198             :             {
    4199           1 :                 CPLError(
    4200             :                     CE_Warning, CPLE_AppDefined,
    4201             :                     "Invalid formatting for SUPERGRIDS_INDICES at index %d. "
    4202             :                     "Expecting %c, got %c",
    4203           1 :                     i, chExpectedChar, pszSUPERGRIDS[i]);
    4204           1 :                 break;
    4205             :             }
    4206          38 :             else if (chExpectedChar == '(')
    4207             :             {
    4208          10 :                 chExpectedChar = 0;
    4209          10 :                 bNextIsY = true;
    4210             :             }
    4211          28 :             else if (chExpectedChar == ',')
    4212             :             {
    4213           2 :                 chExpectedChar = '(';
    4214             :             }
    4215             :             else
    4216             :             {
    4217          26 :                 CPLAssert(chExpectedChar == 0);
    4218          26 :                 if (bNextIsY && pszSUPERGRIDS[i] >= '0' &&
    4219           9 :                     pszSUPERGRIDS[i] <= '9')
    4220             :                 {
    4221           8 :                     nY = atoi(pszSUPERGRIDS + i);
    4222           8 :                     bNextIsY = false;
    4223           8 :                     bHasY = true;
    4224             :                 }
    4225          18 :                 else if (bNextIsX && pszSUPERGRIDS[i] >= '0' &&
    4226           5 :                          pszSUPERGRIDS[i] <= '9')
    4227             :                 {
    4228           4 :                     int nX = atoi(pszSUPERGRIDS + i);
    4229           4 :                     bNextIsX = false;
    4230           4 :                     oSupergrids.insert(yx(nY, nX));
    4231           4 :                     bHasX = true;
    4232             :                 }
    4233          14 :                 else if ((bHasX || bHasY) && pszSUPERGRIDS[i] >= '0' &&
    4234           1 :                          pszSUPERGRIDS[i] <= '9')
    4235             :                 {
    4236             :                     // ok
    4237             :                 }
    4238          14 :                 else if (bHasY && pszSUPERGRIDS[i] == ',')
    4239             :                 {
    4240           7 :                     bNextIsX = true;
    4241             :                 }
    4242           7 :                 else if (bHasX && bHasY && pszSUPERGRIDS[i] == ')')
    4243             :                 {
    4244           4 :                     chExpectedChar = ',';
    4245           4 :                     bHasX = false;
    4246           4 :                     bHasY = false;
    4247             :                 }
    4248             :                 else
    4249             :                 {
    4250           3 :                     CPLError(CE_Warning, CPLE_AppDefined,
    4251             :                              "Invalid formatting for SUPERGRIDS_INDICES at "
    4252             :                              "index %d. "
    4253             :                              "Got %c",
    4254           3 :                              i, pszSUPERGRIDS[i]);
    4255           3 :                     break;
    4256             :                 }
    4257             :             }
    4258             :         }
    4259          11 :         if (pszSUPERGRIDS[i] == 0 && chExpectedChar != ',')
    4260             :         {
    4261           5 :             CPLError(CE_Warning, CPLE_AppDefined,
    4262             :                      "Invalid formatting for SUPERGRIDS_INDICES at index %d.",
    4263             :                      i);
    4264             :         }
    4265             : 
    4266          11 :         bool bFirst = true;
    4267          15 :         for (const auto &yxPair : oSupergrids)
    4268             :         {
    4269           4 :             if (bFirst)
    4270             :             {
    4271           3 :                 nMinX = yxPair.x;
    4272           3 :                 nMaxX = yxPair.x;
    4273           3 :                 nMinY = yxPair.y;
    4274           3 :                 nMaxY = yxPair.y;
    4275           3 :                 bFirst = false;
    4276             :             }
    4277             :             else
    4278             :             {
    4279           1 :                 nMinX = std::min(nMinX, yxPair.x);
    4280           1 :                 nMaxX = std::max(nMaxX, yxPair.x);
    4281           1 :                 nMinY = std::min(nMinY, yxPair.y);
    4282           1 :                 nMaxY = std::max(nMaxY, yxPair.y);
    4283             :             }
    4284             :         }
    4285             :     }
    4286          37 :     const char *pszMinX = CSLFetchNameValue(l_papszOpenOptions, "MINX");
    4287          37 :     const char *pszMinY = CSLFetchNameValue(l_papszOpenOptions, "MINY");
    4288          37 :     const char *pszMaxX = CSLFetchNameValue(l_papszOpenOptions, "MAXX");
    4289          37 :     const char *pszMaxY = CSLFetchNameValue(l_papszOpenOptions, "MAXY");
    4290          37 :     const int nCountBBoxElts = (pszMinX ? 1 : 0) + (pszMinY ? 1 : 0) +
    4291          37 :                                (pszMaxX ? 1 : 0) + (pszMaxY ? 1 : 0);
    4292          37 :     const bool bHasBoundingBoxFilter =
    4293          37 :         !(nYSubDS >= 0 && nXSubDS >= 0) && (nCountBBoxElts == 4);
    4294          37 :     double dfFilterMinX = 0.0;
    4295          37 :     double dfFilterMinY = 0.0;
    4296          37 :     double dfFilterMaxX = 0.0;
    4297          37 :     double dfFilterMaxY = 0.0;
    4298          37 :     if (nYSubDS >= 0 && nXSubDS >= 0)
    4299             :     {
    4300             :         // do nothing
    4301             :     }
    4302          16 :     else if (bHasBoundingBoxFilter)
    4303             :     {
    4304           1 :         dfFilterMinX = CPLAtof(pszMinX);
    4305           1 :         dfFilterMinY = CPLAtof(pszMinY);
    4306           1 :         dfFilterMaxX = CPLAtof(pszMaxX);
    4307           1 :         dfFilterMaxY = CPLAtof(pszMaxY);
    4308             : 
    4309           1 :         nMinX = std::max(nMinX,
    4310           2 :                          static_cast<int>((dfFilterMinX - adfGeoTransform[0]) /
    4311           1 :                                           adfGeoTransform[1]));
    4312           1 :         nMaxX = std::min(nMaxX,
    4313           2 :                          static_cast<int>((dfFilterMaxX - adfGeoTransform[0]) /
    4314           1 :                                           adfGeoTransform[1]));
    4315             : 
    4316           1 :         nMinY = std::max(
    4317           2 :             nMinY, static_cast<int>(
    4318           1 :                        (dfFilterMinY - (adfGeoTransform[3] +
    4319           1 :                                         m_nLowResHeight * adfGeoTransform[5])) /
    4320           1 :                        -adfGeoTransform[5]));
    4321           1 :         nMaxY = std::min(
    4322           1 :             nMaxY, static_cast<int>(
    4323           1 :                        (dfFilterMaxY - (adfGeoTransform[3] +
    4324           1 :                                         m_nLowResHeight * adfGeoTransform[5])) /
    4325           1 :                        -adfGeoTransform[5]));
    4326             :     }
    4327          15 :     else if (nCountBBoxElts > 0)
    4328             :     {
    4329           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    4330             :                  "Bounding box filter ignored since only part of "
    4331             :                  "MINX, MINY, MAXX and MAXY has been specified");
    4332             :     }
    4333             : 
    4334          37 :     const double dfResFilterMin = CPLAtof(
    4335             :         CSLFetchNameValueDef(l_papszOpenOptions, "RES_FILTER_MIN", "0"));
    4336          37 :     const double dfResFilterMax = CPLAtof(
    4337             :         CSLFetchNameValueDef(l_papszOpenOptions, "RES_FILTER_MAX", "inf"));
    4338             : 
    4339          74 :     std::vector<std::string> georefMDLayerNames;
    4340          37 :     auto poGeoref_metadata = m_poRootGroup->OpenGroupFromFullname(
    4341         111 :         "/BAG_root/georef_metadata", nullptr);
    4342          37 :     if (poGeoref_metadata)
    4343             :     {
    4344          18 :         const auto groupNames = poGeoref_metadata->GetGroupNames(nullptr);
    4345          27 :         for (const auto &groupName : groupNames)
    4346             :         {
    4347          18 :             georefMDLayerNames.push_back(groupName);
    4348             :         }
    4349             :     }
    4350             : 
    4351             :     const int nMaxSizeMap =
    4352          37 :         atoi(CPLGetConfigOption("GDAL_BAG_MAX_SIZE_VARRES_MAP", "50000000"));
    4353          37 :     const int nChunkXSize = m_nChunkXSizeVarresMD;
    4354          37 :     const int nChunkYSize = m_nChunkYSizeVarresMD;
    4355          37 :     std::vector<BAGRefinementGrid> rgrids(static_cast<size_t>(nChunkXSize) *
    4356          74 :                                           nChunkYSize);
    4357          37 :     bool bOK = true;
    4358          74 :     for (int blockY = nMinY / nChunkYSize; bOK && blockY <= nMaxY / nChunkYSize;
    4359             :          blockY++)
    4360             :     {
    4361             :         int nReqCountY =
    4362          37 :             std::min(nChunkYSize, m_nLowResHeight - blockY * nChunkYSize);
    4363          74 :         for (int blockX = nMinX / nChunkXSize;
    4364          74 :              bOK && blockX <= nMaxX / nChunkXSize; blockX++)
    4365             :         {
    4366             :             int nReqCountX =
    4367          37 :                 std::min(nChunkXSize, m_nLowResWidth - blockX * nChunkXSize);
    4368             : 
    4369             :             // Create memory space to receive the data.
    4370          37 :             hsize_t count[2] = {static_cast<hsize_t>(nReqCountY),
    4371          37 :                                 static_cast<hsize_t>(nReqCountX)};
    4372          37 :             const hid_t memspace = H5Screate_simple(2, count, nullptr);
    4373          37 :             H5OFFSET_TYPE mem_offset[2] = {static_cast<H5OFFSET_TYPE>(0),
    4374             :                                            static_cast<H5OFFSET_TYPE>(0)};
    4375          37 :             if (H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mem_offset,
    4376          37 :                                     nullptr, count, nullptr) < 0)
    4377             :             {
    4378           0 :                 H5Sclose(memspace);
    4379           0 :                 bOK = false;
    4380           0 :                 break;
    4381             :             }
    4382             : 
    4383          74 :             if (!ReadVarresMetadataValue(blockY * nChunkYSize,
    4384          37 :                                          blockX * nChunkXSize, memspace,
    4385             :                                          rgrids.data(), nReqCountY, nReqCountX))
    4386             :             {
    4387           0 :                 bOK = false;
    4388           0 :                 H5Sclose(memspace);
    4389           0 :                 break;
    4390             :             }
    4391          37 :             H5Sclose(memspace);
    4392             : 
    4393         113 :             for (int yInBlock = std::max(0, nMinY - blockY * nChunkYSize);
    4394         226 :                  bOK && yInBlock <= std::min(nReqCountY - 1,
    4395         226 :                                              nMaxY - blockY * nChunkYSize);
    4396             :                  yInBlock++)
    4397             :             {
    4398         413 :                 for (int xInBlock = std::max(0, nMinX - blockX * nChunkXSize);
    4399         826 :                      bOK && xInBlock <= std::min(nReqCountX - 1,
    4400         826 :                                                  nMaxX - blockX * nChunkXSize);
    4401             :                      xInBlock++)
    4402             :                 {
    4403         337 :                     const int y = yInBlock + blockY * nChunkYSize;
    4404         337 :                     const int x = xInBlock + blockX * nChunkXSize;
    4405             :                     const auto &rgrid =
    4406         337 :                         rgrids[yInBlock * nReqCountX + xInBlock];
    4407         337 :                     if (rgrid.nWidth > 0)
    4408             :                     {
    4409         337 :                         if (rgrid.fResX <= 0 || rgrid.fResY <= 0)
    4410             :                         {
    4411           0 :                             CPLError(CE_Failure, CPLE_NotSupported,
    4412             :                                      "Incorrect resolution for supergrid "
    4413             :                                      "(%d, %d).",
    4414             :                                      static_cast<int>(y), static_cast<int>(x));
    4415           0 :                             bOK = false;
    4416           0 :                             break;
    4417             :                         }
    4418         337 :                         if (rgrid.nIndex + static_cast<GUInt64>(rgrid.nWidth) *
    4419         337 :                                                rgrid.nHeight >
    4420             :                             nRefinementsSize)
    4421             :                         {
    4422           0 :                             CPLError(
    4423             :                                 CE_Failure, CPLE_NotSupported,
    4424             :                                 "Incorrect index / dimensions for supergrid "
    4425             :                                 "(%d, %d).",
    4426             :                                 static_cast<int>(y), static_cast<int>(x));
    4427           0 :                             bOK = false;
    4428           0 :                             break;
    4429             :                         }
    4430             : 
    4431         337 :                         if (rgrid.fSWX < 0.0f || rgrid.fSWY < 0.0f ||
    4432             :                             // 0.1 is to deal with numeric imprecisions
    4433         337 :                             rgrid.fSWX +
    4434         337 :                                     (rgrid.nWidth - 1 - 0.1) * rgrid.fResX >
    4435         337 :                                 adfGeoTransform[1] ||
    4436         337 :                             rgrid.fSWY +
    4437         337 :                                     (rgrid.nHeight - 1 - 0.1) * rgrid.fResY >
    4438         337 :                                 -adfGeoTransform[5])
    4439             :                         {
    4440           0 :                             CPLError(
    4441             :                                 CE_Failure, CPLE_NotSupported,
    4442             :                                 "Incorrect bounds for supergrid "
    4443             :                                 "(%d, %d): %f, %f, %f, %f.",
    4444             :                                 static_cast<int>(y), static_cast<int>(x),
    4445           0 :                                 rgrid.fSWX, rgrid.fSWY,
    4446           0 :                                 rgrid.fSWX + (rgrid.nWidth - 1) * rgrid.fResX,
    4447           0 :                                 rgrid.fSWY + (rgrid.nHeight - 1) * rgrid.fResY);
    4448           0 :                             bOK = false;
    4449           0 :                             break;
    4450             :                         }
    4451             : 
    4452             :                         const float gridRes =
    4453         337 :                             std::max(rgrid.fResX, rgrid.fResY);
    4454         337 :                         if (gridRes < dfResFilterMin ||
    4455         337 :                             gridRes >= dfResFilterMax)
    4456             :                         {
    4457          12 :                             continue;
    4458             :                         }
    4459             : 
    4460         325 :                         if (static_cast<int>(m_oMapRefinemendGrids.size()) ==
    4461             :                             nMaxSizeMap)
    4462             :                         {
    4463           0 :                             CPLError(
    4464             :                                 CE_Failure, CPLE_AppDefined,
    4465             :                                 "Size of map of refinement grids has reached "
    4466             :                                 "%d entries. "
    4467             :                                 "Set the GDAL_BAG_MAX_SIZE_VARRES_MAP "
    4468             :                                 "configuration option "
    4469             :                                 "to an higher value if you want to allow more",
    4470             :                                 nMaxSizeMap);
    4471           0 :                             return false;
    4472             :                         }
    4473             : 
    4474             :                         try
    4475             :                         {
    4476         325 :                             m_oMapRefinemendGrids[y * m_nLowResWidth + x] =
    4477             :                                 rgrid;
    4478             :                         }
    4479           0 :                         catch (const std::exception &e)
    4480             :                         {
    4481           0 :                             CPLError(CE_Failure, CPLE_OutOfMemory,
    4482             :                                      "Out of memory adding entries to map of "
    4483             :                                      "refinement "
    4484             :                                      "grids: %s",
    4485           0 :                                      e.what());
    4486           0 :                             return false;
    4487             :                         }
    4488             : 
    4489         325 :                         const double dfMinX = adfGeoTransform[0] +
    4490         325 :                                               x * adfGeoTransform[1] +
    4491         325 :                                               rgrid.fSWX - rgrid.fResX / 2;
    4492         325 :                         const double dfMaxX =
    4493             :                             dfMinX +
    4494         325 :                             rgrid.nWidth * static_cast<double>(rgrid.fResX);
    4495         325 :                         const double dfMinY =
    4496         325 :                             adfGeoTransform[3] +
    4497         325 :                             m_nLowResHeight * adfGeoTransform[5] +
    4498         325 :                             y * -adfGeoTransform[5] + rgrid.fSWY -
    4499         325 :                             rgrid.fResY / 2;
    4500         325 :                         const double dfMaxY =
    4501             :                             dfMinY +
    4502         325 :                             static_cast<double>(rgrid.nHeight) * rgrid.fResY;
    4503             : 
    4504         335 :                         if ((oSupergrids.empty() ||
    4505          10 :                              oSupergrids.find(yx(static_cast<int>(y),
    4506          10 :                                                  static_cast<int>(x))) !=
    4507         650 :                                  oSupergrids.end()) &&
    4508         319 :                             (!bHasBoundingBoxFilter ||
    4509          16 :                              (dfMinX >= dfFilterMinX &&
    4510          12 :                               dfMinY >= dfFilterMinY &&
    4511           9 :                               dfMaxX <= dfFilterMaxX &&
    4512             :                               dfMaxY <= dfFilterMaxY)))
    4513             :                         {
    4514             :                             {
    4515             :                                 const int nIdx =
    4516         305 :                                     m_aosSubdatasets.size() / 2 + 1;
    4517             :                                 m_aosSubdatasets.AddNameValue(
    4518             :                                     CPLSPrintf("SUBDATASET_%d_NAME", nIdx),
    4519             :                                     CPLSPrintf("BAG:\"%s\":supergrid:%d:%d",
    4520         305 :                                                GetDescription(),
    4521             :                                                static_cast<int>(y),
    4522         305 :                                                static_cast<int>(x)));
    4523             :                                 m_aosSubdatasets.AddNameValue(
    4524             :                                     CPLSPrintf("SUBDATASET_%d_DESC", nIdx),
    4525             :                                     CPLSPrintf(
    4526             :                                         "Supergrid (y=%d, x=%d) from "
    4527             :                                         "(x=%f,y=%f) to "
    4528             :                                         "(x=%f,y=%f), resolution (x=%f,y=%f)",
    4529             :                                         static_cast<int>(y),
    4530             :                                         static_cast<int>(x), dfMinX, dfMinY,
    4531         305 :                                         dfMaxX, dfMaxY, rgrid.fResX,
    4532         305 :                                         rgrid.fResY));
    4533             :                             }
    4534             : 
    4535         369 :                             for (const auto &groupName : georefMDLayerNames)
    4536             :                             {
    4537             :                                 const int nIdx =
    4538          64 :                                     m_aosSubdatasets.size() / 2 + 1;
    4539             :                                 m_aosSubdatasets.AddNameValue(
    4540             :                                     CPLSPrintf("SUBDATASET_%d_NAME", nIdx),
    4541             :                                     CPLSPrintf(
    4542             :                                         "BAG:\"%s\":georef_metadata:%s:%d:%d",
    4543          64 :                                         GetDescription(), groupName.c_str(),
    4544             :                                         static_cast<int>(y),
    4545          64 :                                         static_cast<int>(x)));
    4546             :                                 m_aosSubdatasets.AddNameValue(
    4547             :                                     CPLSPrintf("SUBDATASET_%d_DESC", nIdx),
    4548             :                                     CPLSPrintf("Georeferenced metadata %s of "
    4549             :                                                "supergrid (y=%d, x=%d)",
    4550             :                                                groupName.c_str(),
    4551             :                                                static_cast<int>(y),
    4552          64 :                                                static_cast<int>(x)));
    4553             :                             }
    4554             :                         }
    4555             :                     }
    4556             :                 }
    4557             :             }
    4558             :         }
    4559             :     }
    4560             : 
    4561          37 :     if (!bOK || m_oMapRefinemendGrids.empty())
    4562             :     {
    4563           2 :         m_aosSubdatasets.Clear();
    4564           2 :         m_oMapRefinemendGrids.clear();
    4565           2 :         return false;
    4566             :     }
    4567             : 
    4568          35 :     CPLDebug("BAG", "variable resolution extensions detected");
    4569          35 :     return true;
    4570             : }
    4571             : 
    4572             : /************************************************************************/
    4573             : /*                            LoadMetadata()                            */
    4574             : /************************************************************************/
    4575             : 
    4576          81 : void BAGDataset::LoadMetadata()
    4577             : 
    4578             : {
    4579             :     // Load the metadata from the file.
    4580             :     const hid_t hMDDS =
    4581          81 :         H5Dopen(m_poSharedResources->m_hHDF5, "/BAG_root/metadata");
    4582          81 :     const hid_t datatype = H5Dget_type(hMDDS);
    4583          81 :     const hid_t dataspace = H5Dget_space(hMDDS);
    4584          81 :     const hid_t native = H5Tget_native_type(datatype, H5T_DIR_ASCEND);
    4585             : 
    4586          81 :     const int n_dims = H5Sget_simple_extent_ndims(dataspace);
    4587          81 :     hsize_t dims[1] = {static_cast<hsize_t>(0)};
    4588          81 :     hsize_t maxdims[1] = {static_cast<hsize_t>(0)};
    4589             : 
    4590          80 :     if (n_dims == 1 && H5Tget_class(native) == H5T_STRING &&
    4591         161 :         !H5Tis_variable_str(native) && H5Tget_size(native) == 1)
    4592             :     {
    4593          80 :         H5Sget_simple_extent_dims(dataspace, dims, maxdims);
    4594             : 
    4595          80 :         pszXMLMetadata =
    4596          80 :             static_cast<char *>(CPLCalloc(static_cast<int>(dims[0] + 1), 1));
    4597             : 
    4598          80 :         H5Dread(hMDDS, native, H5S_ALL, dataspace, H5P_DEFAULT, pszXMLMetadata);
    4599             :     }
    4600             : 
    4601          81 :     H5Tclose(native);
    4602          81 :     H5Sclose(dataspace);
    4603          81 :     H5Tclose(datatype);
    4604          81 :     H5Dclose(hMDDS);
    4605             : 
    4606          81 :     if (pszXMLMetadata == nullptr || pszXMLMetadata[0] == 0)
    4607           1 :         return;
    4608             : 
    4609             :     // Try to get the geotransform.
    4610          80 :     CPLXMLNode *psRoot = CPLParseXMLString(pszXMLMetadata);
    4611             : 
    4612          80 :     if (psRoot == nullptr)
    4613           0 :         return;
    4614             : 
    4615          80 :     CPLStripXMLNamespace(psRoot, nullptr, TRUE);
    4616             : 
    4617          80 :     CPLXMLNode *const psGeo = CPLSearchXMLNode(psRoot, "=MD_Georectified");
    4618             : 
    4619          80 :     if (psGeo != nullptr)
    4620             :     {
    4621         160 :         CPLString osResHeight, osResWidth;
    4622         722 :         for (const auto *psIter = psGeo->psChild; psIter;
    4623         642 :              psIter = psIter->psNext)
    4624             :         {
    4625         642 :             if (strcmp(psIter->pszValue, "axisDimensionProperties") == 0)
    4626             :             {
    4627             :                 // since BAG format 1.5 version
    4628         160 :                 const char *pszDim = CPLGetXMLValue(
    4629             :                     psIter,
    4630             :                     "MD_Dimension.dimensionName.MD_DimensionNameTypeCode",
    4631             :                     nullptr);
    4632         160 :                 const char *pszRes = nullptr;
    4633         160 :                 if (pszDim)
    4634             :                 {
    4635         152 :                     pszRes = CPLGetXMLValue(
    4636             :                         psIter, "MD_Dimension.resolution.Measure", nullptr);
    4637             :                 }
    4638             :                 else
    4639             :                 {
    4640             :                     // prior to BAG format 1.5 version
    4641           8 :                     pszDim = CPLGetXMLValue(
    4642             :                         psIter, "MD_Dimension.dimensionName", nullptr);
    4643           8 :                     pszRes = CPLGetXMLValue(
    4644             :                         psIter, "MD_Dimension.resolution.Measure.value",
    4645             :                         nullptr);
    4646             :                 }
    4647             : 
    4648         160 :                 if (pszDim && EQUAL(pszDim, "row") && pszRes)
    4649             :                 {
    4650          80 :                     osResHeight = pszRes;
    4651             :                 }
    4652          80 :                 else if (pszDim && EQUAL(pszDim, "column") && pszRes)
    4653             :                 {
    4654          80 :                     osResWidth = pszRes;
    4655             :                 }
    4656             :             }
    4657             :         }
    4658             : 
    4659          80 :         char **papszCornerTokens = CSLTokenizeStringComplex(
    4660             :             CPLGetXMLValue(psGeo, "cornerPoints.Point.coordinates", ""), " ,",
    4661             :             FALSE, FALSE);
    4662             : 
    4663          80 :         if (CSLCount(papszCornerTokens) == 4)
    4664             :         {
    4665          80 :             const double dfLLX = CPLAtof(papszCornerTokens[0]);
    4666          80 :             const double dfLLY = CPLAtof(papszCornerTokens[1]);
    4667          80 :             const double dfURX = CPLAtof(papszCornerTokens[2]);
    4668          80 :             const double dfURY = CPLAtof(papszCornerTokens[3]);
    4669             : 
    4670          80 :             double dfResWidth = CPLAtof(osResWidth);
    4671          80 :             double dfResHeight = CPLAtof(osResHeight);
    4672          80 :             if (dfResWidth > 0 && dfResHeight > 0)
    4673             :             {
    4674          80 :                 if (fabs((dfURX - dfLLX) / dfResWidth - m_nLowResWidth) <
    4675           0 :                         1e-2 &&
    4676           0 :                     fabs((dfURY - dfLLY) / dfResHeight - m_nLowResHeight) <
    4677             :                         1e-2)
    4678             :                 {
    4679             :                     // Found with
    4680             :                     // https://data.ngdc.noaa.gov/platforms/ocean/nos/coast/H12001-H14000/H12525/BAG/H12525_MB_4m_MLLW_1of2.bag
    4681             :                     // to address issue
    4682             :                     // https://github.com/OSGeo/gdal/issues/1643
    4683           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    4684             :                              "cornerPoints not consistent with resolution "
    4685             :                              "given in metadata");
    4686             :                 }
    4687          80 :                 else if (fabs((dfURX - dfLLX) / dfResWidth -
    4688          80 :                               (m_nLowResWidth - 1)) < 1e-2 &&
    4689          78 :                          fabs((dfURY - dfLLY) / dfResHeight -
    4690          78 :                               (m_nLowResHeight - 1)) < 1e-2)
    4691             :                 {
    4692             :                     // pixel center convention. OK
    4693             :                 }
    4694             :                 else
    4695             :                 {
    4696           2 :                     CPLDebug("BAG", "cornerPoints not consistent with "
    4697             :                                     "resolution given in metadata");
    4698           2 :                     CPLDebug("BAG",
    4699             :                              "Metadata horizontal resolution: %f. "
    4700             :                              "Computed resolution: %f. "
    4701             :                              "Computed width: %f vs %d",
    4702           2 :                              dfResWidth, (dfURX - dfLLX) / (m_nLowResWidth - 1),
    4703           2 :                              (dfURX - dfLLX) / dfResWidth, m_nLowResWidth);
    4704           2 :                     CPLDebug("BAG",
    4705             :                              "Metadata vertical resolution: %f. "
    4706             :                              "Computed resolution: %f. "
    4707             :                              "Computed height: %f vs %d",
    4708             :                              dfResHeight,
    4709           2 :                              (dfURY - dfLLY) / (m_nLowResHeight - 1),
    4710           2 :                              (dfURY - dfLLY) / dfResHeight, m_nLowResHeight);
    4711           2 :                     CPLError(CE_Warning, CPLE_AppDefined,
    4712             :                              "cornerPoints not consistent with resolution "
    4713             :                              "given in metadata");
    4714             :                 }
    4715             :             }
    4716             : 
    4717          80 :             adfGeoTransform[0] = dfLLX;
    4718          80 :             adfGeoTransform[1] = dfResWidth;
    4719          80 :             adfGeoTransform[3] = dfLLY + dfResHeight * (m_nLowResHeight - 1);
    4720          80 :             adfGeoTransform[5] = dfResHeight * (-1);
    4721             : 
    4722             :             // shift to pixel corner convention
    4723          80 :             adfGeoTransform[0] -= adfGeoTransform[1] * 0.5;
    4724          80 :             adfGeoTransform[3] -= adfGeoTransform[5] * 0.5;
    4725             : 
    4726          80 :             m_dfLowResMinX = adfGeoTransform[0];
    4727          80 :             m_dfLowResMaxX =
    4728          80 :                 m_dfLowResMinX + m_nLowResWidth * adfGeoTransform[1];
    4729          80 :             m_dfLowResMaxY = adfGeoTransform[3];
    4730          80 :             m_dfLowResMinY =
    4731          80 :                 m_dfLowResMaxY + m_nLowResHeight * adfGeoTransform[5];
    4732             :         }
    4733          80 :         CSLDestroy(papszCornerTokens);
    4734             :     }
    4735             : 
    4736             :     // Try to get the coordinate system.
    4737          80 :     if (OGR_SRS_ImportFromISO19115(&m_oSRS, pszXMLMetadata) != OGRERR_NONE)
    4738             :     {
    4739          78 :         ParseWKTFromXML(pszXMLMetadata);
    4740             :     }
    4741             : 
    4742             :     // Fetch acquisition date.
    4743          80 :     CPLXMLNode *const psDateTime = CPLSearchXMLNode(psRoot, "=dateTime");
    4744          80 :     if (psDateTime != nullptr)
    4745             :     {
    4746             :         const char *pszDateTimeValue =
    4747          80 :             psDateTime->psChild && psDateTime->psChild->eType == CXT_Element
    4748         156 :                 ? CPLGetXMLValue(psDateTime->psChild, nullptr, nullptr)
    4749           4 :                 : CPLGetXMLValue(psDateTime, nullptr, nullptr);
    4750          80 :         if (pszDateTimeValue)
    4751          80 :             GDALDataset::SetMetadataItem("BAG_DATETIME", pszDateTimeValue);
    4752             :     }
    4753             : 
    4754          80 :     CPLDestroyXMLNode(psRoot);
    4755             : }
    4756             : 
    4757             : /************************************************************************/
    4758             : /*                          ParseWKTFromXML()                           */
    4759             : /************************************************************************/
    4760          78 : OGRErr BAGDataset::ParseWKTFromXML(const char *pszISOXML)
    4761             : {
    4762          78 :     CPLXMLNode *const psRoot = CPLParseXMLString(pszISOXML);
    4763             : 
    4764          78 :     if (psRoot == nullptr)
    4765           0 :         return OGRERR_FAILURE;
    4766             : 
    4767          78 :     CPLStripXMLNamespace(psRoot, nullptr, TRUE);
    4768             : 
    4769          78 :     CPLXMLNode *psRSI = CPLSearchXMLNode(psRoot, "=referenceSystemInfo");
    4770          78 :     if (psRSI == nullptr)
    4771             :     {
    4772           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4773             :                  "Unable to find <referenceSystemInfo> in metadata.");
    4774           0 :         CPLDestroyXMLNode(psRoot);
    4775           0 :         return OGRERR_FAILURE;
    4776             :     }
    4777             : 
    4778             :     const char *pszSRCodeString =
    4779          78 :         CPLGetXMLValue(psRSI,
    4780             :                        "MD_ReferenceSystem.referenceSystemIdentifier."
    4781             :                        "RS_Identifier.code.CharacterString",
    4782             :                        nullptr);
    4783          78 :     if (pszSRCodeString == nullptr)
    4784             :     {
    4785           2 :         CPLDebug("BAG",
    4786             :                  "Unable to find /MI_Metadata/referenceSystemInfo[1]/"
    4787             :                  "MD_ReferenceSystem[1]/referenceSystemIdentifier[1]/"
    4788             :                  "RS_Identifier[1]/code[1]/CharacterString[1] in metadata.");
    4789           2 :         CPLDestroyXMLNode(psRoot);
    4790           2 :         return OGRERR_FAILURE;
    4791             :     }
    4792             : 
    4793             :     const char *pszSRCodeSpace =
    4794          76 :         CPLGetXMLValue(psRSI,
    4795             :                        "MD_ReferenceSystem.referenceSystemIdentifier."
    4796             :                        "RS_Identifier.codeSpace.CharacterString",
    4797             :                        "");
    4798          76 :     if (!EQUAL(pszSRCodeSpace, "WKT"))
    4799             :     {
    4800           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4801             :                  "Spatial reference string is not in WKT.");
    4802           0 :         CPLDestroyXMLNode(psRoot);
    4803           0 :         return OGRERR_FAILURE;
    4804             :     }
    4805             : 
    4806          76 :     if (m_oSRS.importFromWkt(pszSRCodeString) != OGRERR_NONE)
    4807             :     {
    4808           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4809             :                  "Failed parsing WKT string \"%s\".", pszSRCodeString);
    4810           0 :         CPLDestroyXMLNode(psRoot);
    4811           0 :         return OGRERR_FAILURE;
    4812             :     }
    4813             : 
    4814          76 :     psRSI = CPLSearchXMLNode(psRSI->psNext, "=referenceSystemInfo");
    4815          76 :     if (psRSI == nullptr)
    4816             :     {
    4817           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4818             :                  "Unable to find second instance of <referenceSystemInfo> "
    4819             :                  "in metadata.");
    4820           0 :         CPLDestroyXMLNode(psRoot);
    4821           0 :         return OGRERR_NONE;
    4822             :     }
    4823             : 
    4824             :     pszSRCodeString =
    4825          76 :         CPLGetXMLValue(psRSI,
    4826             :                        "MD_ReferenceSystem.referenceSystemIdentifier."
    4827             :                        "RS_Identifier.code.CharacterString",
    4828             :                        nullptr);
    4829          76 :     if (pszSRCodeString == nullptr)
    4830             :     {
    4831           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4832             :                  "Unable to find /MI_Metadata/referenceSystemInfo[2]/"
    4833             :                  "MD_ReferenceSystem[1]/referenceSystemIdentifier[1]/"
    4834             :                  "RS_Identifier[1]/code[1]/CharacterString[1] in metadata.");
    4835           0 :         CPLDestroyXMLNode(psRoot);
    4836           0 :         return OGRERR_NONE;
    4837             :     }
    4838             : 
    4839             :     pszSRCodeSpace =
    4840          76 :         CPLGetXMLValue(psRSI,
    4841             :                        "MD_ReferenceSystem.referenceSystemIdentifier."
    4842             :                        "RS_Identifier.codeSpace.CharacterString",
    4843             :                        "");
    4844          76 :     if (!EQUAL(pszSRCodeSpace, "WKT"))
    4845             :     {
    4846           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4847             :                  "Spatial reference string is not in WKT.");
    4848           0 :         CPLDestroyXMLNode(psRoot);
    4849           0 :         return OGRERR_NONE;
    4850             :     }
    4851             : 
    4852          76 :     if (m_bReportVertCRS && (STARTS_WITH_CI(pszSRCodeString, "VERTCS") ||
    4853          73 :                              STARTS_WITH_CI(pszSRCodeString, "VERT_CS")))
    4854             :     {
    4855         146 :         OGR_SRSNode oVertCRSRootNode;
    4856          73 :         const char *pszInput = pszSRCodeString;
    4857          73 :         if (oVertCRSRootNode.importFromWkt(&pszInput) == OGRERR_NONE)
    4858             :         {
    4859          73 :             if (oVertCRSRootNode.GetNode("UNIT") == nullptr)
    4860             :             {
    4861             :                 // UNIT is required
    4862          68 :                 auto poUnits = new OGR_SRSNode("UNIT");
    4863          68 :                 poUnits->AddChild(new OGR_SRSNode("metre"));
    4864          68 :                 poUnits->AddChild(new OGR_SRSNode("1.0"));
    4865          68 :                 oVertCRSRootNode.AddChild(poUnits);
    4866             :             }
    4867          73 :             if (oVertCRSRootNode.GetNode("AXIS") == nullptr)
    4868             :             {
    4869             :                 // If AXIS is missing, add an explicit Depth AXIS
    4870          68 :                 auto poAxis = new OGR_SRSNode("AXIS");
    4871          68 :                 poAxis->AddChild(new OGR_SRSNode("Depth"));
    4872          68 :                 poAxis->AddChild(new OGR_SRSNode("DOWN"));
    4873          68 :                 oVertCRSRootNode.AddChild(poAxis);
    4874             :             }
    4875             : 
    4876          73 :             char *pszVertCRSWKT = nullptr;
    4877          73 :             oVertCRSRootNode.exportToWkt(&pszVertCRSWKT);
    4878             : 
    4879         146 :             OGRSpatialReference oVertCRS;
    4880          73 :             if (oVertCRS.importFromWkt(pszVertCRSWKT) == OGRERR_NONE)
    4881             :             {
    4882          73 :                 if (EQUAL(oVertCRS.GetName(), "MLLW"))
    4883             :                 {
    4884          60 :                     oVertCRS.importFromEPSG(5866);
    4885             :                 }
    4886             : 
    4887         146 :                 OGRSpatialReference oCompoundCRS;
    4888          73 :                 oCompoundCRS.SetCompoundCS(
    4889         146 :                     (CPLString(m_oSRS.GetName()) + " + " + oVertCRS.GetName())
    4890             :                         .c_str(),
    4891          73 :                     &m_oSRS, &oVertCRS);
    4892          73 :                 oCompoundCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    4893             : 
    4894          73 :                 m_oSRS = std::move(oCompoundCRS);
    4895             :             }
    4896             : 
    4897          73 :             CPLFree(pszVertCRSWKT);
    4898             :         }
    4899             :     }
    4900             : 
    4901          76 :     CPLDestroyXMLNode(psRoot);
    4902             : 
    4903          76 :     return OGRERR_NONE;
    4904             : }
    4905             : 
    4906             : /************************************************************************/
    4907             : /*                          GetGeoTransform()                           */
    4908             : /************************************************************************/
    4909             : 
    4910          78 : CPLErr BAGDataset::GetGeoTransform(double *padfGeoTransform)
    4911             : 
    4912             : {
    4913          78 :     if (adfGeoTransform[0] != 0.0 || adfGeoTransform[3] != 0.0)
    4914             :     {
    4915          78 :         memcpy(padfGeoTransform, adfGeoTransform, sizeof(double) * 6);
    4916          78 :         return CE_None;
    4917             :     }
    4918             : 
    4919           0 :     return GDALPamDataset::GetGeoTransform(padfGeoTransform);
    4920             : }
    4921             : 
    4922             : /************************************************************************/
    4923             : /*                         GetSpatialRef()                              */
    4924             : /************************************************************************/
    4925             : 
    4926          21 : const OGRSpatialReference *BAGDataset::GetSpatialRef() const
    4927             : {
    4928          21 :     if (!m_oSRS.IsEmpty())
    4929          21 :         return &m_oSRS;
    4930           0 :     return GDALPamDataset::GetSpatialRef();
    4931             : }
    4932             : 
    4933             : /************************************************************************/
    4934             : /*                          SetGeoTransform()                           */
    4935             : /************************************************************************/
    4936             : 
    4937           3 : CPLErr BAGDataset::SetGeoTransform(double *padfGeoTransform)
    4938             : {
    4939           3 :     if (eAccess == GA_ReadOnly)
    4940           0 :         return GDALPamDataset::SetGeoTransform(padfGeoTransform);
    4941             : 
    4942           3 :     if (padfGeoTransform[2] != 0 || padfGeoTransform[4] != 0)
    4943             :     {
    4944           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    4945             :                  "BAG driver requires a non-rotated geotransform");
    4946           0 :         return CE_Failure;
    4947             :     }
    4948           3 :     memcpy(adfGeoTransform, padfGeoTransform, sizeof(double) * 6);
    4949           3 :     return WriteMetadataIfNeeded() ? CE_None : CE_Failure;
    4950             : }
    4951             : 
    4952             : /************************************************************************/
    4953             : /*                           SetSpatialRef()                            */
    4954             : /************************************************************************/
    4955             : 
    4956           3 : CPLErr BAGDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    4957             : {
    4958           3 :     if (eAccess == GA_ReadOnly)
    4959           0 :         return GDALPamDataset::SetSpatialRef(poSRS);
    4960             : 
    4961           3 :     if (poSRS == nullptr || poSRS->IsEmpty())
    4962             :     {
    4963           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    4964             :                  "BAG driver requires a valid SRS");
    4965           0 :         return CE_Failure;
    4966             :     }
    4967             : 
    4968           3 :     m_oSRS = *poSRS;
    4969           3 :     return WriteMetadataIfNeeded() ? CE_None : CE_Failure;
    4970             : }
    4971             : 
    4972             : /************************************************************************/
    4973             : /*                         WriteMetadataIfNeeded()                      */
    4974             : /************************************************************************/
    4975             : 
    4976           6 : bool BAGDataset::WriteMetadataIfNeeded()
    4977             : {
    4978           6 :     if (m_bMetadataWritten)
    4979             :     {
    4980           0 :         return true;
    4981             :     }
    4982           2 :     if ((adfGeoTransform[0] == 0.0 && adfGeoTransform[1] == 1.0 &&
    4983          12 :          adfGeoTransform[3] == 0.0 && adfGeoTransform[5] == 1.0) ||
    4984           4 :         m_oSRS.IsEmpty())
    4985             :     {
    4986           3 :         return true;
    4987             :     }
    4988           3 :     m_bMetadataWritten = true;
    4989             : 
    4990             :     CPLString osXMLMetadata = BAGCreator::GenerateMetadata(
    4991           3 :         nRasterXSize, nRasterYSize, adfGeoTransform,
    4992           6 :         m_oSRS.IsEmpty() ? nullptr : &m_oSRS, m_aosCreationOptions.List());
    4993           3 :     if (osXMLMetadata.empty())
    4994             :     {
    4995           0 :         return false;
    4996             :     }
    4997             : 
    4998           3 :     if (!BAGCreator::CreateAndWriteMetadata(m_poSharedResources->m_hHDF5,
    4999             :                                             osXMLMetadata))
    5000             :     {
    5001           0 :         return false;
    5002             :     }
    5003             : 
    5004           3 :     return true;
    5005             : }
    5006             : 
    5007             : /************************************************************************/
    5008             : /*                      GetMetadataDomainList()                         */
    5009             : /************************************************************************/
    5010             : 
    5011           0 : char **BAGDataset::GetMetadataDomainList()
    5012             : {
    5013           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
    5014           0 :                                    TRUE, "xml:BAG", nullptr);
    5015             : }
    5016             : 
    5017             : /************************************************************************/
    5018             : /*                            GetMetadata()                             */
    5019             : /************************************************************************/
    5020             : 
    5021          60 : char **BAGDataset::GetMetadata(const char *pszDomain)
    5022             : 
    5023             : {
    5024          60 :     if (pszDomain != nullptr && EQUAL(pszDomain, "xml:BAG"))
    5025             :     {
    5026           5 :         apszMDList[0] = pszXMLMetadata;
    5027           5 :         apszMDList[1] = nullptr;
    5028             : 
    5029           5 :         return apszMDList;
    5030             :     }
    5031             : 
    5032          55 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
    5033             :     {
    5034          16 :         return m_aosSubdatasets.List();
    5035             :     }
    5036             : 
    5037          39 :     return GDALPamDataset::GetMetadata(pszDomain);
    5038             : }
    5039             : 
    5040             : /************************************************************************/
    5041             : /*                      BAGDatasetDriverUnload()                        */
    5042             : /************************************************************************/
    5043             : 
    5044           5 : static void BAGDatasetDriverUnload(GDALDriver *)
    5045             : {
    5046           5 :     HDF5UnloadFileDriver();
    5047           5 : }
    5048             : 
    5049             : /************************************************************************/
    5050             : /*                            ~BAGCreator()()                           */
    5051             : /************************************************************************/
    5052             : 
    5053         148 : BAGCreator::~BAGCreator()
    5054             : {
    5055          74 :     Close();
    5056          74 : }
    5057             : 
    5058             : /************************************************************************/
    5059             : /*                               Close()                                */
    5060             : /************************************************************************/
    5061             : 
    5062          94 : bool BAGCreator::Close()
    5063             : {
    5064          94 :     bool ret = true;
    5065          94 :     if (m_bagRoot >= 0)
    5066             :     {
    5067          20 :         ret = (H5_CHECK(H5Gclose(m_bagRoot)) >= 0) && ret;
    5068          20 :         m_bagRoot = -1;
    5069             :     }
    5070          94 :     if (m_hdf5 >= 0)
    5071             :     {
    5072          20 :         ret = (H5_CHECK(H5Fclose(m_hdf5)) >= 0) && ret;
    5073          20 :         m_hdf5 = -1;
    5074             :     }
    5075          94 :     return ret;
    5076             : }
    5077             : 
    5078             : /************************************************************************/
    5079             : /*                         SubstituteVariables()                        */
    5080             : /************************************************************************/
    5081             : 
    5082        4893 : bool BAGCreator::SubstituteVariables(CPLXMLNode *psNode, char **papszDict)
    5083             : {
    5084        4893 :     if (psNode->eType == CXT_Text && psNode->pszValue &&
    5085        1617 :         strstr(psNode->pszValue, "${"))
    5086             :     {
    5087         735 :         CPLString osVal(psNode->pszValue);
    5088         735 :         size_t nPos = 0;
    5089             :         while (true)
    5090             :         {
    5091        1470 :             nPos = osVal.find("${", nPos);
    5092        1470 :             if (nPos == std::string::npos)
    5093             :             {
    5094         735 :                 break;
    5095             :             }
    5096         735 :             CPLString osKeyName;
    5097         735 :             bool bHasDefaultValue = false;
    5098         735 :             CPLString osDefaultValue;
    5099         735 :             size_t nAfterKeyName = 0;
    5100       14616 :             for (size_t i = nPos + 2; i < osVal.size(); i++)
    5101             :             {
    5102       14616 :                 if (osVal[i] == ':')
    5103             :                 {
    5104         378 :                     osKeyName = osVal.substr(nPos + 2, i - (nPos + 2));
    5105             :                 }
    5106       14238 :                 else if (osVal[i] == '}')
    5107             :                 {
    5108         735 :                     if (osKeyName.empty())
    5109             :                     {
    5110         357 :                         osKeyName = osVal.substr(nPos + 2, i - (nPos + 2));
    5111             :                     }
    5112             :                     else
    5113             :                     {
    5114         378 :                         bHasDefaultValue = true;
    5115             :                         size_t nStartDefaultVal =
    5116         378 :                             nPos + 2 + osKeyName.size() + 1;
    5117         756 :                         osDefaultValue = osVal.substr(nStartDefaultVal,
    5118         378 :                                                       i - nStartDefaultVal);
    5119             :                     }
    5120         735 :                     nAfterKeyName = i + 1;
    5121         735 :                     break;
    5122             :                 }
    5123             :             }
    5124         735 :             if (nAfterKeyName == 0)
    5125             :             {
    5126           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5127             :                          "Invalid variable name in template");
    5128           0 :                 return false;
    5129             :             }
    5130             : 
    5131         735 :             bool bSubstFound = false;
    5132        9511 :             for (char **papszIter = papszDict;
    5133        9511 :                  !bSubstFound && papszIter && *papszIter; papszIter++)
    5134             :             {
    5135        8776 :                 if (STARTS_WITH_CI(*papszIter, "VAR_"))
    5136             :                 {
    5137        8706 :                     char *pszKey = nullptr;
    5138             :                     const char *pszValue =
    5139        8706 :                         CPLParseNameValue(*papszIter, &pszKey);
    5140        8706 :                     if (pszKey && pszValue)
    5141             :                     {
    5142        8706 :                         const char *pszVarName = pszKey + strlen("VAR_");
    5143        8706 :                         if (EQUAL(pszVarName, osKeyName))
    5144             :                         {
    5145         362 :                             bSubstFound = true;
    5146         724 :                             osVal = osVal.substr(0, nPos) + pszValue +
    5147        1086 :                                     osVal.substr(nAfterKeyName);
    5148             :                         }
    5149        8706 :                         CPLFree(pszKey);
    5150             :                     }
    5151             :                 }
    5152             :             }
    5153         735 :             if (!bSubstFound)
    5154             :             {
    5155         373 :                 if (bHasDefaultValue)
    5156             :                 {
    5157         746 :                     osVal = osVal.substr(0, nPos) + osDefaultValue +
    5158        1119 :                             osVal.substr(nAfterKeyName);
    5159             :                 }
    5160             :                 else
    5161             :                 {
    5162           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    5163             :                              "%s could not be substituted", osKeyName.c_str());
    5164           0 :                     return false;
    5165             :                 }
    5166             :             }
    5167         735 :         }
    5168             : 
    5169         735 :         if (!osVal.empty() && osVal[0] == '<' && osVal.back() == '>')
    5170             :         {
    5171           2 :             CPLXMLNode *psSubNode = CPLParseXMLString(osVal);
    5172           2 :             if (psSubNode)
    5173             :             {
    5174           2 :                 CPLFree(psNode->pszValue);
    5175           2 :                 psNode->eType = psSubNode->eType;
    5176           2 :                 psNode->pszValue = psSubNode->pszValue;
    5177           2 :                 psNode->psChild = psSubNode->psChild;
    5178           2 :                 psSubNode->pszValue = nullptr;
    5179           2 :                 psSubNode->psChild = nullptr;
    5180           2 :                 CPLDestroyXMLNode(psSubNode);
    5181             :             }
    5182             :             else
    5183             :             {
    5184           0 :                 CPLFree(psNode->pszValue);
    5185           0 :                 psNode->pszValue = CPLStrdup(osVal);
    5186             :             }
    5187             :         }
    5188             :         else
    5189             :         {
    5190         733 :             CPLFree(psNode->pszValue);
    5191         733 :             psNode->pszValue = CPLStrdup(osVal);
    5192             :         }
    5193             :     }
    5194             : 
    5195        9765 :     for (CPLXMLNode *psIter = psNode->psChild; psIter; psIter = psIter->psNext)
    5196             :     {
    5197        4872 :         if (!SubstituteVariables(psIter, papszDict))
    5198           0 :             return false;
    5199             :     }
    5200        4893 :     return true;
    5201             : }
    5202             : 
    5203             : /************************************************************************/
    5204             : /*                          GenerateMetadata()                          */
    5205             : /************************************************************************/
    5206             : 
    5207          21 : CPLString BAGCreator::GenerateMetadata(int nXSize, int nYSize,
    5208             :                                        const double *padfGeoTransform,
    5209             :                                        const OGRSpatialReference *poSRS,
    5210             :                                        char **papszOptions)
    5211             : {
    5212             :     CPLXMLNode *psRoot;
    5213             :     CPLString osTemplateFilename =
    5214          42 :         CSLFetchNameValueDef(papszOptions, "TEMPLATE", "");
    5215          21 :     if (!osTemplateFilename.empty())
    5216             :     {
    5217           0 :         psRoot = CPLParseXMLFile(osTemplateFilename);
    5218             :     }
    5219             :     else
    5220             :     {
    5221             :         const char *pszDefaultTemplateFilename =
    5222          21 :             CPLFindFile("gdal", "bag_template.xml");
    5223          21 :         if (pszDefaultTemplateFilename == nullptr)
    5224             :         {
    5225           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    5226             :                      "Cannot find bag_template.xml and TEMPLATE "
    5227             :                      "creation option not specified");
    5228           0 :             return CPLString();
    5229             :         }
    5230          21 :         psRoot = CPLParseXMLFile(pszDefaultTemplateFilename);
    5231             :     }
    5232          21 :     if (psRoot == nullptr)
    5233           0 :         return CPLString();
    5234          42 :     CPLXMLTreeCloser oCloser(psRoot);
    5235          21 :     CPL_IGNORE_RET_VAL(oCloser);
    5236             : 
    5237          21 :     CPLXMLNode *psMain = psRoot;
    5238          42 :     for (; psMain; psMain = psMain->psNext)
    5239             :     {
    5240          42 :         if (psMain->eType == CXT_Element && !STARTS_WITH(psMain->pszValue, "?"))
    5241             :         {
    5242          21 :             break;
    5243             :         }
    5244             :     }
    5245          21 :     if (psMain == nullptr)
    5246             :     {
    5247           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find main XML node");
    5248           0 :         return CPLString();
    5249             :     }
    5250             : 
    5251          42 :     CPLStringList osOptions(papszOptions, FALSE);
    5252          21 :     if (osOptions.FetchNameValue("VAR_PROCESS_STEP_DESCRIPTION") == nullptr)
    5253             :     {
    5254             :         osOptions.SetNameValue("VAR_PROCESS_STEP_DESCRIPTION",
    5255             :                                CPLSPrintf("Generated by GDAL %s",
    5256          21 :                                           GDALVersionInfo("RELEASE_NAME")));
    5257             :     }
    5258          21 :     osOptions.SetNameValue("VAR_HEIGHT", CPLSPrintf("%d", nYSize));
    5259          21 :     osOptions.SetNameValue("VAR_WIDTH", CPLSPrintf("%d", nXSize));
    5260             : 
    5261             :     struct tm brokenDown;
    5262          21 :     CPLUnixTimeToYMDHMS(time(nullptr), &brokenDown);
    5263          21 :     if (osOptions.FetchNameValue("VAR_DATE") == nullptr)
    5264             :     {
    5265             :         osOptions.SetNameValue(
    5266          21 :             "VAR_DATE", CPLSPrintf("%04d-%02d-%02d", brokenDown.tm_year + 1900,
    5267          21 :                                    brokenDown.tm_mon + 1, brokenDown.tm_mday));
    5268             :     }
    5269          21 :     if (osOptions.FetchNameValue("VAR_DATETIME") == nullptr)
    5270             :     {
    5271             :         osOptions.SetNameValue(
    5272             :             "VAR_DATETIME",
    5273             :             CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
    5274          21 :                        brokenDown.tm_year + 1900, brokenDown.tm_mon + 1,
    5275             :                        brokenDown.tm_mday, brokenDown.tm_hour,
    5276          21 :                        brokenDown.tm_min, brokenDown.tm_sec));
    5277             :     }
    5278             : 
    5279             :     osOptions.SetNameValue("VAR_RESX",
    5280          21 :                            CPLSPrintf("%.18g", padfGeoTransform[1]));
    5281             :     osOptions.SetNameValue("VAR_RESY",
    5282          21 :                            CPLSPrintf("%.18g", fabs(padfGeoTransform[5])));
    5283             :     osOptions.SetNameValue(
    5284          21 :         "VAR_RES", CPLSPrintf("%.18g", std::max(padfGeoTransform[1],
    5285          21 :                                                 fabs(padfGeoTransform[5]))));
    5286             : 
    5287          21 :     char *pszProjection = nullptr;
    5288          21 :     if (poSRS)
    5289          21 :         poSRS->exportToWkt(&pszProjection);
    5290          21 :     if (pszProjection == nullptr || EQUAL(pszProjection, ""))
    5291             :     {
    5292           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    5293             :                  "BAG driver requires a source dataset with a projection");
    5294             :     }
    5295          21 :     osOptions.SetNameValue("VAR_HORIZ_WKT", pszProjection);
    5296          21 :     CPLFree(pszProjection);
    5297             : 
    5298          42 :     OGRSpatialReference oSRS;
    5299          21 :     if (poSRS)
    5300          21 :         oSRS = *poSRS;
    5301          21 :     if (oSRS.IsCompound())
    5302             :     {
    5303           2 :         auto node = oSRS.GetRoot();
    5304           2 :         if (node && node->GetChildCount() == 3)
    5305             :         {
    5306           2 :             char *pszHorizWKT = nullptr;
    5307           2 :             node->GetChild(1)->exportToWkt(&pszHorizWKT);
    5308           2 :             char *pszVertWKT = nullptr;
    5309           2 :             node->GetChild(2)->exportToWkt(&pszVertWKT);
    5310             : 
    5311           2 :             oSRS.StripVertical();
    5312             : 
    5313           2 :             osOptions.SetNameValue("VAR_HORIZ_WKT", pszHorizWKT);
    5314           2 :             if (osOptions.FetchNameValue("VAR_VERT_WKT") == nullptr)
    5315             :             {
    5316           2 :                 osOptions.SetNameValue("VAR_VERT_WKT", pszVertWKT);
    5317             :             }
    5318           2 :             CPLFree(pszHorizWKT);
    5319           2 :             CPLFree(pszVertWKT);
    5320             :         }
    5321             :     }
    5322             : 
    5323          21 :     const char *pszUnits = "m";
    5324          21 :     if (oSRS.IsProjected())
    5325             :     {
    5326           6 :         oSRS.GetLinearUnits(&pszUnits);
    5327           6 :         if (EQUAL(pszUnits, "metre"))
    5328           6 :             pszUnits = "m";
    5329             :     }
    5330             :     else
    5331             :     {
    5332          15 :         pszUnits = "deg";
    5333             :     }
    5334          21 :     osOptions.SetNameValue("VAR_RES_UNIT", pszUnits);
    5335             : 
    5336             :     // get bounds as pixel center
    5337          21 :     double dfMinX = padfGeoTransform[0] + padfGeoTransform[1] / 2;
    5338          21 :     double dfMaxX = dfMinX + (nXSize - 1) * padfGeoTransform[1];
    5339          21 :     double dfMaxY = padfGeoTransform[3] + padfGeoTransform[5] / 2;
    5340          21 :     double dfMinY = dfMaxY + (nYSize - 1) * padfGeoTransform[5];
    5341             : 
    5342          21 :     if (padfGeoTransform[5] > 0)
    5343             :     {
    5344           1 :         std::swap(dfMinY, dfMaxY);
    5345             :     }
    5346             :     osOptions.SetNameValue(
    5347             :         "VAR_CORNER_POINTS",
    5348          21 :         CPLSPrintf("%.18g,%.18g %.18g,%.18g", dfMinX, dfMinY, dfMaxX, dfMaxY));
    5349             : 
    5350          21 :     double adfCornerX[4] = {dfMinX, dfMinX, dfMaxX, dfMaxX};
    5351          21 :     double adfCornerY[4] = {dfMinY, dfMaxY, dfMaxY, dfMinY};
    5352          42 :     OGRSpatialReference oSRS_WGS84;
    5353          21 :     oSRS_WGS84.SetFromUserInput("WGS84");
    5354          21 :     oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    5355             :     OGRCoordinateTransformation *poCT =
    5356          21 :         OGRCreateCoordinateTransformation(&oSRS, &oSRS_WGS84);
    5357          21 :     if (!poCT)
    5358           0 :         return CPLString();
    5359          21 :     if (!poCT->Transform(4, adfCornerX, adfCornerY))
    5360             :     {
    5361           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5362             :                  "Cannot compute raster extent in geodetic coordinates");
    5363           0 :         delete poCT;
    5364           0 :         return CPLString();
    5365             :     }
    5366          21 :     delete poCT;
    5367             :     double dfWest = std::min(std::min(adfCornerX[0], adfCornerX[1]),
    5368          21 :                              std::min(adfCornerX[2], adfCornerX[3]));
    5369             :     double dfSouth = std::min(std::min(adfCornerY[0], adfCornerY[1]),
    5370          21 :                               std::min(adfCornerY[2], adfCornerY[3]));
    5371             :     double dfEast = std::max(std::max(adfCornerX[0], adfCornerX[1]),
    5372          21 :                              std::max(adfCornerX[2], adfCornerX[3]));
    5373             :     double dfNorth = std::max(std::max(adfCornerY[0], adfCornerY[1]),
    5374          21 :                               std::max(adfCornerY[2], adfCornerY[3]));
    5375          21 :     osOptions.SetNameValue("VAR_WEST_LONGITUDE", CPLSPrintf("%.18g", dfWest));
    5376          21 :     osOptions.SetNameValue("VAR_SOUTH_LATITUDE", CPLSPrintf("%.18g", dfSouth));
    5377          21 :     osOptions.SetNameValue("VAR_EAST_LONGITUDE", CPLSPrintf("%.18g", dfEast));
    5378          21 :     osOptions.SetNameValue("VAR_NORTH_LATITUDE", CPLSPrintf("%.18g", dfNorth));
    5379             : 
    5380          21 :     if (!SubstituteVariables(psMain, osOptions.List()))
    5381             :     {
    5382           0 :         return CPLString();
    5383             :     }
    5384             : 
    5385          21 :     char *pszXML = CPLSerializeXMLTree(psRoot);
    5386          42 :     CPLString osXML(pszXML);
    5387          21 :     CPLFree(pszXML);
    5388          21 :     return osXML;
    5389             : }
    5390             : 
    5391             : /************************************************************************/
    5392             : /*                         CreateAndWriteMetadata()                     */
    5393             : /************************************************************************/
    5394             : 
    5395          19 : bool BAGCreator::CreateAndWriteMetadata(hid_t hdf5,
    5396             :                                         const CPLString &osXMLMetadata)
    5397             : {
    5398          19 :     hsize_t dim_init[1] = {1 + osXMLMetadata.size()};
    5399          19 :     hsize_t dim_max[1] = {H5S_UNLIMITED};
    5400             : 
    5401          19 :     hid_t hDataSpace = H5_CHECK(H5Screate_simple(1, dim_init, dim_max));
    5402          19 :     if (hDataSpace < 0)
    5403           0 :         return false;
    5404             : 
    5405          19 :     hid_t hParams = -1;
    5406          19 :     hid_t hDataType = -1;
    5407          19 :     hid_t hDatasetID = -1;
    5408          19 :     hid_t hFileSpace = -1;
    5409          19 :     bool ret = false;
    5410             :     do
    5411             :     {
    5412          19 :         hParams = H5_CHECK(H5Pcreate(H5P_DATASET_CREATE));
    5413          19 :         if (hParams < 0)
    5414           0 :             break;
    5415             : 
    5416          19 :         hsize_t chunk_dims[1] = {1024};
    5417          19 :         if (H5_CHECK(H5Pset_chunk(hParams, 1, chunk_dims)) < 0)
    5418           0 :             break;
    5419             : 
    5420          19 :         hDataType = H5_CHECK(H5Tcopy(H5T_C_S1));
    5421          19 :         if (hDataType < 0)
    5422           0 :             break;
    5423             : 
    5424          19 :         hDatasetID = H5_CHECK(H5Dcreate(hdf5, "/BAG_root/metadata", hDataType,
    5425             :                                         hDataSpace, hParams));
    5426          19 :         if (hDatasetID < 0)
    5427           0 :             break;
    5428             : 
    5429          19 :         if (H5_CHECK(H5Dextend(hDatasetID, dim_init)) < 0)
    5430           0 :             break;
    5431             : 
    5432          19 :         hFileSpace = H5_CHECK(H5Dget_space(hDatasetID));
    5433          19 :         if (hFileSpace < 0)
    5434           0 :             break;
    5435             : 
    5436          19 :         H5OFFSET_TYPE offset[1] = {0};
    5437          19 :         if (H5_CHECK(H5Sselect_hyperslab(hFileSpace, H5S_SELECT_SET, offset,
    5438          19 :                                          nullptr, dim_init, nullptr)) < 0)
    5439             :         {
    5440           0 :             break;
    5441             :         }
    5442             : 
    5443          19 :         if (H5_CHECK(H5Dwrite(hDatasetID, hDataType, hDataSpace, hFileSpace,
    5444          19 :                               H5P_DEFAULT, osXMLMetadata.data())) < 0)
    5445             :         {
    5446           0 :             break;
    5447             :         }
    5448             : 
    5449          19 :         ret = true;
    5450             :     } while (0);
    5451             : 
    5452          19 :     if (hParams >= 0)
    5453          19 :         H5_CHECK(H5Pclose(hParams));
    5454          19 :     if (hDataType >= 0)
    5455          19 :         H5_CHECK(H5Tclose(hDataType));
    5456          19 :     if (hFileSpace >= 0)
    5457          19 :         H5_CHECK(H5Sclose(hFileSpace));
    5458          19 :     if (hDatasetID >= 0)
    5459          19 :         H5_CHECK(H5Dclose(hDatasetID));
    5460          19 :     H5_CHECK(H5Sclose(hDataSpace));
    5461             : 
    5462          19 :     return ret;
    5463             : }
    5464             : 
    5465             : /************************************************************************/
    5466             : /*                       CreateTrackingListDataset()                    */
    5467             : /************************************************************************/
    5468             : 
    5469          20 : bool BAGCreator::CreateTrackingListDataset()
    5470             : {
    5471             :     typedef struct
    5472             :     {
    5473             :         uint32_t row;
    5474             :         uint32_t col;
    5475             :         float depth;
    5476             :         float uncertainty;
    5477             :         uint8_t track_code;
    5478             :         uint16_t list_series;
    5479             :     } TrackingListItem;
    5480             : 
    5481          20 :     hsize_t dim_init[1] = {0};
    5482          20 :     hsize_t dim_max[1] = {H5S_UNLIMITED};
    5483             : 
    5484          20 :     hid_t hDataSpace = H5_CHECK(H5Screate_simple(1, dim_init, dim_max));
    5485          20 :     if (hDataSpace < 0)
    5486           0 :         return false;
    5487             : 
    5488          20 :     hid_t hParams = -1;
    5489          20 :     hid_t hDataType = -1;
    5490          20 :     hid_t hDatasetID = -1;
    5491          20 :     bool ret = false;
    5492             :     do
    5493             :     {
    5494          20 :         hParams = H5_CHECK(H5Pcreate(H5P_DATASET_CREATE));
    5495          20 :         if (hParams < 0)
    5496           0 :             break;
    5497             : 
    5498          20 :         hsize_t chunk_dims[1] = {10};
    5499          20 :         if (H5_CHECK(H5Pset_chunk(hParams, 1, chunk_dims)) < 0)
    5500           0 :             break;
    5501             : 
    5502             :         TrackingListItem unusedItem;
    5503             :         (void)unusedItem.row;
    5504             :         (void)unusedItem.col;
    5505             :         (void)unusedItem.depth;
    5506             :         (void)unusedItem.uncertainty;
    5507             :         (void)unusedItem.track_code;
    5508             :         (void)unusedItem.list_series;
    5509          20 :         hDataType = H5_CHECK(H5Tcreate(H5T_COMPOUND, sizeof(unusedItem)));
    5510          20 :         if (hDataType < 0)
    5511           0 :             break;
    5512             : 
    5513          20 :         if (H5Tinsert(hDataType, "row", HOFFSET(TrackingListItem, row),
    5514          20 :                       H5T_NATIVE_UINT) < 0 ||
    5515          20 :             H5Tinsert(hDataType, "col", HOFFSET(TrackingListItem, col),
    5516          20 :                       H5T_NATIVE_UINT) < 0 ||
    5517          20 :             H5Tinsert(hDataType, "depth", HOFFSET(TrackingListItem, depth),
    5518          20 :                       H5T_NATIVE_FLOAT) < 0 ||
    5519          20 :             H5Tinsert(hDataType, "uncertainty",
    5520             :                       HOFFSET(TrackingListItem, uncertainty),
    5521          20 :                       H5T_NATIVE_FLOAT) < 0 ||
    5522          20 :             H5Tinsert(hDataType, "track_code",
    5523             :                       HOFFSET(TrackingListItem, track_code),
    5524          60 :                       H5T_NATIVE_UCHAR) < 0 ||
    5525          20 :             H5Tinsert(hDataType, "list_series",
    5526             :                       HOFFSET(TrackingListItem, list_series),
    5527          20 :                       H5T_NATIVE_SHORT) < 0)
    5528             :         {
    5529           0 :             break;
    5530             :         }
    5531             : 
    5532          20 :         hDatasetID = H5_CHECK(H5Dcreate(m_hdf5, "/BAG_root/tracking_list",
    5533             :                                         hDataType, hDataSpace, hParams));
    5534          20 :         if (hDatasetID < 0)
    5535           0 :             break;
    5536             : 
    5537          20 :         if (H5_CHECK(H5Dextend(hDatasetID, dim_init)) < 0)
    5538           0 :             break;
    5539             : 
    5540          20 :         if (!GH5_CreateAttribute(hDatasetID, "Tracking List Length",
    5541          20 :                                  H5T_NATIVE_UINT))
    5542           0 :             break;
    5543             : 
    5544          20 :         if (!GH5_WriteAttribute(hDatasetID, "Tracking List Length", 0U))
    5545           0 :             break;
    5546             : 
    5547          20 :         ret = true;
    5548             :     } while (0);
    5549             : 
    5550          20 :     if (hParams >= 0)
    5551          20 :         H5_CHECK(H5Pclose(hParams));
    5552          20 :     if (hDataType >= 0)
    5553          20 :         H5_CHECK(H5Tclose(hDataType));
    5554          20 :     if (hDatasetID >= 0)
    5555          20 :         H5_CHECK(H5Dclose(hDatasetID));
    5556          20 :     H5_CHECK(H5Sclose(hDataSpace));
    5557             : 
    5558          20 :     return ret;
    5559             : }
    5560             : 
    5561             : /************************************************************************/
    5562             : /*                     CreateElevationOrUncertainty()                   */
    5563             : /************************************************************************/
    5564             : 
    5565          32 : bool BAGCreator::CreateElevationOrUncertainty(
    5566             :     GDALDataset *poSrcDS, int nBand, const char *pszDSName,
    5567             :     const char *pszMaxAttrName, const char *pszMinAttrName, char **papszOptions,
    5568             :     GDALProgressFunc pfnProgress, void *pProgressData)
    5569             : {
    5570          32 :     const int nYSize = poSrcDS->GetRasterYSize();
    5571          32 :     const int nXSize = poSrcDS->GetRasterXSize();
    5572             : 
    5573             :     double adfGeoTransform[6];
    5574          32 :     poSrcDS->GetGeoTransform(adfGeoTransform);
    5575             : 
    5576          32 :     hsize_t dims[2] = {static_cast<hsize_t>(nYSize),
    5577          32 :                        static_cast<hsize_t>(nXSize)};
    5578             : 
    5579          32 :     hid_t hDataSpace = H5_CHECK(H5Screate_simple(2, dims, nullptr));
    5580          32 :     if (hDataSpace < 0)
    5581           0 :         return false;
    5582             : 
    5583          32 :     hid_t hParams = -1;
    5584          32 :     hid_t hDataType = -1;
    5585          32 :     hid_t hDatasetID = -1;
    5586          32 :     hid_t hFileSpace = -1;
    5587          32 :     bool bDeflate = EQUAL(
    5588             :         CSLFetchNameValueDef(papszOptions, "COMPRESS", "DEFLATE"), "DEFLATE");
    5589             :     int nCompressionLevel =
    5590          32 :         atoi(CSLFetchNameValueDef(papszOptions, "ZLEVEL", "6"));
    5591          32 :     const int nBlockSize = std::min(
    5592          32 :         4096, atoi(CSLFetchNameValueDef(papszOptions, "BLOCK_SIZE", "100")));
    5593          32 :     const int nBlockXSize = std::min(nXSize, nBlockSize);
    5594          32 :     const int nBlockYSize = std::min(nYSize, nBlockSize);
    5595          32 :     bool ret = false;
    5596          32 :     const float fNoDataValue = fDEFAULT_NODATA;
    5597             :     do
    5598             :     {
    5599          32 :         hDataType = H5_CHECK(H5Tcopy(H5T_NATIVE_FLOAT));
    5600          32 :         if (hDataType < 0)
    5601           0 :             break;
    5602             : 
    5603          32 :         if (H5_CHECK(H5Tset_order(hDataType, H5T_ORDER_LE)) < 0)
    5604           0 :             break;
    5605             : 
    5606          32 :         hParams = H5_CHECK(H5Pcreate(H5P_DATASET_CREATE));
    5607          32 :         if (hParams < 0)
    5608           0 :             break;
    5609             : 
    5610          32 :         if (H5_CHECK(H5Pset_fill_time(hParams, H5D_FILL_TIME_ALLOC)) < 0)
    5611           0 :             break;
    5612             : 
    5613          32 :         if (H5_CHECK(H5Pset_fill_value(hParams, hDataType, &fNoDataValue)) < 0)
    5614           0 :             break;
    5615             : 
    5616          32 :         if (H5_CHECK(H5Pset_layout(hParams, H5D_CHUNKED)) < 0)
    5617           0 :             break;
    5618          32 :         hsize_t chunk_size[2] = {static_cast<hsize_t>(nBlockYSize),
    5619          32 :                                  static_cast<hsize_t>(nBlockXSize)};
    5620          32 :         if (H5_CHECK(H5Pset_chunk(hParams, 2, chunk_size)) < 0)
    5621           0 :             break;
    5622             : 
    5623          32 :         if (bDeflate)
    5624             :         {
    5625          32 :             if (H5_CHECK(H5Pset_deflate(hParams, nCompressionLevel)) < 0)
    5626           0 :                 break;
    5627             :         }
    5628             : 
    5629          32 :         hDatasetID = H5_CHECK(
    5630             :             H5Dcreate(m_hdf5, pszDSName, hDataType, hDataSpace, hParams));
    5631          32 :         if (hDatasetID < 0)
    5632           0 :             break;
    5633             : 
    5634          32 :         if (!GH5_CreateAttribute(hDatasetID, pszMaxAttrName, hDataType))
    5635           0 :             break;
    5636             : 
    5637          32 :         if (!GH5_CreateAttribute(hDatasetID, pszMinAttrName, hDataType))
    5638           0 :             break;
    5639             : 
    5640          32 :         hFileSpace = H5_CHECK(H5Dget_space(hDatasetID));
    5641          32 :         if (hFileSpace < 0)
    5642           0 :             break;
    5643             : 
    5644          32 :         const int nYBlocks =
    5645          32 :             static_cast<int>((nYSize + nBlockYSize - 1) / nBlockYSize);
    5646          32 :         const int nXBlocks =
    5647          32 :             static_cast<int>((nXSize + nBlockXSize - 1) / nBlockXSize);
    5648          32 :         std::vector<float> afValues(static_cast<size_t>(nBlockYSize) *
    5649          32 :                                     nBlockXSize);
    5650          32 :         ret = true;
    5651          32 :         const bool bReverseY = adfGeoTransform[5] < 0;
    5652             : 
    5653          32 :         float fMin = std::numeric_limits<float>::infinity();
    5654          32 :         float fMax = -std::numeric_limits<float>::infinity();
    5655             : 
    5656          32 :         if (nBand == 1 || poSrcDS->GetRasterCount() == 2)
    5657             :         {
    5658          18 :             int bHasNoData = FALSE;
    5659             :             const double dfSrcNoData =
    5660          18 :                 poSrcDS->GetRasterBand(nBand)->GetNoDataValue(&bHasNoData);
    5661          18 :             const float fSrcNoData = static_cast<float>(dfSrcNoData);
    5662             : 
    5663          38 :             for (int iY = 0; ret && iY < nYBlocks; iY++)
    5664             :             {
    5665             :                 const int nSrcYOff =
    5666          20 :                     bReverseY ? std::max(0, nYSize - (iY + 1) * nBlockYSize)
    5667           1 :                               : iY * nBlockYSize;
    5668             :                 const int nReqCountY =
    5669          20 :                     std::min(nBlockYSize, nYSize - iY * nBlockYSize);
    5670          48 :                 for (int iX = 0; iX < nXBlocks; iX++)
    5671             :                 {
    5672             :                     const int nReqCountX =
    5673          28 :                         std::min(nBlockXSize, nXSize - iX * nBlockXSize);
    5674             : 
    5675          55 :                     if (poSrcDS->GetRasterBand(nBand)->RasterIO(
    5676          28 :                             GF_Read, iX * nBlockXSize, nSrcYOff, nReqCountX,
    5677             :                             nReqCountY,
    5678          27 :                             bReverseY ? afValues.data() +
    5679          27 :                                             (nReqCountY - 1) * nReqCountX
    5680           1 :                                       : afValues.data(),
    5681             :                             nReqCountX, nReqCountY, GDT_Float32, 0,
    5682          27 :                             bReverseY ? -4 * nReqCountX : 0,
    5683          28 :                             nullptr) != CE_None)
    5684             :                     {
    5685           0 :                         ret = false;
    5686           0 :                         break;
    5687             :                     }
    5688             : 
    5689        2376 :                     for (int i = 0; i < nReqCountY * nReqCountX; i++)
    5690             :                     {
    5691        2348 :                         const float fVal = afValues[i];
    5692        4696 :                         if ((bHasNoData && fVal == fSrcNoData) ||
    5693        2348 :                             std::isnan(fVal))
    5694             :                         {
    5695           0 :                             afValues[i] = fNoDataValue;
    5696             :                         }
    5697             :                         else
    5698             :                         {
    5699        2348 :                             fMin = std::min(fMin, fVal);
    5700        2348 :                             fMax = std::max(fMax, fVal);
    5701             :                         }
    5702             :                     }
    5703             : 
    5704             :                     H5OFFSET_TYPE offset[2] = {
    5705          28 :                         static_cast<H5OFFSET_TYPE>(iY) *
    5706          28 :                             static_cast<H5OFFSET_TYPE>(nBlockYSize),
    5707          28 :                         static_cast<H5OFFSET_TYPE>(iX) *
    5708          28 :                             static_cast<H5OFFSET_TYPE>(nBlockXSize)};
    5709          28 :                     hsize_t count[2] = {static_cast<hsize_t>(nReqCountY),
    5710          28 :                                         static_cast<hsize_t>(nReqCountX)};
    5711          28 :                     if (H5_CHECK(H5Sselect_hyperslab(hFileSpace, H5S_SELECT_SET,
    5712             :                                                      offset, nullptr, count,
    5713          28 :                                                      nullptr)) < 0)
    5714             :                     {
    5715           0 :                         ret = false;
    5716           0 :                         break;
    5717             :                     }
    5718             : 
    5719          28 :                     hid_t hMemSpace = H5Screate_simple(2, count, nullptr);
    5720          28 :                     if (hMemSpace < 0)
    5721           0 :                         break;
    5722             : 
    5723          28 :                     if (H5_CHECK(H5Dwrite(hDatasetID, H5T_NATIVE_FLOAT,
    5724             :                                           hMemSpace, hFileSpace, H5P_DEFAULT,
    5725          28 :                                           afValues.data())) < 0)
    5726             :                     {
    5727           0 :                         H5Sclose(hMemSpace);
    5728           0 :                         ret = false;
    5729           0 :                         break;
    5730             :                     }
    5731             : 
    5732          28 :                     H5Sclose(hMemSpace);
    5733             : 
    5734          28 :                     if (!pfnProgress(
    5735          28 :                             static_cast<double>(iY * nXBlocks + iX + 1) /
    5736          28 :                                 (nXBlocks * nYBlocks),
    5737             :                             "", pProgressData))
    5738             :                     {
    5739           0 :                         ret = false;
    5740           0 :                         break;
    5741             :                     }
    5742             :                 }
    5743             :             }
    5744             :         }
    5745          32 :         if (!ret)
    5746           0 :             break;
    5747             : 
    5748          32 :         if (fMin > fMax)
    5749          14 :             fMin = fMax = fNoDataValue;
    5750             : 
    5751          32 :         if (!GH5_WriteAttribute(hDatasetID, pszMaxAttrName, fMax))
    5752           0 :             break;
    5753             : 
    5754          32 :         if (!GH5_WriteAttribute(hDatasetID, pszMinAttrName, fMin))
    5755           0 :             break;
    5756             : 
    5757          32 :         ret = true;
    5758             :     } while (0);
    5759             : 
    5760          32 :     if (hParams >= 0)
    5761          32 :         H5_CHECK(H5Pclose(hParams));
    5762          32 :     if (hDataType >= 0)
    5763          32 :         H5_CHECK(H5Tclose(hDataType));
    5764          32 :     if (hFileSpace >= 0)
    5765          32 :         H5_CHECK(H5Sclose(hFileSpace));
    5766          32 :     if (hDatasetID >= 0)
    5767          32 :         H5_CHECK(H5Dclose(hDatasetID));
    5768          32 :     H5_CHECK(H5Sclose(hDataSpace));
    5769             : 
    5770          32 :     return ret;
    5771             : }
    5772             : 
    5773             : /************************************************************************/
    5774             : /*                           CreateBase()                               */
    5775             : /************************************************************************/
    5776             : 
    5777          22 : bool BAGCreator::CreateBase(const char *pszFilename, char **papszOptions)
    5778             : {
    5779          22 :     hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
    5780          22 :     H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
    5781          22 :     m_hdf5 = H5Fcreate(pszFilename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl);
    5782          22 :     H5Pclose(fapl);
    5783          22 :     if (m_hdf5 < 0)
    5784             :     {
    5785           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot create file");
    5786           2 :         return false;
    5787             :     }
    5788             : 
    5789          20 :     m_bagRoot = H5_CHECK(H5Gcreate(m_hdf5, "/BAG_root", 0));
    5790          20 :     if (m_bagRoot < 0)
    5791             :     {
    5792           0 :         return false;
    5793             :     }
    5794             : 
    5795             :     const char *pszVersion =
    5796          20 :         CSLFetchNameValueDef(papszOptions, "BAG_VERSION", "1.6.2");
    5797          20 :     constexpr unsigned knVersionLength = 32;
    5798          20 :     char szVersion[knVersionLength] = {};
    5799          20 :     snprintf(szVersion, sizeof(szVersion), "%s", pszVersion);
    5800          20 :     if (!GH5_CreateAttribute(m_bagRoot, "Bag Version", H5T_C_S1,
    5801          40 :                              knVersionLength) ||
    5802          20 :         !GH5_WriteAttribute(m_bagRoot, "Bag Version", szVersion))
    5803             :     {
    5804           0 :         return false;
    5805             :     }
    5806             : 
    5807          20 :     if (!CreateTrackingListDataset())
    5808             :     {
    5809           0 :         return false;
    5810             :     }
    5811          20 :     return true;
    5812             : }
    5813             : 
    5814             : /************************************************************************/
    5815             : /*                               Create()                               */
    5816             : /************************************************************************/
    5817             : 
    5818          22 : bool BAGCreator::Create(const char *pszFilename, GDALDataset *poSrcDS,
    5819             :                         char **papszOptions, GDALProgressFunc pfnProgress,
    5820             :                         void *pProgressData)
    5821             : {
    5822          22 :     const int nBands = poSrcDS->GetRasterCount();
    5823          22 :     if (nBands != 1 && nBands != 2)
    5824             :     {
    5825           4 :         CPLError(CE_Failure, CPLE_NotSupported,
    5826             :                  "BAG driver doesn't support %d bands. Must be 1 or 2.",
    5827             :                  nBands);
    5828           4 :         return false;
    5829             :     }
    5830             :     double adfGeoTransform[6];
    5831          18 :     if (poSrcDS->GetGeoTransform(adfGeoTransform) != CE_None)
    5832             :     {
    5833           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    5834             :                  "BAG driver requires a source dataset with a geotransform");
    5835           0 :         return false;
    5836             :     }
    5837          18 :     if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0)
    5838             :     {
    5839           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    5840             :                  "BAG driver requires a source dataset with a non-rotated "
    5841             :                  "geotransform");
    5842           0 :         return false;
    5843             :     }
    5844             : 
    5845             :     CPLString osXMLMetadata = GenerateMetadata(
    5846             :         poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize(), adfGeoTransform,
    5847          36 :         poSrcDS->GetSpatialRef(), papszOptions);
    5848          18 :     if (osXMLMetadata.empty())
    5849             :     {
    5850           0 :         return false;
    5851             :     }
    5852             : 
    5853          18 :     if (!CreateBase(pszFilename, papszOptions))
    5854             :     {
    5855           2 :         return false;
    5856             :     }
    5857             : 
    5858          16 :     if (!CreateAndWriteMetadata(m_hdf5, osXMLMetadata))
    5859             :     {
    5860           0 :         return false;
    5861             :     }
    5862             : 
    5863          16 :     void *pScaled = GDALCreateScaledProgress(0, 1. / poSrcDS->GetRasterCount(),
    5864             :                                              pfnProgress, pProgressData);
    5865             :     bool bRet;
    5866          16 :     bRet = CreateElevationOrUncertainty(
    5867             :         poSrcDS, 1, "/BAG_root/elevation", "Maximum Elevation Value",
    5868             :         "Minimum Elevation Value", papszOptions, GDALScaledProgress, pScaled);
    5869          16 :     GDALDestroyScaledProgress(pScaled);
    5870          16 :     if (!bRet)
    5871             :     {
    5872           0 :         return false;
    5873             :     }
    5874             : 
    5875          16 :     pScaled = GDALCreateScaledProgress(1. / poSrcDS->GetRasterCount(), 1.0,
    5876             :                                        pfnProgress, pProgressData);
    5877          16 :     bRet = CreateElevationOrUncertainty(
    5878             :         poSrcDS, 2, "/BAG_root/uncertainty", "Maximum Uncertainty Value",
    5879             :         "Minimum Uncertainty Value", papszOptions, GDALScaledProgress, pScaled);
    5880          16 :     GDALDestroyScaledProgress(pScaled);
    5881          16 :     if (!bRet)
    5882             :     {
    5883           0 :         return false;
    5884             :     }
    5885             : 
    5886          16 :     return Close();
    5887             : }
    5888             : 
    5889             : /************************************************************************/
    5890             : /*                               Create()                               */
    5891             : /************************************************************************/
    5892             : 
    5893          52 : bool BAGCreator::Create(const char *pszFilename, int nBands, GDALDataType eType,
    5894             :                         char **papszOptions)
    5895             : {
    5896          52 :     if (nBands != 1 && nBands != 2)
    5897             :     {
    5898          34 :         CPLError(CE_Failure, CPLE_NotSupported,
    5899             :                  "BAG driver doesn't support %d bands. Must be 1 or 2.",
    5900             :                  nBands);
    5901          34 :         return false;
    5902             :     }
    5903          18 :     if (eType != GDT_Float32)
    5904             :     {
    5905          14 :         CPLError(CE_Failure, CPLE_NotSupported,
    5906             :                  "BAG driver only supports Float32");
    5907          14 :         return false;
    5908             :     }
    5909             : 
    5910           4 :     if (!CreateBase(pszFilename, papszOptions))
    5911             :     {
    5912           0 :         return false;
    5913             :     }
    5914             : 
    5915           4 :     return Close();
    5916             : }
    5917             : 
    5918             : /************************************************************************/
    5919             : /*                              CreateCopy()                            */
    5920             : /************************************************************************/
    5921             : 
    5922          22 : GDALDataset *BAGDataset::CreateCopy(const char *pszFilename,
    5923             :                                     GDALDataset *poSrcDS, int /* bStrict */,
    5924             :                                     char **papszOptions,
    5925             :                                     GDALProgressFunc pfnProgress,
    5926             :                                     void *pProgressData)
    5927             : 
    5928             : {
    5929          22 :     if (!BAGCreator().Create(pszFilename, poSrcDS, papszOptions, pfnProgress,
    5930             :                              pProgressData))
    5931             :     {
    5932           6 :         return nullptr;
    5933             :     }
    5934             : 
    5935          32 :     GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
    5936          16 :     oOpenInfo.nOpenFlags = GDAL_OF_RASTER;
    5937          16 :     return Open(&oOpenInfo);
    5938             : }
    5939             : 
    5940             : /************************************************************************/
    5941             : /*                               Create()                               */
    5942             : /************************************************************************/
    5943             : 
    5944          52 : GDALDataset *BAGDataset::Create(const char *pszFilename, int nXSize, int nYSize,
    5945             :                                 int nBandsIn, GDALDataType eType,
    5946             :                                 char **papszOptions)
    5947             : {
    5948          52 :     if (!BAGCreator().Create(pszFilename, nBandsIn, eType, papszOptions))
    5949             :     {
    5950          48 :         return nullptr;
    5951             :     }
    5952             : 
    5953           8 :     GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
    5954           4 :     oOpenInfo.nOpenFlags = GDAL_OF_RASTER;
    5955           4 :     return OpenForCreate(&oOpenInfo, nXSize, nYSize, nBandsIn, papszOptions);
    5956             : }
    5957             : 
    5958             : /************************************************************************/
    5959             : /*                          GDALRegister_BAG()                          */
    5960             : /************************************************************************/
    5961           8 : void GDALRegister_BAG()
    5962             : 
    5963             : {
    5964           8 :     if (!GDAL_CHECK_VERSION("BAG"))
    5965           0 :         return;
    5966             : 
    5967           8 :     if (GDALGetDriverByName(BAG_DRIVER_NAME) != nullptr)
    5968           0 :         return;
    5969             : 
    5970           8 :     GDALDriver *poDriver = new GDALDriver();
    5971             : 
    5972           8 :     BAGDriverSetCommonMetadata(poDriver);
    5973             : 
    5974           8 :     poDriver->pfnOpen = BAGDataset::Open;
    5975           8 :     poDriver->pfnUnloadDriver = BAGDatasetDriverUnload;
    5976           8 :     poDriver->pfnCreateCopy = BAGDataset::CreateCopy;
    5977           8 :     poDriver->pfnCreate = BAGDataset::Create;
    5978             : 
    5979           8 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    5980             : }

Generated by: LCOV version 1.14