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

Generated by: LCOV version 1.14