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

Generated by: LCOV version 1.14