LCOV - code coverage report
Current view: top level - frmts/dimap - dimapdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 612 751 81.5 %
Date: 2025-09-10 17:48:50 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           8 :         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 :                 static_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       61931 : int DIMAPDataset::Identify(GDALOpenInfo *poOpenInfo)
     436             : 
     437             : {
     438       61931 :     if (STARTS_WITH(poOpenInfo->pszFilename, "DIMAP:"))
     439           4 :         return true;
     440             : 
     441       61927 :     if (poOpenInfo->nHeaderBytes >= 100)
     442             :     {
     443        6096 :         if ((strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
     444        6086 :                     "<Dimap_Document") == nullptr) &&
     445        6086 :             (strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
     446             :                     "<PHR_DIMAP_Document") == nullptr))
     447        6086 :             return FALSE;
     448             : 
     449          10 :         return TRUE;
     450             :     }
     451       55831 :     else if (poOpenInfo->bIsDirectory)
     452             :     {
     453             :         // DIMAP file.
     454         621 :         CPLString osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
     455         621 :                                                        "METADATA.DIM", nullptr);
     456             : 
     457             :         VSIStatBufL sStat;
     458         621 :         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         621 :             osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
     475         621 :                                                  "VOL_PHR.XML", nullptr);
     476             : 
     477         621 :             if (VSIStatL(osMDFilename, &sStat) == 0)
     478           4 :                 return TRUE;
     479             : 
     480             :             // DIMAP VHR2020 file.
     481         617 :             osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
     482         617 :                                                  "VOL_PNEO.XML", nullptr);
     483             : 
     484         617 :             if (VSIStatL(osMDFilename, &sStat) == 0)
     485           2 :                 return TRUE;
     486             : 
     487         615 :             return FALSE;
     488             :         }
     489             :     }
     490             : 
     491       55210 :     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          20 :     CPLXMLTreeCloser psProduct(CPLParseXMLFile(osMDFilename.c_str()));
     558          10 :     if (psProduct == nullptr)
     559           0 :         return nullptr;
     560             : 
     561          10 :     const CPLXMLNode *psDoc = CPLGetXMLNode(psProduct.get(), "=Dimap_Document");
     562          10 :     if (!psDoc)
     563           0 :         psDoc = CPLGetXMLNode(psProduct.get(), "=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             :         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          20 :     CPLXMLTreeCloser psProductDim(nullptr);
     577          20 :     CPLXMLTreeCloser psProductStrip(nullptr);
     578             : 
     579          20 :     CPLStringList aosSubdatasets;
     580             : 
     581             :     // Check needed information for the DIMAP format.
     582          10 :     if (nProductVersion == 1)
     583             :     {
     584             :         const 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 :             return nullptr;
     591             :         }
     592             :     }
     593             :     else  // DIMAP2.
     594             :     {
     595             :         // Verify if the opened file is not already a product dimap
     596           8 :         if (CPLGetXMLNode(psDoc, "Raster_Data"))
     597             :         {
     598           2 :             std::swap(psProductDim, psProduct);
     599           2 :             osDIMAPFilename = osMDFilename;
     600             :         }
     601             :         else
     602             :         {
     603             :             // Verify the presence of the DIMAP product file.
     604             :             const CPLXMLNode *psDatasetComponents =
     605           6 :                 CPLGetXMLNode(psDoc, "Dataset_Content.Dataset_Components");
     606             : 
     607           6 :             if (psDatasetComponents == nullptr)
     608             :             {
     609           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
     610             :                          "Failed to find <Dataset_Components> in document.");
     611           0 :                 return nullptr;
     612             :             }
     613             : 
     614           6 :             for (CPLXMLNode *psDatasetComponent = psDatasetComponents->psChild;
     615          17 :                  psDatasetComponent != nullptr;
     616          11 :                  psDatasetComponent = psDatasetComponent->psNext)
     617             :             {
     618             :                 const char *pszComponentType =
     619          11 :                     CPLGetXMLValue(psDatasetComponent, "COMPONENT_TYPE", "");
     620          11 :                 if (strcmp(pszComponentType, "DIMAP") == 0)
     621             :                 {
     622             :                     // DIMAP product found.
     623          10 :                     const char *pszHref = CPLGetXMLValue(
     624             :                         psDatasetComponent, "COMPONENT_PATH.href", "");
     625             :                     const CPLString osComponentTitle(CPLGetXMLValue(
     626          10 :                         psDatasetComponent, "COMPONENT_TITLE", ""));
     627             :                     const CPLString osComponentTitleLaundered(
     628          10 :                         CPLString(osComponentTitle).replaceAll(' ', '_'));
     629             : 
     630          20 :                     if (strlen(pszHref) > 0 && osDIMAPFilename.empty() &&
     631          10 :                         (osSelectedSubdataset.empty() ||
     632           3 :                          osSelectedSubdataset == osComponentTitleLaundered))
     633             :                     {
     634           6 :                         if (CPLHasPathTraversal(pszHref))
     635             :                         {
     636           0 :                             CPLError(CE_Failure, CPLE_NotSupported,
     637             :                                      "Path traversal detected in %s", pszHref);
     638           0 :                             return nullptr;
     639             :                         }
     640             : 
     641           6 :                         if (poOpenInfo->bIsDirectory)
     642             :                         {
     643           3 :                             osDIMAPFilename = CPLFormCIFilenameSafe(
     644           3 :                                 poOpenInfo->pszFilename, pszHref, nullptr);
     645             :                         }
     646             :                         else
     647             :                         {
     648             :                             CPLString osPath =
     649           3 :                                 CPLGetPathSafe(osMDFilename.c_str());
     650             :                             osDIMAPFilename =
     651           3 :                                 CPLFormFilenameSafe(osPath, pszHref, nullptr);
     652             :                         }
     653             : 
     654             :                         // Data file might be specified there.
     655           6 :                         const char *pszDataFileHref = CPLGetXMLValue(
     656             :                             psDatasetComponent,
     657             :                             "Data_Files.Data_File.DATA_FILE_PATH.href", "");
     658             : 
     659           6 :                         if (strlen(pszDataFileHref) > 0)
     660             :                         {
     661             :                             CPLString osPath =
     662           0 :                                 CPLGetPathSafe(osMDFilename.c_str());
     663           0 :                             osImageDSFilename = CPLFormFilenameSafe(
     664           0 :                                 osPath, pszDataFileHref, nullptr);
     665           0 :                             if (CPLHasPathTraversal(osImageDSFilename.c_str()))
     666             :                             {
     667           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
     668             :                                          "Path traversal detected in %s",
     669             :                                          osImageDSFilename.c_str());
     670           0 :                                 return nullptr;
     671             :                             }
     672             :                         }
     673             :                     }
     674             : 
     675             :                     const int iIdx =
     676          10 :                         static_cast<int>(aosSubdatasets.size() / 2 + 1);
     677             :                     aosSubdatasets.SetNameValue(
     678             :                         CPLSPrintf("SUBDATASET_%d_NAME", iIdx),
     679             :                         CPLSPrintf("DIMAP:\"%s\":%s", poOpenInfo->pszFilename,
     680          10 :                                    osComponentTitleLaundered.c_str()));
     681             :                     aosSubdatasets.SetNameValue(
     682             :                         CPLSPrintf("SUBDATASET_%d_DESC", iIdx),
     683          10 :                         CPLSPrintf("Component %s", osComponentTitle.c_str()));
     684             :                 }
     685             :             }
     686             : 
     687           6 :             psProductDim.reset(CPLParseXMLFile(osDIMAPFilename.c_str()));
     688           6 :             if (psProductDim == nullptr)
     689             :             {
     690           0 :                 return nullptr;
     691             :             }
     692             :         }
     693             : 
     694             :         // We need the {STRIP|RPC}_<product_id>.XML file for a few metadata.
     695             :         const CPLXMLNode *psDocDim =
     696           8 :             CPLGetXMLNode(psProductDim.get(), "=Dimap_Document");
     697           8 :         if (!psDocDim)
     698           0 :             psDocDim = CPLGetXMLNode(psProductDim.get(), "=PHR_DIMAP_Document");
     699             : 
     700             :         const CPLXMLNode *psDatasetSources =
     701           8 :             CPLGetXMLNode(psDocDim, "Dataset_Sources");
     702           8 :         if (psDatasetSources != nullptr)
     703             :         {
     704           7 :             CPLString osSTRIPFilename;
     705             : 
     706           7 :             for (CPLXMLNode *psDatasetSource = psDatasetSources->psChild;
     707           8 :                  psDatasetSource != nullptr;
     708           1 :                  psDatasetSource = psDatasetSource->psNext)
     709             :             {
     710             :                 const char *pszSourceType =
     711           7 :                     CPLGetXMLValue(psDatasetSource, "SOURCE_TYPE", "");
     712           7 :                 if (strcmp(pszSourceType, "Strip_Source") == 0)
     713             :                 {
     714           7 :                     const char *pszHref = CPLGetXMLValue(
     715             :                         psDatasetSource, "Component.COMPONENT_PATH.href", "");
     716             : 
     717           7 :                     if (strlen(pszHref) > 0)  // STRIP product found.
     718             :                     {
     719           6 :                         if (CPLHasPathTraversal(pszHref))
     720             :                         {
     721           0 :                             CPLError(CE_Failure, CPLE_NotSupported,
     722             :                                      "Path traversal detected in %s", pszHref);
     723           0 :                             return nullptr;
     724             :                         }
     725             :                         CPLString osPath =
     726           6 :                             CPLGetPathSafe(osDIMAPFilename.c_str());
     727             :                         osSTRIPFilename =
     728           6 :                             CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
     729           6 :                         if (VSIStatL(osSTRIPFilename, &sStat) == 0)
     730             :                         {
     731           6 :                             psProductStrip.reset(
     732             :                                 CPLParseXMLFile(osSTRIPFilename));
     733           6 :                             break;
     734             :                         }
     735             :                     }
     736             :                 }
     737             :             }
     738             :         }
     739             : 
     740           8 :         const CPLXMLNode *psDatasetRFMComponents = CPLGetXMLNode(
     741             :             psDocDim, "Geoposition.Geoposition_Models.Rational_Function_Model");
     742           8 :         if (psDatasetRFMComponents != nullptr)
     743             :         {
     744           7 :             for (const CPLXMLNode *psDatasetRFMComponent =
     745             :                      psDatasetRFMComponents->psChild;
     746           7 :                  psDatasetRFMComponent != nullptr;
     747           0 :                  psDatasetRFMComponent = psDatasetRFMComponent->psNext)
     748             :             {
     749           7 :                 const char *pszComponentTitle = CPLGetXMLValue(
     750             :                     psDatasetRFMComponent, "COMPONENT_TITLE", "");
     751           7 :                 if (strcmp(pszComponentTitle, "RPC Model") == 0)
     752             :                 {
     753           7 :                     const char *pszHref = CPLGetXMLValue(
     754             :                         psDatasetRFMComponent, "COMPONENT_PATH.href", "");
     755             : 
     756           7 :                     if (strlen(pszHref) > 0)  // RPC product found.
     757             :                     {
     758           7 :                         if (CPLHasPathTraversal(pszHref))
     759             :                         {
     760           0 :                             CPLError(CE_Failure, CPLE_NotSupported,
     761             :                                      "Path traversal detected in %s", pszHref);
     762           0 :                             return nullptr;
     763             :                         }
     764             :                         CPLString osPath =
     765          14 :                             CPLGetPathSafe(osDIMAPFilename.c_str());
     766             :                         osRPCFilename =
     767           7 :                             CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
     768           7 :                         break;
     769             :                     }
     770             :                 }
     771             :             }
     772             :         }
     773             :     }
     774             : 
     775             :     /* -------------------------------------------------------------------- */
     776             :     /*      Create the dataset.                                             */
     777             :     /* -------------------------------------------------------------------- */
     778          20 :     auto poDS = std::make_unique<DIMAPDataset>();
     779             : 
     780          10 :     if (osSelectedSubdataset.empty() && aosSubdatasets.size() > 2)
     781             :     {
     782           2 :         poDS->GDALDataset::SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
     783             :     }
     784          10 :     poDS->psProduct = psProduct.release();
     785          10 :     poDS->psProductDim = psProductDim.release();
     786          10 :     poDS->psProductStrip = psProductStrip.release();
     787          10 :     poDS->osRPCFilename = std::move(osRPCFilename);
     788          10 :     poDS->nProductVersion = nProductVersion;
     789          10 :     poDS->osMDFilename = std::move(osMDFilename);
     790          10 :     poDS->osImageDSFilename = std::move(osImageDSFilename);
     791          10 :     poDS->osDIMAPFilename = std::move(osDIMAPFilename);
     792             : 
     793          10 :     const int res = (nProductVersion == 2) ? poDS->ReadImageInformation2()
     794           2 :                                            : poDS->ReadImageInformation();
     795             : 
     796          10 :     if (res == FALSE)
     797             :     {
     798           0 :         return nullptr;
     799             :     }
     800             : 
     801          10 :     return poDS.release();
     802             : }
     803             : 
     804             : /************************************************************************/
     805             : /*               ReadImageInformation() DIMAP Version 1                 */
     806             : /************************************************************************/
     807             : 
     808           2 : int DIMAPDataset::ReadImageInformation()
     809             : {
     810           2 :     CPLXMLNode *psDoc = CPLGetXMLNode(psProduct, "=Dimap_Document");
     811           2 :     if (!psDoc)
     812           0 :         psDoc = CPLGetXMLNode(psProduct, "=PHR_DIMAP_Document");
     813             : 
     814             :     /* -------------------------------------------------------------------- */
     815             :     /*      Get overall image information.                                  */
     816             :     /* -------------------------------------------------------------------- */
     817             : 
     818             :     // TODO: DIMAP 1 probably handle mosaics? Like DIMAP 2?
     819             : 
     820             :     /* -------------------------------------------------------------------- */
     821             :     /*      Get the name of the underlying file.                            */
     822             :     /* -------------------------------------------------------------------- */
     823             : 
     824             :     const char *pszHref =
     825           2 :         CPLGetXMLValue(psDoc, "Data_Access.Data_File.DATA_FILE_PATH.href", "");
     826           4 :     CPLString osPath = CPLGetPathSafe(osMDFilename);
     827           4 :     CPLString osImageFilename = CPLFormFilenameSafe(osPath, pszHref, nullptr);
     828           2 :     if (CPLHasPathTraversal(pszHref))
     829             :     {
     830           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Path traversal detected in %s",
     831             :                  pszHref);
     832           0 :         return false;
     833             :     }
     834             :     /* -------------------------------------------------------------------- */
     835             :     /*      Try and open the file.                                          */
     836             :     /* -------------------------------------------------------------------- */
     837             : 
     838             :     auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     839           4 :         osImageFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
     840           2 :     if (poImageDS == nullptr)
     841             :     {
     842           0 :         return FALSE;
     843             :     }
     844           2 :     nRasterXSize = poImageDS->GetRasterXSize();
     845           2 :     nRasterYSize = poImageDS->GetRasterYSize();
     846             : 
     847             :     /* -------------------------------------------------------------------- */
     848             :     /*      Create and initialize the corresponding VRT dataset used to     */
     849             :     /*      manage the tiled data access.                                   */
     850             :     /* -------------------------------------------------------------------- */
     851           2 :     poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
     852             : 
     853             :     // Don't try to write a VRT file.
     854           2 :     poVRTDS->SetWritable(FALSE);
     855             : 
     856           4 :     for (int iBand = 0; iBand < poImageDS->GetRasterCount(); iBand++)
     857             :     {
     858           2 :         poVRTDS->AddBand(
     859           2 :             poImageDS->GetRasterBand(iBand + 1)->GetRasterDataType(), nullptr);
     860             : 
     861             :         VRTSourcedRasterBand *poVRTBand =
     862             :             reinterpret_cast<VRTSourcedRasterBand *>(
     863           2 :                 poVRTDS->GetRasterBand(iBand + 1));
     864             : 
     865           2 :         poVRTBand->AddSimpleSource(osImageFilename, iBand + 1, 0, 0,
     866           2 :                                    nRasterXSize, nRasterYSize, 0, 0,
     867           2 :                                    nRasterXSize, nRasterYSize);
     868             :     }
     869             : 
     870             :     /* -------------------------------------------------------------------- */
     871             :     /*      Create band information objects.                                */
     872             :     /* -------------------------------------------------------------------- */
     873           4 :     for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
     874             :     {
     875           2 :         SetBand(iBand, new DIMAPRasterBand(this, iBand,
     876             :                                            static_cast<VRTSourcedRasterBand *>(
     877           2 :                                                poVRTDS->GetRasterBand(iBand))));
     878             :     }
     879             : 
     880             :     /* -------------------------------------------------------------------- */
     881             :     /*      Try to collect simple insertion point.                          */
     882             :     /* -------------------------------------------------------------------- */
     883             :     CPLXMLNode *psGeoLoc =
     884           2 :         CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
     885             : 
     886           2 :     if (psGeoLoc != nullptr)
     887             :     {
     888           0 :         bHaveGeoTransform = TRUE;
     889           0 :         m_gt[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
     890           0 :         m_gt[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
     891           0 :         m_gt[2] = 0.0;
     892           0 :         m_gt[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
     893           0 :         m_gt[4] = 0.0;
     894           0 :         m_gt[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
     895             :     }
     896             :     else
     897             :     {
     898             :         // Try to get geotransform from underlying raster.
     899           2 :         if (poImageDS->GetGeoTransform(m_gt) == CE_None)
     900           0 :             bHaveGeoTransform = TRUE;
     901             :     }
     902             : 
     903             :     /* -------------------------------------------------------------------- */
     904             :     /*      Collect GCPs.                                                   */
     905             :     /* -------------------------------------------------------------------- */
     906           2 :     psGeoLoc = CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Points");
     907             : 
     908           2 :     if (psGeoLoc != nullptr)
     909             :     {
     910             :         // Count gcps.
     911           2 :         nGCPCount = 0;
     912          10 :         for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
     913           8 :              psNode = psNode->psNext)
     914             :         {
     915           8 :             if (EQUAL(psNode->pszValue, "Tie_Point"))
     916           8 :                 nGCPCount++;
     917             :         }
     918             : 
     919           2 :         pasGCPList =
     920           2 :             static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nGCPCount));
     921             : 
     922           2 :         nGCPCount = 0;
     923             : 
     924          10 :         for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
     925           8 :              psNode = psNode->psNext)
     926             :         {
     927           8 :             GDAL_GCP *psGCP = pasGCPList + nGCPCount;
     928             : 
     929           8 :             if (!EQUAL(psNode->pszValue, "Tie_Point"))
     930           0 :                 continue;
     931             : 
     932           8 :             nGCPCount++;
     933             : 
     934           8 :             char szID[32] = {};
     935           8 :             snprintf(szID, sizeof(szID), "%d", nGCPCount);
     936           8 :             psGCP->pszId = CPLStrdup(szID);
     937           8 :             psGCP->pszInfo = CPLStrdup("");
     938           8 :             psGCP->dfGCPPixel =
     939           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_X", "0")) - 0.5;
     940           8 :             psGCP->dfGCPLine =
     941           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_Y", "0")) - 0.5;
     942           8 :             psGCP->dfGCPX =
     943           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_X", ""));
     944           8 :             psGCP->dfGCPY =
     945           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Y", ""));
     946           8 :             psGCP->dfGCPZ =
     947           8 :                 CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Z", ""));
     948             :         }
     949             :     }
     950             : 
     951             :     /* -------------------------------------------------------------------- */
     952             :     /*      Collect the CRS.  For now we look only for EPSG codes.          */
     953             :     /* -------------------------------------------------------------------- */
     954           2 :     const char *pszSRS = CPLGetXMLValue(
     955             :         psDoc, "Coordinate_Reference_System.Horizontal_CS.HORIZONTAL_CS_CODE",
     956             :         nullptr);
     957             : 
     958           2 :     if (pszSRS != nullptr)
     959             :     {
     960           2 :         OGRSpatialReference &oSRS = nGCPCount > 0 ? m_oGCPSRS : m_oSRS;
     961           2 :         oSRS.SetFromUserInput(
     962             :             pszSRS, OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
     963             :     }
     964             :     else
     965             :     {
     966             :         // Check underlying raster for SRS. We have cases where
     967             :         // HORIZONTAL_CS_CODE is empty and the underlying raster
     968             :         // is georeferenced (rprinceley).
     969           0 :         const auto poSRS = poImageDS->GetSpatialRef();
     970           0 :         if (poSRS)
     971             :         {
     972           0 :             m_oSRS = *poSRS;
     973             :         }
     974             :     }
     975             : 
     976             :     /* -------------------------------------------------------------------- */
     977             :     /*      Translate other metadata of interest.                           */
     978             :     /* -------------------------------------------------------------------- */
     979             :     static const char *const apszMetadataTranslation[] = {
     980             :         "Production",
     981             :         "",
     982             :         "Production.Facility",
     983             :         "FACILITY_",
     984             :         "Dataset_Sources.Source_Information.Scene_Source",
     985             :         "",
     986             :         "Data_Processing",
     987             :         "",
     988             :         "Image_Interpretation.Spectral_Band_Info",
     989             :         "SPECTRAL_",
     990             :         nullptr,
     991             :         nullptr};
     992             : 
     993           2 :     SetMetadataFromXML(psProduct, apszMetadataTranslation);
     994             : 
     995             :     /* -------------------------------------------------------------------- */
     996             :     /*      Set Band metadata from the <Spectral_Band_Info> content         */
     997             :     /* -------------------------------------------------------------------- */
     998             : 
     999             :     CPLXMLNode *psImageInterpretationNode =
    1000           2 :         CPLGetXMLNode(psDoc, "Image_Interpretation");
    1001           2 :     if (psImageInterpretationNode != nullptr)
    1002             :     {
    1003           2 :         CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
    1004           4 :         while (psSpectralBandInfoNode != nullptr)
    1005             :         {
    1006           2 :             if (psSpectralBandInfoNode->eType == CXT_Element &&
    1007           2 :                 EQUAL(psSpectralBandInfoNode->pszValue, "Spectral_Band_Info"))
    1008             :             {
    1009           2 :                 CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
    1010           2 :                 int nBandIndex = 0;
    1011          14 :                 while (psTag != nullptr)
    1012             :                 {
    1013          12 :                     if (psTag->eType == CXT_Element &&
    1014          12 :                         psTag->psChild != nullptr &&
    1015          12 :                         psTag->psChild->eType == CXT_Text &&
    1016          12 :                         psTag->pszValue != nullptr)
    1017             :                     {
    1018          12 :                         if (EQUAL(psTag->pszValue, "BAND_INDEX"))
    1019             :                         {
    1020           2 :                             nBandIndex = atoi(psTag->psChild->pszValue);
    1021           4 :                             if (nBandIndex <= 0 ||
    1022           2 :                                 nBandIndex > poImageDS->GetRasterCount())
    1023             :                             {
    1024           0 :                                 CPLError(CE_Warning, CPLE_AppDefined,
    1025             :                                          "Bad BAND_INDEX value : %s",
    1026           0 :                                          psTag->psChild->pszValue);
    1027           0 :                                 nBandIndex = 0;
    1028             :                             }
    1029             :                         }
    1030          10 :                         else if (nBandIndex >= 1)
    1031             :                         {
    1032          10 :                             GetRasterBand(nBandIndex)
    1033          10 :                                 ->SetMetadataItem(psTag->pszValue,
    1034          10 :                                                   psTag->psChild->pszValue);
    1035             :                         }
    1036             :                     }
    1037          12 :                     psTag = psTag->psNext;
    1038             :                 }
    1039             :             }
    1040           2 :             psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
    1041             :         }
    1042             :     }
    1043             : 
    1044             :     /* -------------------------------------------------------------------- */
    1045             :     /*      Initialize any PAM information.                                 */
    1046             :     /* -------------------------------------------------------------------- */
    1047           2 :     SetDescription(osMDFilename);
    1048           2 :     TryLoadXML();
    1049             : 
    1050             :     /* -------------------------------------------------------------------- */
    1051             :     /*      Check for overviews.                                            */
    1052             :     /* -------------------------------------------------------------------- */
    1053           2 :     oOvManager.Initialize(this, osMDFilename);
    1054             : 
    1055             :     // CID 163546 - poTileDS dereferenced above.
    1056             :     // coverity[leaked_storage]
    1057           2 :     return TRUE;
    1058             : }
    1059             : 
    1060             : /************************************************************************/
    1061             : /*               ReadImageInformation() DIMAP Version 2                 */
    1062             : /************************************************************************/
    1063             : 
    1064           8 : int DIMAPDataset::ReadImageInformation2()
    1065             : {
    1066           8 :     CPLXMLNode *psDoc = CPLGetXMLNode(psProductDim, "=Dimap_Document");
    1067           8 :     if (!psDoc)
    1068           0 :         psDoc = CPLGetXMLNode(psProductDim, "=PHR_DIMAP_Document");
    1069             : 
    1070             :     CPLXMLNode *psImageAttributes =
    1071           8 :         CPLGetXMLNode(psDoc, "Raster_Data.Raster_Dimensions");
    1072           8 :     if (psImageAttributes == nullptr)
    1073             :     {
    1074           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1075             :                  "Failed to find <Raster_Dimensions> in document.");
    1076           0 :         return FALSE;
    1077             :     }
    1078             : 
    1079             :     /* -------------------------------------------------------------------- */
    1080             :     /*      Get overall image information.                                  */
    1081             :     /* -------------------------------------------------------------------- */
    1082             : 
    1083             :     /*
    1084             :         <Raster_Dimensions>
    1085             :            <NROWS>30</NROWS>
    1086             :            <NCOLS>20</NCOLS>
    1087             :            <NBANDS>4</NBANDS>
    1088             :            <Tile_Set>
    1089             :               <NTILES>2</NTILES>
    1090             :               <Regular_Tiling>
    1091             :                  <NTILES_SIZE nrows="20" ncols="20"/>
    1092             :                  <NTILES_COUNT ntiles_R="2" ntiles_C="1"/>
    1093             :                  <OVERLAP_ROW>0</OVERLAP_ROW>
    1094             :                  <OVERLAP_COL>0</OVERLAP_COL>
    1095             :               </Regular_Tiling>
    1096             :            </Tile_Set>
    1097             :         </Raster_Dimensions>
    1098             :       */
    1099             : 
    1100             :     const int l_nBands =
    1101           8 :         atoi(CPLGetXMLValue(psImageAttributes, "NBANDS", "-1"));
    1102           8 :     nRasterXSize = atoi(CPLGetXMLValue(psImageAttributes, "NCOLS", "-1"));
    1103           8 :     nRasterYSize = atoi(CPLGetXMLValue(psImageAttributes, "NROWS", "-1"));
    1104           8 :     if (nRasterXSize <= 0 || nRasterYSize <= 0)
    1105             :     {
    1106           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1107             :                  "Invalid NCOLS(=%d)/NROWS(=%d) value", nRasterXSize,
    1108             :                  nRasterYSize);
    1109           0 :         return FALSE;
    1110             :     }
    1111           8 :     int nTileWidth = atoi(CPLGetXMLValue(
    1112             :         psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.ncols", "-1"));
    1113           8 :     int nTileHeight = atoi(CPLGetXMLValue(
    1114             :         psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.nrows", "-1"));
    1115           8 :     int nOverlapRow = atoi(CPLGetXMLValue(
    1116             :         psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_ROW", "-1"));
    1117           8 :     int nOverlapCol = atoi(CPLGetXMLValue(
    1118             :         psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_COL", "-1"));
    1119             :     const int nBits =
    1120           8 :         atoi(CPLGetXMLValue(psDoc, "Raster_Data.Raster_Encoding.NBITS", "-1"));
    1121             :     CPLString osDataFormat =
    1122          16 :         CPLGetXMLValue(psDoc, "Raster_Data.Data_Access.DATA_FILE_FORMAT", "");
    1123           8 :     if (osDataFormat == "image/jp2")
    1124             :     {
    1125           1 :         SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
    1126             :     }
    1127             : 
    1128             :     // For VHR2020: SPECTRAL_PROCESSING = PAN, MS, MS-FS, PMS, PMS-N, PMS-X,
    1129             :     // PMS-FS
    1130             :     const CPLString osSpectralProcessing = CPLGetXMLValue(
    1131             :         psDoc, "Processing_Information.Product_Settings.SPECTRAL_PROCESSING",
    1132          16 :         "");
    1133             :     const bool bTwoDataFilesPerTile =
    1134           8 :         osSpectralProcessing == "MS-FS" || osSpectralProcessing == "PMS-FS";
    1135             : 
    1136             :     /* -------------------------------------------------------------------- */
    1137             :     /*      Get the name of the underlying file.                            */
    1138             :     /* -------------------------------------------------------------------- */
    1139             : 
    1140             :     CPLXMLNode *psDataFiles =
    1141           8 :         CPLGetXMLNode(psDoc, "Raster_Data.Data_Access.Data_Files");
    1142             : 
    1143             :     /*  <Data_Files>
    1144             :             <Data_File tile_R="1" tile_C="1">
    1145             :                <DATA_FILE_PATH href="IMG_foo_R1C1.TIF"/>
    1146             :             </Data_File>
    1147             :             <Data_File tile_R="2" tile_C="1">
    1148             :                <DATA_FILE_PATH href="IMG_foo_R2C1.TIF"/>
    1149             :             </Data_File>
    1150             :          </Data_Files>
    1151             :     */
    1152             : 
    1153             :     struct TileIdx
    1154             :     {
    1155             :         int nRow;
    1156             :         int nCol;
    1157             :         int nPart;  // typically 0.  But for VHR2020 0=RGB, 1=NED
    1158             : 
    1159          15 :         TileIdx(int nRowIn, int nColIn, int nPartIn = 0)
    1160          15 :             : nRow(nRowIn), nCol(nColIn), nPart(nPartIn)
    1161             :         {
    1162          15 :         }
    1163             : 
    1164          21 :         bool operator<(const TileIdx &other) const
    1165             :         {
    1166          21 :             if (nRow < other.nRow)
    1167          12 :                 return true;
    1168           9 :             if (nRow > other.nRow)
    1169           6 :                 return false;
    1170           3 :             if (nCol < other.nCol)
    1171           0 :                 return true;
    1172           3 :             if (nCol > other.nCol)
    1173           0 :                 return false;
    1174           3 :             return nPart < other.nPart;
    1175             :         }
    1176             :     };
    1177             : 
    1178          16 :     std::map<TileIdx, CPLString> oMapTileIdxToName;
    1179           8 :     int nImageDSRow = 1, nImageDSCol = 1;
    1180           8 :     if (psDataFiles)
    1181             :     {
    1182           8 :         const CPLString osPath = CPLGetPathSafe(osDIMAPFilename);
    1183          17 :         for (int nPart = 0; psDataFiles != nullptr;
    1184           9 :              psDataFiles = psDataFiles->psNext, nPart++)
    1185             :         {
    1186          27 :             for (CPLXMLNode *psDataFile = psDataFiles->psChild; psDataFile;
    1187          18 :                  psDataFile = psDataFile->psNext)
    1188             :             {
    1189          18 :                 if (psDataFile->eType == CXT_Element &&
    1190          18 :                     strcmp(psDataFile->pszValue, "Data_File") == 0)
    1191             :                 {
    1192             :                     const char *pszR =
    1193          15 :                         CPLGetXMLValue(psDataFile, "tile_R", nullptr);
    1194             :                     const char *pszC =
    1195          15 :                         CPLGetXMLValue(psDataFile, "tile_C", nullptr);
    1196          15 :                     const char *pszHref = CPLGetXMLValue(
    1197             :                         psDataFile, "DATA_FILE_PATH.href", nullptr);
    1198          15 :                     if (pszR && pszC && pszHref)
    1199             :                     {
    1200          15 :                         int nRow = atoi(pszR);
    1201          15 :                         int nCol = atoi(pszC);
    1202          15 :                         if (nRow < 0 || nCol < 0)
    1203             :                         {
    1204           0 :                             return false;
    1205             :                         }
    1206             :                         std::string osTileFilename(
    1207          15 :                             CPLFormCIFilenameSafe(osPath, pszHref, nullptr));
    1208          15 :                         if (CPLHasPathTraversal(pszHref))
    1209             :                         {
    1210           0 :                             CPLError(CE_Failure, CPLE_NotSupported,
    1211             :                                      "Path traversal detected in %s", pszHref);
    1212           0 :                             return false;
    1213             :                         }
    1214          22 :                         if ((nRow == 1 && nCol == 1 && nPart == 0) ||
    1215           7 :                             osImageDSFilename.empty())
    1216             :                         {
    1217           8 :                             osImageDSFilename = osTileFilename;
    1218           8 :                             nImageDSRow = nRow;
    1219           8 :                             nImageDSCol = nCol;
    1220             :                         }
    1221          15 :                         oMapTileIdxToName[TileIdx(nRow, nCol, nPart)] =
    1222          30 :                             std::move(osTileFilename);
    1223             :                     }
    1224             :                 }
    1225             :             }
    1226             :         }
    1227           8 :         if (nOverlapRow > 0 || nOverlapCol > 0)
    1228             :         {
    1229           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1230             :                      "Overlap between tiles is not handled currently. "
    1231             :                      "Only taking into account top left tile");
    1232           0 :             oMapTileIdxToName.clear();
    1233           0 :             oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
    1234             :         }
    1235             :     }
    1236             :     else
    1237             :     {
    1238           0 :         oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
    1239             :     }
    1240             : 
    1241           8 :     if (osImageDSFilename.empty())
    1242             :     {
    1243           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1244             :                  "Failed to find <DATA_FILE_PATH> in document.");
    1245           0 :         return FALSE;
    1246             :     }
    1247             : 
    1248             :     /* -------------------------------------------------------------------- */
    1249             :     /*      Try and open the file.                                          */
    1250             :     /* -------------------------------------------------------------------- */
    1251             :     auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    1252          16 :         osImageDSFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
    1253           8 :     if (poImageDS == nullptr)
    1254             :     {
    1255           0 :         return FALSE;
    1256             :     }
    1257           8 :     if (bTwoDataFilesPerTile)
    1258             :     {
    1259           1 :         if (l_nBands != 6 || poImageDS->GetRasterCount() != 3)
    1260             :         {
    1261           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
    1262           0 :             return FALSE;
    1263             :         }
    1264             :     }
    1265           7 :     else if (poImageDS->GetRasterCount() != l_nBands)
    1266             :     {
    1267           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
    1268           0 :         return FALSE;
    1269             :     }
    1270             : 
    1271           8 :     if (nTileWidth > 0 && nTileHeight > 0)
    1272             :     {
    1273             :         // ok
    1274             :     }
    1275           3 :     else if (oMapTileIdxToName.size() == 1 ||
    1276           1 :              (bTwoDataFilesPerTile && oMapTileIdxToName.size() == 2))
    1277             :     {
    1278           2 :         nTileWidth = poImageDS->GetRasterXSize();
    1279           2 :         nTileHeight = poImageDS->GetRasterYSize();
    1280             :     }
    1281             : 
    1282           8 :     if (!(nTileWidth > 0 && nTileHeight > 0))
    1283             :     {
    1284           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot get tile dimension");
    1285           0 :         return FALSE;
    1286             :     }
    1287             : 
    1288             :     /* -------------------------------------------------------------------- */
    1289             :     /*      Create and initialize the corresponding VRT dataset used to     */
    1290             :     /*      manage the tiled data access.                                   */
    1291             :     /* -------------------------------------------------------------------- */
    1292           8 :     poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
    1293             : 
    1294             :     // Don't try to write a VRT file.
    1295           8 :     poVRTDS->SetWritable(FALSE);
    1296             : 
    1297          36 :     for (int iBand = 0; iBand < l_nBands; iBand++)
    1298             :     {
    1299          56 :         auto poSrcBandFirstImage = poImageDS->GetRasterBand(
    1300          28 :             iBand < poImageDS->GetRasterCount() ? iBand + 1 : 1);
    1301          56 :         CPLStringList aosAddBandOptions;
    1302             :         int nSrcBlockXSize, nSrcBlockYSize;
    1303          28 :         poSrcBandFirstImage->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
    1304          55 :         if (oMapTileIdxToName.size() == 1 ||
    1305          27 :             ((nTileWidth % nSrcBlockXSize) == 0 &&
    1306          15 :              (nTileHeight % nSrcBlockYSize) == 0))
    1307             :         {
    1308             :             aosAddBandOptions.SetNameValue("BLOCKXSIZE",
    1309          16 :                                            CPLSPrintf("%d", nSrcBlockXSize));
    1310             :             aosAddBandOptions.SetNameValue("BLOCKYSIZE",
    1311          16 :                                            CPLSPrintf("%d", nSrcBlockYSize));
    1312             :         }
    1313          28 :         poVRTDS->AddBand(poSrcBandFirstImage->GetRasterDataType(),
    1314          28 :                          aosAddBandOptions.List());
    1315             : 
    1316             :         VRTSourcedRasterBand *poVRTBand =
    1317             :             reinterpret_cast<VRTSourcedRasterBand *>(
    1318          28 :                 poVRTDS->GetRasterBand(iBand + 1));
    1319          28 :         if (nBits > 0 && nBits != 8 && nBits != 16)
    1320             :         {
    1321          21 :             poVRTBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
    1322          21 :                                        "IMAGE_STRUCTURE");
    1323             :         }
    1324             : 
    1325          83 :         for (const auto &oTileIdxNameTuple : oMapTileIdxToName)
    1326             :         {
    1327          55 :             const int nRow = oTileIdxNameTuple.first.nRow;
    1328          55 :             const int nCol = oTileIdxNameTuple.first.nCol;
    1329          55 :             if (static_cast<int64_t>(nRow - 1) * nTileHeight < nRasterYSize &&
    1330          55 :                 static_cast<int64_t>(nCol - 1) * nTileWidth < nRasterXSize)
    1331             :             {
    1332             :                 int nSrcBand;
    1333          55 :                 if (bTwoDataFilesPerTile)
    1334             :                 {
    1335          12 :                     const int nPart = oTileIdxNameTuple.first.nPart;
    1336          12 :                     if (nPart == 0 && iBand < 3)
    1337             :                     {
    1338           3 :                         nSrcBand = iBand + 1;
    1339             :                     }
    1340           9 :                     else if (nPart == 1 && iBand >= 3)
    1341             :                     {
    1342           3 :                         nSrcBand = iBand + 1 - 3;
    1343             :                     }
    1344             :                     else
    1345             :                     {
    1346           6 :                         continue;
    1347             :                     }
    1348             :                 }
    1349             :                 else
    1350             :                 {
    1351          43 :                     nSrcBand = iBand + 1;
    1352             :                 }
    1353             : 
    1354          49 :                 int nHeight = nTileHeight;
    1355          49 :                 if (static_cast<int64_t>(nRow) * nTileHeight > nRasterYSize)
    1356             :                 {
    1357          21 :                     nHeight = nRasterYSize - (nRow - 1) * nTileHeight;
    1358             :                 }
    1359          49 :                 int nWidth = nTileWidth;
    1360          49 :                 if (static_cast<int64_t>(nCol) * nTileWidth > nRasterXSize)
    1361             :                 {
    1362           0 :                     nWidth = nRasterXSize - (nCol - 1) * nTileWidth;
    1363             :                 }
    1364             : 
    1365          49 :                 poVRTBand->AddSimpleSource(
    1366             :                     oTileIdxNameTuple.second, nSrcBand, 0, 0, nWidth, nHeight,
    1367          49 :                     (nCol - 1) * nTileWidth, (nRow - 1) * nTileHeight, nWidth,
    1368             :                     nHeight);
    1369             :             }
    1370             :         }
    1371             :     }
    1372             : 
    1373             :     /* -------------------------------------------------------------------- */
    1374             :     /*      Expose Overviews if available                                   */
    1375             :     /* -------------------------------------------------------------------- */
    1376           8 :     auto poSrcBandFirstImage = poImageDS->GetRasterBand(1);
    1377             :     const int nSrcOverviews =
    1378           8 :         std::min(30, poSrcBandFirstImage->GetOverviewCount());
    1379           8 :     if (nSrcOverviews > 0)
    1380             :     {
    1381           6 :         CPLConfigOptionSetter oSetter("VRT_VIRTUAL_OVERVIEWS", "YES", false);
    1382           6 :         std::unique_ptr<int[]> ovrLevels(new int[nSrcOverviews]);
    1383           3 :         int iLvl = 1;
    1384           6 :         for (int i = 0; i < nSrcOverviews; i++)
    1385             :         {
    1386           3 :             iLvl *= 2;
    1387           3 :             ovrLevels[i] = iLvl;
    1388             :         }
    1389           3 :         poVRTDS->IBuildOverviews("average", nSrcOverviews, ovrLevels.get(), 0,
    1390           3 :                                  nullptr, nullptr, nullptr, nullptr);
    1391             :     }
    1392             : 
    1393             : #ifdef DEBUG_VERBOSE
    1394             :     CPLDebug("DIMAP", "VRT XML: %s", poVRTDS->GetMetadata("xml:VRT")[0]);
    1395             : #endif
    1396             : 
    1397             :     /* -------------------------------------------------------------------- */
    1398             :     /*      Create band information objects.                                */
    1399             :     /* -------------------------------------------------------------------- */
    1400          36 :     for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
    1401             :     {
    1402             :         GDALRasterBand *poBand = new DIMAPRasterBand(
    1403             :             this, iBand,
    1404          28 :             static_cast<VRTSourcedRasterBand *>(poVRTDS->GetRasterBand(iBand)));
    1405          28 :         if (nBits > 0 && nBits != 8 && nBits != 16)
    1406             :         {
    1407          21 :             poBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
    1408          21 :                                     "IMAGE_STRUCTURE");
    1409             :         }
    1410          28 :         if (bTwoDataFilesPerTile)
    1411             :         {
    1412           6 :             switch (iBand)
    1413             :             {
    1414           1 :                 case 1:
    1415             :                 {
    1416           1 :                     poBand->SetColorInterpretation(GCI_RedBand);
    1417           1 :                     poBand->SetDescription("Red");
    1418           1 :                     break;
    1419             :                 }
    1420           1 :                 case 2:
    1421             :                 {
    1422           1 :                     poBand->SetColorInterpretation(GCI_GreenBand);
    1423           1 :                     poBand->SetDescription("Green");
    1424           1 :                     break;
    1425             :                 }
    1426           1 :                 case 3:
    1427             :                 {
    1428           1 :                     poBand->SetColorInterpretation(GCI_BlueBand);
    1429           1 :                     poBand->SetDescription("Blue");
    1430           1 :                     break;
    1431             :                 }
    1432           1 :                 case 4:
    1433             :                 {
    1434           1 :                     poBand->SetColorInterpretation(GCI_NIRBand);
    1435           1 :                     poBand->SetDescription("NIR");
    1436           1 :                     break;
    1437             :                 }
    1438           1 :                 case 5:
    1439             :                 {
    1440           1 :                     poBand->SetColorInterpretation(GCI_RedEdgeBand);
    1441           1 :                     poBand->SetDescription("Red Edge");
    1442           1 :                     break;
    1443             :                 }
    1444           1 :                 case 6:
    1445             :                 {
    1446           1 :                     poBand->SetColorInterpretation(GCI_CoastalBand);
    1447           1 :                     poBand->SetDescription("Deep Blue");
    1448           1 :                     break;
    1449             :                 }
    1450           0 :                 default:
    1451           0 :                     break;
    1452             :             }
    1453             :         }
    1454          22 :         else if (l_nBands == 1 && osSpectralProcessing == "PAN")
    1455             :         {
    1456           0 :             poBand->SetColorInterpretation(GCI_PanBand);
    1457           0 :             poBand->SetDescription("Panchromatic");
    1458             :         }
    1459          28 :         SetBand(iBand, poBand);
    1460             :     }
    1461             : 
    1462             :     /* -------------------------------------------------------------------- */
    1463             :     /*      Try to collect simple insertion point.                          */
    1464             :     /* -------------------------------------------------------------------- */
    1465             :     CPLXMLNode *psGeoLoc =
    1466           8 :         CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
    1467             : 
    1468           8 :     if (psGeoLoc != nullptr)
    1469             :     {
    1470           1 :         bHaveGeoTransform = TRUE;
    1471           1 :         m_gt[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
    1472           1 :         m_gt[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
    1473           1 :         m_gt[2] = 0.0;
    1474           1 :         m_gt[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
    1475           1 :         m_gt[4] = 0.0;
    1476           1 :         m_gt[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
    1477             :     }
    1478             :     else
    1479             :     {
    1480             :         // Try to get geotransform from underlying raster,
    1481             :         // but make sure it is a real geotransform.
    1482          13 :         if (poImageDS->GetGeoTransform(m_gt) == CE_None &&
    1483           6 :             !(m_gt[0] <= 1.5 && fabs(m_gt[3]) <= 1.5))
    1484             :         {
    1485           6 :             bHaveGeoTransform = TRUE;
    1486             :             // fix up the origin if we did not get the geotransform from the
    1487             :             // top-left tile
    1488           6 :             m_gt[0] -= (nImageDSCol - 1) * m_gt[1] * nTileWidth +
    1489           6 :                        (nImageDSRow - 1) * m_gt[2] * nTileHeight;
    1490          12 :             m_gt[3] -= (nImageDSCol - 1) * m_gt[4] * nTileWidth +
    1491           6 :                        (nImageDSRow - 1) * m_gt[5] * nTileHeight;
    1492             :         }
    1493             :     }
    1494             : 
    1495             :     /* -------------------------------------------------------------------- */
    1496             :     /*      Collect the CRS.  For now we look only for EPSG codes.          */
    1497             :     /* -------------------------------------------------------------------- */
    1498           8 :     const char *pszSRS = CPLGetXMLValue(
    1499             :         psDoc, "Coordinate_Reference_System.Projected_CRS.PROJECTED_CRS_CODE",
    1500             :         nullptr);
    1501           8 :     if (pszSRS == nullptr)
    1502           8 :         pszSRS = CPLGetXMLValue(
    1503             :             psDoc, "Coordinate_Reference_System.Geodetic_CRS.GEODETIC_CRS_CODE",
    1504             :             nullptr);
    1505             : 
    1506           8 :     if (pszSRS != nullptr)
    1507             :     {
    1508           8 :         if (bHaveGeoTransform)
    1509             :         {
    1510           7 :             OGRSpatialReference &oSRS = m_oSRS;
    1511           7 :             oSRS.SetFromUserInput(
    1512             :                 pszSRS,
    1513             :                 OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
    1514             :         }
    1515             :     }
    1516             :     else
    1517             :     {
    1518             :         // Check underlying raster for SRS. We have cases where
    1519             :         // HORIZONTAL_CS_CODE is empty and the underlying raster
    1520             :         // is georeferenced (rprinceley).
    1521           0 :         const auto poSRS = poImageDS->GetSpatialRef();
    1522           0 :         GDALGeoTransform tmpGT;
    1523           0 :         if (poSRS && poImageDS->GetGeoTransform(tmpGT) == CE_None)
    1524             :         {
    1525           0 :             m_oSRS = *poSRS;
    1526             :         }
    1527             :     }
    1528             : 
    1529             :     /* -------------------------------------------------------------------- */
    1530             :     /*      Translate other metadata of interest: DIM_<product_name>.XML    */
    1531             :     /* -------------------------------------------------------------------- */
    1532             : 
    1533             :     static const char *const apszMetadataTranslationDim[] = {
    1534             :         "Product_Information.Delivery_Identification",
    1535             :         "DATASET_",
    1536             :         "Product_Information.Producer_Information",
    1537             :         "DATASET_",
    1538             :         "Dataset_Sources.Source_Identification.Strip_Source",
    1539             :         "",
    1540             :         "Processing_Information.Production_Facility",
    1541             :         "FACILITY_",
    1542             :         "Processing_Information.Product_Settings",
    1543             :         "",
    1544             :         "Processing_Information.Product_Settings.Geometric_Settings",
    1545             :         "GEOMETRIC_",
    1546             :         "Processing_Information.Product_Settings.Radiometric_Settings",
    1547             :         "RADIOMETRIC_",
    1548             :         "Quality_Assessment.Imaging_Quality_Measurement",
    1549             :         "CLOUDCOVER_",
    1550             :         nullptr,
    1551             :         nullptr};
    1552             : 
    1553           8 :     SetMetadataFromXML(psProductDim, apszMetadataTranslationDim);
    1554             : 
    1555             :     /* -------------------------------------------------------------------- */
    1556             :     /*      Translate other metadata of interest: STRIP_<product_name>.XML    */
    1557             :     /* -------------------------------------------------------------------- */
    1558             : 
    1559             :     static const char *const apszMetadataTranslationStrip[] = {
    1560             :         "Catalog.Full_Strip.Notations.Cloud_And_Quality_Notation."
    1561             :         "Data_Strip_Notation",
    1562             :         "CLOUDCOVER_",
    1563             :         "Acquisition_Configuration.Platform_Configuration."
    1564             :         "Ephemeris_Configuration",
    1565             :         "EPHEMERIS_",
    1566             :         nullptr,
    1567             :         nullptr};
    1568             : 
    1569           8 :     if (psProductStrip != nullptr)
    1570           6 :         SetMetadataFromXML(psProductStrip, apszMetadataTranslationStrip);
    1571             : 
    1572           8 :     if (!osRPCFilename.empty())
    1573             :     {
    1574             :         GDALMDReaderPleiades *poReader =
    1575           7 :             GDALMDReaderPleiades::CreateReaderForRPC(osRPCFilename);
    1576           7 :         char **papszRPC = poReader->LoadRPCXmlFile(psDoc);
    1577           7 :         delete poReader;
    1578           7 :         if (papszRPC)
    1579           7 :             SetMetadata(papszRPC, "RPC");
    1580           7 :         CSLDestroy(papszRPC);
    1581             :     }
    1582             : 
    1583             :     CPLXMLNode *psLocatedUseAreaNode =
    1584           8 :         CPLGetXMLNode(psDoc, "Geometric_Data.Use_Area");
    1585           8 :     if (psLocatedUseAreaNode != nullptr)
    1586             :     {
    1587           4 :         CPLXMLNode *psLocatedGeometricValuesNode =
    1588             :             psLocatedUseAreaNode->psChild;
    1589          11 :         while (psLocatedGeometricValuesNode != nullptr)
    1590             :         {
    1591             :             CPLXMLNode *psLocationType =
    1592          11 :                 CPLGetXMLNode(psLocatedGeometricValuesNode, "LOCATION_TYPE");
    1593          11 :             if (psLocationType == nullptr ||
    1594          11 :                 psLocationType->psChild == nullptr ||
    1595          11 :                 !EQUAL(psLocationType->psChild->pszValue, "center"))
    1596             :             {
    1597           7 :                 psLocatedGeometricValuesNode =
    1598             :                     psLocatedGeometricValuesNode->psNext;
    1599           7 :                 continue;
    1600             :             }
    1601             :             static const char *const apszLGVTranslationDim[] = {
    1602             :                 "SATELLITE_ALTITUDE",
    1603             :                 "",
    1604             :                 "Acquisition_Angles",
    1605             :                 "",
    1606             :                 "Solar_Incidences",
    1607             :                 "",
    1608             :                 "Ground_Sample_Distance",
    1609             :                 "",
    1610             :                 nullptr,
    1611             :                 nullptr};
    1612             : 
    1613           4 :             SetMetadataFromXML(psLocatedGeometricValuesNode,
    1614             :                                apszLGVTranslationDim, false);
    1615           4 :             break;
    1616             :         }
    1617             :     }
    1618             : 
    1619             :     /* -------------------------------------------------------------------- */
    1620             :     /*      Set Band metadata from the <Band_Radiance> and                  */
    1621             :     /*                                <Band_Spectral_Range> content         */
    1622             :     /* -------------------------------------------------------------------- */
    1623             : 
    1624           8 :     CPLXMLNode *psImageInterpretationNode = CPLGetXMLNode(
    1625             :         psDoc,
    1626             :         "Radiometric_Data.Radiometric_Calibration.Instrument_Calibration."
    1627             :         "Band_Measurement_List");
    1628           8 :     if (psImageInterpretationNode != nullptr)
    1629             :     {
    1630           7 :         CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
    1631         100 :         while (psSpectralBandInfoNode != nullptr)
    1632             :         {
    1633          93 :             if (psSpectralBandInfoNode->eType == CXT_Element &&
    1634          93 :                 (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance") ||
    1635          66 :                  EQUAL(psSpectralBandInfoNode->pszValue,
    1636          39 :                        "Band_Spectral_Range") ||
    1637          39 :                  EQUAL(psSpectralBandInfoNode->pszValue,
    1638             :                        "Band_Solar_Irradiance")))
    1639             :             {
    1640         162 :                 CPLString osName;
    1641             : 
    1642          81 :                 if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance"))
    1643          27 :                     osName = "RADIANCE_";
    1644          54 :                 else if (EQUAL(psSpectralBandInfoNode->pszValue,
    1645             :                                "Band_Spectral_Range"))
    1646          27 :                     osName = "SPECTRAL_RANGE_";
    1647          27 :                 else if (EQUAL(psSpectralBandInfoNode->pszValue,
    1648             :                                "Band_Solar_Irradiance"))
    1649          27 :                     osName = "SOLAR_IRRADIANCE_";
    1650             : 
    1651          81 :                 CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
    1652          81 :                 int nBandIndex = 0;
    1653         651 :                 while (psTag != nullptr)
    1654             :                 {
    1655         570 :                     if (psTag->eType == CXT_Element &&
    1656         570 :                         psTag->psChild != nullptr &&
    1657         558 :                         psTag->pszValue != nullptr &&
    1658         558 :                         (psTag->psChild->eType == CXT_Text ||
    1659           6 :                          EQUAL(psTag->pszValue, "FWHM")))
    1660             :                     {
    1661         558 :                         if (EQUAL(psTag->pszValue, "BAND_ID"))
    1662             :                         {
    1663          81 :                             nBandIndex = 0;
    1664          81 :                             if (EQUAL(psTag->psChild->pszValue, "P") ||
    1665          81 :                                 EQUAL(psTag->psChild->pszValue, "PAN") ||
    1666          81 :                                 EQUAL(psTag->psChild->pszValue, "B0") ||
    1667          63 :                                 EQUAL(psTag->psChild->pszValue, "R"))
    1668          21 :                                 nBandIndex = 1;
    1669          60 :                             else if (EQUAL(psTag->psChild->pszValue, "B1") ||
    1670          45 :                                      EQUAL(psTag->psChild->pszValue, "G"))
    1671          18 :                                 nBandIndex = 2;
    1672          42 :                             else if (EQUAL(psTag->psChild->pszValue, "B2") ||
    1673          27 :                                      EQUAL(psTag->psChild->pszValue, "B"))
    1674          18 :                                 nBandIndex = 3;
    1675          24 :                             else if (EQUAL(psTag->psChild->pszValue, "B3") ||
    1676           9 :                                      EQUAL(psTag->psChild->pszValue, "NIR"))
    1677          18 :                                 nBandIndex = 4;
    1678           6 :                             else if (EQUAL(psTag->psChild->pszValue, "RE"))
    1679           3 :                                 nBandIndex = 5;
    1680           3 :                             else if (EQUAL(psTag->psChild->pszValue, "DB"))
    1681           3 :                                 nBandIndex = 6;
    1682             : 
    1683         162 :                             if (nBandIndex <= 0 ||
    1684          81 :                                 nBandIndex > GetRasterCount())
    1685             :                             {
    1686           0 :                                 CPLError(CE_Warning, CPLE_AppDefined,
    1687             :                                          "Bad BAND_ID value : %s",
    1688           0 :                                          psTag->psChild->pszValue);
    1689           0 :                                 nBandIndex = 0;
    1690             :                             }
    1691             :                         }
    1692         477 :                         else if (nBandIndex >= 1)
    1693             :                         {
    1694         954 :                             CPLString osMDName = osName;
    1695         477 :                             osMDName += psTag->pszValue;
    1696             : 
    1697         477 :                             auto poBand = GetRasterBand(nBandIndex);
    1698         477 :                             if (EQUAL(psTag->pszValue, "FWHM"))
    1699             :                             {
    1700           6 :                                 if (const char *pszMIN =
    1701           6 :                                         CPLGetXMLValue(psTag, "MIN", nullptr))
    1702           6 :                                     poBand->SetMetadataItem(
    1703          12 :                                         (osMDName + "_MIN").c_str(), pszMIN);
    1704           6 :                                 if (const char *pszMAX =
    1705           6 :                                         CPLGetXMLValue(psTag, "MAX", nullptr))
    1706           6 :                                     poBand->SetMetadataItem(
    1707          12 :                                         (osMDName + "_MAX").c_str(), pszMAX);
    1708             :                             }
    1709             :                             else
    1710             :                             {
    1711         471 :                                 poBand->SetMetadataItem(
    1712         471 :                                     osMDName, psTag->psChild->pszValue);
    1713             :                             }
    1714             :                         }
    1715             :                     }
    1716         570 :                     psTag = psTag->psNext;
    1717             :                 }
    1718             :             }
    1719          93 :             psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
    1720             :         }
    1721             :     }
    1722             : 
    1723             :     // Fill raster band IMAGERY metadata domain from FWHM metadata.
    1724          36 :     for (int i = 1; i <= nBands; ++i)
    1725             :     {
    1726          28 :         auto poBand = GetRasterBand(i);
    1727             :         const char *SPECTRAL_RANGE_MEASURE_UNIT =
    1728          28 :             poBand->GetMetadataItem("SPECTRAL_RANGE_MEASURE_UNIT");
    1729             :         const char *SPECTRAL_RANGE_FWHM_MIN =
    1730          28 :             poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MIN");
    1731             :         const char *SPECTRAL_RANGE_FWHM_MAX =
    1732          28 :             poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MAX");
    1733          28 :         if (SPECTRAL_RANGE_MEASURE_UNIT && SPECTRAL_RANGE_FWHM_MIN &&
    1734           6 :             SPECTRAL_RANGE_FWHM_MAX &&
    1735           6 :             (EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ||
    1736           5 :              EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "micrometer")))
    1737             :         {
    1738           6 :             const double dfFactorToMicrometer =
    1739           6 :                 EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ? 1e-3 : 1.0;
    1740             :             const double dfMin =
    1741           6 :                 CPLAtof(SPECTRAL_RANGE_FWHM_MIN) * dfFactorToMicrometer;
    1742             :             const double dfMax =
    1743           6 :                 CPLAtof(SPECTRAL_RANGE_FWHM_MAX) * dfFactorToMicrometer;
    1744           6 :             poBand->SetMetadataItem("CENTRAL_WAVELENGTH_UM",
    1745           6 :                                     CPLSPrintf("%.3f", (dfMin + dfMax) / 2),
    1746           6 :                                     "IMAGERY");
    1747           6 :             poBand->SetMetadataItem(
    1748           6 :                 "FWHM_UM", CPLSPrintf("%.3f", dfMax - dfMin), "IMAGERY");
    1749             :         }
    1750             :     }
    1751             : 
    1752             :     /* -------------------------------------------------------------------- */
    1753             :     /*      Initialize any PAM information.                                 */
    1754             :     /* -------------------------------------------------------------------- */
    1755           8 :     SetDescription(osMDFilename);
    1756           8 :     TryLoadXML();
    1757             : 
    1758             :     /* -------------------------------------------------------------------- */
    1759             :     /*      Check for overviews.                                            */
    1760             :     /* -------------------------------------------------------------------- */
    1761           8 :     oOvManager.Initialize(this, osMDFilename);
    1762             : 
    1763           8 :     return TRUE;
    1764             : }
    1765             : 
    1766             : /************************************************************************/
    1767             : /*                          SetMetadataFromXML()                        */
    1768             : /************************************************************************/
    1769             : 
    1770          20 : void DIMAPDataset::SetMetadataFromXML(
    1771             :     CPLXMLNode *psProductIn, const char *const apszMetadataTranslation[],
    1772             :     bool bKeysFromRoot)
    1773             : {
    1774          20 :     CPLXMLNode *psDoc = psProductIn;
    1775          20 :     if (bKeysFromRoot)
    1776             :     {
    1777          16 :         psDoc = CPLGetXMLNode(psProductIn, "=Dimap_Document");
    1778          16 :         if (psDoc == nullptr)
    1779             :         {
    1780           0 :             psDoc = CPLGetXMLNode(psProductIn, "=PHR_DIMAP_Document");
    1781             :         }
    1782             :     }
    1783             : 
    1784          20 :     bool bWarnedDiscarding = false;
    1785             : 
    1786         122 :     for (int iTrItem = 0; apszMetadataTranslation[iTrItem] != nullptr;
    1787         102 :          iTrItem += 2)
    1788             :     {
    1789             :         CPLXMLNode *psParent =
    1790         102 :             CPLGetXMLNode(psDoc, apszMetadataTranslation[iTrItem]);
    1791             : 
    1792         102 :         if (psParent == nullptr)
    1793          16 :             continue;
    1794             : 
    1795             :         // Logic to support directly access a name/value entry
    1796          86 :         if (psParent->psChild != nullptr &&
    1797          86 :             psParent->psChild->eType == CXT_Text)
    1798             :         {
    1799           8 :             CPLString osName = apszMetadataTranslation[iTrItem + 1];
    1800           4 :             osName += apszMetadataTranslation[iTrItem];
    1801             :             // Limit size to avoid perf issues when inserting
    1802             :             // in metadata list
    1803           4 :             if (osName.size() < 128)
    1804           4 :                 SetMetadataItem(osName, psParent->psChild->pszValue);
    1805           0 :             else if (!bWarnedDiscarding)
    1806             :             {
    1807           0 :                 bWarnedDiscarding = true;
    1808           0 :                 CPLDebug("DIMAP", "Discarding too long metadata item");
    1809             :             }
    1810           4 :             continue;
    1811             :         }
    1812             : 
    1813             :         // Logic to support a parent element with many name/values.
    1814          82 :         CPLXMLNode *psTarget = psParent->psChild;
    1815         525 :         for (; psTarget != nullptr && psTarget != psParent;
    1816         443 :              psTarget = psTarget->psNext)
    1817             :         {
    1818         443 :             if (psTarget->eType == CXT_Element && psTarget->psChild != nullptr)
    1819             :             {
    1820         874 :                 CPLString osName = apszMetadataTranslation[iTrItem + 1];
    1821             : 
    1822         437 :                 if (psTarget->psChild->eType == CXT_Text)
    1823             :                 {
    1824         307 :                     osName += psTarget->pszValue;
    1825             :                     // Limit size to avoid perf issues when inserting
    1826             :                     // in metadata list
    1827         307 :                     if (osName.size() < 128)
    1828         307 :                         SetMetadataItem(osName, psTarget->psChild->pszValue);
    1829           0 :                     else if (!bWarnedDiscarding)
    1830             :                     {
    1831           0 :                         bWarnedDiscarding = true;
    1832           0 :                         CPLDebug("DIMAP", "Discarding too long metadata item");
    1833             :                     }
    1834             :                 }
    1835         130 :                 else if (psTarget->psChild->eType == CXT_Attribute)
    1836             :                 {
    1837             :                     // find the tag value, at the end of the attributes.
    1838          76 :                     for (CPLXMLNode *psNode = psTarget->psChild;
    1839         219 :                          psNode != nullptr; psNode = psNode->psNext)
    1840             :                     {
    1841         143 :                         if (psNode->eType == CXT_Attribute)
    1842          76 :                             continue;
    1843          67 :                         else if (psNode->eType == CXT_Text)
    1844             :                         {
    1845          67 :                             osName += psTarget->pszValue;
    1846             :                             // Limit size to avoid perf issues when inserting
    1847             :                             // in metadata list
    1848          67 :                             if (osName.size() < 128)
    1849          67 :                                 SetMetadataItem(osName, psNode->pszValue);
    1850           0 :                             else if (!bWarnedDiscarding)
    1851             :                             {
    1852           0 :                                 bWarnedDiscarding = true;
    1853           0 :                                 CPLDebug("DIMAP",
    1854             :                                          "Discarding too long metadata item");
    1855             :                             }
    1856             :                         }
    1857             :                     }
    1858             :                 }
    1859             :             }
    1860             :         }
    1861             :     }
    1862          20 : }
    1863             : 
    1864             : /************************************************************************/
    1865             : /*                            GetGCPCount()                             */
    1866             : /************************************************************************/
    1867             : 
    1868           1 : int DIMAPDataset::GetGCPCount()
    1869             : 
    1870             : {
    1871           1 :     return nGCPCount;
    1872             : }
    1873             : 
    1874             : /************************************************************************/
    1875             : /*                          GetGCPSpatialRef()                          */
    1876             : /************************************************************************/
    1877             : 
    1878           1 : const OGRSpatialReference *DIMAPDataset::GetGCPSpatialRef() const
    1879             : 
    1880             : {
    1881           1 :     return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
    1882             : }
    1883             : 
    1884             : /************************************************************************/
    1885             : /*                               GetGCPs()                              */
    1886             : /************************************************************************/
    1887             : 
    1888           1 : const GDAL_GCP *DIMAPDataset::GetGCPs()
    1889             : 
    1890             : {
    1891           1 :     return pasGCPList;
    1892             : }
    1893             : 
    1894             : /************************************************************************/
    1895             : /*                         GDALRegister_DIMAP()                         */
    1896             : /************************************************************************/
    1897             : 
    1898        2024 : void GDALRegister_DIMAP()
    1899             : 
    1900             : {
    1901        2024 :     if (GDALGetDriverByName("DIMAP") != nullptr)
    1902         283 :         return;
    1903             : 
    1904        1741 :     GDALDriver *poDriver = new GDALDriver();
    1905             : 
    1906        1741 :     poDriver->SetDescription("DIMAP");
    1907        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1908        1741 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "SPOT DIMAP");
    1909        1741 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/dimap.html");
    1910        1741 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1911        1741 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
    1912             : 
    1913        1741 :     poDriver->pfnOpen = DIMAPDataset::Open;
    1914        1741 :     poDriver->pfnIdentify = DIMAPDataset::Identify;
    1915             : 
    1916        1741 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1917             : }

Generated by: LCOV version 1.14