LCOV - code coverage report
Current view: top level - frmts/dimap - dimapdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 608 738 82.4 %
Date: 2025-07-11 10:11:13 Functions: 24 31 77.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  SPOT Dimap Driver
       4             :  * Purpose:  Implementation of SPOT Dimap driver.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  * Docs: http://www.spotimage.fr/dimap/spec/documentation/refdoc.htm
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
      11             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "cpl_minixml.h"
      17             : #include "gdal_frmts.h"
      18             : #include "gdal_pam.h"
      19             : #include "ogr_spatialref.h"
      20             : #include "mdreader/reader_pleiades.h"
      21             : #include "vrtdataset.h"
      22             : #include <map>
      23             : #include <algorithm>
      24             : 
      25             : /************************************************************************/
      26             : /* ==================================================================== */
      27             : /*                              DIMAPDataset                            */
      28             : /* ==================================================================== */
      29             : /************************************************************************/
      30             : 
      31             : class DIMAPDataset final : public GDALPamDataset
      32             : {
      33             :     CPLXMLNode *psProduct{};
      34             : 
      35             :     CPLXMLNode *psProductDim{};    // DIMAP2, DIM_<product_id>.XML
      36             :     CPLXMLNode *psProductStrip{};  // DIMAP2, STRIP_<product_id>.XML
      37             :     CPLString osRPCFilename{};     // DIMAP2, RPC_<product_id>.XML
      38             : 
      39             :     VRTDataset *poVRTDS{};
      40             : 
      41             :     int nGCPCount{};
      42             :     GDAL_GCP *pasGCPList{};
      43             : 
      44             :     OGRSpatialReference m_oSRS{};
      45             :     OGRSpatialReference m_oGCPSRS{};
      46             : 
      47             :     int bHaveGeoTransform{};
      48             :     GDALGeoTransform m_gt{};
      49             : 
      50             :     CPLString osMDFilename{};
      51             :     CPLString osImageDSFilename{};
      52             :     CPLString osDIMAPFilename{};
      53             :     int nProductVersion = 1;
      54             : 
      55             :     char **papszXMLDimapMetadata{};
      56             : 
      57             :     CPL_DISALLOW_COPY_ASSIGN(DIMAPDataset)
      58             : 
      59             :   protected:
      60             :     int CloseDependentDatasets() override;
      61             : 
      62             :     int ReadImageInformation();
      63             :     int ReadImageInformation2();  // DIMAP 2.
      64             : 
      65             :     void SetMetadataFromXML(CPLXMLNode *psProduct,
      66             :                             const char *const apszMetadataTranslation[],
      67             :                             bool bKeysFromRoot = true);
      68             : 
      69             :   public:
      70             :     DIMAPDataset();
      71             :     ~DIMAPDataset() override;
      72             : 
      73             :     const OGRSpatialReference *GetSpatialRef() const override;
      74             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
      75             :     int GetGCPCount() override;
      76             :     const OGRSpatialReference *GetGCPSpatialRef() const override;
      77             :     const GDAL_GCP *GetGCPs() override;
      78             :     char **GetMetadataDomainList() override;
      79             :     char **GetMetadata(const char *pszDomain) override;
      80             :     char **GetFileList() override;
      81             : 
      82             :     CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
      83             :                      GDALDataType, int, BANDMAP_TYPE, GSpacing, GSpacing,
      84             :                      GSpacing, GDALRasterIOExtraArg *psExtraArg) override;
      85             : 
      86             :     static int Identify(GDALOpenInfo *);
      87             :     static GDALDataset *Open(GDALOpenInfo *);
      88             : 
      89             :     CPLXMLNode *GetProduct()
      90             :     {
      91             :         return psProduct;
      92             :     }
      93             : };
      94             : 
      95             : /************************************************************************/
      96             : /* ==================================================================== */
      97             : /*                              DIMAPDataset                            */
      98             : /* ==================================================================== */
      99             : /************************************************************************/
     100             : 
     101             : /************************************************************************/
     102             : /*                             DIMAPDataset()                            */
     103             : /************************************************************************/
     104             : 
     105          10 : DIMAPDataset::DIMAPDataset()
     106             : {
     107          10 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     108          10 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     109          10 : }
     110             : 
     111             : /************************************************************************/
     112             : /*                            ~DIMAPDataset()                           */
     113             : /************************************************************************/
     114             : 
     115          20 : DIMAPDataset::~DIMAPDataset()
     116             : 
     117             : {
     118          10 :     DIMAPDataset::FlushCache(true);
     119             : 
     120          10 :     CPLDestroyXMLNode(psProduct);
     121             : 
     122          10 :     if (psProductDim != nullptr && psProductDim != psProduct)
     123           6 :         CPLDestroyXMLNode(psProductDim);
     124          10 :     if (psProductStrip != nullptr)
     125           6 :         CPLDestroyXMLNode(psProductStrip);
     126          10 :     if (nGCPCount > 0)
     127             :     {
     128           2 :         GDALDeinitGCPs(nGCPCount, pasGCPList);
     129           2 :         CPLFree(pasGCPList);
     130             :     }
     131             : 
     132          10 :     CSLDestroy(papszXMLDimapMetadata);
     133             : 
     134          10 :     DIMAPDataset::CloseDependentDatasets();
     135          20 : }
     136             : 
     137             : /************************************************************************/
     138             : /*                        CloseDependentDatasets()                      */
     139             : /************************************************************************/
     140             : 
     141          25 : int DIMAPDataset::CloseDependentDatasets()
     142             : {
     143          25 :     int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
     144             : 
     145          25 :     if (poVRTDS != nullptr)
     146             :     {
     147          10 :         delete poVRTDS;
     148          10 :         poVRTDS = nullptr;
     149          10 :         bHasDroppedRef = TRUE;
     150             :     }
     151             : 
     152          25 :     return bHasDroppedRef;
     153             : }
     154             : 
     155             : /************************************************************************/
     156             : /*                      GetMetadataDomainList()                         */
     157             : /************************************************************************/
     158             : 
     159           0 : char **DIMAPDataset::GetMetadataDomainList()
     160             : {
     161           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
     162           0 :                                    TRUE, "xml:dimap", nullptr);
     163             : }
     164             : 
     165             : /************************************************************************/
     166             : /*                            GetMetadata()                             */
     167             : /*                                                                      */
     168             : /*      We implement special support for fetching the full product      */
     169             : /*      metadata as xml.                                                */
     170             : /************************************************************************/
     171             : 
     172          17 : char **DIMAPDataset::GetMetadata(const char *pszDomain)
     173             : 
     174             : {
     175          17 :     if (pszDomain && EQUAL(pszDomain, "xml:dimap"))
     176             :     {
     177           0 :         if (papszXMLDimapMetadata == nullptr)
     178             :         {
     179           0 :             papszXMLDimapMetadata =
     180           0 :                 reinterpret_cast<char **>(CPLCalloc(sizeof(char *), 2));
     181           0 :             papszXMLDimapMetadata[0] = CPLSerializeXMLTree(psProduct);
     182             :         }
     183           0 :         return papszXMLDimapMetadata;
     184             :     }
     185             : 
     186          17 :     return GDALPamDataset::GetMetadata(pszDomain);
     187             : }
     188             : 
     189             : /************************************************************************/
     190             : /*                          GetSpatialRef()                             */
     191             : /************************************************************************/
     192             : 
     193           1 : const OGRSpatialReference *DIMAPDataset::GetSpatialRef() const
     194             : 
     195             : {
     196           1 :     if (!m_oSRS.IsEmpty())
     197           0 :         return &m_oSRS;
     198             : 
     199           1 :     return GDALPamDataset::GetSpatialRef();
     200             : }
     201             : 
     202             : /************************************************************************/
     203             : /*                          GetGeoTransform()                           */
     204             : /************************************************************************/
     205             : 
     206           0 : CPLErr DIMAPDataset::GetGeoTransform(GDALGeoTransform &gt) const
     207             : 
     208             : {
     209           0 :     if (bHaveGeoTransform)
     210             :     {
     211           0 :         gt = m_gt;
     212           0 :         return CE_None;
     213             :     }
     214             : 
     215           0 :     return GDALPamDataset::GetGeoTransform(gt);
     216             : }
     217             : 
     218             : /************************************************************************/
     219             : /*                            GetFileList()                             */
     220             : /************************************************************************/
     221             : 
     222           0 : char **DIMAPDataset::GetFileList()
     223             : 
     224             : {
     225           0 :     char **papszFileList = GDALPamDataset::GetFileList();
     226           0 :     char **papszImageFiles = poVRTDS->GetFileList();
     227             : 
     228           0 :     papszFileList = CSLInsertStrings(papszFileList, -1, papszImageFiles);
     229             : 
     230           0 :     CSLDestroy(papszImageFiles);
     231             : 
     232           0 :     return papszFileList;
     233             : }
     234             : 
     235             : /************************************************************************/
     236             : /* ==================================================================== */
     237             : /*                            DIMAPRasterBand                           */
     238             : /* ==================================================================== */
     239             : /************************************************************************/
     240             : 
     241             : class DIMAPRasterBand final : public GDALPamRasterBand
     242             : {
     243             :     friend class DIMAPDataset;
     244             : 
     245             :     VRTSourcedRasterBand *poVRTBand;
     246             : 
     247             :     CPL_DISALLOW_COPY_ASSIGN(DIMAPRasterBand)
     248             : 
     249             :   public:
     250             :     DIMAPRasterBand(DIMAPDataset *, int, VRTSourcedRasterBand *);
     251             : 
     252          60 :     ~DIMAPRasterBand() override
     253          30 :     {
     254          60 :     }
     255             : 
     256             :     CPLErr IReadBlock(int, int, void *) override;
     257             :     CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
     258             :                      GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
     259             :                      GDALRasterIOExtraArg *psExtraArg) override;
     260             :     int GetOverviewCount() override;
     261             :     GDALRasterBand *GetOverview(int) override;
     262             :     CPLErr ComputeRasterMinMax(int bApproxOK, double adfMinMax[2]) override;
     263             :     CPLErr ComputeStatistics(int bApproxOK, double *pdfMin, double *pdfMax,
     264             :                              double *pdfMean, double *pdfStdDev,
     265             :                              GDALProgressFunc, void *pProgressData) override;
     266             : 
     267             :     CPLErr GetHistogram(double dfMin, double dfMax, int nBuckets,
     268             :                         GUIntBig *panHistogram, int bIncludeOutOfRange,
     269             :                         int bApproxOK, GDALProgressFunc,
     270             :                         void *pProgressData) override;
     271             : };
     272             : 
     273             : /************************************************************************/
     274             : /*                          DIMAPRasterBand()                           */
     275             : /************************************************************************/
     276             : 
     277          30 : DIMAPRasterBand::DIMAPRasterBand(DIMAPDataset *poDIMAPDS, int nBandIn,
     278          30 :                                  VRTSourcedRasterBand *poVRTBandIn)
     279          30 :     : poVRTBand(poVRTBandIn)
     280             : {
     281          30 :     poDS = poDIMAPDS;
     282          30 :     nBand = nBandIn;
     283          30 :     eDataType = poVRTBandIn->GetRasterDataType();
     284             : 
     285          30 :     poVRTBandIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
     286          30 : }
     287             : 
     288             : /************************************************************************/
     289             : /*                             IReadBlock()                             */
     290             : /************************************************************************/
     291             : 
     292           0 : CPLErr DIMAPRasterBand::IReadBlock(int iBlockX, int iBlockY, void *pBuffer)
     293             : 
     294             : {
     295           0 :     return poVRTBand->ReadBlock(iBlockX, iBlockY, pBuffer);
     296             : }
     297             : 
     298             : /************************************************************************/
     299             : /*                             IRasterIO()                              */
     300             : /************************************************************************/
     301             : 
     302          12 : CPLErr DIMAPRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     303             :                                   int nXSize, int nYSize, void *pData,
     304             :                                   int nBufXSize, int nBufYSize,
     305             :                                   GDALDataType eBufType, GSpacing nPixelSpace,
     306             :                                   GSpacing nLineSpace,
     307             :                                   GDALRasterIOExtraArg *psExtraArg)
     308             : 
     309             : {
     310          12 :     if (GDALPamRasterBand::GetOverviewCount() > 0)
     311             :     {
     312           0 :         return GDALPamRasterBand::IRasterIO(
     313             :             eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
     314           0 :             eBufType, nPixelSpace, nLineSpace, psExtraArg);
     315             :     }
     316             : 
     317             :     // If not exist DIMAP overviews, try to use band source overviews.
     318          12 :     return poVRTBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     319             :                                 nBufXSize, nBufYSize, eBufType, nPixelSpace,
     320          12 :                                 nLineSpace, psExtraArg);
     321             : }
     322             : 
     323             : /************************************************************************/
     324             : /*                             IRasterIO()                              */
     325             : /************************************************************************/
     326             : 
     327           2 : CPLErr DIMAPDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     328             :                                int nXSize, int nYSize, void *pData,
     329             :                                int nBufXSize, int nBufYSize,
     330             :                                GDALDataType eBufType, int nBandCount,
     331             :                                BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
     332             :                                GSpacing nLineSpace, GSpacing nBandSpace,
     333             :                                GDALRasterIOExtraArg *psExtraArg)
     334             : 
     335             : {
     336           2 :     if (cpl::down_cast<DIMAPRasterBand *>(papoBands[0])
     337           2 :             ->GDALPamRasterBand::GetOverviewCount() > 0)
     338             :     {
     339           0 :         return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     340             :                                          pData, nBufXSize, nBufYSize, eBufType,
     341             :                                          nBandCount, panBandMap, nPixelSpace,
     342           0 :                                          nLineSpace, nBandSpace, psExtraArg);
     343             :     }
     344             : 
     345           2 :     return poVRTDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     346             :                               nBufXSize, nBufYSize, eBufType, nBandCount,
     347             :                               panBandMap, nPixelSpace, nLineSpace, nBandSpace,
     348           2 :                               psExtraArg);
     349             : }
     350             : 
     351             : /************************************************************************/
     352             : /*                          GetOverviewCount()                          */
     353             : /************************************************************************/
     354             : 
     355           3 : int DIMAPRasterBand::GetOverviewCount()
     356             : {
     357           3 :     if (GDALPamRasterBand::GetOverviewCount() > 0)
     358             :     {
     359           0 :         return GDALPamRasterBand::GetOverviewCount();
     360             :     }
     361           3 :     return poVRTBand->GetOverviewCount();
     362             : }
     363             : 
     364             : /************************************************************************/
     365             : /*                             GetOverview()                            */
     366             : /************************************************************************/
     367             : 
     368           0 : GDALRasterBand *DIMAPRasterBand::GetOverview(int iOvr)
     369             : {
     370           0 :     if (GDALPamRasterBand::GetOverviewCount() > 0)
     371             :     {
     372           0 :         return GDALPamRasterBand::GetOverview(iOvr);
     373             :     }
     374           0 :     return poVRTBand->GetOverview(iOvr);
     375             : }
     376             : 
     377             : /************************************************************************/
     378             : /*                         ComputeRasterMinMax()                        */
     379             : /************************************************************************/
     380             : 
     381           6 : CPLErr DIMAPRasterBand::ComputeRasterMinMax(int bApproxOK, double adfMinMax[2])
     382             : {
     383           6 :     if (GDALPamRasterBand::GetOverviewCount() > 0)
     384             :     {
     385           0 :         return GDALPamRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
     386             :     }
     387           6 :     return poVRTBand->ComputeRasterMinMax(bApproxOK, adfMinMax);
     388             : }
     389             : 
     390             : /************************************************************************/
     391             : /*                          ComputeStatistics()                         */
     392             : /************************************************************************/
     393             : 
     394           0 : CPLErr DIMAPRasterBand::ComputeStatistics(int bApproxOK, double *pdfMin,
     395             :                                           double *pdfMax, double *pdfMean,
     396             :                                           double *pdfStdDev,
     397             :                                           GDALProgressFunc pfnProgress,
     398             :                                           void *pProgressData)
     399             : {
     400           0 :     if (GDALPamRasterBand::GetOverviewCount() > 0)
     401             :     {
     402           0 :         return GDALPamRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax,
     403             :                                                     pdfMean, pdfStdDev,
     404           0 :                                                     pfnProgress, pProgressData);
     405             :     }
     406           0 :     return poVRTBand->ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean,
     407           0 :                                         pdfStdDev, pfnProgress, pProgressData);
     408             : }
     409             : 
     410             : /************************************************************************/
     411             : /*                            GetHistogram()                            */
     412             : /************************************************************************/
     413             : 
     414           0 : CPLErr DIMAPRasterBand::GetHistogram(double dfMin, double dfMax, int nBuckets,
     415             :                                      GUIntBig *panHistogram,
     416             :                                      int bIncludeOutOfRange, int bApproxOK,
     417             :                                      GDALProgressFunc pfnProgress,
     418             :                                      void *pProgressData)
     419             : {
     420           0 :     if (GDALPamRasterBand::GetOverviewCount() > 0)
     421             :     {
     422           0 :         return GDALPamRasterBand::GetHistogram(
     423             :             dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
     424           0 :             pfnProgress, pProgressData);
     425             :     }
     426           0 :     return poVRTBand->GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
     427             :                                    bIncludeOutOfRange, bApproxOK, pfnProgress,
     428           0 :                                    pProgressData);
     429             : }
     430             : 
     431             : /************************************************************************/
     432             : /*                              Identify()                              */
     433             : /************************************************************************/
     434             : 
     435       61491 : int DIMAPDataset::Identify(GDALOpenInfo *poOpenInfo)
     436             : 
     437             : {
     438       61491 :     if (STARTS_WITH(poOpenInfo->pszFilename, "DIMAP:"))
     439           4 :         return true;
     440             : 
     441       61487 :     if (poOpenInfo->nHeaderBytes >= 100)
     442             :     {
     443        6086 :         if ((strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
     444        6076 :                     "<Dimap_Document") == nullptr) &&
     445        6076 :             (strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
     446             :                     "<PHR_DIMAP_Document") == nullptr))
     447        6076 :             return FALSE;
     448             : 
     449          10 :         return TRUE;
     450             :     }
     451       55401 :     else if (poOpenInfo->bIsDirectory)
     452             :     {
     453             :         // DIMAP file.
     454         619 :         CPLString osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
     455         619 :                                                        "METADATA.DIM", nullptr);
     456             : 
     457             :         VSIStatBufL sStat;
     458         619 :         if (VSIStatL(osMDFilename, &sStat) == 0)
     459             :         {
     460             :             // Make sure this is really a Dimap format.
     461           0 :             GDALOpenInfo oOpenInfo(osMDFilename, GA_ReadOnly, nullptr);
     462           0 :             if (oOpenInfo.nHeaderBytes >= 100)
     463             :             {
     464           0 :                 if (strstr(reinterpret_cast<char *>(oOpenInfo.pabyHeader),
     465             :                            "<Dimap_Document") == nullptr)
     466           0 :                     return FALSE;
     467             : 
     468           0 :                 return TRUE;
     469             :             }
     470             :         }
     471             :         else
     472             :         {
     473             :             // DIMAP 2 file.
     474         619 :             osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
     475         619 :                                                  "VOL_PHR.XML", nullptr);
     476             : 
     477         619 :             if (VSIStatL(osMDFilename, &sStat) == 0)
     478           4 :                 return TRUE;
     479             : 
     480             :             // DIMAP VHR2020 file.
     481         615 :             osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
     482         615 :                                                  "VOL_PNEO.XML", nullptr);
     483             : 
     484         615 :             if (VSIStatL(osMDFilename, &sStat) == 0)
     485           2 :                 return TRUE;
     486             : 
     487         613 :             return FALSE;
     488             :         }
     489             :     }
     490             : 
     491       54782 :     return FALSE;
     492             : }
     493             : 
     494             : /************************************************************************/
     495             : /*                                Open()                                */
     496             : /************************************************************************/
     497             : 
     498          10 : GDALDataset *DIMAPDataset::Open(GDALOpenInfo *poOpenInfo)
     499             : 
     500             : {
     501          10 :     if (!Identify(poOpenInfo))
     502           0 :         return nullptr;
     503             : 
     504             :     /* -------------------------------------------------------------------- */
     505             :     /*      Confirm the requested access is supported.                      */
     506             :     /* -------------------------------------------------------------------- */
     507          10 :     if (poOpenInfo->eAccess == GA_Update)
     508             :     {
     509           0 :         ReportUpdateNotSupportedByDriver("DIMAP");
     510           0 :         return nullptr;
     511             :     }
     512             :     /* -------------------------------------------------------------------- */
     513             :     /*      Get the metadata filename.                                      */
     514             :     /* -------------------------------------------------------------------- */
     515          20 :     CPLString osFilename;
     516          20 :     CPLString osSelectedSubdataset;
     517             : 
     518          10 :     if (STARTS_WITH(poOpenInfo->pszFilename, "DIMAP:"))
     519             :     {
     520           2 :         CPLStringList aosTokens(CSLTokenizeString2(poOpenInfo->pszFilename, ":",
     521           2 :                                                    CSLT_HONOURSTRINGS));
     522           2 :         if (aosTokens.size() != 3)
     523           0 :             return nullptr;
     524             : 
     525           2 :         osFilename = aosTokens[1];
     526           2 :         osSelectedSubdataset = aosTokens[2];
     527             :     }
     528             :     else
     529             :     {
     530           8 :         osFilename = poOpenInfo->pszFilename;
     531             :     }
     532             : 
     533             :     VSIStatBufL sStat;
     534          20 :     std::string osMDFilename(osFilename);
     535          10 :     if (VSIStatL(osFilename.c_str(), &sStat) == 0 && VSI_ISDIR(sStat.st_mode))
     536             :     {
     537             :         osMDFilename =
     538           5 :             CPLFormCIFilenameSafe(osFilename, "METADATA.DIM", nullptr);
     539             : 
     540             :         /* DIMAP2 */
     541           5 :         if (VSIStatL(osMDFilename.c_str(), &sStat) != 0)
     542             :         {
     543             :             osMDFilename =
     544           5 :                 CPLFormCIFilenameSafe(osFilename, "VOL_PHR.XML", nullptr);
     545           5 :             if (VSIStatL(osMDFilename.c_str(), &sStat) != 0)
     546             :             {
     547             :                 // DIMAP VHR2020 file.
     548             :                 osMDFilename =
     549           1 :                     CPLFormCIFilenameSafe(osFilename, "VOL_PNEO.XML", nullptr);
     550             :             }
     551             :         }
     552             :     }
     553             : 
     554             :     /* -------------------------------------------------------------------- */
     555             :     /*      Ingest the xml file.                                            */
     556             :     /* -------------------------------------------------------------------- */
     557          10 :     CPLXMLNode *psProduct = CPLParseXMLFile(osMDFilename.c_str());
     558          10 :     if (psProduct == nullptr)
     559           0 :         return nullptr;
     560             : 
     561          10 :     CPLXMLNode *psDoc = CPLGetXMLNode(psProduct, "=Dimap_Document");
     562          10 :     if (!psDoc)
     563           0 :         psDoc = CPLGetXMLNode(psProduct, "=PHR_DIMAP_Document");
     564             : 
     565             :     // We check the for the tag Metadata_Identification.METADATA_FORMAT.
     566             :     // The metadata will be set to 2.0 for DIMAP2.
     567          10 :     double dfMetadataFormatVersion = CPLAtof(CPLGetXMLValue(
     568          10 :         CPLGetXMLNode(psDoc, "Metadata_Identification.METADATA_FORMAT"),
     569             :         "version", "1"));
     570             : 
     571          10 :     const int nProductVersion = dfMetadataFormatVersion >= 2.0 ? 2 : 1;
     572             : 
     573          20 :     std::string osImageDSFilename;
     574          20 :     std::string osDIMAPFilename;
     575          20 :     std::string osRPCFilename;
     576          10 :     CPLXMLNode *psProductDim = nullptr;
     577          10 :     CPLXMLNode *psProductStrip = nullptr;
     578             : 
     579          20 :     CPLStringList aosSubdatasets;
     580             : 
     581             :     // Check needed information for the DIMAP format.
     582          10 :     if (nProductVersion == 1)
     583             :     {
     584             :         CPLXMLNode *psImageAttributes =
     585           2 :             CPLGetXMLNode(psDoc, "Raster_Dimensions");
     586           2 :         if (psImageAttributes == nullptr)
     587             :         {
     588           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     589             :                      "Failed to find <Raster_Dimensions> in document.");
     590           0 :             CPLDestroyXMLNode(psProduct);
     591           0 :             return nullptr;
     592             :         }
     593             :     }
     594             :     else  // DIMAP2.
     595             :     {
     596             :         // Verify if the opened file is not already a product dimap
     597           8 :         if (CPLGetXMLNode(psDoc, "Raster_Data"))
     598             :         {
     599           2 :             psProductDim = psProduct;
     600           2 :             osDIMAPFilename = osMDFilename;
     601             :         }
     602             :         else
     603             :         {
     604             :             // Verify the presence of the DIMAP product file.
     605             :             CPLXMLNode *psDatasetComponents =
     606           6 :                 CPLGetXMLNode(psDoc, "Dataset_Content.Dataset_Components");
     607             : 
     608           6 :             if (psDatasetComponents == nullptr)
     609             :             {
     610           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
     611             :                          "Failed to find <Dataset_Components> in document.");
     612           0 :                 CPLDestroyXMLNode(psProduct);
     613           0 :                 return nullptr;
     614             :             }
     615             : 
     616           6 :             for (CPLXMLNode *psDatasetComponent = psDatasetComponents->psChild;
     617          17 :                  psDatasetComponent != nullptr;
     618          11 :                  psDatasetComponent = psDatasetComponent->psNext)
     619             :             {
     620             :                 const char *pszComponentType =
     621          11 :                     CPLGetXMLValue(psDatasetComponent, "COMPONENT_TYPE", "");
     622          11 :                 if (strcmp(pszComponentType, "DIMAP") == 0)
     623             :                 {
     624             :                     // DIMAP product found.
     625          10 :                     const char *pszHref = CPLGetXMLValue(
     626             :                         psDatasetComponent, "COMPONENT_PATH.href", "");
     627             :                     const CPLString osComponentTitle(CPLGetXMLValue(
     628          20 :                         psDatasetComponent, "COMPONENT_TITLE", ""));
     629             :                     const CPLString osComponentTitleLaundered(
     630          20 :                         CPLString(osComponentTitle).replaceAll(' ', '_'));
     631             : 
     632          20 :                     if (strlen(pszHref) > 0 && osDIMAPFilename.empty() &&
     633          10 :                         (osSelectedSubdataset.empty() ||
     634           3 :                          osSelectedSubdataset == osComponentTitleLaundered))
     635             :                     {
     636           6 :                         if (poOpenInfo->bIsDirectory)
     637             :                         {
     638           3 :                             osDIMAPFilename = CPLFormCIFilenameSafe(
     639           3 :                                 poOpenInfo->pszFilename, pszHref, nullptr);
     640             :                         }
     641             :                         else
     642             :                         {
     643             :                             CPLString osPath =
     644           3 :                                 CPLGetPathSafe(osMDFilename.c_str());
     645             :                             osDIMAPFilename =
     646           3 :                                 CPLFormFilenameSafe(osPath, pszHref, nullptr);
     647             :                         }
     648             : 
     649             :                         // Data file might be specified there.
     650           6 :                         const char *pszDataFileHref = CPLGetXMLValue(
     651             :                             psDatasetComponent,
     652             :                             "Data_Files.Data_File.DATA_FILE_PATH.href", "");
     653             : 
     654           6 :                         if (strlen(pszDataFileHref) > 0)
     655             :                         {
     656             :                             CPLString osPath =
     657           0 :                                 CPLGetPathSafe(osMDFilename.c_str());
     658           0 :                             osImageDSFilename = CPLFormFilenameSafe(
     659           0 :                                 osPath, pszDataFileHref, nullptr);
     660             :                         }
     661             :                     }
     662             : 
     663             :                     const int iIdx =
     664          10 :                         static_cast<int>(aosSubdatasets.size() / 2 + 1);
     665             :                     aosSubdatasets.SetNameValue(
     666             :                         CPLSPrintf("SUBDATASET_%d_NAME", iIdx),
     667             :                         CPLSPrintf("DIMAP:\"%s\":%s", poOpenInfo->pszFilename,
     668          10 :                                    osComponentTitleLaundered.c_str()));
     669             :                     aosSubdatasets.SetNameValue(
     670             :                         CPLSPrintf("SUBDATASET_%d_DESC", iIdx),
     671          10 :                         CPLSPrintf("Component %s", osComponentTitle.c_str()));
     672             :                 }
     673             :             }
     674             : 
     675           6 :             psProductDim = CPLParseXMLFile(osDIMAPFilename.c_str());
     676           6 :             if (psProductDim == nullptr)
     677             :             {
     678           0 :                 CPLDestroyXMLNode(psProduct);
     679           0 :                 return nullptr;
     680             :             }
     681             :         }
     682             : 
     683             :         // We need the {STRIP|RPC}_<product_id>.XML file for a few metadata.
     684           8 :         CPLXMLNode *psDocDim = CPLGetXMLNode(psProductDim, "=Dimap_Document");
     685           8 :         if (!psDocDim)
     686           0 :             psDocDim = CPLGetXMLNode(psProductDim, "=PHR_DIMAP_Document");
     687             : 
     688             :         CPLXMLNode *psDatasetSources =
     689           8 :             CPLGetXMLNode(psDocDim, "Dataset_Sources");
     690           8 :         if (psDatasetSources != nullptr)
     691             :         {
     692          14 :             CPLString osSTRIPFilename;
     693             : 
     694           7 :             for (CPLXMLNode *psDatasetSource = psDatasetSources->psChild;
     695           8 :                  psDatasetSource != nullptr;
     696           1 :                  psDatasetSource = psDatasetSource->psNext)
     697             :             {
     698             :                 const char *pszSourceType =
     699           7 :                     CPLGetXMLValue(psDatasetSource, "SOURCE_TYPE", "");
     700           7 :                 if (strcmp(pszSourceType, "Strip_Source") == 0)
     701             :                 {
     702           7 :                     const char *pszHref = CPLGetXMLValue(
     703             :                         psDatasetSource, "Component.COMPONENT_PATH.href", "");
     704             : 
     705           7 :                     if (strlen(pszHref) > 0)  // STRIP product found.
     706             :                     {
     707             :                         CPLString osPath =
     708           6 :                             CPLGetPathSafe(osDIMAPFilename.c_str());
     709             :                         osSTRIPFilename =
     710           6 :                             CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
     711           6 :                         if (VSIStatL(osSTRIPFilename, &sStat) == 0)
     712             :                         {
     713           6 :                             psProductStrip = CPLParseXMLFile(osSTRIPFilename);
     714           6 :                             break;
     715             :                         }
     716             :                     }
     717             :                 }
     718             :             }
     719             :         }
     720             : 
     721           8 :         CPLXMLNode *psDatasetRFMComponents = CPLGetXMLNode(
     722             :             psDocDim, "Geoposition.Geoposition_Models.Rational_Function_Model");
     723           8 :         if (psDatasetRFMComponents != nullptr)
     724             :         {
     725           7 :             for (CPLXMLNode *psDatasetRFMComponent =
     726             :                      psDatasetRFMComponents->psChild;
     727           7 :                  psDatasetRFMComponent != nullptr;
     728           0 :                  psDatasetRFMComponent = psDatasetRFMComponent->psNext)
     729             :             {
     730           7 :                 const char *pszComponentTitle = CPLGetXMLValue(
     731             :                     psDatasetRFMComponent, "COMPONENT_TITLE", "");
     732           7 :                 if (strcmp(pszComponentTitle, "RPC Model") == 0)
     733             :                 {
     734           7 :                     const char *pszHref = CPLGetXMLValue(
     735             :                         psDatasetRFMComponent, "COMPONENT_PATH.href", "");
     736             : 
     737           7 :                     if (strlen(pszHref) > 0)  // RPC product found.
     738             :                     {
     739             :                         CPLString osPath =
     740          14 :                             CPLGetPathSafe(osDIMAPFilename.c_str());
     741             :                         osRPCFilename =
     742           7 :                             CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
     743             : 
     744           7 :                         break;
     745             :                     }
     746             :                 }
     747             :             }
     748             :         }
     749             :     }
     750             : 
     751             :     /* -------------------------------------------------------------------- */
     752             :     /*      Create the dataset.                                             */
     753             :     /* -------------------------------------------------------------------- */
     754          10 :     DIMAPDataset *poDS = new DIMAPDataset();
     755             : 
     756          10 :     if (osSelectedSubdataset.empty() && aosSubdatasets.size() > 2)
     757             :     {
     758           2 :         poDS->GDALDataset::SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
     759             :     }
     760          10 :     poDS->psProduct = psProduct;
     761          10 :     poDS->psProductDim = psProductDim;
     762          10 :     poDS->psProductStrip = psProductStrip;
     763          10 :     poDS->osRPCFilename = std::move(osRPCFilename);
     764          10 :     poDS->nProductVersion = nProductVersion;
     765          10 :     poDS->osMDFilename = std::move(osMDFilename);
     766          10 :     poDS->osImageDSFilename = std::move(osImageDSFilename);
     767          10 :     poDS->osDIMAPFilename = std::move(osDIMAPFilename);
     768             : 
     769          10 :     const int res = (nProductVersion == 2) ? poDS->ReadImageInformation2()
     770           2 :                                            : poDS->ReadImageInformation();
     771             : 
     772          10 :     if (res == FALSE)
     773             :     {
     774           0 :         delete poDS;
     775           0 :         return nullptr;
     776             :     }
     777             : 
     778          10 :     return poDS;
     779             : }
     780             : 
     781             : /************************************************************************/
     782             : /*               ReadImageInformation() DIMAP Version 1                 */
     783             : /************************************************************************/
     784             : 
     785           2 : int DIMAPDataset::ReadImageInformation()
     786             : {
     787           2 :     CPLXMLNode *psDoc = CPLGetXMLNode(psProduct, "=Dimap_Document");
     788           2 :     if (!psDoc)
     789           0 :         psDoc = CPLGetXMLNode(psProduct, "=PHR_DIMAP_Document");
     790             : 
     791             :     /* -------------------------------------------------------------------- */
     792             :     /*      Get overall image information.                                  */
     793             :     /* -------------------------------------------------------------------- */
     794             : 
     795             :     // TODO: DIMAP 1 probably handle mosaics? Like DIMAP 2?
     796             : 
     797             :     /* -------------------------------------------------------------------- */
     798             :     /*      Get the name of the underlying file.                            */
     799             :     /* -------------------------------------------------------------------- */
     800             : 
     801             :     const char *pszHref =
     802           2 :         CPLGetXMLValue(psDoc, "Data_Access.Data_File.DATA_FILE_PATH.href", "");
     803           4 :     CPLString osPath = CPLGetPathSafe(osMDFilename);
     804           4 :     CPLString osImageFilename = CPLFormFilenameSafe(osPath, pszHref, nullptr);
     805             : 
     806             :     /* -------------------------------------------------------------------- */
     807             :     /*      Try and open the file.                                          */
     808             :     /* -------------------------------------------------------------------- */
     809             : 
     810             :     auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     811           4 :         osImageFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
     812           2 :     if (poImageDS == nullptr)
     813             :     {
     814           0 :         return FALSE;
     815             :     }
     816           2 :     nRasterXSize = poImageDS->GetRasterXSize();
     817           2 :     nRasterYSize = poImageDS->GetRasterYSize();
     818             : 
     819             :     /* -------------------------------------------------------------------- */
     820             :     /*      Create and initialize the corresponding VRT dataset used to     */
     821             :     /*      manage the tiled data access.                                   */
     822             :     /* -------------------------------------------------------------------- */
     823           2 :     poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
     824             : 
     825             :     // Don't try to write a VRT file.
     826           2 :     poVRTDS->SetWritable(FALSE);
     827             : 
     828           4 :     for (int iBand = 0; iBand < poImageDS->GetRasterCount(); iBand++)
     829             :     {
     830           2 :         poVRTDS->AddBand(
     831           2 :             poImageDS->GetRasterBand(iBand + 1)->GetRasterDataType(), nullptr);
     832             : 
     833             :         VRTSourcedRasterBand *poVRTBand =
     834             :             reinterpret_cast<VRTSourcedRasterBand *>(
     835           2 :                 poVRTDS->GetRasterBand(iBand + 1));
     836             : 
     837           2 :         poVRTBand->AddSimpleSource(osImageFilename, iBand + 1, 0, 0,
     838           2 :                                    nRasterXSize, nRasterYSize, 0, 0,
     839           2 :                                    nRasterXSize, nRasterYSize);
     840             :     }
     841             : 
     842             :     /* -------------------------------------------------------------------- */
     843             :     /*      Create band information objects.                                */
     844             :     /* -------------------------------------------------------------------- */
     845           4 :     for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
     846             :     {
     847           2 :         SetBand(iBand, new DIMAPRasterBand(this, iBand,
     848             :                                            static_cast<VRTSourcedRasterBand *>(
     849           2 :                                                poVRTDS->GetRasterBand(iBand))));
     850             :     }
     851             : 
     852             :     /* -------------------------------------------------------------------- */
     853             :     /*      Try to collect simple insertion point.                          */
     854             :     /* -------------------------------------------------------------------- */
     855             :     CPLXMLNode *psGeoLoc =
     856           2 :         CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
     857             : 
     858           2 :     if (psGeoLoc != nullptr)
     859             :     {
     860           0 :         bHaveGeoTransform = TRUE;
     861           0 :         m_gt[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
     862           0 :         m_gt[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
     863           0 :         m_gt[2] = 0.0;
     864           0 :         m_gt[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
     865           0 :         m_gt[4] = 0.0;
     866           0 :         m_gt[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
     867             :     }
     868             :     else
     869             :     {
     870             :         // Try to get geotransform from underlying raster.
     871           2 :         if (poImageDS->GetGeoTransform(m_gt) == CE_None)
     872           0 :             bHaveGeoTransform = TRUE;
     873             :     }
     874             : 
     875             :     /* -------------------------------------------------------------------- */
     876             :     /*      Collect GCPs.                                                   */
     877             :     /* -------------------------------------------------------------------- */
     878           2 :     psGeoLoc = CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Points");
     879             : 
     880           2 :     if (psGeoLoc != nullptr)
     881             :     {
     882             :         // Count gcps.
     883           2 :         nGCPCount = 0;
     884          10 :         for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
     885           8 :              psNode = psNode->psNext)
     886             :         {
     887           8 :             if (EQUAL(psNode->pszValue, "Tie_Point"))
     888           8 :                 nGCPCount++;
     889             :         }
     890             : 
     891           2 :         pasGCPList =
     892           2 :             static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nGCPCount));
     893             : 
     894           2 :         nGCPCount = 0;
     895             : 
     896          10 :         for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
     897           8 :              psNode = psNode->psNext)
     898             :         {
     899           8 :             GDAL_GCP *psGCP = pasGCPList + nGCPCount;
     900             : 
     901           8 :             if (!EQUAL(psNode->pszValue, "Tie_Point"))
     902           0 :                 continue;
     903             : 
     904           8 :             nGCPCount++;
     905             : 
     906           8 :             char szID[32] = {};
     907           8 :             snprintf(szID, sizeof(szID), "%d", nGCPCount);
     908           8 :             psGCP->pszId = CPLStrdup(szID);
     909           8 :             psGCP->pszInfo = CPLStrdup("");
     910           8 :             psGCP->dfGCPPixel =
     911           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_X", "0")) - 0.5;
     912           8 :             psGCP->dfGCPLine =
     913           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_Y", "0")) - 0.5;
     914           8 :             psGCP->dfGCPX =
     915           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_X", ""));
     916           8 :             psGCP->dfGCPY =
     917           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Y", ""));
     918           8 :             psGCP->dfGCPZ =
     919           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Z", ""));
     920             :         }
     921             :     }
     922             : 
     923             :     /* -------------------------------------------------------------------- */
     924             :     /*      Collect the CRS.  For now we look only for EPSG codes.          */
     925             :     /* -------------------------------------------------------------------- */
     926           2 :     const char *pszSRS = CPLGetXMLValue(
     927             :         psDoc, "Coordinate_Reference_System.Horizontal_CS.HORIZONTAL_CS_CODE",
     928             :         nullptr);
     929             : 
     930           2 :     if (pszSRS != nullptr)
     931             :     {
     932           2 :         OGRSpatialReference &oSRS = nGCPCount > 0 ? m_oGCPSRS : m_oSRS;
     933           2 :         oSRS.SetFromUserInput(
     934             :             pszSRS, OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
     935             :     }
     936             :     else
     937             :     {
     938             :         // Check underlying raster for SRS. We have cases where
     939             :         // HORIZONTAL_CS_CODE is empty and the underlying raster
     940             :         // is georeferenced (rprinceley).
     941           0 :         const auto poSRS = poImageDS->GetSpatialRef();
     942           0 :         if (poSRS)
     943             :         {
     944           0 :             m_oSRS = *poSRS;
     945             :         }
     946             :     }
     947             : 
     948             :     /* -------------------------------------------------------------------- */
     949             :     /*      Translate other metadata of interest.                           */
     950             :     /* -------------------------------------------------------------------- */
     951             :     static const char *const apszMetadataTranslation[] = {
     952             :         "Production",
     953             :         "",
     954             :         "Production.Facility",
     955             :         "FACILITY_",
     956             :         "Dataset_Sources.Source_Information.Scene_Source",
     957             :         "",
     958             :         "Data_Processing",
     959             :         "",
     960             :         "Image_Interpretation.Spectral_Band_Info",
     961             :         "SPECTRAL_",
     962             :         nullptr,
     963             :         nullptr};
     964             : 
     965           2 :     SetMetadataFromXML(psProduct, apszMetadataTranslation);
     966             : 
     967             :     /* -------------------------------------------------------------------- */
     968             :     /*      Set Band metadata from the <Spectral_Band_Info> content         */
     969             :     /* -------------------------------------------------------------------- */
     970             : 
     971             :     CPLXMLNode *psImageInterpretationNode =
     972           2 :         CPLGetXMLNode(psDoc, "Image_Interpretation");
     973           2 :     if (psImageInterpretationNode != nullptr)
     974             :     {
     975           2 :         CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
     976           4 :         while (psSpectralBandInfoNode != nullptr)
     977             :         {
     978           2 :             if (psSpectralBandInfoNode->eType == CXT_Element &&
     979           2 :                 EQUAL(psSpectralBandInfoNode->pszValue, "Spectral_Band_Info"))
     980             :             {
     981           2 :                 CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
     982           2 :                 int nBandIndex = 0;
     983          14 :                 while (psTag != nullptr)
     984             :                 {
     985          12 :                     if (psTag->eType == CXT_Element &&
     986          12 :                         psTag->psChild != nullptr &&
     987          12 :                         psTag->psChild->eType == CXT_Text &&
     988          12 :                         psTag->pszValue != nullptr)
     989             :                     {
     990          12 :                         if (EQUAL(psTag->pszValue, "BAND_INDEX"))
     991             :                         {
     992           2 :                             nBandIndex = atoi(psTag->psChild->pszValue);
     993           4 :                             if (nBandIndex <= 0 ||
     994           2 :                                 nBandIndex > poImageDS->GetRasterCount())
     995             :                             {
     996           0 :                                 CPLError(CE_Warning, CPLE_AppDefined,
     997             :                                          "Bad BAND_INDEX value : %s",
     998           0 :                                          psTag->psChild->pszValue);
     999           0 :                                 nBandIndex = 0;
    1000             :                             }
    1001             :                         }
    1002          10 :                         else if (nBandIndex >= 1)
    1003             :                         {
    1004          10 :                             GetRasterBand(nBandIndex)
    1005          10 :                                 ->SetMetadataItem(psTag->pszValue,
    1006          10 :                                                   psTag->psChild->pszValue);
    1007             :                         }
    1008             :                     }
    1009          12 :                     psTag = psTag->psNext;
    1010             :                 }
    1011             :             }
    1012           2 :             psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
    1013             :         }
    1014             :     }
    1015             : 
    1016             :     /* -------------------------------------------------------------------- */
    1017             :     /*      Initialize any PAM information.                                 */
    1018             :     /* -------------------------------------------------------------------- */
    1019           2 :     SetDescription(osMDFilename);
    1020           2 :     TryLoadXML();
    1021             : 
    1022             :     /* -------------------------------------------------------------------- */
    1023             :     /*      Check for overviews.                                            */
    1024             :     /* -------------------------------------------------------------------- */
    1025           2 :     oOvManager.Initialize(this, osMDFilename);
    1026             : 
    1027             :     // CID 163546 - poTileDS dereferenced above.
    1028             :     // coverity[leaked_storage]
    1029           2 :     return TRUE;
    1030             : }
    1031             : 
    1032             : /************************************************************************/
    1033             : /*               ReadImageInformation() DIMAP Version 2                 */
    1034             : /************************************************************************/
    1035             : 
    1036           8 : int DIMAPDataset::ReadImageInformation2()
    1037             : {
    1038           8 :     CPLXMLNode *psDoc = CPLGetXMLNode(psProductDim, "=Dimap_Document");
    1039           8 :     if (!psDoc)
    1040           0 :         psDoc = CPLGetXMLNode(psProductDim, "=PHR_DIMAP_Document");
    1041             : 
    1042             :     CPLXMLNode *psImageAttributes =
    1043           8 :         CPLGetXMLNode(psDoc, "Raster_Data.Raster_Dimensions");
    1044           8 :     if (psImageAttributes == nullptr)
    1045             :     {
    1046           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1047             :                  "Failed to find <Raster_Dimensions> in document.");
    1048           0 :         return FALSE;
    1049             :     }
    1050             : 
    1051             :     /* -------------------------------------------------------------------- */
    1052             :     /*      Get overall image information.                                  */
    1053             :     /* -------------------------------------------------------------------- */
    1054             : 
    1055             :     /*
    1056             :         <Raster_Dimensions>
    1057             :            <NROWS>30</NROWS>
    1058             :            <NCOLS>20</NCOLS>
    1059             :            <NBANDS>4</NBANDS>
    1060             :            <Tile_Set>
    1061             :               <NTILES>2</NTILES>
    1062             :               <Regular_Tiling>
    1063             :                  <NTILES_SIZE nrows="20" ncols="20"/>
    1064             :                  <NTILES_COUNT ntiles_R="2" ntiles_C="1"/>
    1065             :                  <OVERLAP_ROW>0</OVERLAP_ROW>
    1066             :                  <OVERLAP_COL>0</OVERLAP_COL>
    1067             :               </Regular_Tiling>
    1068             :            </Tile_Set>
    1069             :         </Raster_Dimensions>
    1070             :       */
    1071             : 
    1072             :     const int l_nBands =
    1073           8 :         atoi(CPLGetXMLValue(psImageAttributes, "NBANDS", "-1"));
    1074           8 :     nRasterXSize = atoi(CPLGetXMLValue(psImageAttributes, "NCOLS", "-1"));
    1075           8 :     nRasterYSize = atoi(CPLGetXMLValue(psImageAttributes, "NROWS", "-1"));
    1076           8 :     if (nRasterXSize <= 0 || nRasterYSize <= 0)
    1077             :     {
    1078           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1079             :                  "Invalid NCOLS(=%d)/NROWS(=%d) value", nRasterXSize,
    1080             :                  nRasterYSize);
    1081           0 :         return FALSE;
    1082             :     }
    1083           8 :     int nTileWidth = atoi(CPLGetXMLValue(
    1084             :         psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.ncols", "-1"));
    1085           8 :     int nTileHeight = atoi(CPLGetXMLValue(
    1086             :         psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.nrows", "-1"));
    1087           8 :     int nOverlapRow = atoi(CPLGetXMLValue(
    1088             :         psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_ROW", "-1"));
    1089           8 :     int nOverlapCol = atoi(CPLGetXMLValue(
    1090             :         psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_COL", "-1"));
    1091             :     const int nBits =
    1092           8 :         atoi(CPLGetXMLValue(psDoc, "Raster_Data.Raster_Encoding.NBITS", "-1"));
    1093             :     CPLString osDataFormat =
    1094          16 :         CPLGetXMLValue(psDoc, "Raster_Data.Data_Access.DATA_FILE_FORMAT", "");
    1095           8 :     if (osDataFormat == "image/jp2")
    1096             :     {
    1097           1 :         SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
    1098             :     }
    1099             : 
    1100             :     // For VHR2020: SPECTRAL_PROCESSING = PAN, MS, MS-FS, PMS, PMS-N, PMS-X,
    1101             :     // PMS-FS
    1102             :     const CPLString osSpectralProcessing = CPLGetXMLValue(
    1103             :         psDoc, "Processing_Information.Product_Settings.SPECTRAL_PROCESSING",
    1104          16 :         "");
    1105             :     const bool bTwoDataFilesPerTile =
    1106           8 :         osSpectralProcessing == "MS-FS" || osSpectralProcessing == "PMS-FS";
    1107             : 
    1108             :     /* -------------------------------------------------------------------- */
    1109             :     /*      Get the name of the underlying file.                            */
    1110             :     /* -------------------------------------------------------------------- */
    1111             : 
    1112             :     CPLXMLNode *psDataFiles =
    1113           8 :         CPLGetXMLNode(psDoc, "Raster_Data.Data_Access.Data_Files");
    1114             : 
    1115             :     /*  <Data_Files>
    1116             :             <Data_File tile_R="1" tile_C="1">
    1117             :                <DATA_FILE_PATH href="IMG_foo_R1C1.TIF"/>
    1118             :             </Data_File>
    1119             :             <Data_File tile_R="2" tile_C="1">
    1120             :                <DATA_FILE_PATH href="IMG_foo_R2C1.TIF"/>
    1121             :             </Data_File>
    1122             :          </Data_Files>
    1123             :     */
    1124             : 
    1125             :     struct TileIdx
    1126             :     {
    1127             :         int nRow;
    1128             :         int nCol;
    1129             :         int nPart;  // typically 0.  But for VHR2020 0=RGB, 1=NED
    1130             : 
    1131          15 :         TileIdx(int nRowIn, int nColIn, int nPartIn = 0)
    1132          15 :             : nRow(nRowIn), nCol(nColIn), nPart(nPartIn)
    1133             :         {
    1134          15 :         }
    1135             : 
    1136          21 :         bool operator<(const TileIdx &other) const
    1137             :         {
    1138          21 :             if (nRow < other.nRow)
    1139          12 :                 return true;
    1140           9 :             if (nRow > other.nRow)
    1141           6 :                 return false;
    1142           3 :             if (nCol < other.nCol)
    1143           0 :                 return true;
    1144           3 :             if (nCol > other.nCol)
    1145           0 :                 return false;
    1146           3 :             return nPart < other.nPart;
    1147             :         }
    1148             :     };
    1149             : 
    1150          16 :     std::map<TileIdx, CPLString> oMapTileIdxToName;
    1151           8 :     int nImageDSRow = 1, nImageDSCol = 1;
    1152           8 :     if (psDataFiles)
    1153             :     {
    1154           8 :         const CPLString osPath = CPLGetPathSafe(osDIMAPFilename);
    1155          17 :         for (int nPart = 0; psDataFiles != nullptr;
    1156           9 :              psDataFiles = psDataFiles->psNext, nPart++)
    1157             :         {
    1158          27 :             for (CPLXMLNode *psDataFile = psDataFiles->psChild; psDataFile;
    1159          18 :                  psDataFile = psDataFile->psNext)
    1160             :             {
    1161          18 :                 if (psDataFile->eType == CXT_Element &&
    1162          18 :                     strcmp(psDataFile->pszValue, "Data_File") == 0)
    1163             :                 {
    1164             :                     const char *pszR =
    1165          15 :                         CPLGetXMLValue(psDataFile, "tile_R", nullptr);
    1166             :                     const char *pszC =
    1167          15 :                         CPLGetXMLValue(psDataFile, "tile_C", nullptr);
    1168          15 :                     const char *pszHref = CPLGetXMLValue(
    1169             :                         psDataFile, "DATA_FILE_PATH.href", nullptr);
    1170          15 :                     if (pszR && pszC && pszHref)
    1171             :                     {
    1172          15 :                         int nRow = atoi(pszR);
    1173          15 :                         int nCol = atoi(pszC);
    1174          15 :                         if (nRow < 0 || nCol < 0)
    1175             :                         {
    1176           0 :                             return false;
    1177             :                         }
    1178             :                         std::string osTileFilename(
    1179          15 :                             CPLFormCIFilenameSafe(osPath, pszHref, nullptr));
    1180          22 :                         if ((nRow == 1 && nCol == 1 && nPart == 0) ||
    1181           7 :                             osImageDSFilename.empty())
    1182             :                         {
    1183           8 :                             osImageDSFilename = osTileFilename;
    1184           8 :                             nImageDSRow = nRow;
    1185           8 :                             nImageDSCol = nCol;
    1186             :                         }
    1187          15 :                         oMapTileIdxToName[TileIdx(nRow, nCol, nPart)] =
    1188          30 :                             std::move(osTileFilename);
    1189             :                     }
    1190             :                 }
    1191             :             }
    1192             :         }
    1193           8 :         if (nOverlapRow > 0 || nOverlapCol > 0)
    1194             :         {
    1195           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1196             :                      "Overlap between tiles is not handled currently. "
    1197             :                      "Only taking into account top left tile");
    1198           0 :             oMapTileIdxToName.clear();
    1199           0 :             oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
    1200             :         }
    1201             :     }
    1202             :     else
    1203             :     {
    1204           0 :         oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
    1205             :     }
    1206             : 
    1207           8 :     if (osImageDSFilename.empty())
    1208             :     {
    1209           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1210             :                  "Failed to find <DATA_FILE_PATH> in document.");
    1211           0 :         return FALSE;
    1212             :     }
    1213             : 
    1214             :     /* -------------------------------------------------------------------- */
    1215             :     /*      Try and open the file.                                          */
    1216             :     /* -------------------------------------------------------------------- */
    1217             :     auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    1218          16 :         osImageDSFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
    1219           8 :     if (poImageDS == nullptr)
    1220             :     {
    1221           0 :         return FALSE;
    1222             :     }
    1223           8 :     if (bTwoDataFilesPerTile)
    1224             :     {
    1225           1 :         if (l_nBands != 6 || poImageDS->GetRasterCount() != 3)
    1226             :         {
    1227           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
    1228           0 :             return FALSE;
    1229             :         }
    1230             :     }
    1231           7 :     else if (poImageDS->GetRasterCount() != l_nBands)
    1232             :     {
    1233           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
    1234           0 :         return FALSE;
    1235             :     }
    1236             : 
    1237           8 :     if (nTileWidth > 0 && nTileHeight > 0)
    1238             :     {
    1239             :         // ok
    1240             :     }
    1241           3 :     else if (oMapTileIdxToName.size() == 1 ||
    1242           1 :              (bTwoDataFilesPerTile && oMapTileIdxToName.size() == 2))
    1243             :     {
    1244           2 :         nTileWidth = poImageDS->GetRasterXSize();
    1245           2 :         nTileHeight = poImageDS->GetRasterYSize();
    1246             :     }
    1247             : 
    1248           8 :     if (!(nTileWidth > 0 && nTileHeight > 0))
    1249             :     {
    1250           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot get tile dimension");
    1251           0 :         return FALSE;
    1252             :     }
    1253             : 
    1254             :     /* -------------------------------------------------------------------- */
    1255             :     /*      Create and initialize the corresponding VRT dataset used to     */
    1256             :     /*      manage the tiled data access.                                   */
    1257             :     /* -------------------------------------------------------------------- */
    1258           8 :     poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
    1259             : 
    1260             :     // Don't try to write a VRT file.
    1261           8 :     poVRTDS->SetWritable(FALSE);
    1262             : 
    1263          36 :     for (int iBand = 0; iBand < l_nBands; iBand++)
    1264             :     {
    1265          56 :         auto poSrcBandFirstImage = poImageDS->GetRasterBand(
    1266          28 :             iBand < poImageDS->GetRasterCount() ? iBand + 1 : 1);
    1267          56 :         CPLStringList aosAddBandOptions;
    1268             :         int nSrcBlockXSize, nSrcBlockYSize;
    1269          28 :         poSrcBandFirstImage->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
    1270          55 :         if (oMapTileIdxToName.size() == 1 ||
    1271          27 :             ((nTileWidth % nSrcBlockXSize) == 0 &&
    1272          15 :              (nTileHeight % nSrcBlockYSize) == 0))
    1273             :         {
    1274             :             aosAddBandOptions.SetNameValue("BLOCKXSIZE",
    1275          16 :                                            CPLSPrintf("%d", nSrcBlockXSize));
    1276             :             aosAddBandOptions.SetNameValue("BLOCKYSIZE",
    1277          16 :                                            CPLSPrintf("%d", nSrcBlockYSize));
    1278             :         }
    1279          28 :         poVRTDS->AddBand(poSrcBandFirstImage->GetRasterDataType(),
    1280          28 :                          aosAddBandOptions.List());
    1281             : 
    1282             :         VRTSourcedRasterBand *poVRTBand =
    1283             :             reinterpret_cast<VRTSourcedRasterBand *>(
    1284          28 :                 poVRTDS->GetRasterBand(iBand + 1));
    1285          28 :         if (nBits > 0 && nBits != 8 && nBits != 16)
    1286             :         {
    1287          21 :             poVRTBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
    1288          21 :                                        "IMAGE_STRUCTURE");
    1289             :         }
    1290             : 
    1291          83 :         for (const auto &oTileIdxNameTuple : oMapTileIdxToName)
    1292             :         {
    1293          55 :             const int nRow = oTileIdxNameTuple.first.nRow;
    1294          55 :             const int nCol = oTileIdxNameTuple.first.nCol;
    1295          55 :             if (static_cast<int64_t>(nRow - 1) * nTileHeight < nRasterYSize &&
    1296          55 :                 static_cast<int64_t>(nCol - 1) * nTileWidth < nRasterXSize)
    1297             :             {
    1298             :                 int nSrcBand;
    1299          55 :                 if (bTwoDataFilesPerTile)
    1300             :                 {
    1301          12 :                     const int nPart = oTileIdxNameTuple.first.nPart;
    1302          12 :                     if (nPart == 0 && iBand < 3)
    1303             :                     {
    1304           3 :                         nSrcBand = iBand + 1;
    1305             :                     }
    1306           9 :                     else if (nPart == 1 && iBand >= 3)
    1307             :                     {
    1308           3 :                         nSrcBand = iBand + 1 - 3;
    1309             :                     }
    1310             :                     else
    1311             :                     {
    1312           6 :                         continue;
    1313             :                     }
    1314             :                 }
    1315             :                 else
    1316             :                 {
    1317          43 :                     nSrcBand = iBand + 1;
    1318             :                 }
    1319             : 
    1320          49 :                 int nHeight = nTileHeight;
    1321          49 :                 if (static_cast<int64_t>(nRow) * nTileHeight > nRasterYSize)
    1322             :                 {
    1323          21 :                     nHeight = nRasterYSize - (nRow - 1) * nTileHeight;
    1324             :                 }
    1325          49 :                 int nWidth = nTileWidth;
    1326          49 :                 if (static_cast<int64_t>(nCol) * nTileWidth > nRasterXSize)
    1327             :                 {
    1328           0 :                     nWidth = nRasterXSize - (nCol - 1) * nTileWidth;
    1329             :                 }
    1330             : 
    1331          49 :                 poVRTBand->AddSimpleSource(
    1332             :                     oTileIdxNameTuple.second, nSrcBand, 0, 0, nWidth, nHeight,
    1333          49 :                     (nCol - 1) * nTileWidth, (nRow - 1) * nTileHeight, nWidth,
    1334             :                     nHeight);
    1335             :             }
    1336             :         }
    1337             :     }
    1338             : 
    1339             :     /* -------------------------------------------------------------------- */
    1340             :     /*      Expose Overviews if available                                   */
    1341             :     /* -------------------------------------------------------------------- */
    1342           8 :     auto poSrcBandFirstImage = poImageDS->GetRasterBand(1);
    1343             :     const int nSrcOverviews =
    1344           8 :         std::min(30, poSrcBandFirstImage->GetOverviewCount());
    1345           8 :     if (nSrcOverviews > 0)
    1346             :     {
    1347           6 :         CPLConfigOptionSetter oSetter("VRT_VIRTUAL_OVERVIEWS", "YES", false);
    1348           6 :         std::unique_ptr<int[]> ovrLevels(new int[nSrcOverviews]);
    1349           3 :         int iLvl = 1;
    1350           6 :         for (int i = 0; i < nSrcOverviews; i++)
    1351             :         {
    1352           3 :             iLvl *= 2;
    1353           3 :             ovrLevels[i] = iLvl;
    1354             :         }
    1355           3 :         poVRTDS->IBuildOverviews("average", nSrcOverviews, ovrLevels.get(), 0,
    1356           3 :                                  nullptr, nullptr, nullptr, nullptr);
    1357             :     }
    1358             : 
    1359             : #ifdef DEBUG_VERBOSE
    1360             :     CPLDebug("DIMAP", "VRT XML: %s", poVRTDS->GetMetadata("xml:VRT")[0]);
    1361             : #endif
    1362             : 
    1363             :     /* -------------------------------------------------------------------- */
    1364             :     /*      Create band information objects.                                */
    1365             :     /* -------------------------------------------------------------------- */
    1366          36 :     for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
    1367             :     {
    1368             :         GDALRasterBand *poBand = new DIMAPRasterBand(
    1369             :             this, iBand,
    1370          28 :             static_cast<VRTSourcedRasterBand *>(poVRTDS->GetRasterBand(iBand)));
    1371          28 :         if (nBits > 0 && nBits != 8 && nBits != 16)
    1372             :         {
    1373          21 :             poBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
    1374          21 :                                     "IMAGE_STRUCTURE");
    1375             :         }
    1376          28 :         if (bTwoDataFilesPerTile)
    1377             :         {
    1378           6 :             switch (iBand)
    1379             :             {
    1380           1 :                 case 1:
    1381             :                 {
    1382           1 :                     poBand->SetColorInterpretation(GCI_RedBand);
    1383           1 :                     poBand->SetDescription("Red");
    1384           1 :                     break;
    1385             :                 }
    1386           1 :                 case 2:
    1387             :                 {
    1388           1 :                     poBand->SetColorInterpretation(GCI_GreenBand);
    1389           1 :                     poBand->SetDescription("Green");
    1390           1 :                     break;
    1391             :                 }
    1392           1 :                 case 3:
    1393             :                 {
    1394           1 :                     poBand->SetColorInterpretation(GCI_BlueBand);
    1395           1 :                     poBand->SetDescription("Blue");
    1396           1 :                     break;
    1397             :                 }
    1398           1 :                 case 4:
    1399             :                 {
    1400           1 :                     poBand->SetColorInterpretation(GCI_NIRBand);
    1401           1 :                     poBand->SetDescription("NIR");
    1402           1 :                     break;
    1403             :                 }
    1404           1 :                 case 5:
    1405             :                 {
    1406           1 :                     poBand->SetColorInterpretation(GCI_RedEdgeBand);
    1407           1 :                     poBand->SetDescription("Red Edge");
    1408           1 :                     break;
    1409             :                 }
    1410           1 :                 case 6:
    1411             :                 {
    1412           1 :                     poBand->SetColorInterpretation(GCI_CoastalBand);
    1413           1 :                     poBand->SetDescription("Deep Blue");
    1414           1 :                     break;
    1415             :                 }
    1416           0 :                 default:
    1417           0 :                     break;
    1418             :             }
    1419             :         }
    1420          22 :         else if (l_nBands == 1 && osSpectralProcessing == "PAN")
    1421             :         {
    1422           0 :             poBand->SetColorInterpretation(GCI_PanBand);
    1423           0 :             poBand->SetDescription("Panchromatic");
    1424             :         }
    1425          28 :         SetBand(iBand, poBand);
    1426             :     }
    1427             : 
    1428             :     /* -------------------------------------------------------------------- */
    1429             :     /*      Try to collect simple insertion point.                          */
    1430             :     /* -------------------------------------------------------------------- */
    1431             :     CPLXMLNode *psGeoLoc =
    1432           8 :         CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
    1433             : 
    1434           8 :     if (psGeoLoc != nullptr)
    1435             :     {
    1436           1 :         bHaveGeoTransform = TRUE;
    1437           1 :         m_gt[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
    1438           1 :         m_gt[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
    1439           1 :         m_gt[2] = 0.0;
    1440           1 :         m_gt[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
    1441           1 :         m_gt[4] = 0.0;
    1442           1 :         m_gt[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
    1443             :     }
    1444             :     else
    1445             :     {
    1446             :         // Try to get geotransform from underlying raster,
    1447             :         // but make sure it is a real geotransform.
    1448          13 :         if (poImageDS->GetGeoTransform(m_gt) == CE_None &&
    1449           6 :             !(m_gt[0] <= 1.5 && fabs(m_gt[3]) <= 1.5))
    1450             :         {
    1451           6 :             bHaveGeoTransform = TRUE;
    1452             :             // fix up the origin if we did not get the geotransform from the
    1453             :             // top-left tile
    1454           6 :             m_gt[0] -= (nImageDSCol - 1) * m_gt[1] * nTileWidth +
    1455           6 :                        (nImageDSRow - 1) * m_gt[2] * nTileHeight;
    1456          12 :             m_gt[3] -= (nImageDSCol - 1) * m_gt[4] * nTileWidth +
    1457           6 :                        (nImageDSRow - 1) * m_gt[5] * nTileHeight;
    1458             :         }
    1459             :     }
    1460             : 
    1461             :     /* -------------------------------------------------------------------- */
    1462             :     /*      Collect the CRS.  For now we look only for EPSG codes.          */
    1463             :     /* -------------------------------------------------------------------- */
    1464           8 :     const char *pszSRS = CPLGetXMLValue(
    1465             :         psDoc, "Coordinate_Reference_System.Projected_CRS.PROJECTED_CRS_CODE",
    1466             :         nullptr);
    1467           8 :     if (pszSRS == nullptr)
    1468           8 :         pszSRS = CPLGetXMLValue(
    1469             :             psDoc, "Coordinate_Reference_System.Geodetic_CRS.GEODETIC_CRS_CODE",
    1470             :             nullptr);
    1471             : 
    1472           8 :     if (pszSRS != nullptr)
    1473             :     {
    1474           8 :         if (bHaveGeoTransform)
    1475             :         {
    1476           7 :             OGRSpatialReference &oSRS = m_oSRS;
    1477           7 :             oSRS.SetFromUserInput(
    1478             :                 pszSRS,
    1479             :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
    1480             :         }
    1481             :     }
    1482             :     else
    1483             :     {
    1484             :         // Check underlying raster for SRS. We have cases where
    1485             :         // HORIZONTAL_CS_CODE is empty and the underlying raster
    1486             :         // is georeferenced (rprinceley).
    1487           0 :         const auto poSRS = poImageDS->GetSpatialRef();
    1488           0 :         GDALGeoTransform tmpGT;
    1489           0 :         if (poSRS && poImageDS->GetGeoTransform(tmpGT) == CE_None)
    1490             :         {
    1491           0 :             m_oSRS = *poSRS;
    1492             :         }
    1493             :     }
    1494             : 
    1495             :     /* -------------------------------------------------------------------- */
    1496             :     /*      Translate other metadata of interest: DIM_<product_name>.XML    */
    1497             :     /* -------------------------------------------------------------------- */
    1498             : 
    1499             :     static const char *const apszMetadataTranslationDim[] = {
    1500             :         "Product_Information.Delivery_Identification",
    1501             :         "DATASET_",
    1502             :         "Product_Information.Producer_Information",
    1503             :         "DATASET_",
    1504             :         "Dataset_Sources.Source_Identification.Strip_Source",
    1505             :         "",
    1506             :         "Processing_Information.Production_Facility",
    1507             :         "FACILITY_",
    1508             :         "Processing_Information.Product_Settings",
    1509             :         "",
    1510             :         "Processing_Information.Product_Settings.Geometric_Settings",
    1511             :         "GEOMETRIC_",
    1512             :         "Processing_Information.Product_Settings.Radiometric_Settings",
    1513             :         "RADIOMETRIC_",
    1514             :         "Quality_Assessment.Imaging_Quality_Measurement",
    1515             :         "CLOUDCOVER_",
    1516             :         nullptr,
    1517             :         nullptr};
    1518             : 
    1519           8 :     SetMetadataFromXML(psProductDim, apszMetadataTranslationDim);
    1520             : 
    1521             :     /* -------------------------------------------------------------------- */
    1522             :     /*      Translate other metadata of interest: STRIP_<product_name>.XML    */
    1523             :     /* -------------------------------------------------------------------- */
    1524             : 
    1525             :     static const char *const apszMetadataTranslationStrip[] = {
    1526             :         "Catalog.Full_Strip.Notations.Cloud_And_Quality_Notation."
    1527             :         "Data_Strip_Notation",
    1528             :         "CLOUDCOVER_",
    1529             :         "Acquisition_Configuration.Platform_Configuration."
    1530             :         "Ephemeris_Configuration",
    1531             :         "EPHEMERIS_",
    1532             :         nullptr,
    1533             :         nullptr};
    1534             : 
    1535           8 :     if (psProductStrip != nullptr)
    1536           6 :         SetMetadataFromXML(psProductStrip, apszMetadataTranslationStrip);
    1537             : 
    1538           8 :     if (!osRPCFilename.empty())
    1539             :     {
    1540             :         GDALMDReaderPleiades *poReader =
    1541           7 :             GDALMDReaderPleiades::CreateReaderForRPC(osRPCFilename);
    1542           7 :         char **papszRPC = poReader->LoadRPCXmlFile(psDoc);
    1543           7 :         delete poReader;
    1544           7 :         if (papszRPC)
    1545           7 :             SetMetadata(papszRPC, "RPC");
    1546           7 :         CSLDestroy(papszRPC);
    1547             :     }
    1548             : 
    1549             :     CPLXMLNode *psLocatedUseAreaNode =
    1550           8 :         CPLGetXMLNode(psDoc, "Geometric_Data.Use_Area");
    1551           8 :     if (psLocatedUseAreaNode != nullptr)
    1552             :     {
    1553           4 :         CPLXMLNode *psLocatedGeometricValuesNode =
    1554             :             psLocatedUseAreaNode->psChild;
    1555          11 :         while (psLocatedGeometricValuesNode != nullptr)
    1556             :         {
    1557             :             CPLXMLNode *psLocationType =
    1558          11 :                 CPLGetXMLNode(psLocatedGeometricValuesNode, "LOCATION_TYPE");
    1559          11 :             if (psLocationType == nullptr ||
    1560          11 :                 psLocationType->psChild == nullptr ||
    1561          11 :                 !EQUAL(psLocationType->psChild->pszValue, "center"))
    1562             :             {
    1563           7 :                 psLocatedGeometricValuesNode =
    1564             :                     psLocatedGeometricValuesNode->psNext;
    1565           7 :                 continue;
    1566             :             }
    1567             :             static const char *const apszLGVTranslationDim[] = {
    1568             :                 "SATELLITE_ALTITUDE",
    1569             :                 "",
    1570             :                 "Acquisition_Angles",
    1571             :                 "",
    1572             :                 "Solar_Incidences",
    1573             :                 "",
    1574             :                 "Ground_Sample_Distance",
    1575             :                 "",
    1576             :                 nullptr,
    1577             :                 nullptr};
    1578             : 
    1579           4 :             SetMetadataFromXML(psLocatedGeometricValuesNode,
    1580             :                                apszLGVTranslationDim, false);
    1581           4 :             break;
    1582             :         }
    1583             :     }
    1584             : 
    1585             :     /* -------------------------------------------------------------------- */
    1586             :     /*      Set Band metadata from the <Band_Radiance> and                  */
    1587             :     /*                                <Band_Spectral_Range> content         */
    1588             :     /* -------------------------------------------------------------------- */
    1589             : 
    1590           8 :     CPLXMLNode *psImageInterpretationNode = CPLGetXMLNode(
    1591             :         psDoc,
    1592             :         "Radiometric_Data.Radiometric_Calibration.Instrument_Calibration."
    1593             :         "Band_Measurement_List");
    1594           8 :     if (psImageInterpretationNode != nullptr)
    1595             :     {
    1596           7 :         CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
    1597         100 :         while (psSpectralBandInfoNode != nullptr)
    1598             :         {
    1599          93 :             if (psSpectralBandInfoNode->eType == CXT_Element &&
    1600          93 :                 (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance") ||
    1601          66 :                  EQUAL(psSpectralBandInfoNode->pszValue,
    1602          39 :                        "Band_Spectral_Range") ||
    1603          39 :                  EQUAL(psSpectralBandInfoNode->pszValue,
    1604             :                        "Band_Solar_Irradiance")))
    1605             :             {
    1606         162 :                 CPLString osName;
    1607             : 
    1608          81 :                 if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance"))
    1609          27 :                     osName = "RADIANCE_";
    1610          54 :                 else if (EQUAL(psSpectralBandInfoNode->pszValue,
    1611             :                                "Band_Spectral_Range"))
    1612          27 :                     osName = "SPECTRAL_RANGE_";
    1613          27 :                 else if (EQUAL(psSpectralBandInfoNode->pszValue,
    1614             :                                "Band_Solar_Irradiance"))
    1615          27 :                     osName = "SOLAR_IRRADIANCE_";
    1616             : 
    1617          81 :                 CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
    1618          81 :                 int nBandIndex = 0;
    1619         651 :                 while (psTag != nullptr)
    1620             :                 {
    1621         570 :                     if (psTag->eType == CXT_Element &&
    1622         570 :                         psTag->psChild != nullptr &&
    1623         558 :                         psTag->pszValue != nullptr &&
    1624         558 :                         (psTag->psChild->eType == CXT_Text ||
    1625           6 :                          EQUAL(psTag->pszValue, "FWHM")))
    1626             :                     {
    1627         558 :                         if (EQUAL(psTag->pszValue, "BAND_ID"))
    1628             :                         {
    1629          81 :                             nBandIndex = 0;
    1630          81 :                             if (EQUAL(psTag->psChild->pszValue, "P") ||
    1631          81 :                                 EQUAL(psTag->psChild->pszValue, "PAN") ||
    1632          81 :                                 EQUAL(psTag->psChild->pszValue, "B0") ||
    1633          63 :                                 EQUAL(psTag->psChild->pszValue, "R"))
    1634          21 :                                 nBandIndex = 1;
    1635          60 :                             else if (EQUAL(psTag->psChild->pszValue, "B1") ||
    1636          45 :                                      EQUAL(psTag->psChild->pszValue, "G"))
    1637          18 :                                 nBandIndex = 2;
    1638          42 :                             else if (EQUAL(psTag->psChild->pszValue, "B2") ||
    1639          27 :                                      EQUAL(psTag->psChild->pszValue, "B"))
    1640          18 :                                 nBandIndex = 3;
    1641          24 :                             else if (EQUAL(psTag->psChild->pszValue, "B3") ||
    1642           9 :                                      EQUAL(psTag->psChild->pszValue, "NIR"))
    1643          18 :                                 nBandIndex = 4;
    1644           6 :                             else if (EQUAL(psTag->psChild->pszValue, "RE"))
    1645           3 :                                 nBandIndex = 5;
    1646           3 :                             else if (EQUAL(psTag->psChild->pszValue, "DB"))
    1647           3 :                                 nBandIndex = 6;
    1648             : 
    1649         162 :                             if (nBandIndex <= 0 ||
    1650          81 :                                 nBandIndex > GetRasterCount())
    1651             :                             {
    1652           0 :                                 CPLError(CE_Warning, CPLE_AppDefined,
    1653             :                                          "Bad BAND_ID value : %s",
    1654           0 :                                          psTag->psChild->pszValue);
    1655           0 :                                 nBandIndex = 0;
    1656             :                             }
    1657             :                         }
    1658         477 :                         else if (nBandIndex >= 1)
    1659             :                         {
    1660         954 :                             CPLString osMDName = osName;
    1661         477 :                             osMDName += psTag->pszValue;
    1662             : 
    1663         477 :                             auto poBand = GetRasterBand(nBandIndex);
    1664         477 :                             if (EQUAL(psTag->pszValue, "FWHM"))
    1665             :                             {
    1666           6 :                                 if (const char *pszMIN =
    1667           6 :                                         CPLGetXMLValue(psTag, "MIN", nullptr))
    1668           6 :                                     poBand->SetMetadataItem(
    1669          12 :                                         (osMDName + "_MIN").c_str(), pszMIN);
    1670           6 :                                 if (const char *pszMAX =
    1671           6 :                                         CPLGetXMLValue(psTag, "MAX", nullptr))
    1672           6 :                                     poBand->SetMetadataItem(
    1673          12 :                                         (osMDName + "_MAX").c_str(), pszMAX);
    1674             :                             }
    1675             :                             else
    1676             :                             {
    1677         471 :                                 poBand->SetMetadataItem(
    1678         471 :                                     osMDName, psTag->psChild->pszValue);
    1679             :                             }
    1680             :                         }
    1681             :                     }
    1682         570 :                     psTag = psTag->psNext;
    1683             :                 }
    1684             :             }
    1685          93 :             psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
    1686             :         }
    1687             :     }
    1688             : 
    1689             :     // Fill raster band IMAGERY metadata domain from FWHM metadata.
    1690          36 :     for (int i = 1; i <= nBands; ++i)
    1691             :     {
    1692          28 :         auto poBand = GetRasterBand(i);
    1693             :         const char *SPECTRAL_RANGE_MEASURE_UNIT =
    1694          28 :             poBand->GetMetadataItem("SPECTRAL_RANGE_MEASURE_UNIT");
    1695             :         const char *SPECTRAL_RANGE_FWHM_MIN =
    1696          28 :             poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MIN");
    1697             :         const char *SPECTRAL_RANGE_FWHM_MAX =
    1698          28 :             poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MAX");
    1699          28 :         if (SPECTRAL_RANGE_MEASURE_UNIT && SPECTRAL_RANGE_FWHM_MIN &&
    1700           6 :             SPECTRAL_RANGE_FWHM_MAX &&
    1701           6 :             (EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ||
    1702           5 :              EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "micrometer")))
    1703             :         {
    1704           6 :             const double dfFactorToMicrometer =
    1705           6 :                 EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ? 1e-3 : 1.0;
    1706             :             const double dfMin =
    1707           6 :                 CPLAtof(SPECTRAL_RANGE_FWHM_MIN) * dfFactorToMicrometer;
    1708             :             const double dfMax =
    1709           6 :                 CPLAtof(SPECTRAL_RANGE_FWHM_MAX) * dfFactorToMicrometer;
    1710           6 :             poBand->SetMetadataItem("CENTRAL_WAVELENGTH_UM",
    1711           6 :                                     CPLSPrintf("%.3f", (dfMin + dfMax) / 2),
    1712           6 :                                     "IMAGERY");
    1713           6 :             poBand->SetMetadataItem(
    1714           6 :                 "FWHM_UM", CPLSPrintf("%.3f", dfMax - dfMin), "IMAGERY");
    1715             :         }
    1716             :     }
    1717             : 
    1718             :     /* -------------------------------------------------------------------- */
    1719             :     /*      Initialize any PAM information.                                 */
    1720             :     /* -------------------------------------------------------------------- */
    1721           8 :     SetDescription(osMDFilename);
    1722           8 :     TryLoadXML();
    1723             : 
    1724             :     /* -------------------------------------------------------------------- */
    1725             :     /*      Check for overviews.                                            */
    1726             :     /* -------------------------------------------------------------------- */
    1727           8 :     oOvManager.Initialize(this, osMDFilename);
    1728             : 
    1729           8 :     return TRUE;
    1730             : }
    1731             : 
    1732             : /************************************************************************/
    1733             : /*                          SetMetadataFromXML()                        */
    1734             : /************************************************************************/
    1735             : 
    1736          20 : void DIMAPDataset::SetMetadataFromXML(
    1737             :     CPLXMLNode *psProductIn, const char *const apszMetadataTranslation[],
    1738             :     bool bKeysFromRoot)
    1739             : {
    1740          20 :     CPLXMLNode *psDoc = psProductIn;
    1741          20 :     if (bKeysFromRoot)
    1742             :     {
    1743          16 :         psDoc = CPLGetXMLNode(psProductIn, "=Dimap_Document");
    1744          16 :         if (psDoc == nullptr)
    1745             :         {
    1746           0 :             psDoc = CPLGetXMLNode(psProductIn, "=PHR_DIMAP_Document");
    1747             :         }
    1748             :     }
    1749             : 
    1750          20 :     bool bWarnedDiscarding = false;
    1751             : 
    1752         122 :     for (int iTrItem = 0; apszMetadataTranslation[iTrItem] != nullptr;
    1753         102 :          iTrItem += 2)
    1754             :     {
    1755             :         CPLXMLNode *psParent =
    1756         102 :             CPLGetXMLNode(psDoc, apszMetadataTranslation[iTrItem]);
    1757             : 
    1758         102 :         if (psParent == nullptr)
    1759          16 :             continue;
    1760             : 
    1761             :         // Logic to support directly access a name/value entry
    1762          86 :         if (psParent->psChild != nullptr &&
    1763          86 :             psParent->psChild->eType == CXT_Text)
    1764             :         {
    1765           8 :             CPLString osName = apszMetadataTranslation[iTrItem + 1];
    1766           4 :             osName += apszMetadataTranslation[iTrItem];
    1767             :             // Limit size to avoid perf issues when inserting
    1768             :             // in metadata list
    1769           4 :             if (osName.size() < 128)
    1770           4 :                 SetMetadataItem(osName, psParent->psChild->pszValue);
    1771           0 :             else if (!bWarnedDiscarding)
    1772             :             {
    1773           0 :                 bWarnedDiscarding = true;
    1774           0 :                 CPLDebug("DIMAP", "Discarding too long metadata item");
    1775             :             }
    1776           4 :             continue;
    1777             :         }
    1778             : 
    1779             :         // Logic to support a parent element with many name/values.
    1780          82 :         CPLXMLNode *psTarget = psParent->psChild;
    1781         525 :         for (; psTarget != nullptr && psTarget != psParent;
    1782         443 :              psTarget = psTarget->psNext)
    1783             :         {
    1784         443 :             if (psTarget->eType == CXT_Element && psTarget->psChild != nullptr)
    1785             :             {
    1786         874 :                 CPLString osName = apszMetadataTranslation[iTrItem + 1];
    1787             : 
    1788         437 :                 if (psTarget->psChild->eType == CXT_Text)
    1789             :                 {
    1790         307 :                     osName += psTarget->pszValue;
    1791             :                     // Limit size to avoid perf issues when inserting
    1792             :                     // in metadata list
    1793         307 :                     if (osName.size() < 128)
    1794         307 :                         SetMetadataItem(osName, psTarget->psChild->pszValue);
    1795           0 :                     else if (!bWarnedDiscarding)
    1796             :                     {
    1797           0 :                         bWarnedDiscarding = true;
    1798           0 :                         CPLDebug("DIMAP", "Discarding too long metadata item");
    1799             :                     }
    1800             :                 }
    1801         130 :                 else if (psTarget->psChild->eType == CXT_Attribute)
    1802             :                 {
    1803             :                     // find the tag value, at the end of the attributes.
    1804          76 :                     for (CPLXMLNode *psNode = psTarget->psChild;
    1805         219 :                          psNode != nullptr; psNode = psNode->psNext)
    1806             :                     {
    1807         143 :                         if (psNode->eType == CXT_Attribute)
    1808          76 :                             continue;
    1809          67 :                         else if (psNode->eType == CXT_Text)
    1810             :                         {
    1811          67 :                             osName += psTarget->pszValue;
    1812             :                             // Limit size to avoid perf issues when inserting
    1813             :                             // in metadata list
    1814          67 :                             if (osName.size() < 128)
    1815          67 :                                 SetMetadataItem(osName, psNode->pszValue);
    1816           0 :                             else if (!bWarnedDiscarding)
    1817             :                             {
    1818           0 :                                 bWarnedDiscarding = true;
    1819           0 :                                 CPLDebug("DIMAP",
    1820             :                                          "Discarding too long metadata item");
    1821             :                             }
    1822             :                         }
    1823             :                     }
    1824             :                 }
    1825             :             }
    1826             :         }
    1827             :     }
    1828          20 : }
    1829             : 
    1830             : /************************************************************************/
    1831             : /*                            GetGCPCount()                             */
    1832             : /************************************************************************/
    1833             : 
    1834           1 : int DIMAPDataset::GetGCPCount()
    1835             : 
    1836             : {
    1837           1 :     return nGCPCount;
    1838             : }
    1839             : 
    1840             : /************************************************************************/
    1841             : /*                          GetGCPSpatialRef()                          */
    1842             : /************************************************************************/
    1843             : 
    1844           1 : const OGRSpatialReference *DIMAPDataset::GetGCPSpatialRef() const
    1845             : 
    1846             : {
    1847           1 :     return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
    1848             : }
    1849             : 
    1850             : /************************************************************************/
    1851             : /*                               GetGCPs()                              */
    1852             : /************************************************************************/
    1853             : 
    1854           1 : const GDAL_GCP *DIMAPDataset::GetGCPs()
    1855             : 
    1856             : {
    1857           1 :     return pasGCPList;
    1858             : }
    1859             : 
    1860             : /************************************************************************/
    1861             : /*                         GDALRegister_DIMAP()                         */
    1862             : /************************************************************************/
    1863             : 
    1864        1935 : void GDALRegister_DIMAP()
    1865             : 
    1866             : {
    1867        1935 :     if (GDALGetDriverByName("DIMAP") != nullptr)
    1868         282 :         return;
    1869             : 
    1870        1653 :     GDALDriver *poDriver = new GDALDriver();
    1871             : 
    1872        1653 :     poDriver->SetDescription("DIMAP");
    1873        1653 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1874        1653 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "SPOT DIMAP");
    1875        1653 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/dimap.html");
    1876        1653 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1877        1653 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
    1878             : 
    1879        1653 :     poDriver->pfnOpen = DIMAPDataset::Open;
    1880        1653 :     poDriver->pfnIdentify = DIMAPDataset::Identify;
    1881             : 
    1882        1653 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1883             : }

Generated by: LCOV version 1.14