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

Generated by: LCOV version 1.14