LCOV - code coverage report
Current view: top level - frmts/adrg - adrgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 472 597 79.1 %
Date: 2025-03-28 21:34:50 Functions: 18 21 85.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Purpose:  ADRG reader
       4             :  * Author:   Even Rouault, even.rouault at spatialys.com
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "cpl_string.h"
      13             : #include "gdal_pam.h"
      14             : #include "gdal_frmts.h"
      15             : #include "iso8211.h"
      16             : #include "ogr_spatialref.h"
      17             : 
      18             : #include <limits>
      19             : #include <new>
      20             : 
      21             : #define N_ELEMENTS(x) (sizeof(x) / sizeof(x[0]))
      22             : 
      23             : #define DIGIT_ZERO '0'
      24             : 
      25             : class ADRGDataset final : public GDALPamDataset
      26             : {
      27             :     friend class ADRGRasterBand;
      28             : 
      29             :     CPLString osGENFileName;
      30             :     CPLString osIMGFileName;
      31             :     OGRSpatialReference m_oSRS{};
      32             : 
      33             :     VSILFILE *fdIMG;
      34             :     int *TILEINDEX;
      35             :     int offsetInIMG;
      36             :     int NFC;
      37             :     int NFL;
      38             :     double LSO;
      39             :     double PSO;
      40             :     int ARV;
      41             :     int BRV;
      42             : 
      43             :     char **papszSubDatasets;
      44             : 
      45             :     double adfGeoTransform[6];
      46             : 
      47             :     static char **GetGENListFromTHF(const char *pszFileName);
      48             :     static char **GetIMGListFromGEN(const char *pszFileName,
      49             :                                     int *pnRecordIndex = nullptr);
      50             :     static ADRGDataset *OpenDataset(const char *pszGENFileName,
      51             :                                     const char *pszIMGFileName,
      52             :                                     DDFRecord *record = nullptr);
      53             :     static DDFRecord *FindRecordInGENForIMG(DDFModule &module,
      54             :                                             const char *pszGENFileName,
      55             :                                             const char *pszIMGFileName);
      56             : 
      57             :   public:
      58             :     ADRGDataset();
      59             :     ~ADRGDataset() override;
      60             : 
      61             :     const OGRSpatialReference *GetSpatialRef() const override;
      62             :     CPLErr GetGeoTransform(double *padfGeoTransform) override;
      63             : 
      64             :     char **GetMetadataDomainList() override;
      65             :     char **GetMetadata(const char *pszDomain = "") override;
      66             : 
      67             :     char **GetFileList() override;
      68             : 
      69             :     void AddSubDataset(const char *pszGENFileName, const char *pszIMGFileName);
      70             : 
      71             :     static GDALDataset *Open(GDALOpenInfo *);
      72             : 
      73             :     static double GetLongitudeFromString(const char *str);
      74             :     static double GetLatitudeFromString(const char *str);
      75             : };
      76             : 
      77             : /************************************************************************/
      78             : /* ==================================================================== */
      79             : /*                            ADRGRasterBand                             */
      80             : /* ==================================================================== */
      81             : /************************************************************************/
      82             : 
      83             : class ADRGRasterBand final : public GDALPamRasterBand
      84             : {
      85             :     friend class ADRGDataset;
      86             : 
      87             :   public:
      88             :     ADRGRasterBand(ADRGDataset *, int);
      89             : 
      90             :     GDALColorInterp GetColorInterpretation() override;
      91             :     CPLErr IReadBlock(int, int, void *) override;
      92             : 
      93             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
      94             : 
      95             :     // virtual int GetOverviewCount();
      96             :     // virtual GDALRasterBand* GetOverview(int i);
      97             : };
      98             : 
      99             : /************************************************************************/
     100             : /*                           ADRGRasterBand()                            */
     101             : /************************************************************************/
     102             : 
     103          24 : ADRGRasterBand::ADRGRasterBand(ADRGDataset *poDSIn, int nBandIn)
     104             : 
     105             : {
     106          24 :     poDS = poDSIn;
     107          24 :     nBand = nBandIn;
     108             : 
     109          24 :     eDataType = GDT_Byte;
     110             : 
     111          24 :     nBlockXSize = 128;
     112          24 :     nBlockYSize = 128;
     113          24 : }
     114             : 
     115             : /************************************************************************/
     116             : /*                            GetNoDataValue()                          */
     117             : /************************************************************************/
     118             : 
     119           0 : double ADRGRasterBand::GetNoDataValue(int *pbSuccess)
     120             : {
     121           0 :     if (pbSuccess)
     122           0 :         *pbSuccess = TRUE;
     123             : 
     124           0 :     return 0.0;
     125             : }
     126             : 
     127             : /************************************************************************/
     128             : /*                       GetColorInterpretation()                       */
     129             : /************************************************************************/
     130             : 
     131           0 : GDALColorInterp ADRGRasterBand::GetColorInterpretation()
     132             : 
     133             : {
     134           0 :     if (nBand == 1)
     135           0 :         return GCI_RedBand;
     136             : 
     137           0 :     else if (nBand == 2)
     138           0 :         return GCI_GreenBand;
     139             : 
     140           0 :     return GCI_BlueBand;
     141             : }
     142             : 
     143             : /************************************************************************/
     144             : /*                             IReadBlock()                             */
     145             : /************************************************************************/
     146             : 
     147           4 : CPLErr ADRGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     148             : 
     149             : {
     150           4 :     ADRGDataset *l_poDS = (ADRGDataset *)this->poDS;
     151           4 :     int nBlock = nBlockYOff * l_poDS->NFC + nBlockXOff;
     152           4 :     if (nBlockXOff >= l_poDS->NFC || nBlockYOff >= l_poDS->NFL)
     153             :     {
     154           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     155             :                  "nBlockXOff=%d, NFC=%d, nBlockYOff=%d, NFL=%d", nBlockXOff,
     156             :                  l_poDS->NFC, nBlockYOff, l_poDS->NFL);
     157           0 :         return CE_Failure;
     158             :     }
     159           4 :     CPLDebug("ADRG", "(%d,%d) -> nBlock = %d", nBlockXOff, nBlockYOff, nBlock);
     160             : 
     161             :     vsi_l_offset offset;
     162           4 :     if (l_poDS->TILEINDEX)
     163             :     {
     164           4 :         if (l_poDS->TILEINDEX[nBlock] <= 0)
     165             :         {
     166           0 :             memset(pImage, 0, 128 * 128);
     167           0 :             return CE_None;
     168             :         }
     169           4 :         offset = l_poDS->offsetInIMG +
     170           4 :                  static_cast<vsi_l_offset>(l_poDS->TILEINDEX[nBlock] - 1) *
     171           4 :                      128 * 128 * 3 +
     172           4 :                  (nBand - 1) * 128 * 128;
     173             :     }
     174             :     else
     175           0 :         offset = l_poDS->offsetInIMG +
     176           0 :                  static_cast<vsi_l_offset>(nBlock) * 128 * 128 * 3 +
     177           0 :                  (nBand - 1) * 128 * 128;
     178             : 
     179           4 :     if (VSIFSeekL(l_poDS->fdIMG, offset, SEEK_SET) != 0)
     180             :     {
     181           0 :         CPLError(CE_Failure, CPLE_FileIO,
     182             :                  "Cannot seek to offset " CPL_FRMT_GUIB, offset);
     183           0 :         return CE_Failure;
     184             :     }
     185           4 :     if (VSIFReadL(pImage, 1, 128 * 128, l_poDS->fdIMG) != 128 * 128)
     186             :     {
     187           0 :         CPLError(CE_Failure, CPLE_FileIO,
     188             :                  "Cannot read data at offset " CPL_FRMT_GUIB, offset);
     189           0 :         return CE_Failure;
     190             :     }
     191             : 
     192           4 :     return CE_None;
     193             : }
     194             : 
     195             : /************************************************************************/
     196             : /*                          ADRGDataset()                               */
     197             : /************************************************************************/
     198             : 
     199           9 : ADRGDataset::ADRGDataset()
     200             :     : fdIMG(nullptr), TILEINDEX(nullptr), offsetInIMG(0), NFC(0), NFL(0),
     201           9 :       LSO(0.0), PSO(0.0), ARV(0), BRV(0), papszSubDatasets(nullptr)
     202             : {
     203           9 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     204           9 :     memset(adfGeoTransform, 0, sizeof(adfGeoTransform));
     205           9 : }
     206             : 
     207             : /************************************************************************/
     208             : /*                          ~ADRGDataset()                              */
     209             : /************************************************************************/
     210             : 
     211          18 : ADRGDataset::~ADRGDataset()
     212             : {
     213           9 :     CSLDestroy(papszSubDatasets);
     214             : 
     215           9 :     if (fdIMG)
     216             :     {
     217           8 :         VSIFCloseL(fdIMG);
     218             :     }
     219             : 
     220           9 :     if (TILEINDEX)
     221             :     {
     222           8 :         delete[] TILEINDEX;
     223             :     }
     224          18 : }
     225             : 
     226             : /************************************************************************/
     227             : /*                            GetFileList()                             */
     228             : /************************************************************************/
     229             : 
     230           3 : char **ADRGDataset::GetFileList()
     231             : {
     232           3 :     char **papszFileList = GDALPamDataset::GetFileList();
     233             : 
     234           3 :     if (!osGENFileName.empty() && !osIMGFileName.empty())
     235             :     {
     236           3 :         CPLString osMainFilename = GetDescription();
     237             :         VSIStatBufL sStat;
     238             : 
     239           3 :         const bool bMainFileReal = VSIStatL(osMainFilename, &sStat) == 0;
     240           3 :         if (bMainFileReal)
     241             :         {
     242           4 :             CPLString osShortMainFilename = CPLGetFilename(osMainFilename);
     243           4 :             CPLString osShortGENFileName = CPLGetFilename(osGENFileName);
     244           2 :             if (!EQUAL(osShortMainFilename.c_str(), osShortGENFileName.c_str()))
     245             :                 papszFileList =
     246           1 :                     CSLAddString(papszFileList, osGENFileName.c_str());
     247             :         }
     248             :         else
     249           1 :             papszFileList = CSLAddString(papszFileList, osGENFileName.c_str());
     250             : 
     251           3 :         papszFileList = CSLAddString(papszFileList, osIMGFileName.c_str());
     252             :     }
     253             : 
     254           3 :     return papszFileList;
     255             : }
     256             : 
     257             : /************************************************************************/
     258             : /*                           AddSubDataset()                            */
     259             : /************************************************************************/
     260             : 
     261           2 : void ADRGDataset::AddSubDataset(const char *pszGENFileName,
     262             :                                 const char *pszIMGFileName)
     263             : {
     264             :     char szName[80];
     265           2 :     int nCount = CSLCount(papszSubDatasets) / 2;
     266             : 
     267           2 :     CPLString osSubDatasetName;
     268           2 :     osSubDatasetName = "ADRG:";
     269           2 :     osSubDatasetName += pszGENFileName;
     270           2 :     osSubDatasetName += ",";
     271           2 :     osSubDatasetName += pszIMGFileName;
     272             : 
     273           2 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
     274           2 :     papszSubDatasets =
     275           2 :         CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
     276             : 
     277           2 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
     278           2 :     papszSubDatasets =
     279           2 :         CSLSetNameValue(papszSubDatasets, szName, osSubDatasetName);
     280           2 : }
     281             : 
     282             : /************************************************************************/
     283             : /*                      GetMetadataDomainList()                         */
     284             : /************************************************************************/
     285             : 
     286           0 : char **ADRGDataset::GetMetadataDomainList()
     287             : {
     288           0 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
     289           0 :                                    TRUE, "SUBDATASETS", nullptr);
     290             : }
     291             : 
     292             : /************************************************************************/
     293             : /*                            GetMetadata()                             */
     294             : /************************************************************************/
     295             : 
     296           1 : char **ADRGDataset::GetMetadata(const char *pszDomain)
     297             : 
     298             : {
     299           1 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
     300           0 :         return papszSubDatasets;
     301             : 
     302           1 :     return GDALPamDataset::GetMetadata(pszDomain);
     303             : }
     304             : 
     305             : /************************************************************************/
     306             : /*                        GetSpatialRef()                               */
     307             : /************************************************************************/
     308             : 
     309           2 : const OGRSpatialReference *ADRGDataset::GetSpatialRef() const
     310             : {
     311           2 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     312             : }
     313             : 
     314             : /************************************************************************/
     315             : /*                        GetGeoTransform()                             */
     316             : /************************************************************************/
     317             : 
     318           2 : CPLErr ADRGDataset::GetGeoTransform(double *padfGeoTransform)
     319             : {
     320           2 :     if (papszSubDatasets != nullptr)
     321           0 :         return CE_Failure;
     322             : 
     323           2 :     memcpy(padfGeoTransform, adfGeoTransform, sizeof(double) * 6);
     324             : 
     325           2 :     return CE_None;
     326             : }
     327             : 
     328             : /************************************************************************/
     329             : /*                     GetLongitudeFromString()                         */
     330             : /************************************************************************/
     331             : 
     332           8 : double ADRGDataset::GetLongitudeFromString(const char *str)
     333             : {
     334           8 :     char ddd[3 + 1] = {0};
     335           8 :     char mm[2 + 1] = {0};
     336           8 :     char ssdotss[5 + 1] = {0};
     337           8 :     int sign = (str[0] == '+') ? 1 : -1;
     338           8 :     str++;
     339           8 :     strncpy(ddd, str, 3);
     340           8 :     str += 3;
     341           8 :     strncpy(mm, str, 2);
     342           8 :     str += 2;
     343           8 :     strncpy(ssdotss, str, 5);
     344           8 :     return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600);
     345             : }
     346             : 
     347             : /************************************************************************/
     348             : /*                      GetLatitudeFromString()                         */
     349             : /************************************************************************/
     350             : 
     351           8 : double ADRGDataset::GetLatitudeFromString(const char *str)
     352             : {
     353           8 :     char ddd[2 + 1] = {0};
     354           8 :     char mm[2 + 1] = {0};
     355           8 :     char ssdotss[5 + 1] = {0};
     356           8 :     int sign = (str[0] == '+') ? 1 : -1;
     357           8 :     str++;
     358           8 :     strncpy(ddd, str, 2);
     359           8 :     str += 2;
     360           8 :     strncpy(mm, str, 2);
     361           8 :     str += 2;
     362           8 :     strncpy(ssdotss, str, 5);
     363           8 :     return sign * (CPLAtof(ddd) + CPLAtof(mm) / 60 + CPLAtof(ssdotss) / 3600);
     364             : }
     365             : 
     366             : /************************************************************************/
     367             : /*                      FindRecordInGENForIMG()                         */
     368             : /************************************************************************/
     369             : 
     370           2 : DDFRecord *ADRGDataset::FindRecordInGENForIMG(DDFModule &module,
     371             :                                               const char *pszGENFileName,
     372             :                                               const char *pszIMGFileName)
     373             : {
     374             :     /* Finds the GEN file corresponding to the IMG file */
     375           2 :     if (!module.Open(pszGENFileName, TRUE))
     376           0 :         return nullptr;
     377             : 
     378           4 :     CPLString osShortIMGFilename = CPLGetFilename(pszIMGFileName);
     379             : 
     380             :     /* Now finds the record */
     381             :     while (true)
     382             :     {
     383           7 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     384           7 :         DDFRecord *record = module.ReadRecord();
     385           7 :         CPLPopErrorHandler();
     386           7 :         CPLErrorReset();
     387           7 :         if (record == nullptr)
     388           0 :             return nullptr;
     389             : 
     390           7 :         if (record->GetFieldCount() >= 5)
     391             :         {
     392           5 :             DDFField *field = record->GetField(0);
     393           5 :             DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     394          10 :             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
     395           5 :                   fieldDefn->GetSubfieldCount() == 2))
     396             :             {
     397           2 :                 continue;
     398             :             }
     399             : 
     400           5 :             const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
     401           5 :             if (RTY == nullptr)
     402           0 :                 continue;
     403             :             /* Ignore overviews */
     404           5 :             if (strcmp(RTY, "OVV") == 0)
     405           2 :                 continue;
     406             : 
     407           3 :             if (strcmp(RTY, "GIN") != 0)
     408           0 :                 continue;
     409             : 
     410           3 :             field = record->GetField(3);
     411           3 :             fieldDefn = field->GetFieldDefn();
     412             : 
     413           6 :             if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
     414           3 :                   fieldDefn->GetSubfieldCount() == 15))
     415             :             {
     416           0 :                 continue;
     417             :             }
     418             : 
     419           3 :             const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
     420           3 :             if (pszBAD == nullptr || strlen(pszBAD) != 12)
     421           0 :                 continue;
     422           3 :             CPLString osBAD = pszBAD;
     423             :             {
     424           3 :                 char *c = (char *)strchr(osBAD.c_str(), ' ');
     425           3 :                 if (c)
     426           0 :                     *c = 0;
     427             :             }
     428             : 
     429           3 :             if (EQUAL(osShortIMGFilename.c_str(), osBAD.c_str()))
     430             :             {
     431           2 :                 return record;
     432             :             }
     433             :         }
     434           5 :     }
     435             : }
     436             : 
     437             : /************************************************************************/
     438             : /*                           OpenDataset()                              */
     439             : /************************************************************************/
     440             : 
     441           8 : ADRGDataset *ADRGDataset::OpenDataset(const char *pszGENFileName,
     442             :                                       const char *pszIMGFileName,
     443             :                                       DDFRecord *record)
     444             : {
     445          16 :     DDFModule module;
     446             : 
     447           8 :     int SCA = 0;
     448           8 :     int ZNA = 0;
     449             :     double PSP;
     450             :     int ARV;
     451             :     int BRV;
     452             :     double LSO;
     453             :     double PSO;
     454             :     int NFL;
     455             :     int NFC;
     456          16 :     CPLString osBAD;
     457             :     int TIF;
     458           8 :     int *TILEINDEX = nullptr;
     459             : 
     460           8 :     if (record == nullptr)
     461             :     {
     462           2 :         record = FindRecordInGENForIMG(module, pszGENFileName, pszIMGFileName);
     463           2 :         if (record == nullptr)
     464           0 :             return nullptr;
     465             :     }
     466             : 
     467           8 :     DDFField *field = record->GetField(1);
     468           8 :     if (field == nullptr)
     469           0 :         return nullptr;
     470           8 :     DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     471             : 
     472          16 :     if (!(strcmp(fieldDefn->GetName(), "DSI") == 0 &&
     473           8 :           fieldDefn->GetSubfieldCount() == 2))
     474             :     {
     475           0 :         return nullptr;
     476             :     }
     477             : 
     478           8 :     const char *pszPTR = record->GetStringSubfield("DSI", 0, "PRT", 0);
     479           8 :     if (pszPTR == nullptr || !EQUAL(pszPTR, "ADRG"))
     480           0 :         return nullptr;
     481             : 
     482           8 :     const char *pszNAM = record->GetStringSubfield("DSI", 0, "NAM", 0);
     483           8 :     if (pszNAM == nullptr || strlen(pszNAM) != 8)
     484           0 :         return nullptr;
     485          16 :     CPLString osNAM = pszNAM;
     486             : 
     487           8 :     field = record->GetField(2);
     488           8 :     if (field == nullptr)
     489           0 :         return nullptr;
     490           8 :     fieldDefn = field->GetFieldDefn();
     491             : 
     492             :     // TODO: Support on GIN things.  And what is GIN?
     493             :     // GIN might mean general information and might be a typo of GEN.
     494             :     // if( isGIN )
     495             :     {
     496          16 :         if (!(strcmp(fieldDefn->GetName(), "GEN") == 0 &&
     497           8 :               fieldDefn->GetSubfieldCount() == 21))
     498             :         {
     499           0 :             return nullptr;
     500             :         }
     501             : 
     502           8 :         if (record->GetIntSubfield("GEN", 0, "STR", 0) != 3)
     503           0 :             return nullptr;
     504             : 
     505           8 :         SCA = record->GetIntSubfield("GEN", 0, "SCA", 0);
     506           8 :         CPLDebug("ADRG", "SCA=%d", SCA);
     507             : 
     508           8 :         ZNA = record->GetIntSubfield("GEN", 0, "ZNA", 0);
     509           8 :         CPLDebug("ADRG", "ZNA=%d", ZNA);
     510             : 
     511           8 :         PSP = record->GetFloatSubfield("GEN", 0, "PSP", 0);
     512           8 :         CPLDebug("ADRG", "PSP=%f", PSP);
     513             : 
     514           8 :         ARV = record->GetIntSubfield("GEN", 0, "ARV", 0);
     515           8 :         CPLDebug("ADRG", "ARV=%d", ARV);
     516             : 
     517           8 :         BRV = record->GetIntSubfield("GEN", 0, "BRV", 0);
     518           8 :         CPLDebug("ADRG", "BRV=%d", BRV);
     519           8 :         if (ARV <= 0 || (ZNA != 9 && ZNA != 18 && BRV <= 0))
     520           0 :             return nullptr;
     521             : 
     522           8 :         const char *pszLSO = record->GetStringSubfield("GEN", 0, "LSO", 0);
     523           8 :         if (pszLSO == nullptr || strlen(pszLSO) != 11)
     524           0 :             return nullptr;
     525           8 :         LSO = GetLongitudeFromString(pszLSO);
     526           8 :         CPLDebug("ADRG", "LSO=%f", LSO);
     527             : 
     528           8 :         const char *pszPSO = record->GetStringSubfield("GEN", 0, "PSO", 0);
     529           8 :         if (pszPSO == nullptr || strlen(pszPSO) != 10)
     530           0 :             return nullptr;
     531           8 :         PSO = GetLatitudeFromString(pszPSO);
     532           8 :         CPLDebug("ADRG", "PSO=%f", PSO);
     533             :     }
     534             : #if 0
     535             :     else
     536             :     {
     537             :         if( !(strcmp(fieldDefn->GetName(), "OVI") == 0 &&
     538             :                 fieldDefn->GetSubfieldCount() == 5) )
     539             :         {
     540             :             return NULL;
     541             :         }
     542             : 
     543             :         if( record->GetIntSubfield("OVI", 0, "STR", 0) != 3 )
     544             :             return NULL;
     545             : 
     546             :         ARV = record->GetIntSubfield("OVI", 0, "ARV", 0);
     547             :         CPLDebug("ADRG", "ARV=%d", ARV);
     548             : 
     549             :         BRV = record->GetIntSubfield("OVI", 0, "BRV", 0);
     550             :         CPLDebug("ADRG", "BRV=%d", BRV);
     551             : 
     552             :         const char* pszLSO = record->GetStringSubfield("OVI", 0, "LSO", 0);
     553             :         if( pszLSO == NULL || strlen(pszLSO) != 11 )
     554             :             return NULL;
     555             :         LSO = GetLongitudeFromString(pszLSO);
     556             :         CPLDebug("ADRG", "LSO=%f", LSO);
     557             : 
     558             :         const char* pszPSO = record->GetStringSubfield("OVI", 0, "PSO", 0);
     559             :         if( pszPSO == NULL || strlen(pszPSO) != 10 )
     560             :             return NULL;
     561             :         PSO = GetLatitudeFromString(pszPSO);
     562             :         CPLDebug("ADRG", "PSO=%f", PSO);
     563             :     }
     564             : #endif
     565             : 
     566           8 :     field = record->GetField(3);
     567           8 :     if (field == nullptr)
     568           0 :         return nullptr;
     569           8 :     fieldDefn = field->GetFieldDefn();
     570             : 
     571          16 :     if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
     572           8 :           fieldDefn->GetSubfieldCount() == 15))
     573             :     {
     574           0 :         return nullptr;
     575             :     }
     576             : 
     577           8 :     NFL = record->GetIntSubfield("SPR", 0, "NFL", 0);
     578           8 :     CPLDebug("ADRG", "NFL=%d", NFL);
     579             : 
     580           8 :     NFC = record->GetIntSubfield("SPR", 0, "NFC", 0);
     581           8 :     CPLDebug("ADRG", "NFC=%d", NFC);
     582             : 
     583           8 :     const auto knIntMax = std::numeric_limits<int>::max();
     584           8 :     if (NFL <= 0 || NFC <= 0 || NFL > knIntMax / 128 || NFC > knIntMax / 128 ||
     585           8 :         NFL > (knIntMax - 1) / (NFC * 5))
     586             :     {
     587           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid NFL / NFC values");
     588           0 :         return nullptr;
     589             :     }
     590             : 
     591           8 :     int PNC = record->GetIntSubfield("SPR", 0, "PNC", 0);
     592           8 :     CPLDebug("ADRG", "PNC=%d", PNC);
     593           8 :     if (PNC != 128)
     594             :     {
     595           0 :         return nullptr;
     596             :     }
     597             : 
     598           8 :     int PNL = record->GetIntSubfield("SPR", 0, "PNL", 0);
     599           8 :     CPLDebug("ADRG", "PNL=%d", PNL);
     600           8 :     if (PNL != 128)
     601             :     {
     602           0 :         return nullptr;
     603             :     }
     604             : 
     605           8 :     const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
     606           8 :     if (pszBAD == nullptr || strlen(pszBAD) != 12)
     607           0 :         return nullptr;
     608           8 :     osBAD = pszBAD;
     609             :     {
     610           8 :         char *c = (char *)strchr(osBAD.c_str(), ' ');
     611           8 :         if (c)
     612           0 :             *c = 0;
     613             :     }
     614           8 :     CPLDebug("ADRG", "BAD=%s", osBAD.c_str());
     615             : 
     616           8 :     DDFSubfieldDefn *subfieldDefn = fieldDefn->GetSubfield(14);
     617          16 :     if (!(strcmp(subfieldDefn->GetName(), "TIF") == 0 &&
     618           8 :           (subfieldDefn->GetFormat())[0] == 'A'))
     619             :     {
     620           0 :         return nullptr;
     621             :     }
     622             : 
     623           8 :     const char *pszTIF = record->GetStringSubfield("SPR", 0, "TIF", 0);
     624           8 :     if (pszTIF == nullptr)
     625           0 :         return nullptr;
     626           8 :     TIF = pszTIF[0] == 'Y';
     627           8 :     CPLDebug("ADRG", "TIF=%d", TIF);
     628             : 
     629           8 :     if (TIF)
     630             :     {
     631           8 :         if (record->GetFieldCount() != 6)
     632             :         {
     633           0 :             return nullptr;
     634             :         }
     635             : 
     636           8 :         field = record->GetField(5);
     637           8 :         if (field == nullptr)
     638           0 :             return nullptr;
     639           8 :         fieldDefn = field->GetFieldDefn();
     640             : 
     641           8 :         if (!(strcmp(fieldDefn->GetName(), "TIM") == 0))
     642             :         {
     643           0 :             return nullptr;
     644             :         }
     645             : 
     646           8 :         if (field->GetDataSize() != 5 * NFL * NFC + 1)
     647             :         {
     648           0 :             return nullptr;
     649             :         }
     650             : 
     651             :         try
     652             :         {
     653           8 :             TILEINDEX = new int[NFL * NFC];
     654             :         }
     655           0 :         catch (const std::exception &)
     656             :         {
     657           0 :             return nullptr;
     658             :         }
     659           8 :         const char *ptr = field->GetData();
     660           8 :         char offset[5 + 1] = {0};
     661          16 :         for (int i = 0; i < NFL * NFC; i++)
     662             :         {
     663           8 :             strncpy(offset, ptr, 5);
     664           8 :             ptr += 5;
     665           8 :             TILEINDEX[i] = atoi(offset);
     666             :             // CPLDebug("ADRG", "TSI[%d]=%d", i, TILEINDEX[i]);
     667             :         }
     668             :     }
     669             : 
     670           8 :     VSILFILE *fdIMG = VSIFOpenL(pszIMGFileName, "rb");
     671           8 :     if (fdIMG == nullptr)
     672             :     {
     673           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s\n",
     674             :                  pszIMGFileName);
     675           0 :         delete[] TILEINDEX;
     676           0 :         return nullptr;
     677             :     }
     678             : 
     679             :     /* Skip ISO8211 header of IMG file */
     680           8 :     int offsetInIMG = 0;
     681             :     char c;
     682             :     char recordName[3];
     683           8 :     if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
     684             :     {
     685           0 :         VSIFCloseL(fdIMG);
     686           0 :         delete[] TILEINDEX;
     687           0 :         return nullptr;
     688             :     }
     689        2064 :     while (!VSIFEofL(fdIMG))
     690             :     {
     691        2064 :         if (c == 30)
     692             :         {
     693          48 :             if (VSIFReadL(recordName, 1, 3, fdIMG) != 3)
     694             :             {
     695           0 :                 VSIFCloseL(fdIMG);
     696           0 :                 delete[] TILEINDEX;
     697           0 :                 return nullptr;
     698             :             }
     699          48 :             offsetInIMG += 3;
     700          48 :             if (STARTS_WITH(recordName, "IMG"))
     701             :             {
     702           8 :                 offsetInIMG += 4;
     703          16 :                 if (VSIFSeekL(fdIMG, 3, SEEK_CUR) != 0 ||
     704           8 :                     VSIFReadL(&c, 1, 1, fdIMG) != 1)
     705             :                 {
     706           0 :                     VSIFCloseL(fdIMG);
     707           0 :                     delete[] TILEINDEX;
     708           0 :                     return nullptr;
     709             :                 }
     710       14152 :                 while (c == ' ')
     711             :                 {
     712       14144 :                     offsetInIMG++;
     713       14144 :                     if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
     714             :                     {
     715           0 :                         VSIFCloseL(fdIMG);
     716           0 :                         delete[] TILEINDEX;
     717           0 :                         return nullptr;
     718             :                     }
     719             :                 }
     720           8 :                 offsetInIMG++;
     721           8 :                 break;
     722             :             }
     723             :         }
     724             : 
     725        2056 :         offsetInIMG++;
     726        2056 :         if (VSIFReadL(&c, 1, 1, fdIMG) != 1)
     727             :         {
     728           0 :             VSIFCloseL(fdIMG);
     729           0 :             delete[] TILEINDEX;
     730           0 :             return nullptr;
     731             :         }
     732             :     }
     733             : 
     734           8 :     if (VSIFEofL(fdIMG))
     735             :     {
     736           0 :         VSIFCloseL(fdIMG);
     737           0 :         delete[] TILEINDEX;
     738           0 :         return nullptr;
     739             :     }
     740             : 
     741           8 :     CPLDebug("ADRG", "Img offset data = %d", offsetInIMG);
     742             : 
     743           8 :     ADRGDataset *poDS = new ADRGDataset();
     744             : 
     745           8 :     poDS->osGENFileName = pszGENFileName;
     746           8 :     poDS->osIMGFileName = pszIMGFileName;
     747           8 :     poDS->NFC = NFC;
     748           8 :     poDS->NFL = NFL;
     749           8 :     poDS->nRasterXSize = NFC * 128;
     750           8 :     poDS->nRasterYSize = NFL * 128;
     751           8 :     poDS->LSO = LSO;
     752           8 :     poDS->PSO = PSO;
     753           8 :     poDS->ARV = ARV;
     754           8 :     poDS->BRV = BRV;
     755           8 :     poDS->TILEINDEX = TILEINDEX;
     756           8 :     poDS->fdIMG = fdIMG;
     757           8 :     poDS->offsetInIMG = offsetInIMG;
     758             : 
     759           8 :     if (ZNA == 9)
     760             :     {
     761             :         // North Polar Case
     762           1 :         poDS->adfGeoTransform[0] =
     763           1 :             111319.4907933 * (90.0 - PSO) * sin(LSO * M_PI / 180.0);
     764           1 :         poDS->adfGeoTransform[1] = 40075016.68558 / ARV;
     765           1 :         poDS->adfGeoTransform[2] = 0.0;
     766           1 :         poDS->adfGeoTransform[3] =
     767           1 :             -111319.4907933 * (90.0 - PSO) * cos(LSO * M_PI / 180.0);
     768           1 :         poDS->adfGeoTransform[4] = 0.0;
     769           1 :         poDS->adfGeoTransform[5] = -40075016.68558 / ARV;
     770           1 :         poDS->m_oSRS.importFromWkt(
     771             :             "PROJCS[\"ARC_System_Zone_09\",GEOGCS[\"GCS_Sphere\","
     772             :             "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
     773             :             "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
     774             :             "PROJECTION[\"Azimuthal_Equidistant\"],"
     775             :             "PARAMETER[\"latitude_of_center\",90],"
     776             :             "PARAMETER[\"longitude_of_center\",0],"
     777             :             "PARAMETER[\"false_easting\",0],"
     778             :             "PARAMETER[\"false_northing\",0],"
     779             :             "UNIT[\"metre\",1]]");
     780             :     }
     781           7 :     else if (ZNA == 18)
     782             :     {
     783             :         // South Polar Case
     784           1 :         poDS->adfGeoTransform[0] =
     785           1 :             111319.4907933 * (90.0 + PSO) * sin(LSO * M_PI / 180.0);
     786           1 :         poDS->adfGeoTransform[1] = 40075016.68558 / ARV;
     787           1 :         poDS->adfGeoTransform[2] = 0.0;
     788           1 :         poDS->adfGeoTransform[3] =
     789           1 :             111319.4907933 * (90.0 + PSO) * cos(LSO * M_PI / 180.0);
     790           1 :         poDS->adfGeoTransform[4] = 0.0;
     791           1 :         poDS->adfGeoTransform[5] = -40075016.68558 / ARV;
     792           1 :         poDS->m_oSRS.importFromWkt(
     793             :             "PROJCS[\"ARC_System_Zone_18\",GEOGCS[\"GCS_Sphere\","
     794             :             "DATUM[\"D_Sphere\",SPHEROID[\"Sphere\",6378137.0,0.0]],"
     795             :             "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]],"
     796             :             "PROJECTION[\"Azimuthal_Equidistant\"],"
     797             :             "PARAMETER[\"latitude_of_center\",-90],"
     798             :             "PARAMETER[\"longitude_of_center\",0],"
     799             :             "PARAMETER[\"false_easting\",0],"
     800             :             "PARAMETER[\"false_northing\",0],"
     801             :             "UNIT[\"metre\",1]]");
     802             :     }
     803             :     else
     804             :     {
     805           6 :         poDS->adfGeoTransform[0] = LSO;
     806           6 :         poDS->adfGeoTransform[1] = 360. / ARV;
     807           6 :         poDS->adfGeoTransform[2] = 0.0;
     808           6 :         poDS->adfGeoTransform[3] = PSO;
     809           6 :         poDS->adfGeoTransform[4] = 0.0;
     810           6 :         poDS->adfGeoTransform[5] = -360. / BRV;
     811           6 :         poDS->m_oSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
     812             :     }
     813             : 
     814             :     // if( isGIN )
     815             :     {
     816             :         char szValue[32];
     817           8 :         snprintf(szValue, sizeof(szValue), "%d", SCA);
     818           8 :         poDS->SetMetadataItem("ADRG_SCA", szValue);
     819           8 :         snprintf(szValue, sizeof(szValue), "%d", ZNA);
     820           8 :         poDS->SetMetadataItem("ADRG_ZNA", szValue);
     821             :     }
     822             : 
     823           8 :     poDS->SetMetadataItem("ADRG_NAM", osNAM.c_str());
     824             : 
     825           8 :     poDS->nBands = 3;
     826          32 :     for (int i = 0; i < poDS->nBands; i++)
     827          24 :         poDS->SetBand(i + 1, new ADRGRasterBand(poDS, i + 1));
     828             : 
     829           8 :     return poDS;
     830             : }
     831             : 
     832             : /************************************************************************/
     833             : /*                          GetGENListFromTHF()                         */
     834             : /************************************************************************/
     835             : 
     836           7 : char **ADRGDataset::GetGENListFromTHF(const char *pszFileName)
     837             : {
     838          14 :     DDFModule module;
     839           7 :     DDFRecord *record = nullptr;
     840           7 :     int nFilenames = 0;
     841           7 :     char **papszFileNames = nullptr;
     842             : 
     843           7 :     if (!module.Open(pszFileName, TRUE))
     844           0 :         return papszFileNames;
     845             : 
     846             :     while (true)
     847             :     {
     848          27 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     849          27 :         record = module.ReadRecord();
     850          27 :         CPLPopErrorHandler();
     851          27 :         CPLErrorReset();
     852          27 :         if (record == nullptr)
     853           7 :             break;
     854             : 
     855          20 :         if (record->GetFieldCount() >= 2)
     856             :         {
     857          20 :             DDFField *field = record->GetField(0);
     858          20 :             DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     859          40 :             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
     860          20 :                   fieldDefn->GetSubfieldCount() == 2))
     861             :             {
     862           0 :                 continue;
     863             :             }
     864             : 
     865          20 :             const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
     866          20 :             if (RTY == nullptr || !(strcmp(RTY, "TFN") == 0))
     867             :             {
     868          17 :                 continue;
     869             :             }
     870             : 
     871           3 :             int iVFFFieldInstance = 0;
     872          16 :             for (int i = 1; i < record->GetFieldCount(); i++)
     873             :             {
     874          13 :                 field = record->GetField(i);
     875          13 :                 fieldDefn = field->GetFieldDefn();
     876             : 
     877          26 :                 if (!(strcmp(fieldDefn->GetName(), "VFF") == 0 &&
     878          13 :                       fieldDefn->GetSubfieldCount() == 1))
     879             :                 {
     880           0 :                     continue;
     881             :                 }
     882             : 
     883          13 :                 const char *pszVFF = record->GetStringSubfield(
     884             :                     "VFF", iVFFFieldInstance++, "VFF", 0);
     885          13 :                 if (pszVFF == nullptr)
     886           0 :                     continue;
     887          13 :                 CPLString osSubFileName(pszVFF);
     888          13 :                 char *c = (char *)strchr(osSubFileName.c_str(), ' ');
     889          13 :                 if (c)
     890          13 :                     *c = 0;
     891          13 :                 if (EQUAL(CPLGetExtensionSafe(osSubFileName.c_str()).c_str(),
     892             :                           "GEN"))
     893             :                 {
     894           3 :                     CPLDebug("ADRG", "Found GEN file in THF : %s",
     895             :                              osSubFileName.c_str());
     896           3 :                     CPLString osGENFileName(CPLGetDirnameSafe(pszFileName));
     897             :                     char **tokens =
     898           3 :                         CSLTokenizeString2(osSubFileName.c_str(), "/\"", 0);
     899           3 :                     char **ptr = tokens;
     900           3 :                     if (ptr == nullptr)
     901           0 :                         continue;
     902           6 :                     while (*ptr)
     903             :                     {
     904             :                         char **papszDirContent =
     905           3 :                             VSIReadDir(osGENFileName.c_str());
     906           3 :                         char **ptrDir = papszDirContent;
     907           3 :                         if (ptrDir)
     908             :                         {
     909           7 :                             while (*ptrDir)
     910             :                             {
     911           7 :                                 if (EQUAL(*ptrDir, *ptr))
     912             :                                 {
     913           3 :                                     osGENFileName = CPLFormFilenameSafe(
     914             :                                         osGENFileName.c_str(), *ptrDir,
     915           3 :                                         nullptr);
     916           3 :                                     CPLDebug("ADRG",
     917             :                                              "Building GEN full file name : %s",
     918             :                                              osGENFileName.c_str());
     919           3 :                                     break;
     920             :                                 }
     921           4 :                                 ptrDir++;
     922             :                             }
     923             :                         }
     924           3 :                         if (ptrDir == nullptr)
     925           0 :                             break;
     926           3 :                         CSLDestroy(papszDirContent);
     927           3 :                         ptr++;
     928             :                     }
     929           3 :                     int isNameValid = *ptr == nullptr;
     930           3 :                     CSLDestroy(tokens);
     931           3 :                     if (isNameValid)
     932             :                     {
     933           6 :                         papszFileNames = (char **)CPLRealloc(
     934           3 :                             papszFileNames, sizeof(char *) * (nFilenames + 2));
     935           6 :                         papszFileNames[nFilenames] =
     936           3 :                             CPLStrdup(osGENFileName.c_str());
     937           3 :                         papszFileNames[nFilenames + 1] = nullptr;
     938           3 :                         nFilenames++;
     939             :                     }
     940             :                 }
     941             :             }
     942             :         }
     943          20 :     }
     944           7 :     return papszFileNames;
     945             : }
     946             : 
     947             : /************************************************************************/
     948             : /*                          GetIMGListFromGEN()                         */
     949             : /************************************************************************/
     950             : 
     951           7 : char **ADRGDataset::GetIMGListFromGEN(const char *pszFileName,
     952             :                                       int *pnRecordIndex)
     953             : {
     954           7 :     DDFRecord *record = nullptr;
     955           7 :     int nFilenames = 0;
     956           7 :     char **papszFileNames = nullptr;
     957           7 :     int nRecordIndex = -1;
     958             : 
     959           7 :     if (pnRecordIndex)
     960           7 :         *pnRecordIndex = -1;
     961             : 
     962          14 :     DDFModule module;
     963           7 :     if (!module.Open(pszFileName, TRUE))
     964           0 :         return nullptr;
     965             : 
     966             :     while (true)
     967             :     {
     968          29 :         nRecordIndex++;
     969             : 
     970          29 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     971          29 :         record = module.ReadRecord();
     972          29 :         CPLPopErrorHandler();
     973          29 :         CPLErrorReset();
     974          29 :         if (record == nullptr)
     975           7 :             break;
     976             : 
     977          22 :         if (record->GetFieldCount() >= 5)
     978             :         {
     979          15 :             DDFField *field = record->GetField(0);
     980          15 :             DDFFieldDefn *fieldDefn = field->GetFieldDefn();
     981          30 :             if (!(strcmp(fieldDefn->GetName(), "001") == 0 &&
     982          15 :                   fieldDefn->GetSubfieldCount() == 2))
     983             :             {
     984           7 :                 continue;
     985             :             }
     986             : 
     987          15 :             const char *RTY = record->GetStringSubfield("001", 0, "RTY", 0);
     988          15 :             if (RTY == nullptr)
     989           0 :                 continue;
     990             :             /* Ignore overviews */
     991          15 :             if (strcmp(RTY, "OVV") == 0)
     992           7 :                 continue;
     993             : 
     994             :             // TODO: Fix the non-GIN section or remove it.
     995           8 :             if (strcmp(RTY, "GIN") != 0)
     996           0 :                 continue;
     997             : 
     998             :             /* make sure that the GEN file is part of an ADRG dataset, not a SRP
     999             :              * dataset, by checking that the GEN field contains a NWO subfield
    1000             :              */
    1001           8 :             const char *NWO = record->GetStringSubfield("GEN", 0, "NWO", 0);
    1002           8 :             if (NWO == nullptr)
    1003             :             {
    1004           0 :                 CSLDestroy(papszFileNames);
    1005           0 :                 return nullptr;
    1006             :             }
    1007             : 
    1008           8 :             field = record->GetField(3);
    1009           8 :             if (field == nullptr)
    1010           0 :                 continue;
    1011           8 :             fieldDefn = field->GetFieldDefn();
    1012             : 
    1013          16 :             if (!(strcmp(fieldDefn->GetName(), "SPR") == 0 &&
    1014           8 :                   fieldDefn->GetSubfieldCount() == 15))
    1015             :             {
    1016           0 :                 continue;
    1017             :             }
    1018             : 
    1019           8 :             const char *pszBAD = record->GetStringSubfield("SPR", 0, "BAD", 0);
    1020           8 :             if (pszBAD == nullptr || strlen(pszBAD) != 12)
    1021           0 :                 continue;
    1022          16 :             CPLString osBAD = pszBAD;
    1023             :             {
    1024           8 :                 char *c = (char *)strchr(osBAD.c_str(), ' ');
    1025           8 :                 if (c)
    1026           0 :                     *c = 0;
    1027             :             }
    1028           8 :             CPLDebug("ADRG", "BAD=%s", osBAD.c_str());
    1029             : 
    1030             :             /* Build full IMG file name from BAD value */
    1031          16 :             CPLString osGENDir(CPLGetDirnameSafe(pszFileName));
    1032             : 
    1033             :             const CPLString osFileName =
    1034           8 :                 CPLFormFilenameSafe(osGENDir.c_str(), osBAD.c_str(), nullptr);
    1035             :             VSIStatBufL sStatBuf;
    1036           8 :             if (VSIStatL(osFileName, &sStatBuf) == 0)
    1037             :             {
    1038           8 :                 osBAD = osFileName;
    1039           8 :                 CPLDebug("ADRG", "Building IMG full file name : %s",
    1040             :                          osBAD.c_str());
    1041             :             }
    1042             :             else
    1043             :             {
    1044           0 :                 char **papszDirContent = nullptr;
    1045           0 :                 if (strcmp(osGENDir.c_str(), "/vsimem") == 0)
    1046             :                 {
    1047           0 :                     CPLString osTmp = osGENDir + "/";
    1048           0 :                     papszDirContent = VSIReadDir(osTmp);
    1049             :                 }
    1050             :                 else
    1051           0 :                     papszDirContent = VSIReadDir(osGENDir);
    1052           0 :                 char **ptrDir = papszDirContent;
    1053           0 :                 while (ptrDir && *ptrDir)
    1054             :                 {
    1055           0 :                     if (EQUAL(*ptrDir, osBAD.c_str()))
    1056             :                     {
    1057           0 :                         osBAD = CPLFormFilenameSafe(osGENDir.c_str(), *ptrDir,
    1058           0 :                                                     nullptr);
    1059           0 :                         CPLDebug("ADRG", "Building IMG full file name : %s",
    1060             :                                  osBAD.c_str());
    1061           0 :                         break;
    1062             :                     }
    1063           0 :                     ptrDir++;
    1064             :                 }
    1065           0 :                 CSLDestroy(papszDirContent);
    1066             :             }
    1067             : 
    1068           8 :             if (nFilenames == 0 && pnRecordIndex)
    1069           7 :                 *pnRecordIndex = nRecordIndex;
    1070             : 
    1071          16 :             papszFileNames = (char **)CPLRealloc(
    1072           8 :                 papszFileNames, sizeof(char *) * (nFilenames + 2));
    1073           8 :             papszFileNames[nFilenames] = CPLStrdup(osBAD.c_str());
    1074           8 :             papszFileNames[nFilenames + 1] = nullptr;
    1075           8 :             nFilenames++;
    1076             :         }
    1077          22 :     }
    1078             : 
    1079           7 :     return papszFileNames;
    1080             : }
    1081             : 
    1082             : /************************************************************************/
    1083             : /*                                Open()                                */
    1084             : /************************************************************************/
    1085             : 
    1086       28867 : GDALDataset *ADRGDataset::Open(GDALOpenInfo *poOpenInfo)
    1087             : {
    1088       28867 :     int nRecordIndex = -1;
    1089       57731 :     CPLString osGENFileName;
    1090       57732 :     CPLString osIMGFileName;
    1091       28866 :     bool bFromSubdataset = false;
    1092             : 
    1093       28866 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "ADRG:"))
    1094             :     {
    1095             :         char **papszTokens =
    1096           2 :             CSLTokenizeString2(poOpenInfo->pszFilename + 5, ",", 0);
    1097           2 :         if (CSLCount(papszTokens) == 2)
    1098             :         {
    1099           2 :             osGENFileName = papszTokens[0];
    1100           2 :             osIMGFileName = papszTokens[1];
    1101           2 :             bFromSubdataset = true;
    1102             :         }
    1103           2 :         CSLDestroy(papszTokens);
    1104             :     }
    1105             :     else
    1106             :     {
    1107       28864 :         if (poOpenInfo->nHeaderBytes < 500)
    1108       26849 :             return nullptr;
    1109             : 
    1110        2020 :         CPLString osFileName(poOpenInfo->pszFilename);
    1111        2020 :         if (EQUAL(CPLGetExtensionSafe(osFileName.c_str()).c_str(), "THF"))
    1112             :         {
    1113           7 :             char **papszFileNames = GetGENListFromTHF(osFileName.c_str());
    1114           7 :             if (papszFileNames == nullptr)
    1115           4 :                 return nullptr;
    1116           3 :             if (papszFileNames[1] == nullptr)
    1117             :             {
    1118           3 :                 osFileName = papszFileNames[0];
    1119           3 :                 CSLDestroy(papszFileNames);
    1120             :             }
    1121             :             else
    1122             :             {
    1123           0 :                 char **ptr = papszFileNames;
    1124           0 :                 ADRGDataset *poDS = new ADRGDataset();
    1125           0 :                 while (*ptr)
    1126             :                 {
    1127           0 :                     char **papszIMGFileNames = GetIMGListFromGEN(*ptr);
    1128           0 :                     char **papszIMGIter = papszIMGFileNames;
    1129           0 :                     while (papszIMGIter && *papszIMGIter)
    1130             :                     {
    1131           0 :                         poDS->AddSubDataset(*ptr, *papszIMGIter);
    1132           0 :                         papszIMGIter++;
    1133             :                     }
    1134           0 :                     CSLDestroy(papszIMGFileNames);
    1135             : 
    1136           0 :                     ptr++;
    1137             :                 }
    1138           0 :                 CSLDestroy(papszFileNames);
    1139           0 :                 return poDS;
    1140             :             }
    1141             :         }
    1142             : 
    1143        2016 :         if (EQUAL(CPLGetExtensionSafe(osFileName.c_str()).c_str(), "GEN"))
    1144             :         {
    1145           7 :             osGENFileName = osFileName;
    1146             : 
    1147             :             char **papszFileNames =
    1148           7 :                 GetIMGListFromGEN(osFileName.c_str(), &nRecordIndex);
    1149           7 :             if (papszFileNames == nullptr)
    1150           0 :                 return nullptr;
    1151           7 :             if (papszFileNames[1] == nullptr)
    1152             :             {
    1153           6 :                 osIMGFileName = papszFileNames[0];
    1154           6 :                 CSLDestroy(papszFileNames);
    1155             :             }
    1156             :             else
    1157             :             {
    1158           1 :                 char **ptr = papszFileNames;
    1159           1 :                 ADRGDataset *poDS = new ADRGDataset();
    1160           3 :                 while (*ptr)
    1161             :                 {
    1162           2 :                     poDS->AddSubDataset(osFileName.c_str(), *ptr);
    1163           2 :                     ptr++;
    1164             :                 }
    1165           1 :                 CSLDestroy(papszFileNames);
    1166           1 :                 return poDS;
    1167             :             }
    1168             :         }
    1169             :     }
    1170             : 
    1171        2017 :     if (!osGENFileName.empty() && !osIMGFileName.empty())
    1172             :     {
    1173           8 :         if (poOpenInfo->eAccess == GA_Update)
    1174             :         {
    1175           0 :             ReportUpdateNotSupportedByDriver("ADRG");
    1176           8 :             return nullptr;
    1177             :         }
    1178             : 
    1179           8 :         DDFModule module;
    1180           8 :         DDFRecord *record = nullptr;
    1181           8 :         if (nRecordIndex >= 0 && module.Open(osGENFileName.c_str(), TRUE))
    1182             :         {
    1183          24 :             for (int i = 0; i <= nRecordIndex; i++)
    1184             :             {
    1185          18 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
    1186          18 :                 record = module.ReadRecord();
    1187          18 :                 CPLPopErrorHandler();
    1188          18 :                 CPLErrorReset();
    1189          18 :                 if (record == nullptr)
    1190           0 :                     break;
    1191             :             }
    1192             :         }
    1193             : 
    1194             :         ADRGDataset *poDS =
    1195           8 :             OpenDataset(osGENFileName.c_str(), osIMGFileName.c_str(), record);
    1196             : 
    1197           8 :         if (poDS)
    1198             :         {
    1199             :             /* -------------------------------------------------------------- */
    1200             :             /*      Initialize any PAM information.                           */
    1201             :             /* -------------------------------------------------------------- */
    1202           8 :             poDS->SetDescription(poOpenInfo->pszFilename);
    1203           8 :             poDS->TryLoadXML();
    1204             : 
    1205             :             /* -------------------------------------------------------------- */
    1206             :             /*      Check for external overviews.                             */
    1207             :             /* -------------------------------------------------------------- */
    1208           8 :             if (bFromSubdataset)
    1209           2 :                 poDS->oOvManager.Initialize(poDS, osIMGFileName.c_str());
    1210             :             else
    1211           6 :                 poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    1212             : 
    1213           8 :             return poDS;
    1214             :         }
    1215             :     }
    1216             : 
    1217        2009 :     return nullptr;
    1218             : }
    1219             : 
    1220             : /************************************************************************/
    1221             : /*                         GDALRegister_ADRG()                          */
    1222             : /************************************************************************/
    1223             : 
    1224        1667 : void GDALRegister_ADRG()
    1225             : 
    1226             : {
    1227        1667 :     if (GDALGetDriverByName("ADRG") != nullptr)
    1228         282 :         return;
    1229             : 
    1230        1385 :     GDALDriver *poDriver = new GDALDriver();
    1231             : 
    1232        1385 :     poDriver->SetDescription("ADRG");
    1233        1385 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1234        1385 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
    1235        1385 :                               "ARC Digitized Raster Graphics");
    1236        1385 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/adrg.html");
    1237        1385 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "gen");
    1238        1385 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
    1239        1385 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1240             : 
    1241        1385 :     poDriver->pfnOpen = ADRGDataset::Open;
    1242             : 
    1243        1385 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1244             : }

Generated by: LCOV version 1.14