LCOV - code coverage report
Current view: top level - frmts/dimap - dimapdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 577 714 80.8 %
Date: 2024-11-21 22:18:42 Functions: 23 31 74.2 %

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

Generated by: LCOV version 1.14