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

Generated by: LCOV version 1.14