LCOV - code coverage report
Current view: top level - frmts/raw - cphddataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 387 520 74.4 %
Date: 2026-05-02 18:51:51 Functions: 33 38 86.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  CPHD driver multidimensional classes
       5             :  * Author:   Norman Barker <norman at analyticaspatial.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2026, Norman Barker <norman at analyticaspatial.com>
       9             :  *
      10             :  ****************************************************************************/
      11             : 
      12             : #include <cmath>
      13             : #include <functional>
      14             : #include <iostream>
      15             : #include <limits>
      16             : 
      17             : #include "cpl_vsi_virtual.h"
      18             : #include "gdal_frmts.h"
      19             : #include "gdal_multidim.h"
      20             : #include "memmultidim.h"
      21             : #include "rawdataset.h"
      22             : 
      23             : static int CPHDDatasetIdentify(GDALOpenInfo *poOpenInfo);
      24             : 
      25             : constexpr const char *PVP_ARRAY_NAME = "PVP";
      26             : 
      27             : /************************************************************************/
      28             : /*                         ParseComplexDataType                         */
      29             : /************************************************************************/
      30             : 
      31           3 : static GDALDataType ParseComplexDataType(const char *pszFormat,
      32             :                                          const char *pszFileName)
      33             : {
      34           3 :     GDALDataType dt = GDT_Unknown;
      35           3 :     if EQUAL (pszFormat, "CI4")
      36           1 :         dt = GDT_CInt16;
      37           2 :     else if EQUAL (pszFormat, "CI8")
      38           0 :         dt = GDT_CInt32;
      39           2 :     else if (EQUAL(pszFormat, "CI2") || EQUAL(pszFormat, "CI16"))
      40           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Format %s not supported : %s.",
      41             :                  pszFormat, pszFileName);
      42           2 :     else if EQUAL (pszFormat, "CF8")
      43           2 :         dt = GDT_CFloat32;
      44           0 :     else if EQUAL (pszFormat, "CF16")
      45           0 :         dt = GDT_CFloat64;
      46             :     else
      47           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Format %s not recognized : %s.",
      48             :                  pszFormat, pszFileName);
      49             : 
      50           3 :     return dt;
      51             : }
      52             : 
      53             : /************************************************************************/
      54             : /*                           ParsePVPDataType                           */
      55             : /************************************************************************/
      56             : 
      57           3 : static GDALExtendedDataType ParsePVPDataType(const CPLXMLNode *psPvpXML,
      58             :                                              const char *pszFileName)
      59             : {
      60           6 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
      61           3 :     bool bSort = false;
      62           3 :     size_t nSize = 0;
      63           3 :     int nLastOffset = 0;
      64           3 :     auto *psPvpChild = psPvpXML->psChild;
      65             : 
      66           6 :     std::function<bool(const CPLXMLNode *, const char *)> parseChildPVPDataType;
      67             :     parseChildPVPDataType =
      68          70 :         [&parseChildPVPDataType, &bSort, &comps, &nSize, &nLastOffset,
      69         278 :          &pszFileName](const CPLXMLNode *psNode, const char *pszPrefix)
      70             :     {
      71          70 :         if (psNode->eType != CXT_Element)
      72             :         {
      73           0 :             CPLError(CE_Failure, CPLE_AppDefined,
      74             :                      "Unexpected XML error parsing : %s", pszFileName);
      75           0 :             return false;
      76             :         }
      77             :         auto osElementName =
      78             :             (pszPrefix == nullptr)
      79          64 :                 ? std::string(psNode->pszValue)
      80         164 :                 : std::string(pszPrefix) + std::string(psNode->pszValue);
      81          70 :         if ((osElementName == "TxAntenna") || (osElementName == "RcvAntenna"))
      82             :         {
      83           2 :             auto *psChild = psNode->psChild;
      84           8 :             for (; psChild != nullptr; psChild = psChild->psNext)
      85             :             {
      86           6 :                 if (!parseChildPVPDataType(
      87          12 :                         psChild, (std::string(psNode->pszValue) + ".").c_str()))
      88             :                 {
      89           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
      90             :                              "Expected child elements for %s : %s",
      91             :                              osElementName.c_str(), pszFileName);
      92           0 :                     return false;
      93             :                 }
      94             :             }
      95           2 :             return true;
      96             :         }
      97          68 :         else if (osElementName == "AddedPVP")
      98             :         {
      99           2 :             osElementName = CPLGetXMLValue(psNode, "Name", "");
     100             :         }
     101             : 
     102          68 :         const auto pszFormat = CPLGetXMLValue(psNode, "Format", nullptr);
     103          68 :         const auto pszOffset = CPLGetXMLValue(psNode, "Offset", nullptr);
     104             : 
     105          68 :         if (pszFormat && pszOffset)
     106             :         {
     107             :             // all values are multiples of 8 bytes
     108          68 :             auto nOffset = atoi(pszOffset);
     109          68 :             if (nOffset < 0 || nOffset > (std::numeric_limits<int>::max() / 8))
     110             :             {
     111           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     112             :                          "Invalid offset %s for %s in %s.", pszOffset,
     113             :                          osElementName.c_str(), pszFileName);
     114           0 :                 return false;
     115             :             }
     116          68 :             nOffset *= 8;
     117             : 
     118          68 :             if (nOffset < nLastOffset)
     119          11 :                 bSort = true;
     120             :             else
     121          57 :                 nLastOffset = nOffset;
     122             : 
     123          68 :             if EQUAL (pszFormat, "X=F8;Y=F8;Z=F8;")
     124             :             {
     125          38 :                 std::vector<std::unique_ptr<GDALEDTComponent>> xyzComps;
     126             :                 xyzComps.emplace_back(
     127          76 :                     std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
     128          57 :                         "X", 0, GDALExtendedDataType::Create(GDT_Float64))));
     129             :                 xyzComps.emplace_back(
     130          76 :                     std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
     131          57 :                         "Y", 8, GDALExtendedDataType::Create(GDT_Float64))));
     132             :                 xyzComps.emplace_back(
     133          76 :                     std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
     134          57 :                         "Z", 16, GDALExtendedDataType::Create(GDT_Float64))));
     135             :                 auto dtXYZ(GDALExtendedDataType::Create("XYZ", 24,
     136          38 :                                                         std::move(xyzComps)));
     137          38 :                 comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
     138          38 :                     new GDALEDTComponent(osElementName, nOffset, dtXYZ)));
     139          19 :                 nSize += 24;
     140             :             }
     141          49 :             else if EQUAL (pszFormat, "DCX=F8;DCY=F8;")
     142             :             {
     143           4 :                 std::vector<std::unique_ptr<GDALEDTComponent>> xyComps;
     144             :                 xyComps.emplace_back(
     145           8 :                     std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
     146           6 :                         "DCX", 0, GDALExtendedDataType::Create(GDT_Float64))));
     147             :                 xyComps.emplace_back(
     148           8 :                     std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
     149           6 :                         "DCY", 8, GDALExtendedDataType::Create(GDT_Float64))));
     150             :                 auto dtXY(GDALExtendedDataType::Create("DCXDCY", 16,
     151           4 :                                                        std::move(xyComps)));
     152           4 :                 comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
     153           4 :                     new GDALEDTComponent(osElementName, nOffset, dtXY)));
     154           2 :                 nSize += 16;
     155             :             }
     156          47 :             else if EQUAL (pszFormat, "F8")
     157             :             {
     158             :                 comps.emplace_back(
     159          86 :                     std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
     160             :                         osElementName, nOffset,
     161         129 :                         GDALExtendedDataType::Create(GDT_Float64))));
     162          43 :                 nSize += 8;
     163             :             }
     164           4 :             else if EQUAL (pszFormat, "I8")
     165             :             {
     166             :                 comps.emplace_back(
     167           8 :                     std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
     168             :                         osElementName, nOffset,
     169          12 :                         GDALExtendedDataType::Create(GDT_Int64))));
     170           4 :                 nSize += 8;
     171             :             }
     172             :             else
     173             :             {
     174           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     175             :                          "Unrecognized format %s for %s : %s.", pszFormat,
     176             :                          osElementName.c_str(), pszFileName);
     177           0 :                 return false;
     178          68 :             }
     179             :         }
     180             :         else
     181             :         {
     182           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     183             :                      "Expected offset or format for %s : %s",
     184             :                      osElementName.c_str(), pszFileName);
     185           0 :             return false;
     186             :         }
     187          68 :         return true;
     188           3 :     };
     189             : 
     190          67 :     for (; psPvpChild != nullptr; psPvpChild = psPvpChild->psNext)
     191             :     {
     192          64 :         if (!parseChildPVPDataType(psPvpChild, nullptr))
     193           0 :             return GDALExtendedDataType::Create(GDT_Unknown);
     194             :     }
     195             : 
     196           3 :     if (bSort)
     197             :     {
     198           1 :         sort(comps.begin(), comps.end(),
     199         135 :              [](const std::unique_ptr<GDALEDTComponent> &comp1,
     200             :                 const std::unique_ptr<GDALEDTComponent> &comp2)
     201         135 :              { return comp1->GetOffset() < comp2->GetOffset(); });
     202             :     }
     203             : 
     204           3 :     return GDALExtendedDataType::Create("PVPDataType", nSize, std::move(comps));
     205             : }
     206             : 
     207             : /************************************************************************/
     208             : /*                          CPHDSharedResource                          */
     209             : /************************************************************************/
     210             : 
     211             : struct CPHDSharedResources
     212             : {
     213             :     VSIVirtualHandleUniquePtr m_fp;
     214             :     std::string m_osFilename;
     215             :     CPLXMLTreeCloser m_poXMLTree;
     216             : 
     217             :     GIntBig nXmlBlockSize = 0;
     218             :     vsi_l_offset nXmlBlockByteOffset = 0;
     219             :     GIntBig nSupportBlockSize = 0;
     220             :     vsi_l_offset nSupportBlockByteOffset = 0;
     221             :     GIntBig nPVPBlockSize = 0;
     222             :     vsi_l_offset nPVPBlockByteOffset = 0;
     223             :     vsi_l_offset nPVPArrayByteOffset = 0;
     224             :     GIntBig nSignalBlockSize = 0;
     225             :     vsi_l_offset nSignalBlockByteOffset = 0;
     226             : 
     227           3 :     CPHDSharedResources(const std::string &osFilename, VSILFILE *fp)
     228           3 :         : m_fp(fp), m_osFilename(osFilename), m_poXMLTree(nullptr)
     229             :     {
     230           3 :     }
     231             : };
     232             : 
     233             : /************************************************************************/
     234             : /*                         CPHDInternalDataset                          */
     235             : /************************************************************************/
     236             : 
     237             : class CPHDInternalDataset final : public RawDataset
     238             : {
     239             :     friend class CPHDGroup;
     240             : 
     241             :   protected:
     242             :     CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override;
     243             : };
     244             : 
     245             : /************************************************************************/
     246             : /*                                Close                                 */
     247             : /************************************************************************/
     248             : 
     249           0 : CPLErr CPHDInternalDataset::Close(GDALProgressFunc, void *)
     250             : 
     251             : {
     252           0 :     return CE_None;
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                           CPHDInternalBand                           */
     257             : /************************************************************************/
     258             : 
     259             : class CPHDInternalBand final : public RawRasterBand
     260             : {
     261             :   public:
     262           5 :     CPHDInternalBand(CPHDInternalDataset *poDSIn, int nBandIn, VSILFILE *fpRaw,
     263             :                      vsi_l_offset nImgOffsetIn, int nPixelOffsetIn,
     264             :                      int nLineOffsetIn, GDALDataType eDataTypeIn)
     265           5 :         : RawRasterBand(poDSIn, nBandIn, fpRaw, nImgOffsetIn, nPixelOffsetIn,
     266             :                         nLineOffsetIn, eDataTypeIn,
     267             :                         RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
     268           5 :                         RawRasterBand::OwnFP::NO)
     269             :     {
     270           5 :     }
     271             : 
     272             :     ~CPHDInternalBand() override;
     273             : };
     274             : 
     275             : /************************************************************************/
     276             : /*                          ~CPHDInternalBand                           */
     277             : /************************************************************************/
     278             : 
     279          10 : CPHDInternalBand::~CPHDInternalBand()
     280             : 
     281             : {
     282          10 : }
     283             : 
     284             : /************************************************************************/
     285             : /*                             CPHDMDArray                              */
     286             : /************************************************************************/
     287             : 
     288             : class CPHDMDArray final : public GDALMDArray
     289             : {
     290             :     CPL_DISALLOW_COPY_ASSIGN(CPHDMDArray)
     291             : 
     292             :     friend class CPHDGroup;
     293             : 
     294             :     std::unique_ptr<CPHDInternalDataset> m_poDS;
     295             :     GDALExtendedDataType m_dt;
     296             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
     297             :     mutable std::vector<std::shared_ptr<GDALAttribute>> m_apoAttributes{};
     298             :     std::string m_osFilename{};
     299             : 
     300             :   protected:
     301           5 :     CPHDMDArray(std::unique_ptr<CPHDInternalDataset> poDS,
     302             :                 const std::string &osParentName, const std::string &osName,
     303             :                 const GDALExtendedDataType &oEDT)
     304           5 :         : GDALAbstractMDArray(osParentName, osName),
     305           5 :           GDALMDArray(osParentName, osName), m_poDS(std::move(poDS)),
     306           5 :           m_dt(oEDT), m_osFilename(m_poDS->GetDescription())
     307             :     {
     308           5 :         const int nXSize = m_poDS->GetRasterXSize();
     309           5 :         const int nYSize = m_poDS->GetRasterYSize();
     310             : 
     311           5 :         std::string osArrayPath = osParentName.empty()
     312             :                                       ? "/" + osName
     313           8 :                                       : "/" + osParentName + "/" + osName;
     314             : 
     315          30 :         m_dims = {std::make_shared<GDALDimensionWeakIndexingVar>(
     316             :                       osArrayPath, "Y", "", "", nYSize),
     317          10 :                   std::make_shared<GDALDimensionWeakIndexingVar>(
     318          15 :                       osArrayPath, "X", "", "", nXSize)};
     319           5 :     }
     320             : 
     321           3 :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
     322             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     323             :                const GDALExtendedDataType &bufferDataType,
     324             :                void *pDstBuffer) const override
     325             :     {
     326           3 :         const size_t iDimX = 1;
     327           3 :         const size_t iDimY = 0;
     328           3 :         return GDALMDRasterIOFromBand(m_poDS->GetRasterBand(1), GF_Read, iDimX,
     329             :                                       iDimY, arrayStartIdx, count, arrayStep,
     330           3 :                                       bufferStride, bufferDataType, pDstBuffer);
     331             :     }
     332             : 
     333             :   public:
     334             :     static std::shared_ptr<CPHDMDArray>
     335           5 :     Create(std::unique_ptr<CPHDInternalDataset> poDS,
     336             :            const std::string &osParentName, const std::string &osName,
     337             :            const GDALExtendedDataType &oEDT)
     338             :     {
     339             :         auto array(std::shared_ptr<CPHDMDArray>(
     340           5 :             new CPHDMDArray(std::move(poDS), osParentName, osName, oEDT)));
     341           5 :         array->SetSelf(array);
     342           5 :         return array;
     343             :     }
     344             : 
     345           0 :     bool IsWritable() const override
     346             :     {
     347           0 :         return false;
     348             :     }
     349             : 
     350           3 :     const std::string &GetFilename() const override
     351             :     {
     352           3 :         return m_osFilename;
     353             :     }
     354             : 
     355             :     const std::vector<std::shared_ptr<GDALDimension>> &
     356          24 :     GetDimensions() const override
     357             :     {
     358          24 :         return m_dims;
     359             :     }
     360             : 
     361           6 :     const GDALExtendedDataType &GetDataType() const override
     362             :     {
     363           6 :         return m_dt;
     364             :     }
     365             : 
     366           0 :     std::vector<GUInt64> GetBlockSize() const override
     367             :     {
     368           0 :         int nBlockXSize = 0;
     369           0 :         int nBlockYSize = 0;
     370           0 :         m_poDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     371           0 :         return std::vector<GUInt64>{static_cast<GUInt64>(nBlockYSize),
     372           0 :                                     static_cast<GUInt64>(nBlockXSize)};
     373             :     }
     374             : 
     375             :     std::vector<std::shared_ptr<GDALAttribute>>
     376           0 :     GetAttributes(CSLConstList) const override
     377             :     {
     378           0 :         return m_apoAttributes;
     379             :     }
     380             : 
     381             :     ~CPHDMDArray() override;
     382             : };
     383             : 
     384             : /************************************************************************/
     385             : /*                             ~CPHDMDArray                             */
     386             : /************************************************************************/
     387             : 
     388          10 : CPHDMDArray::~CPHDMDArray()
     389             : 
     390             : {
     391          10 : }
     392             : 
     393             : /************************************************************************/
     394             : /*                              CPHDGroup                               */
     395             : /************************************************************************/
     396             : 
     397             : class CPHDGroup final : public GDALGroup
     398             : {
     399             :     friend class CPHDDataset;
     400             :     std::shared_ptr<CPHDSharedResources> m_poShared{};
     401             :     mutable std::vector<std::shared_ptr<GDALMDArray>> m_apoArrays{};
     402             :     mutable std::vector<std::shared_ptr<CPHDGroup>> m_apoGroups{};
     403             :     mutable std::vector<std::shared_ptr<GDALAttribute>> m_apoAttributes{};
     404             :     mutable std::vector<GByte> m_abyPVPData{};
     405             :     bool Init();
     406             :     std::shared_ptr<CPHDGroup> AddChannel(const CPLXMLNode *psChannel);
     407             :     bool AddSupportArray(const CPLXMLNode *psSupportArray);
     408             : 
     409             :   public:
     410           6 :     CPHDGroup(const std::string &osParentName, const std::string &osName,
     411             :               const std::shared_ptr<CPHDSharedResources> &poShared)
     412           6 :         : GDALGroup(osParentName, osName), m_poShared(poShared)
     413             :     {
     414           6 :     }
     415             : 
     416             :     std::vector<std::string>
     417             :     GetMDArrayNames(CSLConstList papszOptions) const override;
     418             :     std::shared_ptr<GDALMDArray>
     419             :     OpenMDArray(const std::string &osName,
     420             :                 CSLConstList papszOptions) const override;
     421             : 
     422             :     std::vector<std::string>
     423             :     GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const override;
     424             : 
     425             :     std::shared_ptr<GDALGroup>
     426             :     OpenGroup(const std::string &osName,
     427             :               CSLConstList papszOptions = nullptr) const override;
     428             : 
     429             :     std::vector<std::shared_ptr<GDALAttribute>>
     430          25 :     GetAttributes(CSLConstList) const override
     431             :     {
     432          25 :         return m_apoAttributes;
     433             :     }
     434             : };
     435             : 
     436             : /************************************************************************/
     437             : /* ==================================================================== */
     438             : /*                        CPHDDataset                                   */
     439             : /* ==================================================================== */
     440             : /************************************************************************/
     441             : 
     442             : class CPHDDataset final : public RawDataset
     443             : {
     444             :     CPL_DISALLOW_COPY_ASSIGN(CPHDDataset)
     445             :     std::shared_ptr<GDALGroup> m_poRootGroup{};
     446             : 
     447             :   protected:
     448             :     CPLErr Close(GDALProgressFunc, void *) override;
     449             : 
     450             :   public:
     451           3 :     CPHDDataset()
     452           3 :     {
     453           3 :     }
     454             : 
     455             :     static GDALDataset *OpenMultiDim(GDALOpenInfo *poOpenInfo);
     456             : 
     457           3 :     std::shared_ptr<GDALGroup> GetRootGroup() const override
     458             :     {
     459           3 :         return m_poRootGroup;
     460             :     }
     461             : 
     462             :     static GDALDataset *Open(GDALOpenInfo *);
     463             : };
     464             : 
     465             : /************************************************************************/
     466             : /*                                Close                                 */
     467             : /************************************************************************/
     468             : 
     469           3 : CPLErr CPHDDataset::Close(GDALProgressFunc, void *)
     470             : 
     471             : {
     472           3 :     return CE_None;
     473             : }
     474             : 
     475             : /************************************************************************/
     476             : /*                            OpenMultiDim()                            */
     477             : /************************************************************************/
     478             : 
     479           3 : GDALDataset *CPHDDataset::OpenMultiDim(GDALOpenInfo *poOpenInfo)
     480             : 
     481             : {
     482           6 :     auto poDS = std::make_unique<CPHDDataset>();
     483             :     auto poShared = std::make_shared<CPHDSharedResources>(
     484           6 :         poOpenInfo->pszFilename, poOpenInfo->fpL);
     485           3 :     poOpenInfo->fpL = nullptr;
     486             :     auto poRootGroup =
     487           6 :         std::make_shared<CPHDGroup>(std::string(), "/", poShared);
     488             : 
     489           3 :     poShared->m_fp->Seek(0, SEEK_SET);
     490           3 :     const char *pszLine = nullptr;
     491             : 
     492          33 :     while ((pszLine = CPLReadLineL(poShared->m_fp.get())) != nullptr)
     493             :     {
     494             :         // header section terminator (required)
     495          33 :         if EQUAL (pszLine, "\f")
     496           3 :             break;
     497             : 
     498          60 :         const CPLStringList aosTokens(CSLTokenizeString2(pszLine, " := /", 0));
     499             : 
     500          30 :         if (aosTokens.size() == 2)
     501             :         {
     502          29 :             if EQUAL (aosTokens[0], "CPHD")
     503           3 :                 poRootGroup->m_apoAttributes.emplace_back(
     504           3 :                     std::make_shared<GDALAttributeString>("/", "cphd_version",
     505           6 :                                                           aosTokens[1]));
     506          26 :             else if EQUAL (aosTokens[0], "RELEASE_INFO")
     507           2 :                 poRootGroup->m_apoAttributes.emplace_back(
     508           2 :                     std::make_shared<GDALAttributeString>("/", "release_info",
     509           4 :                                                           aosTokens[1]));
     510          24 :             else if EQUAL (aosTokens[0], "CLASSIFICATION")
     511           3 :                 poRootGroup->m_apoAttributes.emplace_back(
     512           3 :                     std::make_shared<GDALAttributeString>("/", "classification",
     513           6 :                                                           aosTokens[1]));
     514          21 :             else if EQUAL (aosTokens[0], "XML_BLOCK_SIZE")
     515           3 :                 poShared->nXmlBlockSize = CPLAtoGIntBig(aosTokens[1]);
     516          18 :             else if EQUAL (aosTokens[0], "XML_BLOCK_BYTE_OFFSET")
     517           3 :                 poShared->nXmlBlockByteOffset =
     518           3 :                     static_cast<GUIntBig>(CPLAtoGIntBig(aosTokens[1]));
     519          15 :             else if EQUAL (aosTokens[0], "SUPPORT_BLOCK_SIZE")
     520           1 :                 poShared->nSupportBlockSize = CPLAtoGIntBig(aosTokens[1]);
     521          14 :             else if EQUAL (aosTokens[0], "SUPPORT_BLOCK_BYTE_OFFSET")
     522           1 :                 poShared->nSupportBlockByteOffset =
     523           1 :                     static_cast<GUIntBig>(CPLAtoGIntBig(aosTokens[1]));
     524          13 :             else if EQUAL (aosTokens[0], "PVP_BLOCK_SIZE")
     525           3 :                 poShared->nPVPBlockSize = CPLAtoGIntBig(aosTokens[1]);
     526          10 :             else if EQUAL (aosTokens[0], "PVP_BLOCK_BYTE_OFFSET")
     527           3 :                 poShared->nPVPBlockByteOffset =
     528           3 :                     static_cast<GUIntBig>(CPLAtoGIntBig(aosTokens[1]));
     529           7 :             else if EQUAL (aosTokens[0], "SIGNAL_BLOCK_SIZE")
     530           3 :                 poShared->nSignalBlockSize = CPLAtoGIntBig(aosTokens[1]);
     531           4 :             else if EQUAL (aosTokens[0], "SIGNAL_BLOCK_BYTE_OFFSET")
     532           3 :                 poShared->nSignalBlockByteOffset =
     533           3 :                     static_cast<GUIntBig>(CPLAtoGIntBig(aosTokens[1]));
     534             :             else
     535           1 :                 poRootGroup->m_apoAttributes.emplace_back(
     536           2 :                     std::make_shared<GDALAttributeString>(
     537           3 :                         "/", CPLString(aosTokens[0]).tolower(), aosTokens[1]));
     538             :         }
     539             :     }
     540             : 
     541             :     // read XML block
     542           3 :     if (poShared->nXmlBlockByteOffset && poShared->nXmlBlockSize)
     543             :     {
     544             :         std::map<CPLString, CPLString> oAttrs{
     545             :             {"collector_name", "CollectionId.CollectorName"},
     546             :             {"core_name", "CollectionId.CoreName"},
     547             :             {"collect_type", "CollectionId.CollectType"},
     548          18 :             {"radar_mode", "CollectionId.RadarMode.ModeType"}};
     549             : 
     550           3 :         poShared->m_fp->Seek(poShared->nXmlBlockByteOffset, SEEK_SET);
     551           3 :         CPLString osBuffer;
     552           3 :         if (poShared->nXmlBlockSize > std::numeric_limits<int>::max())
     553             :         {
     554           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     555             :                      "XML block size is too large for file %s",
     556             :                      poOpenInfo->pszFilename);
     557           0 :             return nullptr;
     558             :         }
     559             :         try
     560             :         {
     561           3 :             if (poShared->nXmlBlockSize > 100 * 1024 * 1024)
     562             :             {
     563             :                 VSIStatBufL sStat;
     564           0 :                 if (VSIStatL(poOpenInfo->pszFilename, &sStat) == 0)
     565             :                 {
     566           0 :                     GIntBig nFileSize = static_cast<GIntBig>(sStat.st_size);
     567           0 :                     if (poShared->nXmlBlockSize > nFileSize)
     568             :                     {
     569           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     570             :                                  "XML block size is too large for file %s",
     571             :                                  poOpenInfo->pszFilename);
     572           0 :                         return nullptr;
     573             :                     }
     574             :                 }
     575             :             }
     576             : 
     577           3 :             osBuffer.resize(static_cast<size_t>(poShared->nXmlBlockSize));
     578             :         }
     579           0 :         catch (const std::exception &e)
     580             :         {
     581           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     582           0 :             return nullptr;
     583             :         }
     584             : 
     585           6 :         poShared->m_fp->Read(&osBuffer[0],
     586           3 :                              static_cast<size_t>(poShared->nXmlBlockSize), 1);
     587             : 
     588           3 :         poShared->m_poXMLTree.reset(CPLParseXMLString(osBuffer));
     589             : 
     590          15 :         for (auto const &[osName, osPath] : oAttrs)
     591             :         {
     592             :             auto pszValue =
     593          12 :                 CPLGetXMLValue(poShared->m_poXMLTree.get(), osPath, nullptr);
     594          12 :             if (pszValue != nullptr)
     595          12 :                 poRootGroup->m_apoAttributes.emplace_back(
     596          12 :                     std::make_shared<GDALAttributeString>("/", osName,
     597          12 :                                                           pszValue));
     598             :         }
     599             : 
     600             :         // make the entire metadata xml tree available at the root level
     601           3 :         if (CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     602             :                                              "INCLUDE_XML", "YES")))
     603             :         {
     604           3 :             poRootGroup->m_apoAttributes.emplace_back(
     605           3 :                 std::make_shared<GDALAttributeString>("/", "xml", osBuffer));
     606             :         }
     607             :     }
     608             :     else
     609             :     {
     610           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     611             :                  "XML Offset and/or Size not found in CPHD header: %s.",
     612             :                  poOpenInfo->pszFilename);
     613           0 :         return nullptr;
     614             :     }
     615             : 
     616           3 :     if (!poShared->m_poXMLTree)
     617             :     {
     618           0 :         CPLError(CE_Failure, CPLE_AppDefined, "XML block not parsed: %s.",
     619             :                  poOpenInfo->pszFilename);
     620           0 :         return nullptr;
     621             :     }
     622             : 
     623           3 :     if (poRootGroup->Init())
     624             :     {
     625           3 :         poDS->m_poRootGroup = std::move(poRootGroup);
     626           3 :         poDS->SetDescription(poOpenInfo->pszFilename);
     627             : 
     628             :         // Setup/check for pam .aux.xml.
     629           3 :         poDS->TryLoadXML();
     630             : 
     631           3 :         return poDS.release();
     632             :     }
     633             :     else
     634           0 :         return nullptr;
     635             : }
     636             : 
     637             : /************************************************************************/
     638             : /*                                Open()                                */
     639             : /************************************************************************/
     640             : 
     641           3 : GDALDataset *CPHDDataset::Open(GDALOpenInfo *poOpenInfo)
     642             : 
     643             : {
     644           3 :     const auto eIdentify = CPHDDatasetIdentify(poOpenInfo);
     645           3 :     if (eIdentify == GDAL_IDENTIFY_FALSE)
     646           0 :         return nullptr;
     647             : 
     648           3 :     return CPHDDataset::OpenMultiDim(poOpenInfo);
     649             : }
     650             : 
     651             : /************************************************************************/
     652             : /* ==================================================================== */
     653             : /*                            CPHDGroup                                 */
     654             : /* ==================================================================== */
     655             : /************************************************************************/
     656             : 
     657             : /************************************************************************/
     658             : /*                             AddChannel()                             */
     659             : /************************************************************************/
     660           3 : std::shared_ptr<CPHDGroup> CPHDGroup::AddChannel(const CPLXMLNode *psChannel)
     661             : {
     662             :     // assign datasource and arrays for this group as there are not many channels
     663           3 :     const auto pszIdentifier = CPLGetXMLValue(psChannel, "Identifier", "");
     664             : 
     665             :     // these are required by the XML schema
     666           3 :     const auto pszSignalBlockFormat = CPLGetXMLValue(
     667           3 :         m_poShared->m_poXMLTree.get(), "Data.SignalArrayFormat", nullptr);
     668             :     const auto pszSignalArrayByteOffset =
     669           3 :         CPLGetXMLValue(psChannel, "SignalArrayByteOffset", nullptr);
     670             :     const auto pszSignalArrayWidth =
     671           3 :         CPLGetXMLValue(psChannel, "NumSamples", nullptr);
     672             :     const auto pszSignalArrayHeight =
     673           3 :         CPLGetXMLValue(psChannel, "NumVectors", nullptr);
     674           3 :     const auto pszNumBytesPVP = CPLGetXMLValue(m_poShared->m_poXMLTree.get(),
     675             :                                                "Data.NumBytesPVP", nullptr);
     676             :     const auto pszPVPArrayByteOffset =
     677           3 :         CPLGetXMLValue(psChannel, "PVPArrayByteOffset", nullptr);
     678             : 
     679           3 :     if ((pszSignalBlockFormat == nullptr) ||
     680           3 :         (pszSignalArrayByteOffset == nullptr) ||
     681           3 :         (pszSignalArrayWidth == nullptr) || (pszSignalArrayHeight == nullptr))
     682             :     {
     683           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     684             :                  "Required signal block array offsets and "
     685             :                  "format/height/width not found in XML description : %s.",
     686           0 :                  m_poShared->m_osFilename.c_str());
     687           0 :         return nullptr;
     688             :     }
     689             : 
     690           3 :     if ((pszPVPArrayByteOffset == nullptr) || (pszNumBytesPVP == nullptr))
     691             :     {
     692           0 :         CPLError(
     693             :             CE_Failure, CPLE_AppDefined,
     694             :             "Required PVP array offsets and "
     695             :             "number of bytes (NumBytesPVP) not found in XML description : %s.",
     696           0 :             m_poShared->m_osFilename.c_str());
     697           0 :         return nullptr;
     698             :     }
     699             : 
     700           3 :     const auto nXSize = atoi(pszSignalArrayWidth);
     701           3 :     const auto nYSize = atoi(pszSignalArrayHeight);
     702             : 
     703           3 :     if (nXSize < 0 || nYSize < 0)
     704             :     {
     705           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     706             :                  "Signal block dimensions are incorrect "
     707             :                  "Width %i Height %i : %s.",
     708           0 :                  nXSize, nYSize, m_poShared->m_osFilename.c_str());
     709           0 :         return nullptr;
     710             :     }
     711             : 
     712             :     const auto poSubGroup =
     713           6 :         std::make_shared<CPHDGroup>("/", pszIdentifier, m_poShared);
     714             : 
     715           3 :     const auto osSignalBlockName("SignalBlock");
     716           3 :     const GDALDataType dt = ParseComplexDataType(
     717           3 :         pszSignalBlockFormat, m_poShared->m_osFilename.c_str());
     718             : 
     719           3 :     if (dt == GDT_Unknown)
     720             :     {
     721           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     722             :                  "Unrecognized complex data type %s : %s.",
     723           0 :                  pszSignalBlockFormat, m_poShared->m_osFilename.c_str());
     724           0 :         return nullptr;
     725             :     }
     726             : 
     727             :     // add signal block
     728           6 :     auto poSignalDS = std::make_unique<CPHDInternalDataset>();
     729           3 :     poSignalDS->nRasterXSize = nXSize;
     730           3 :     poSignalDS->nRasterYSize = nYSize;
     731             : 
     732             :     auto poBand = std::make_unique<CPHDInternalBand>(
     733           3 :         poSignalDS.get(), 1, m_poShared->m_fp.get(),
     734           3 :         m_poShared->nSignalBlockByteOffset + atoi(pszSignalArrayByteOffset),
     735           3 :         GDALGetDataTypeSizeBytes(dt), GDALGetDataTypeSizeBytes(dt) * nXSize,
     736           6 :         dt);
     737             : 
     738           3 :     poSignalDS->SetBand(1, std::move(poBand));
     739             :     auto poSignalArray = CPHDMDArray::Create(
     740           6 :         std::move(poSignalDS), std::string(pszIdentifier), osSignalBlockName,
     741          15 :         GDALExtendedDataType::Create(dt));
     742             : 
     743           3 :     poSubGroup->m_apoArrays.emplace_back(std::move(poSignalArray));
     744             : 
     745             :     // add PVPs
     746           3 :     m_poShared->nPVPArrayByteOffset = atoi(pszPVPArrayByteOffset);
     747             : 
     748             :     const auto oEDTPvp =
     749           3 :         ParsePVPDataType(CPLGetXMLNode(m_poShared->m_poXMLTree.get(), "PVP"),
     750           9 :                          m_poShared->m_osFilename.c_str());
     751           3 :     if (oEDTPvp.GetClass() == GEDTC_NUMERIC &&
     752           0 :         oEDTPvp.GetNumericDataType() == GDT_Unknown)
     753           0 :         return nullptr;
     754             : 
     755             :     auto poVectorDim = std::make_shared<GDALDimensionWeakIndexingVar>(
     756           6 :         "/" + std::string(pszIdentifier), "Vector", GDAL_DIM_TYPE_VERTICAL,
     757           6 :         "AZIMUTH", nYSize);
     758             : 
     759             :     auto poPvpArray = MEMMDArray::Create(
     760          12 :         std::string(pszIdentifier), PVP_ARRAY_NAME, {poVectorDim}, oEDTPvp);
     761           3 :     poSubGroup->m_apoArrays.emplace_back(std::move(poPvpArray));
     762             : 
     763             :     // add group to root
     764           3 :     m_apoGroups.emplace_back(std::move(poSubGroup));
     765           3 :     return m_apoGroups.back();
     766             : }
     767             : 
     768             : /************************************************************************/
     769             : /*                          AddSupportArray()                           */
     770             : /************************************************************************/
     771           2 : bool CPHDGroup::AddSupportArray(const CPLXMLNode *psDataSupportArray)
     772             : {
     773             :     // get support array section
     774             :     auto psSupportXml =
     775           2 :         CPLGetXMLNode(m_poShared->m_poXMLTree.get(), "SupportArray");
     776             : 
     777           2 :     if (psSupportXml == nullptr)
     778           0 :         return false;
     779             : 
     780           2 :     auto *psSupportChild = psSupportXml->psChild;
     781             :     const auto pszArrayName =
     782           2 :         CPLGetXMLValue(psDataSupportArray, "Identifier", "");
     783             : 
     784             :     // process only known support arrays for now
     785           3 :     for (; psSupportChild != nullptr; psSupportChild = psSupportChild->psNext)
     786             :     {
     787           3 :         if EQUAL (pszArrayName,
     788             :                   CPLGetXMLValue(psSupportChild, "Identifier", ""))
     789             :         {
     790           2 :             const auto pszElementName = psSupportChild->pszValue;
     791           0 :             std::unique_ptr<CPHDInternalBand> poBand;
     792             :             const auto pszSupportArrayWidth =
     793           2 :                 CPLGetXMLValue(psDataSupportArray, "NumCols", nullptr);
     794             :             const auto pszSupportArrayHeight =
     795           2 :                 CPLGetXMLValue(psDataSupportArray, "NumRows", nullptr);
     796             :             const auto pszArrayByteOffset =
     797           2 :                 CPLGetXMLValue(psDataSupportArray, "ArrayByteOffset", nullptr);
     798             : 
     799           2 :             if (!pszSupportArrayWidth || !pszSupportArrayHeight ||
     800             :                 !pszArrayByteOffset)
     801             :             {
     802           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     803             :                          "Support Array height/width/offset not found in XML "
     804             :                          "description : %s.",
     805           0 :                          m_poShared->m_osFilename.c_str());
     806           0 :                 return false;
     807             :             }
     808             : 
     809           2 :             int nWidth = atoi(pszSupportArrayWidth);
     810           2 :             int nHeight = atoi(pszSupportArrayHeight);
     811             : 
     812           2 :             if (nWidth < 0 || nHeight < 0)
     813             :             {
     814           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     815             :                          "Support array width/height is incorrect "
     816             :                          "width %i height %i for %s",
     817           0 :                          nWidth, nHeight, m_poShared->m_osFilename.c_str());
     818           0 :                 return false;
     819             :             }
     820             : 
     821             :             // add support array
     822           2 :             auto poSupportDS = std::make_unique<CPHDInternalDataset>();
     823           2 :             poSupportDS->nRasterXSize = nWidth;
     824           2 :             poSupportDS->nRasterYSize = nHeight;
     825             : 
     826           2 :             if (EQUAL(pszElementName, "AntGainPhase") ||
     827           0 :                 EQUAL(pszElementName, "DwellTimeArray"))
     828             :                 // DataType: F4;F4
     829           2 :                 poBand = std::make_unique<CPHDInternalBand>(
     830           2 :                     poSupportDS.get(), 1, m_poShared->m_fp.get(),
     831           0 :                     m_poShared->nSupportBlockByteOffset +
     832           2 :                         atoi(pszArrayByteOffset),
     833           2 :                     GDALGetDataTypeSizeBytes(GDT_CFloat64),
     834           0 :                     GDALGetDataTypeSizeBytes(GDT_CFloat64) *
     835           2 :                         poSupportDS->nRasterXSize,
     836           6 :                     GDT_CFloat64);
     837           0 :             else if EQUAL (pszElementName, "IAZArray")
     838             :                 // DataType: IAZ=F4
     839           0 :                 poBand = std::make_unique<CPHDInternalBand>(
     840           0 :                     poSupportDS.get(), 1, m_poShared->m_fp.get(),
     841           0 :                     m_poShared->nSupportBlockByteOffset +
     842           0 :                         atoi(pszArrayByteOffset),
     843           0 :                     GDALGetDataTypeSizeBytes(GDT_Float32),
     844           0 :                     GDALGetDataTypeSizeBytes(GDT_Float32) *
     845           0 :                         poSupportDS->nRasterXSize,
     846           0 :                     GDT_Float32);
     847             :             else
     848             :             {
     849           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     850             :                          "Unsupported Support Array %s : %s.", pszArrayName,
     851           0 :                          m_poShared->m_osFilename.c_str());
     852           0 :                 return false;
     853             :             }
     854             : 
     855           2 :             CPLAssert(poBand);
     856             : 
     857           2 :             const auto dt = poBand->GetRasterDataType();
     858           2 :             poSupportDS->SetBand(1, poBand.release());
     859             :             auto poSupportArray = CPHDMDArray::Create(
     860           6 :                 std::move(poSupportDS), "", std::string(pszArrayName),
     861          10 :                 GDALExtendedDataType::Create(dt));
     862             : 
     863           2 :             poSupportArray->m_apoAttributes.emplace_back(
     864           4 :                 std::make_shared<GDALAttributeString>(
     865           4 :                     "/" + std::string(pszArrayName), "element_format",
     866           4 :                     CPLGetXMLValue(psSupportChild, "ElementFormat", "")));
     867           2 :             poSupportArray->m_apoAttributes.emplace_back(
     868           4 :                 std::make_shared<GDALAttributeNumeric>(
     869           4 :                     "/" + std::string(pszArrayName), "x_0",
     870           4 :                     CPLAtof(CPLGetXMLValue(psSupportChild, "X0", "0."))));
     871           2 :             poSupportArray->m_apoAttributes.emplace_back(
     872           4 :                 std::make_shared<GDALAttributeNumeric>(
     873           4 :                     "/" + std::string(pszArrayName), "y_0",
     874           4 :                     CPLAtof(CPLGetXMLValue(psSupportChild, "Y0", "0."))));
     875           2 :             poSupportArray->m_apoAttributes.emplace_back(
     876           4 :                 std::make_shared<GDALAttributeNumeric>(
     877           4 :                     "/" + std::string(pszArrayName), "xss",
     878           4 :                     CPLAtof(CPLGetXMLValue(psSupportChild, "XSS", "0."))));
     879           2 :             poSupportArray->m_apoAttributes.emplace_back(
     880           4 :                 std::make_shared<GDALAttributeNumeric>(
     881           4 :                     "/" + std::string(pszArrayName), "yss",
     882           4 :                     CPLAtof(CPLGetXMLValue(psSupportChild, "YSS", "0."))));
     883             : 
     884           2 :             m_apoArrays.emplace_back(std::move(poSupportArray));
     885             : 
     886           2 :             break;
     887             :         }
     888             :     }
     889           2 :     return true;
     890             : }
     891             : 
     892             : /************************************************************************/
     893             : /*                                Init()                                */
     894             : /************************************************************************/
     895           3 : bool CPHDGroup::Init()
     896             : {
     897           3 :     if ((m_osName != "/") || (!m_apoArrays.empty()))
     898           0 :         return false;
     899             : 
     900           3 :     auto psDataXml = CPLGetXMLNode(m_poShared->m_poXMLTree.get(), "Data");
     901             : 
     902           3 :     if (psDataXml == nullptr)
     903           0 :         return false;
     904             : 
     905           3 :     CPLXMLNode *psChild = psDataXml->psChild;
     906             : 
     907          19 :     for (; psChild != nullptr; psChild = psChild->psNext)
     908             :     {
     909          16 :         if EQUAL (psChild->pszValue, "Channel")
     910             :         {
     911           3 :             std::shared_ptr<CPHDGroup> poSubGroup = AddChannel(psChild);
     912           3 :             if (!poSubGroup)
     913           0 :                 return false;
     914             :         }
     915          13 :         else if EQUAL (psChild->pszValue, "SupportArray")
     916             :         {
     917           2 :             if (!AddSupportArray(psChild))
     918           0 :                 return false;
     919             :         }
     920             :     }
     921           3 :     return true;
     922             : }
     923             : 
     924             : /************************************************************************/
     925             : /*                          GetMDArrayNames()                           */
     926             : /************************************************************************/
     927             : 
     928           6 : std::vector<std::string> CPHDGroup::GetMDArrayNames(CSLConstList) const
     929             : {
     930           6 :     std::vector<std::string> aosArrayNames;
     931             : 
     932           6 :     if (!CheckValidAndErrorOutIfNot())
     933           0 :         return aosArrayNames;
     934             : 
     935          14 :     for (auto const &poArray : m_apoArrays)
     936           8 :         aosArrayNames.emplace_back(poArray->GetName());
     937             : 
     938           6 :     return aosArrayNames;
     939             : }
     940             : 
     941             : /************************************************************************/
     942             : /*                            OpenMDArray()                             */
     943             : /************************************************************************/
     944             : 
     945           6 : std::shared_ptr<GDALMDArray> CPHDGroup::OpenMDArray(const std::string &osName,
     946             :                                                     CSLConstList) const
     947             : {
     948           6 :     if (!CheckValidAndErrorOutIfNot())
     949           0 :         return nullptr;
     950             : 
     951           9 :     for (const auto &poArray : m_apoArrays)
     952             :     {
     953           9 :         if (poArray->GetName() == osName)
     954             :         {
     955           6 :             if (osName == PVP_ARRAY_NAME)
     956             :             {
     957             :                 const auto poPVPArray =
     958           3 :                     std::dynamic_pointer_cast<MEMMDArray>(poArray);
     959             : 
     960           3 :                 if (!poPVPArray)
     961             :                 {
     962           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     963             :                              "Error opening PVP array : %s",
     964           0 :                              m_poShared->m_osFilename.c_str());
     965           0 :                     return nullptr;
     966             :                 }
     967             : 
     968           3 :                 if (poPVPArray->IsWritable())
     969             :                 {
     970             :                     // read PVP array
     971           3 :                     if (m_poShared->nPVPArrayByteOffset <
     972           3 :                         std::numeric_limits<uint64_t>::max() -
     973           3 :                             m_poShared->nPVPBlockByteOffset)
     974             :                     {
     975           3 :                         m_poShared->m_fp->Seek(
     976           6 :                             m_poShared->nPVPBlockByteOffset +
     977           3 :                                 m_poShared->nPVPArrayByteOffset,
     978           3 :                             SEEK_SET);
     979             :                     }
     980             :                     else
     981             :                     {
     982           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     983             :                                  "Unable to read PVPs for %s, one or both of "
     984             :                                  "PVP_BLOCK_SIZE and PVP_BLOCK_BYTE_OFFSET are "
     985             :                                  "incorrect.",
     986           0 :                                  m_poShared->m_osFilename.c_str());
     987           0 :                         return nullptr;
     988             :                     }
     989             : 
     990             :                     if constexpr (sizeof(size_t) <
     991             :                                   sizeof(m_poShared->nPVPBlockSize))
     992             :                     {
     993             :                         if (m_poShared->nPVPBlockSize >
     994             :                             std::numeric_limits<int>::max())
     995             :                         {
     996             :                             CPLError(CE_Failure, CPLE_AppDefined,
     997             :                                      "PVP block size is too large for file %s",
     998             :                                      m_poShared->m_osFilename.c_str());
     999             :                             return nullptr;
    1000             :                         }
    1001             :                     }
    1002             : 
    1003             :                     try
    1004             :                     {
    1005           3 :                         if (m_poShared->nPVPBlockSize > 100 * 1024 * 1024)
    1006             :                         {
    1007             :                             // possible to have a very large CPHD file so check file size to continue
    1008             :                             // as PVPs should be relatively small
    1009             :                             VSIStatBufL sStat;
    1010           0 :                             if (VSIStatL(m_poShared->m_osFilename.c_str(),
    1011           0 :                                          &sStat) == 0)
    1012             :                             {
    1013           0 :                                 GIntBig nFileSize =
    1014             :                                     static_cast<GIntBig>(sStat.st_size);
    1015           0 :                                 if (m_poShared->nPVPBlockSize > nFileSize)
    1016             :                                 {
    1017           0 :                                     CPLError(CE_Failure, CPLE_AppDefined,
    1018             :                                              "PVP block size is too large for "
    1019             :                                              "file %s",
    1020           0 :                                              m_poShared->m_osFilename.c_str());
    1021           0 :                                     return nullptr;
    1022             :                                 }
    1023             :                             }
    1024             :                             else
    1025             :                             {
    1026           0 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    1027             :                                          "Unable to determine file size for %s",
    1028           0 :                                          m_poShared->m_osFilename.c_str());
    1029           0 :                                 return nullptr;
    1030             :                             }
    1031             :                         }
    1032           6 :                         m_abyPVPData.resize(
    1033           3 :                             static_cast<size_t>(m_poShared->nPVPBlockSize));
    1034             :                     }
    1035           0 :                     catch (const std::exception &)
    1036             :                     {
    1037           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory,
    1038             :                                  "Out of memory allocating PVP buffer");
    1039           0 :                         return nullptr;
    1040             :                     }
    1041           6 :                     if (m_poShared->m_fp->Read(
    1042           3 :                             m_abyPVPData.data(),
    1043           3 :                             static_cast<size_t>(m_poShared->nPVPBlockSize)) !=
    1044           3 :                         static_cast<size_t>(m_poShared->nPVPBlockSize))
    1045             :                     {
    1046           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1047             :                                  "Unable to read PVPs from %s",
    1048           0 :                                  m_poShared->m_osFilename.c_str());
    1049           0 :                         return nullptr;
    1050             :                     }
    1051             : 
    1052             :                     size_t nVectors = static_cast<size_t>(
    1053           3 :                         poPVPArray->GetDimensions()[0]->GetSize());
    1054             : 
    1055           3 :                     const auto &oEDT = poPVPArray->GetDataType();
    1056             : #if CPL_IS_LSB
    1057             :                     // swap all numeric types as CPHD binary data is big endian
    1058           3 :                     auto ptr = m_abyPVPData.data();
    1059             : 
    1060    11353700 :                     auto swapPtr = [](uint8_t *p, GDALDataType dt)
    1061             :                     {
    1062    11353700 :                         switch (dt)
    1063             :                         {
    1064           0 :                             case GDT_Int16:
    1065             :                             case GDT_UInt16:
    1066             :                             case GDT_Float16:
    1067           0 :                                 CPL_SWAP16PTR(p);
    1068           0 :                                 break;
    1069           0 :                             case GDT_Int32:
    1070             :                             case GDT_UInt32:
    1071             :                             case GDT_Float32:
    1072           0 :                                 CPL_SWAP32PTR(p);
    1073           0 :                                 break;
    1074    11353700 :                             case GDT_Int64:
    1075             :                             case GDT_UInt64:
    1076             :                             case GDT_Float64:
    1077    11353700 :                                 CPL_SWAP64PTR(p);
    1078    11353700 :                                 break;
    1079             :                             // complex
    1080           0 :                             case GDT_CInt16:
    1081             :                             case GDT_CFloat16:
    1082           0 :                                 CPL_SWAP16PTR(p);
    1083           0 :                                 CPL_SWAP16PTR(p + 2);
    1084           0 :                                 break;
    1085           0 :                             case GDT_CInt32:
    1086             :                             case GDT_CFloat32:
    1087           0 :                                 CPL_SWAP32PTR(p);
    1088           0 :                                 CPL_SWAP32PTR(p + 4);
    1089           0 :                                 break;
    1090           0 :                             case GDT_CFloat64:
    1091           0 :                                 CPL_SWAP64PTR(p);
    1092           0 :                                 CPL_SWAP64PTR(p + 8);
    1093           0 :                                 break;
    1094           0 :                             default:
    1095           0 :                                 break;
    1096             :                         }
    1097             : 
    1098    11353700 :                         return GDALGetDataTypeSizeBytes(dt);
    1099             :                     };
    1100             : 
    1101             :                     // loop over all vectors
    1102      346624 :                     for (size_t i = 0; i < nVectors; i++)
    1103             :                     {
    1104     8234060 :                         for (const auto &comp : oEDT.GetComponents())
    1105             :                         {
    1106     7887440 :                             if (comp->GetType().GetClass() == GEDTC_COMPOUND)
    1107             :                             {
    1108             :                                 // swap XYZ and similar one level down types
    1109     5199330 :                                 for (const auto &subComp :
    1110     6932440 :                                      comp->GetType().GetComponents())
    1111             :                                 {
    1112     5199330 :                                     ptr +=
    1113     5199330 :                                         swapPtr(ptr, subComp->GetType()
    1114             :                                                          .GetNumericDataType());
    1115             :                                 }
    1116             :                             }
    1117             :                             else
    1118             :                             {
    1119     6154330 :                                 ptr += swapPtr(
    1120     6154330 :                                     ptr, comp->GetType().GetNumericDataType());
    1121             :                             }
    1122             :                         }
    1123             :                     }
    1124             : #endif
    1125           3 :                     if (!poPVPArray->Init(m_abyPVPData.data()))
    1126             :                     {
    1127           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1128             :                                  "Unable to read PVP data.");
    1129           0 :                         return nullptr;
    1130             :                     }
    1131             : 
    1132           3 :                     poPVPArray->SetWritable(false);
    1133             :                 }
    1134             :             }
    1135           6 :             return poArray;
    1136             :         }
    1137             :     }
    1138           0 :     return nullptr;
    1139             : }
    1140             : 
    1141             : /************************************************************************/
    1142             : /*                           GetGroupNames()                            */
    1143             : /************************************************************************/
    1144             : std::vector<std::string>
    1145           3 : CPHDGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
    1146             : 
    1147             : {
    1148           3 :     std::vector<std::string> aosGroupNames;
    1149             : 
    1150           6 :     for (auto const &poGroup : m_apoGroups)
    1151           3 :         aosGroupNames.emplace_back(poGroup->GetName());
    1152             : 
    1153           3 :     return aosGroupNames;
    1154             : }
    1155             : 
    1156             : /************************************************************************/
    1157             : /*                             OpenGroup()                              */
    1158             : /************************************************************************/
    1159           3 : std::shared_ptr<GDALGroup> CPHDGroup::OpenGroup(const std::string &osName,
    1160             :                                                 CSLConstList) const
    1161             : 
    1162             : {
    1163           3 :     for (const auto &poGroup : m_apoGroups)
    1164           3 :         if (poGroup->GetName() == osName)
    1165           3 :             return poGroup;
    1166             : 
    1167           0 :     return nullptr;
    1168             : }
    1169             : 
    1170             : /************************************************************************/
    1171             : /*                        CPHDDatasetIdentify()                         */
    1172             : /************************************************************************/
    1173             : 
    1174       53629 : static int CPHDDatasetIdentify(GDALOpenInfo *poOpenInfo)
    1175             : {
    1176       53635 :     return poOpenInfo->IsExtensionEqualToCI("cphd") && poOpenInfo->bStatOK &&
    1177       53641 :            (poOpenInfo->eAccess == GA_ReadOnly) &&
    1178       53635 :            (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER);
    1179             : }
    1180             : 
    1181             : /************************************************************************/
    1182             : /*                         GDALRegister_CPHD()                          */
    1183             : /************************************************************************/
    1184             : 
    1185        2066 : void GDALRegister_CPHD()
    1186             : 
    1187             : {
    1188        2066 :     if (GDALGetDriverByName("CPHD") != nullptr)
    1189         263 :         return;
    1190             : 
    1191        1803 :     GDALDriver *poDriver = new GDALDriver();
    1192             : 
    1193        1803 :     poDriver->SetDescription("CPHD");
    1194        1803 :     poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES");
    1195        1803 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1196        1803 :                               "Compensated Phase History Data Reader");
    1197        1803 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/cphd.html");
    1198        1803 :     poDriver->SetMetadataItem(
    1199             :         GDAL_DMD_OPENOPTIONLIST,
    1200             :         "<OpenOptionList>"
    1201             :         "<Option name='INCLUDE_XML' type='boolean' "
    1202             :         "description='Whether to include the XML string as a group attribute' "
    1203             :         "default='YES'/>"
    1204        1803 :         "</OpenOptionList>");
    1205        1803 :     poDriver->pfnOpen = CPHDDataset::Open;
    1206        1803 :     poDriver->pfnIdentify = CPHDDatasetIdentify;
    1207             : 
    1208        1803 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1209             : }

Generated by: LCOV version 1.14