LCOV - code coverage report
Current view: top level - frmts/nitf - ecrgtocdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 397 460 86.3 %
Date: 2025-06-19 12:30:01 Functions: 25 25 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ECRG TOC read Translator
       4             :  * Purpose:  Implementation of ECRGTOCDataset and ECRGTOCSubDataset.
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2011, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : 
      15             : #include <array>
      16             : #include <cassert>
      17             : #include <cmath>
      18             : #include <cstddef>
      19             : #include <cstdio>
      20             : #include <cstdlib>
      21             : #include <cstring>
      22             : #include <memory>
      23             : #include <string>
      24             : #include <vector>
      25             : 
      26             : #include "cpl_conv.h"
      27             : #include "cpl_error.h"
      28             : #include "cpl_minixml.h"
      29             : #include "cpl_string.h"
      30             : #include "gdal.h"
      31             : #include "gdal_frmts.h"
      32             : #include "gdal_pam.h"
      33             : #include "gdal_priv.h"
      34             : #include "gdal_proxy.h"
      35             : #include "ogr_srs_api.h"
      36             : #include "vrtdataset.h"
      37             : #include "nitfdrivercore.h"
      38             : 
      39             : /** Overview of used classes :
      40             :    - ECRGTOCDataset : lists the different subdatasets, listed in the .xml,
      41             :                       as subdatasets
      42             :    - ECRGTOCSubDataset : one of these subdatasets, implemented as a VRT, of
      43             :                          the relevant NITF tiles
      44             : */
      45             : 
      46             : namespace
      47             : {
      48             : typedef struct
      49             : {
      50             :     const char *pszName;
      51             :     const char *pszPath;
      52             :     int nScale;
      53             :     int nZone;
      54             : } FrameDesc;
      55             : }  // namespace
      56             : 
      57             : /************************************************************************/
      58             : /* ==================================================================== */
      59             : /*                            ECRGTOCDataset                            */
      60             : /* ==================================================================== */
      61             : /************************************************************************/
      62             : 
      63             : class ECRGTOCDataset final : public GDALPamDataset
      64             : {
      65             :     OGRSpatialReference m_oSRS{};
      66             :     char **papszSubDatasets = nullptr;
      67             :     std::array<double, 6> adfGeoTransform = {0};
      68             :     char **papszFileList = nullptr;
      69             : 
      70             :     CPL_DISALLOW_COPY_ASSIGN(ECRGTOCDataset)
      71             : 
      72             :   public:
      73          21 :     ECRGTOCDataset()
      74          21 :     {
      75          21 :         m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      76          21 :         m_oSRS.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
      77          21 :     }
      78             : 
      79          42 :     virtual ~ECRGTOCDataset()
      80          21 :     {
      81          21 :         CSLDestroy(papszSubDatasets);
      82          21 :         CSLDestroy(papszFileList);
      83          42 :     }
      84             : 
      85             :     virtual char **GetMetadata(const char *pszDomain = "") override;
      86             : 
      87           1 :     virtual char **GetFileList() override
      88             :     {
      89           1 :         return CSLDuplicate(papszFileList);
      90             :     }
      91             : 
      92             :     void AddSubDataset(const char *pszFilename, const char *pszProductTitle,
      93             :                        const char *pszDiscId, const char *pszScale);
      94             : 
      95           1 :     virtual CPLErr GetGeoTransform(double *padfGeoTransform) override
      96             :     {
      97           1 :         memcpy(padfGeoTransform, adfGeoTransform.data(),
      98             :                sizeof(adfGeoTransform));
      99           1 :         return CE_None;
     100             :     }
     101             : 
     102           1 :     const OGRSpatialReference *GetSpatialRef() const override
     103             :     {
     104           1 :         return &m_oSRS;
     105             :     }
     106             : 
     107             :     static GDALDataset *Build(const char *pszTOCFilename, CPLXMLNode *psXML,
     108             :                               const std::string &osProduct,
     109             :                               const std::string &osDiscId,
     110             :                               const std::string &osScale,
     111             :                               const char *pszFilename);
     112             : 
     113             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
     114             : };
     115             : 
     116             : /************************************************************************/
     117             : /* ==================================================================== */
     118             : /*                            ECRGTOCSubDataset                          */
     119             : /* ==================================================================== */
     120             : /************************************************************************/
     121             : 
     122             : class ECRGTOCSubDataset final : public VRTDataset
     123             : {
     124             :     char **papszFileList = nullptr;
     125             :     CPL_DISALLOW_COPY_ASSIGN(ECRGTOCSubDataset)
     126             : 
     127             :   public:
     128          11 :     ECRGTOCSubDataset(int nXSize, int nYSize) : VRTDataset(nXSize, nYSize)
     129             :     {
     130             :         /* Don't try to write a VRT file */
     131          11 :         SetWritable(FALSE);
     132             : 
     133             :         /* The driver is set to VRT in VRTDataset constructor. */
     134             :         /* We have to set it to the expected value ! */
     135          11 :         poDriver = GDALDriver::FromHandle(GDALGetDriverByName("ECRGTOC"));
     136          11 :     }
     137             : 
     138             :     ~ECRGTOCSubDataset() override;
     139             : 
     140           2 :     virtual char **GetFileList() override
     141             :     {
     142           2 :         return CSLDuplicate(papszFileList);
     143             :     }
     144             : 
     145             :     static GDALDataset *
     146             :     Build(const char *pszProductTitle, const char *pszDiscId, int nScale,
     147             :           int nCountSubDataset, const char *pszTOCFilename,
     148             :           const std::vector<FrameDesc> &aosFrameDesc, double dfGlobalMinX,
     149             :           double dfGlobalMinY, double dfGlobalMaxX, double dfGlobalMaxY,
     150             :           double dfGlobalPixelXSize, double dfGlobalPixelYSize);
     151             : };
     152             : 
     153          22 : ECRGTOCSubDataset::~ECRGTOCSubDataset()
     154             : {
     155          11 :     CSLDestroy(papszFileList);
     156          22 : }
     157             : 
     158             : /************************************************************************/
     159             : /*                           LaunderString()                            */
     160             : /************************************************************************/
     161             : 
     162          67 : static CPLString LaunderString(const char *pszStr)
     163             : {
     164          67 :     CPLString osRet(pszStr);
     165         632 :     for (size_t i = 0; i < osRet.size(); i++)
     166             :     {
     167         565 :         if (osRet[i] == ':' || osRet[i] == ' ')
     168          42 :             osRet[i] = '_';
     169             :     }
     170          67 :     return osRet;
     171             : }
     172             : 
     173             : /************************************************************************/
     174             : /*                           AddSubDataset()                            */
     175             : /************************************************************************/
     176             : 
     177           9 : void ECRGTOCDataset::AddSubDataset(const char *pszFilename,
     178             :                                    const char *pszProductTitle,
     179             :                                    const char *pszDiscId, const char *pszScale)
     180             : 
     181             : {
     182             :     char szName[80];
     183           9 :     const int nCount = CSLCount(papszSubDatasets) / 2;
     184             : 
     185           9 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
     186          27 :     papszSubDatasets = CSLSetNameValue(
     187             :         papszSubDatasets, szName,
     188             :         CPLSPrintf("ECRG_TOC_ENTRY:%s:%s:%s:%s",
     189          18 :                    LaunderString(pszProductTitle).c_str(),
     190          18 :                    LaunderString(pszDiscId).c_str(),
     191          18 :                    LaunderString(pszScale).c_str(), pszFilename));
     192             : 
     193           9 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
     194           9 :     papszSubDatasets =
     195           9 :         CSLSetNameValue(papszSubDatasets, szName,
     196             :                         CPLSPrintf("Product %s, disc %s, scale %s",
     197             :                                    pszProductTitle, pszDiscId, pszScale));
     198           9 : }
     199             : 
     200             : /************************************************************************/
     201             : /*                            GetMetadata()                             */
     202             : /************************************************************************/
     203             : 
     204           7 : char **ECRGTOCDataset::GetMetadata(const char *pszDomain)
     205             : 
     206             : {
     207           7 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
     208           7 :         return papszSubDatasets;
     209             : 
     210           0 :     return GDALPamDataset::GetMetadata(pszDomain);
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*                         GetScaleFromString()                         */
     215             : /************************************************************************/
     216             : 
     217          22 : static int GetScaleFromString(const char *pszScale)
     218             : {
     219          22 :     const char *pszPtr = strstr(pszScale, "1:");
     220          22 :     if (pszPtr)
     221          22 :         pszPtr = pszPtr + 2;
     222             :     else
     223           0 :         pszPtr = pszScale;
     224             : 
     225          22 :     int nScale = 0;
     226             :     char ch;
     227         112 :     while ((ch = *pszPtr) != '\0')
     228             :     {
     229         112 :         if (ch >= '0' && ch <= '9')
     230          68 :             nScale = nScale * 10 + ch - '0';
     231          44 :         else if (ch == ' ')
     232             :             ;
     233          22 :         else if (ch == 'k' || ch == 'K')
     234          22 :             return nScale * 1000;
     235           0 :         else if (ch == 'm' || ch == 'M')
     236           0 :             return nScale * 1000000;
     237             :         else
     238           0 :             return 0;
     239          90 :         pszPtr++;
     240             :     }
     241           0 :     return nScale;
     242             : }
     243             : 
     244             : /************************************************************************/
     245             : /*                            GetFromBase34()                           */
     246             : /************************************************************************/
     247             : 
     248          53 : static GIntBig GetFromBase34(const char *pszVal, int nMaxSize)
     249             : {
     250          53 :     GIntBig nFrameNumber = 0;
     251         583 :     for (int i = 0; i < nMaxSize; i++)
     252             :     {
     253         530 :         char ch = pszVal[i];
     254         530 :         if (ch == '\0')
     255           0 :             break;
     256             :         int chVal;
     257         530 :         if (ch >= 'A' && ch <= 'Z')
     258           0 :             ch += 'a' - 'A';
     259             :         /* i and o letters are excluded, */
     260         530 :         if (ch >= '0' && ch <= '9')
     261         477 :             chVal = ch - '0';
     262          53 :         else if (ch >= 'a' && ch <= 'h')
     263           0 :             chVal = ch - 'a' + 10;
     264          53 :         else if (ch >= 'j' && ch <= 'n')
     265           0 :             chVal = ch - 'a' + 10 - 1;
     266          53 :         else if (ch >= 'p' && ch <= 'z')
     267          53 :             chVal = ch - 'a' + 10 - 2;
     268             :         else
     269             :         {
     270           0 :             CPLDebug("ECRG", "Invalid base34 value : %s", pszVal);
     271           0 :             break;
     272             :         }
     273         530 :         nFrameNumber = nFrameNumber * 34 + chVal;
     274             :     }
     275             : 
     276          53 :     return nFrameNumber;
     277             : }
     278             : 
     279             : /************************************************************************/
     280             : /*                             GetExtent()                              */
     281             : /************************************************************************/
     282             : 
     283             : /* MIL-PRF-32283 - Table II. ECRG zone limits. */
     284             : /* starting with a fake zone 0 for convenience. */
     285             : constexpr int anZoneUpperLat[] = {0, 32, 48, 56, 64, 68, 72, 76, 80};
     286             : 
     287             : /* APPENDIX 70, TABLE III of MIL-A-89007 */
     288             : constexpr int anACst_ADRG[] = {369664, 302592, 245760, 199168,
     289             :                                163328, 137216, 110080, 82432};
     290             : constexpr int nBCst_ADRG = 400384;
     291             : 
     292             : // TODO: Why are these two functions done this way?
     293         106 : static int CEIL_ROUND(double a, double b)
     294             : {
     295         106 :     return static_cast<int>(ceil(a / b) * b);
     296             : }
     297             : 
     298         106 : static int NEAR_ROUND(double a, double b)
     299             : {
     300         106 :     return static_cast<int>(floor((a / b) + 0.5) * b);
     301             : }
     302             : 
     303             : constexpr int ECRG_PIXELS = 2304;
     304             : 
     305          53 : static void GetExtent(const char *pszFrameName, int nScale, int nZone,
     306             :                       double &dfMinX, double &dfMaxX, double &dfMinY,
     307             :                       double &dfMaxY, double &dfPixelXSize,
     308             :                       double &dfPixelYSize)
     309             : {
     310          53 :     const int nAbsZone = abs(nZone);
     311             : #ifdef DEBUG
     312          53 :     assert(nAbsZone > 0 && nAbsZone <= 8);
     313             : #endif
     314             : 
     315             :     /************************************************************************/
     316             :     /*  Compute east-west constant                                          */
     317             :     /************************************************************************/
     318             :     /* MIL-PRF-89038 - 60.1.2 - East-west pixel constant. */
     319             :     const int nEW_ADRG =
     320          53 :         CEIL_ROUND(anACst_ADRG[nAbsZone - 1] * (1e6 / nScale), 512);
     321          53 :     const int nEW_CADRG = NEAR_ROUND(nEW_ADRG / (150. / 100.), 256);
     322             :     /* MIL-PRF-32283 - D.2.1.2 - East-west pixel constant. */
     323          53 :     const int nEW = nEW_CADRG / 256 * 384;
     324             : 
     325             :     /************************************************************************/
     326             :     /*  Compute number of longitudinal frames                               */
     327             :     /************************************************************************/
     328             :     /* MIL-PRF-32283 - D.2.1.7 - Longitudinal frames and subframes */
     329          53 :     const int nCols =
     330          53 :         static_cast<int>(ceil(static_cast<double>(nEW) / ECRG_PIXELS));
     331             : 
     332             :     /************************************************************************/
     333             :     /*  Compute north-south constant                                        */
     334             :     /************************************************************************/
     335             :     /* MIL-PRF-89038 - 60.1.1 -  North-south. pixel constant */
     336          53 :     const int nNS_ADRG = CEIL_ROUND(nBCst_ADRG * (1e6 / nScale), 512) / 4;
     337          53 :     const int nNS_CADRG = NEAR_ROUND(nNS_ADRG / (150. / 100.), 256);
     338             :     /* MIL-PRF-32283 - D.2.1.1 - North-south. pixel constant and Frame
     339             :      * Width/Height */
     340          53 :     const int nNS = nNS_CADRG / 256 * 384;
     341             : 
     342             :     /************************************************************************/
     343             :     /*  Compute number of latitudinal frames and latitude of top of zone    */
     344             :     /************************************************************************/
     345          53 :     dfPixelYSize = 90.0 / nNS;
     346             : 
     347          53 :     const double dfFrameLatHeight = dfPixelYSize * ECRG_PIXELS;
     348             : 
     349             :     /* MIL-PRF-32283 - D.2.1.5 - Equatorward and poleward zone extents. */
     350          53 :     int nUpperZoneFrames =
     351          53 :         static_cast<int>(ceil(anZoneUpperLat[nAbsZone] / dfFrameLatHeight));
     352          53 :     int nBottomZoneFrames = static_cast<int>(
     353          53 :         floor(anZoneUpperLat[nAbsZone - 1] / dfFrameLatHeight));
     354          53 :     const int nRows = nUpperZoneFrames - nBottomZoneFrames;
     355             : 
     356             :     /* Not sure to really understand D.2.1.5.a. Testing needed */
     357          53 :     if (nZone < 0)
     358             :     {
     359           0 :         nUpperZoneFrames = -nBottomZoneFrames;
     360             :         /*nBottomZoneFrames = nUpperZoneFrames - nRows;*/
     361             :     }
     362             : 
     363          53 :     const double dfUpperZoneTopLat = dfFrameLatHeight * nUpperZoneFrames;
     364             : 
     365             :     /************************************************************************/
     366             :     /*  Compute coordinates of the frame in the zone                        */
     367             :     /************************************************************************/
     368             : 
     369             :     /* Converts the first 10 characters into a number from base 34 */
     370          53 :     const GIntBig nFrameNumber = GetFromBase34(pszFrameName, 10);
     371             : 
     372             :     /*  MIL-PRF-32283 - A.2.6.1 */
     373          53 :     const GIntBig nY = nFrameNumber / nCols;
     374          53 :     const GIntBig nX = nFrameNumber % nCols;
     375             : 
     376             :     /************************************************************************/
     377             :     /*  Compute extent of the frame                                         */
     378             :     /************************************************************************/
     379             : 
     380             :     /* The nY is counted from the bottom of the zone... Pfff */
     381          53 :     dfMaxY = dfUpperZoneTopLat - (nRows - 1 - nY) * dfFrameLatHeight;
     382          53 :     dfMinY = dfMaxY - dfFrameLatHeight;
     383             : 
     384          53 :     dfPixelXSize = 360.0 / nEW;
     385             : 
     386          53 :     const double dfFrameLongWidth = dfPixelXSize * ECRG_PIXELS;
     387          53 :     dfMinX = -180.0 + nX * dfFrameLongWidth;
     388          53 :     dfMaxX = dfMinX + dfFrameLongWidth;
     389             : 
     390             : #ifdef DEBUG_VERBOSE
     391             :     CPLDebug("ECRG",
     392             :              "Frame %s : minx=%.16g, maxy=%.16g, maxx=%.16g, miny=%.16g",
     393             :              pszFrameName, dfMinX, dfMaxY, dfMaxX, dfMinY);
     394             : #endif
     395          53 : }
     396             : 
     397             : /************************************************************************/
     398             : /*                          ECRGTOCSource                               */
     399             : /************************************************************************/
     400             : 
     401             : class ECRGTOCSource final : public VRTSimpleSource
     402             : {
     403             :     int m_nRasterXSize = 0;
     404             :     int m_nRasterYSize = 0;
     405             :     double m_dfMinX = 0;
     406             :     double m_dfMaxY = 0;
     407             :     double m_dfPixelXSize = 0;
     408             :     double m_dfPixelYSize = 0;
     409             : 
     410             :     bool ValidateOpenedBand(GDALRasterBand *) const override;
     411             : 
     412             :   public:
     413          57 :     ECRGTOCSource(const char *pszFilename, int nBandIn, int nRasterXSize,
     414             :                   int nRasterYSize, double dfDstXOff, double dfDstYOff,
     415             :                   double dfDstXSize, double dfDstYSize, double dfMinX,
     416             :                   double dfMaxY, double dfPixelXSize, double dfPixelYSize)
     417          57 :         : m_nRasterXSize(nRasterXSize), m_nRasterYSize(nRasterYSize),
     418             :           m_dfMinX(dfMinX), m_dfMaxY(dfMaxY), m_dfPixelXSize(dfPixelXSize),
     419          57 :           m_dfPixelYSize(dfPixelYSize)
     420             :     {
     421          57 :         SetSrcBand(pszFilename, nBandIn);
     422          57 :         SetSrcWindow(0, 0, nRasterXSize, nRasterYSize);
     423          57 :         SetDstWindow(dfDstXOff, dfDstYOff, dfDstXSize, dfDstYSize);
     424          57 :     }
     425             : };
     426             : 
     427             : /************************************************************************/
     428             : /*                       ValidateOpenedBand()                           */
     429             : /************************************************************************/
     430             : 
     431             : #define WARN_CHECK_DS(x)                                                       \
     432             :     do                                                                         \
     433             :     {                                                                          \
     434             :         if (!(x))                                                              \
     435             :         {                                                                      \
     436             :             CPLError(CE_Warning, CPLE_AppDefined,                              \
     437             :                      "For %s, assert '" #x "' failed",                         \
     438             :                      poSourceDS->GetDescription());                            \
     439             :             checkOK = false;                                                   \
     440             :         }                                                                      \
     441             :     } while (false)
     442             : 
     443          10 : bool ECRGTOCSource::ValidateOpenedBand(GDALRasterBand *poBand) const
     444             : {
     445          10 :     bool checkOK = true;
     446          10 :     auto poSourceDS = poBand->GetDataset();
     447          10 :     CPLAssert(poSourceDS);
     448             : 
     449          10 :     double l_adfGeoTransform[6] = {};
     450          10 :     poSourceDS->GetGeoTransform(l_adfGeoTransform);
     451          10 :     WARN_CHECK_DS(fabs(l_adfGeoTransform[0] - m_dfMinX) < 1e-10);
     452          10 :     WARN_CHECK_DS(fabs(l_adfGeoTransform[3] - m_dfMaxY) < 1e-10);
     453          10 :     WARN_CHECK_DS(fabs(l_adfGeoTransform[1] - m_dfPixelXSize) < 1e-10);
     454          10 :     WARN_CHECK_DS(fabs(l_adfGeoTransform[5] - (-m_dfPixelYSize)) < 1e-10);
     455          10 :     WARN_CHECK_DS(l_adfGeoTransform[2] == 0 &&
     456             :                   l_adfGeoTransform[4] == 0);  // No rotation.
     457          10 :     WARN_CHECK_DS(poSourceDS->GetRasterCount() == 3);
     458          10 :     WARN_CHECK_DS(poSourceDS->GetRasterXSize() == m_nRasterXSize);
     459          10 :     WARN_CHECK_DS(poSourceDS->GetRasterYSize() == m_nRasterYSize);
     460          10 :     WARN_CHECK_DS(
     461             :         EQUAL(poSourceDS->GetProjectionRef(), SRS_WKT_WGS84_LAT_LONG));
     462          10 :     WARN_CHECK_DS(poSourceDS->GetRasterBand(1)->GetRasterDataType() ==
     463             :                   GDT_Byte);
     464          10 :     return checkOK;
     465             : }
     466             : 
     467             : /************************************************************************/
     468             : /*                           BuildFullName()                            */
     469             : /************************************************************************/
     470             : 
     471          53 : static std::string BuildFullName(const char *pszTOCFilename,
     472             :                                  const char *pszFramePath,
     473             :                                  const char *pszFrameName)
     474             : {
     475          53 :     char *pszPath = nullptr;
     476          53 :     if (pszFramePath[0] == '.' &&
     477           0 :         (pszFramePath[1] == '/' || pszFramePath[1] == '\\'))
     478           0 :         pszPath = CPLStrdup(pszFramePath + 2);
     479             :     else
     480          53 :         pszPath = CPLStrdup(pszFramePath);
     481         371 :     for (int i = 0; pszPath[i] != '\0'; i++)
     482             :     {
     483         318 :         if (pszPath[i] == '\\')
     484          53 :             pszPath[i] = '/';
     485             :     }
     486             :     const std::string osName =
     487         106 :         CPLFormFilenameSafe(pszPath, pszFrameName, nullptr);
     488          53 :     CPLFree(pszPath);
     489          53 :     pszPath = nullptr;
     490         106 :     std::string osTOCPath = CPLGetDirnameSafe(pszTOCFilename);
     491          53 :     const auto nPosFirstSlashInName = osName.find('/');
     492          53 :     if (nPosFirstSlashInName != std::string::npos)
     493             :     {
     494          53 :         if (osTOCPath.size() >= nPosFirstSlashInName + 1 &&
     495          53 :             (osTOCPath[osTOCPath.size() - (nPosFirstSlashInName + 1)] == '/' ||
     496          53 :              osTOCPath[osTOCPath.size() - (nPosFirstSlashInName + 1)] ==
     497         106 :                  '\\') &&
     498           0 :             strncmp(osTOCPath.c_str() + osTOCPath.size() - nPosFirstSlashInName,
     499             :                     osName.c_str(), nPosFirstSlashInName) == 0)
     500             :         {
     501           0 :             osTOCPath = CPLGetDirnameSafe(osTOCPath.c_str());
     502             :         }
     503             :     }
     504         106 :     return CPLProjectRelativeFilenameSafe(osTOCPath.c_str(), osName.c_str());
     505             : }
     506             : 
     507             : /************************************************************************/
     508             : /*                              Build()                                 */
     509             : /************************************************************************/
     510             : 
     511             : /* Builds a ECRGTOCSubDataset from the set of files of the toc entry */
     512          11 : GDALDataset *ECRGTOCSubDataset::Build(
     513             :     const char *pszProductTitle, const char *pszDiscId, int nScale,
     514             :     int nCountSubDataset, const char *pszTOCFilename,
     515             :     const std::vector<FrameDesc> &aosFrameDesc, double dfGlobalMinX,
     516             :     double dfGlobalMinY, double dfGlobalMaxX, double dfGlobalMaxY,
     517             :     double dfGlobalPixelXSize, double dfGlobalPixelYSize)
     518             : {
     519          11 :     GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("VRT");
     520          11 :     if (poDriver == nullptr)
     521           0 :         return nullptr;
     522             : 
     523          11 :     const int nSizeX = static_cast<int>(
     524          11 :         (dfGlobalMaxX - dfGlobalMinX) / dfGlobalPixelXSize + 0.5);
     525          11 :     const int nSizeY = static_cast<int>(
     526          11 :         (dfGlobalMaxY - dfGlobalMinY) / dfGlobalPixelYSize + 0.5);
     527             : 
     528             :     /* ------------------------------------ */
     529             :     /* Create the VRT with the overall size */
     530             :     /* ------------------------------------ */
     531          11 :     ECRGTOCSubDataset *poVirtualDS = new ECRGTOCSubDataset(nSizeX, nSizeY);
     532             : 
     533          11 :     poVirtualDS->SetProjection(SRS_WKT_WGS84_LAT_LONG);
     534             : 
     535          11 :     double adfGeoTransform[6] = {
     536             :         dfGlobalMinX,       dfGlobalPixelXSize, 0, dfGlobalMaxY, 0,
     537          11 :         -dfGlobalPixelYSize};
     538          11 :     poVirtualDS->SetGeoTransform(adfGeoTransform);
     539             : 
     540          44 :     for (int i = 0; i < 3; i++)
     541             :     {
     542          33 :         poVirtualDS->AddBand(GDT_Byte, nullptr);
     543          33 :         GDALRasterBand *poBand = poVirtualDS->GetRasterBand(i + 1);
     544          33 :         poBand->SetColorInterpretation(
     545          33 :             static_cast<GDALColorInterp>(GCI_RedBand + i));
     546             :     }
     547             : 
     548          11 :     poVirtualDS->SetDescription(pszTOCFilename);
     549             : 
     550          11 :     poVirtualDS->SetMetadataItem("PRODUCT_TITLE", pszProductTitle);
     551          11 :     poVirtualDS->SetMetadataItem("DISC_ID", pszDiscId);
     552          11 :     if (nScale != -1)
     553          11 :         poVirtualDS->SetMetadataItem("SCALE", CPLString().Printf("%d", nScale));
     554             : 
     555             :     /* -------------------------------------------------------------------- */
     556             :     /*      Check for overviews.                                            */
     557             :     /* -------------------------------------------------------------------- */
     558             : 
     559          11 :     poVirtualDS->oOvManager.Initialize(
     560             :         poVirtualDS,
     561          22 :         CPLString().Printf("%s.%d", pszTOCFilename, nCountSubDataset));
     562             : 
     563          11 :     poVirtualDS->papszFileList = poVirtualDS->GDALDataset::GetFileList();
     564             : 
     565             :     // Rather hacky... Force GDAL_FORCE_CACHING=NO so that the
     566             :     // GDALProxyPoolRasterBand do not use the GDALRasterBand::IRasterIO()
     567             :     // default implementation, which would rely on the block size of
     568             :     // GDALProxyPoolRasterBand, which we don't know...
     569          11 :     CPLConfigOptionSetter oSetter("GDAL_FORCE_CACHING", "NO", false);
     570             : 
     571          30 :     for (int i = 0; i < static_cast<int>(aosFrameDesc.size()); i++)
     572             :     {
     573             :         const std::string osName = BuildFullName(
     574          38 :             pszTOCFilename, aosFrameDesc[i].pszPath, aosFrameDesc[i].pszName);
     575             : 
     576          19 :         double dfMinX = 0.0;
     577          19 :         double dfMaxX = 0.0;
     578          19 :         double dfMinY = 0.0;
     579          19 :         double dfMaxY = 0.0;
     580          19 :         double dfPixelXSize = 0.0;
     581          19 :         double dfPixelYSize = 0.0;
     582          19 :         ::GetExtent(aosFrameDesc[i].pszName, aosFrameDesc[i].nScale,
     583          19 :                     aosFrameDesc[i].nZone, dfMinX, dfMaxX, dfMinY, dfMaxY,
     584             :                     dfPixelXSize, dfPixelYSize);
     585             : 
     586          19 :         const int nFrameXSize =
     587          19 :             static_cast<int>((dfMaxX - dfMinX) / dfPixelXSize + 0.5);
     588          19 :         const int nFrameYSize =
     589          19 :             static_cast<int>((dfMaxY - dfMinY) / dfPixelYSize + 0.5);
     590             : 
     591          19 :         poVirtualDS->papszFileList =
     592          19 :             CSLAddString(poVirtualDS->papszFileList, osName.c_str());
     593             : 
     594          76 :         for (int j = 0; j < 3; j++)
     595             :         {
     596             :             VRTSourcedRasterBand *poBand =
     597          57 :                 cpl::down_cast<VRTSourcedRasterBand *>(
     598             :                     poVirtualDS->GetRasterBand(j + 1));
     599             :             /* Place the raster band at the right position in the VRT */
     600             :             auto poSource = new ECRGTOCSource(
     601          57 :                 osName.c_str(), j + 1, nFrameXSize, nFrameYSize,
     602          57 :                 static_cast<int>((dfMinX - dfGlobalMinX) / dfGlobalPixelXSize +
     603             :                                  0.5),
     604          57 :                 static_cast<int>((dfGlobalMaxY - dfMaxY) / dfGlobalPixelYSize +
     605             :                                  0.5),
     606          57 :                 static_cast<int>((dfMaxX - dfMinX) / dfGlobalPixelXSize + 0.5),
     607          57 :                 static_cast<int>((dfMaxY - dfMinY) / dfGlobalPixelYSize + 0.5),
     608          57 :                 dfMinX, dfMaxY, dfPixelXSize, dfPixelYSize);
     609          57 :             poBand->AddSource(poSource);
     610             :         }
     611             :     }
     612             : 
     613          11 :     poVirtualDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
     614             : 
     615          11 :     return poVirtualDS;
     616             : }
     617             : 
     618             : /************************************************************************/
     619             : /*                             Build()                                  */
     620             : /************************************************************************/
     621             : 
     622          21 : GDALDataset *ECRGTOCDataset::Build(const char *pszTOCFilename,
     623             :                                    CPLXMLNode *psXML,
     624             :                                    const std::string &osProduct,
     625             :                                    const std::string &osDiscId,
     626             :                                    const std::string &osScale,
     627             :                                    const char *pszOpenInfoFilename)
     628             : {
     629          21 :     CPLXMLNode *psTOC = CPLGetXMLNode(psXML, "=Table_of_Contents");
     630          21 :     if (psTOC == nullptr)
     631             :     {
     632           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     633             :                  "Cannot find Table_of_Contents element");
     634           0 :         return nullptr;
     635             :     }
     636             : 
     637          21 :     double dfGlobalMinX = 0.0;
     638          21 :     double dfGlobalMinY = 0.0;
     639          21 :     double dfGlobalMaxX = 0.0;
     640          21 :     double dfGlobalMaxY = 0.0;
     641          21 :     double dfGlobalPixelXSize = 0.0;
     642          21 :     double dfGlobalPixelYSize = 0.0;
     643          21 :     bool bGlobalExtentValid = false;
     644             : 
     645          21 :     ECRGTOCDataset *poDS = new ECRGTOCDataset();
     646          21 :     int nSubDatasets = 0;
     647             : 
     648          21 :     int bLookForSubDataset = !osProduct.empty() && !osDiscId.empty();
     649             : 
     650          21 :     int nCountSubDataset = 0;
     651             : 
     652          21 :     poDS->SetDescription(pszOpenInfoFilename);
     653          21 :     poDS->papszFileList = poDS->GDALDataset::GetFileList();
     654             : 
     655          62 :     for (CPLXMLNode *psIter1 = psTOC->psChild; psIter1 != nullptr;
     656          41 :          psIter1 = psIter1->psNext)
     657             :     {
     658          52 :         if (!(psIter1->eType == CXT_Element && psIter1->pszValue != nullptr &&
     659          52 :               strcmp(psIter1->pszValue, "product") == 0))
     660          31 :             continue;
     661             : 
     662             :         const char *pszProductTitle =
     663          21 :             CPLGetXMLValue(psIter1, "product_title", nullptr);
     664          21 :         if (pszProductTitle == nullptr)
     665             :         {
     666           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     667             :                      "Cannot find product_title attribute");
     668           0 :             continue;
     669             :         }
     670             : 
     671          35 :         if (bLookForSubDataset &&
     672          35 :             strcmp(LaunderString(pszProductTitle), osProduct.c_str()) != 0)
     673           1 :             continue;
     674             : 
     675          51 :         for (CPLXMLNode *psIter2 = psIter1->psChild; psIter2 != nullptr;
     676          31 :              psIter2 = psIter2->psNext)
     677             :         {
     678          42 :             if (!(psIter2->eType == CXT_Element &&
     679          22 :                   psIter2->pszValue != nullptr &&
     680          22 :                   strcmp(psIter2->pszValue, "disc") == 0))
     681          20 :                 continue;
     682             : 
     683          22 :             const char *pszDiscId = CPLGetXMLValue(psIter2, "id", nullptr);
     684          22 :             if (pszDiscId == nullptr)
     685             :             {
     686           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     687             :                          "Cannot find id attribute");
     688           0 :                 continue;
     689             :             }
     690             : 
     691          36 :             if (bLookForSubDataset &&
     692          36 :                 strcmp(LaunderString(pszDiscId), osDiscId.c_str()) != 0)
     693           2 :                 continue;
     694             : 
     695          20 :             CPLXMLNode *psFrameList = CPLGetXMLNode(psIter2, "frame_list");
     696          20 :             if (psFrameList == nullptr)
     697             :             {
     698           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     699             :                          "Cannot find frame_list element");
     700           0 :                 continue;
     701             :             }
     702             : 
     703          51 :             for (CPLXMLNode *psIter3 = psFrameList->psChild; psIter3 != nullptr;
     704          31 :                  psIter3 = psIter3->psNext)
     705             :             {
     706          42 :                 if (!(psIter3->eType == CXT_Element &&
     707          22 :                       psIter3->pszValue != nullptr &&
     708          22 :                       strcmp(psIter3->pszValue, "scale") == 0))
     709          22 :                     continue;
     710             : 
     711          22 :                 const char *pszSize = CPLGetXMLValue(psIter3, "size", nullptr);
     712          22 :                 if (pszSize == nullptr)
     713             :                 {
     714           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     715             :                              "Cannot find size attribute");
     716           0 :                     continue;
     717             :                 }
     718             : 
     719          22 :                 int nScale = GetScaleFromString(pszSize);
     720          22 :                 if (nScale <= 0)
     721             :                 {
     722           0 :                     CPLError(CE_Warning, CPLE_AppDefined, "Invalid scale %s",
     723             :                              pszSize);
     724           0 :                     continue;
     725             :                 }
     726             : 
     727          22 :                 if (bLookForSubDataset)
     728             :                 {
     729          13 :                     if (!osScale.empty())
     730             :                     {
     731          12 :                         if (strcmp(LaunderString(pszSize), osScale.c_str()) !=
     732             :                             0)
     733             :                         {
     734           2 :                             continue;
     735             :                         }
     736             :                     }
     737             :                     else
     738             :                     {
     739           1 :                         int nCountScales = 0;
     740           1 :                         for (CPLXMLNode *psIter4 = psFrameList->psChild;
     741           3 :                              psIter4 != nullptr; psIter4 = psIter4->psNext)
     742             :                         {
     743           2 :                             if (!(psIter4->eType == CXT_Element &&
     744           1 :                                   psIter4->pszValue != nullptr &&
     745           1 :                                   strcmp(psIter4->pszValue, "scale") == 0))
     746           1 :                                 continue;
     747           1 :                             nCountScales++;
     748             :                         }
     749           1 :                         if (nCountScales > 1)
     750             :                         {
     751           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     752             :                                      "Scale should be mentioned in "
     753             :                                      "subdatasets syntax since this disk "
     754             :                                      "contains several scales");
     755           0 :                             delete poDS;
     756          11 :                             return nullptr;
     757             :                         }
     758             :                     }
     759             :                 }
     760             : 
     761          20 :                 nCountSubDataset++;
     762             : 
     763          20 :                 std::vector<FrameDesc> aosFrameDesc;
     764          20 :                 int nValidFrames = 0;
     765             : 
     766          74 :                 for (CPLXMLNode *psIter4 = psIter3->psChild; psIter4 != nullptr;
     767          54 :                      psIter4 = psIter4->psNext)
     768             :                 {
     769          54 :                     if (!(psIter4->eType == CXT_Element &&
     770          34 :                           psIter4->pszValue != nullptr &&
     771          34 :                           strcmp(psIter4->pszValue, "frame") == 0))
     772          20 :                         continue;
     773             : 
     774             :                     const char *pszFrameName =
     775          34 :                         CPLGetXMLValue(psIter4, "name", nullptr);
     776          34 :                     if (pszFrameName == nullptr)
     777             :                     {
     778           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     779             :                                  "Cannot find name element");
     780           0 :                         continue;
     781             :                     }
     782             : 
     783          34 :                     if (strlen(pszFrameName) != 18)
     784             :                     {
     785           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     786             :                                  "Invalid value for name element : %s",
     787             :                                  pszFrameName);
     788           0 :                         continue;
     789             :                     }
     790             : 
     791             :                     const char *pszFramePath =
     792          34 :                         CPLGetXMLValue(psIter4, "frame_path", nullptr);
     793          34 :                     if (pszFramePath == nullptr)
     794             :                     {
     795           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     796             :                                  "Cannot find frame_path element");
     797           0 :                         continue;
     798             :                     }
     799             : 
     800             :                     const char *pszFrameZone =
     801          34 :                         CPLGetXMLValue(psIter4, "frame_zone", nullptr);
     802          34 :                     if (pszFrameZone == nullptr)
     803             :                     {
     804           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     805             :                                  "Cannot find frame_zone element");
     806           0 :                         continue;
     807             :                     }
     808          34 :                     if (strlen(pszFrameZone) != 1)
     809             :                     {
     810           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     811             :                                  "Invalid value for frame_zone element : %s",
     812             :                                  pszFrameZone);
     813           0 :                         continue;
     814             :                     }
     815          34 :                     char chZone = pszFrameZone[0];
     816          34 :                     int nZone = 0;
     817          34 :                     if (chZone >= '1' && chZone <= '9')
     818          34 :                         nZone = chZone - '0';
     819           0 :                     else if (chZone >= 'a' && chZone <= 'h')
     820           0 :                         nZone = -(chZone - 'a' + 1);
     821           0 :                     else if (chZone >= 'A' && chZone <= 'H')
     822           0 :                         nZone = -(chZone - 'A' + 1);
     823           0 :                     else if (chZone == 'j' || chZone == 'J')
     824           0 :                         nZone = -9;
     825             :                     else
     826             :                     {
     827           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
     828             :                                  "Invalid value for frame_zone element : %s",
     829             :                                  pszFrameZone);
     830           0 :                         continue;
     831             :                     }
     832          34 :                     if (nZone == 9 || nZone == -9)
     833             :                     {
     834           0 :                         CPLError(
     835             :                             CE_Warning, CPLE_AppDefined,
     836             :                             "Polar zones unhandled by current implementation");
     837           0 :                         continue;
     838             :                     }
     839             : 
     840          34 :                     double dfMinX = 0.0;
     841          34 :                     double dfMaxX = 0.0;
     842          34 :                     double dfMinY = 0.0;
     843          34 :                     double dfMaxY = 0.0;
     844          34 :                     double dfPixelXSize = 0.0;
     845          34 :                     double dfPixelYSize = 0.0;
     846          34 :                     ::GetExtent(pszFrameName, nScale, nZone, dfMinX, dfMaxX,
     847             :                                 dfMinY, dfMaxY, dfPixelXSize, dfPixelYSize);
     848             : 
     849          34 :                     nValidFrames++;
     850             : 
     851             :                     const std::string osFullName = BuildFullName(
     852          68 :                         pszTOCFilename, pszFramePath, pszFrameName);
     853          34 :                     poDS->papszFileList =
     854          34 :                         CSLAddString(poDS->papszFileList, osFullName.c_str());
     855             : 
     856          34 :                     if (!bGlobalExtentValid)
     857             :                     {
     858          18 :                         dfGlobalMinX = dfMinX;
     859          18 :                         dfGlobalMinY = dfMinY;
     860          18 :                         dfGlobalMaxX = dfMaxX;
     861          18 :                         dfGlobalMaxY = dfMaxY;
     862          18 :                         dfGlobalPixelXSize = dfPixelXSize;
     863          18 :                         dfGlobalPixelYSize = dfPixelYSize;
     864          18 :                         bGlobalExtentValid = true;
     865             :                     }
     866             :                     else
     867             :                     {
     868          16 :                         if (dfMinX < dfGlobalMinX)
     869           0 :                             dfGlobalMinX = dfMinX;
     870          16 :                         if (dfMinY < dfGlobalMinY)
     871           0 :                             dfGlobalMinY = dfMinY;
     872          16 :                         if (dfMaxX > dfGlobalMaxX)
     873          15 :                             dfGlobalMaxX = dfMaxX;
     874          16 :                         if (dfMaxY > dfGlobalMaxY)
     875           1 :                             dfGlobalMaxY = dfMaxY;
     876          16 :                         if (dfPixelXSize < dfGlobalPixelXSize)
     877           0 :                             dfGlobalPixelXSize = dfPixelXSize;
     878          16 :                         if (dfPixelYSize < dfGlobalPixelYSize)
     879           0 :                             dfGlobalPixelYSize = dfPixelYSize;
     880             :                     }
     881             : 
     882          34 :                     nValidFrames++;
     883             : 
     884          34 :                     if (bLookForSubDataset)
     885             :                     {
     886             :                         FrameDesc frameDesc;
     887          19 :                         frameDesc.pszName = pszFrameName;
     888          19 :                         frameDesc.pszPath = pszFramePath;
     889          19 :                         frameDesc.nScale = nScale;
     890          19 :                         frameDesc.nZone = nZone;
     891          19 :                         aosFrameDesc.push_back(frameDesc);
     892             :                     }
     893             :                 }
     894             : 
     895          20 :                 if (bLookForSubDataset)
     896             :                 {
     897          11 :                     delete poDS;
     898          11 :                     if (nValidFrames == 0)
     899           0 :                         return nullptr;
     900          11 :                     return ECRGTOCSubDataset::Build(
     901             :                         pszProductTitle, pszDiscId, nScale, nCountSubDataset,
     902             :                         pszTOCFilename, aosFrameDesc, dfGlobalMinX,
     903             :                         dfGlobalMinY, dfGlobalMaxX, dfGlobalMaxY,
     904          11 :                         dfGlobalPixelXSize, dfGlobalPixelYSize);
     905             :                 }
     906             : 
     907           9 :                 if (nValidFrames)
     908             :                 {
     909           9 :                     poDS->AddSubDataset(pszOpenInfoFilename, pszProductTitle,
     910             :                                         pszDiscId, pszSize);
     911           9 :                     nSubDatasets++;
     912             :                 }
     913             :             }
     914             :         }
     915             :     }
     916             : 
     917          10 :     if (!bGlobalExtentValid)
     918             :     {
     919           3 :         delete poDS;
     920           3 :         return nullptr;
     921             :     }
     922             : 
     923           7 :     if (nSubDatasets == 1)
     924             :     {
     925          12 :         const char *pszSubDatasetName = CSLFetchNameValue(
     926           6 :             poDS->GetMetadata("SUBDATASETS"), "SUBDATASET_1_NAME");
     927           6 :         GDALOpenInfo oOpenInfo(pszSubDatasetName, GA_ReadOnly);
     928           6 :         delete poDS;
     929           6 :         GDALDataset *poRetDS = Open(&oOpenInfo);
     930           6 :         if (poRetDS)
     931           6 :             poRetDS->SetDescription(pszOpenInfoFilename);
     932           6 :         return poRetDS;
     933             :     }
     934             : 
     935           1 :     poDS->adfGeoTransform[0] = dfGlobalMinX;
     936           1 :     poDS->adfGeoTransform[1] = dfGlobalPixelXSize;
     937           1 :     poDS->adfGeoTransform[2] = 0.0;
     938           1 :     poDS->adfGeoTransform[3] = dfGlobalMaxY;
     939           1 :     poDS->adfGeoTransform[4] = 0.0;
     940           1 :     poDS->adfGeoTransform[5] = -dfGlobalPixelYSize;
     941             : 
     942           1 :     poDS->nRasterXSize = static_cast<int>(0.5 + (dfGlobalMaxX - dfGlobalMinX) /
     943             :                                                     dfGlobalPixelXSize);
     944           1 :     poDS->nRasterYSize = static_cast<int>(0.5 + (dfGlobalMaxY - dfGlobalMinY) /
     945             :                                                     dfGlobalPixelYSize);
     946             : 
     947             :     /* -------------------------------------------------------------------- */
     948             :     /*      Initialize any PAM information.                                 */
     949             :     /* -------------------------------------------------------------------- */
     950           1 :     poDS->TryLoadXML();
     951             : 
     952           1 :     return poDS;
     953             : }
     954             : 
     955             : /************************************************************************/
     956             : /*                                Open()                                */
     957             : /************************************************************************/
     958             : 
     959          30 : GDALDataset *ECRGTOCDataset::Open(GDALOpenInfo *poOpenInfo)
     960             : 
     961             : {
     962          30 :     if (!ECRGTOCDriverIdentify(poOpenInfo))
     963           0 :         return nullptr;
     964             : 
     965          30 :     const char *pszFilename = poOpenInfo->pszFilename;
     966          60 :     CPLString osFilename;
     967          60 :     CPLString osProduct, osDiscId, osScale;
     968             : 
     969          30 :     if (STARTS_WITH_CI(pszFilename, "ECRG_TOC_ENTRY:"))
     970             :     {
     971          23 :         pszFilename += strlen("ECRG_TOC_ENTRY:");
     972             : 
     973             :         /* PRODUCT:DISK:SCALE:FILENAME (or PRODUCT:DISK:FILENAME historically)
     974             :          */
     975             :         /* with FILENAME potentially C:\BLA... */
     976          23 :         char **papszTokens = CSLTokenizeString2(pszFilename, ":", 0);
     977          23 :         int nTokens = CSLCount(papszTokens);
     978          23 :         if (nTokens != 3 && nTokens != 4 && nTokens != 5)
     979             :         {
     980           4 :             CSLDestroy(papszTokens);
     981           4 :             return nullptr;
     982             :         }
     983             : 
     984          19 :         osProduct = papszTokens[0];
     985          19 :         osDiscId = papszTokens[1];
     986             : 
     987          19 :         if (nTokens == 3)
     988           5 :             osFilename = papszTokens[2];
     989          14 :         else if (nTokens == 4)
     990             :         {
     991          13 :             if (strlen(papszTokens[2]) == 1 &&
     992           1 :                 (papszTokens[3][0] == '\\' || papszTokens[3][0] == '/'))
     993             :             {
     994           1 :                 osFilename = papszTokens[2];
     995           1 :                 osFilename += ":";
     996           1 :                 osFilename += papszTokens[3];
     997             :             }
     998             :             else
     999             :             {
    1000          12 :                 osScale = papszTokens[2];
    1001          12 :                 osFilename = papszTokens[3];
    1002             :             }
    1003             :         }
    1004           1 :         else if (nTokens == 5 && strlen(papszTokens[3]) == 1 &&
    1005           1 :                  (papszTokens[4][0] == '\\' || papszTokens[4][0] == '/'))
    1006             :         {
    1007           1 :             osScale = papszTokens[2];
    1008           1 :             osFilename = papszTokens[3];
    1009           1 :             osFilename += ":";
    1010           1 :             osFilename += papszTokens[4];
    1011             :         }
    1012             :         else
    1013             :         {
    1014           0 :             CSLDestroy(papszTokens);
    1015           0 :             return nullptr;
    1016             :         }
    1017             : 
    1018          19 :         CSLDestroy(papszTokens);
    1019          19 :         pszFilename = osFilename.c_str();
    1020             :     }
    1021             : 
    1022             :     /* -------------------------------------------------------------------- */
    1023             :     /*      Parse the XML file                                              */
    1024             :     /* -------------------------------------------------------------------- */
    1025          26 :     CPLXMLNode *psXML = CPLParseXMLFile(pszFilename);
    1026          26 :     if (psXML == nullptr)
    1027             :     {
    1028           5 :         return nullptr;
    1029             :     }
    1030             : 
    1031          42 :     GDALDataset *poDS = Build(pszFilename, psXML, osProduct, osDiscId, osScale,
    1032          21 :                               poOpenInfo->pszFilename);
    1033          21 :     CPLDestroyXMLNode(psXML);
    1034             : 
    1035          21 :     if (poDS && poOpenInfo->eAccess == GA_Update)
    1036             :     {
    1037           0 :         ReportUpdateNotSupportedByDriver("ECRGTOC");
    1038           0 :         delete poDS;
    1039           0 :         return nullptr;
    1040             :     }
    1041             : 
    1042          21 :     return poDS;
    1043             : }
    1044             : 
    1045             : /************************************************************************/
    1046             : /*                         GDALRegister_ECRGTOC()                       */
    1047             : /************************************************************************/
    1048             : 
    1049        1911 : void GDALRegister_ECRGTOC()
    1050             : 
    1051             : {
    1052        1911 :     if (GDALGetDriverByName(ECRGTOC_DRIVER_NAME) != nullptr)
    1053         282 :         return;
    1054             : 
    1055        1629 :     GDALDriver *poDriver = new GDALDriver();
    1056        1629 :     ECRGTOCDriverSetCommonMetadata(poDriver);
    1057             : 
    1058        1629 :     poDriver->pfnOpen = ECRGTOCDataset::Open;
    1059             : 
    1060        1629 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1061             : }

Generated by: LCOV version 1.14