LCOV - code coverage report
Current view: top level - frmts/nitf - rpftocdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 439 555 79.1 %
Date: 2025-09-10 17:48:50 Functions: 36 41 87.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  RPF TOC read Translator
       4             :  * Purpose:  Implementation of RPFTOCDataset and RPFTOCSubDataset.
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "rpftoclib.h"
      15             : 
      16             : #include <array>
      17             : #include <cmath>
      18             : #include <cstdio>
      19             : #include <cstring>
      20             : 
      21             : #include "cpl_conv.h"
      22             : #include "cpl_error.h"
      23             : #include "cpl_multiproc.h"
      24             : #include "cpl_string.h"
      25             : #include "cpl_vsi.h"
      26             : #include "gdal.h"
      27             : #include "gdal_frmts.h"
      28             : #include "gdal_pam.h"
      29             : #include "gdal_priv.h"
      30             : #include "gdal_proxy.h"
      31             : #include "ogr_spatialref.h"
      32             : #include "nitflib.h"
      33             : #include "vrtdataset.h"
      34             : #include "nitfdrivercore.h"
      35             : 
      36             : constexpr int GEOTRSFRM_TOPLEFT_X = 0;
      37             : constexpr int GEOTRSFRM_WE_RES = 1;
      38             : constexpr int GEOTRSFRM_ROTATION_PARAM1 = 2;
      39             : constexpr int GEOTRSFRM_TOPLEFT_Y = 3;
      40             : constexpr int GEOTRSFRM_ROTATION_PARAM2 = 4;
      41             : constexpr int GEOTRSFRM_NS_RES = 5;
      42             : 
      43             : /** Overview of used classes :
      44             :    - RPFTOCDataset : lists the different subdatasets, listed in the A.TOC,
      45             :                      as subdatasets
      46             :    - RPFTOCSubDataset : one of these subdatasets, implemented as a VRT, of
      47             :                         the relevant NITF tiles
      48             :    - RPFTOCProxyRasterDataSet : a "proxy" dataset that maps to a NITF tile
      49             :    - RPFTOCProxyRasterBandPalette / RPFTOCProxyRasterBandRGBA : bands of
      50             :    RPFTOCProxyRasterDataSet
      51             : */
      52             : 
      53             : /************************************************************************/
      54             : /* ==================================================================== */
      55             : /*                            RPFTOCDataset                             */
      56             : /* ==================================================================== */
      57             : /************************************************************************/
      58             : 
      59             : class RPFTOCDataset final : public GDALPamDataset
      60             : {
      61             :     char **papszSubDatasets = nullptr;
      62             :     OGRSpatialReference m_oSRS{};
      63             :     int bGotGeoTransform = false;
      64             :     GDALGeoTransform m_gt{};
      65             :     char **papszFileList = nullptr;
      66             :     CPL_DISALLOW_COPY_ASSIGN(RPFTOCDataset)
      67             : 
      68             :   public:
      69           3 :     RPFTOCDataset()
      70           3 :     {
      71           3 :         m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      72           3 :     }
      73             : 
      74           6 :     ~RPFTOCDataset() override
      75           3 :     {
      76           3 :         CSLDestroy(papszSubDatasets);
      77           3 :         CSLDestroy(papszFileList);
      78           6 :     }
      79             : 
      80             :     char **GetMetadata(const char *pszDomain = "") override;
      81             : 
      82           0 :     char **GetFileList() override
      83             :     {
      84           0 :         return CSLDuplicate(papszFileList);
      85             :     }
      86             : 
      87             :     void AddSubDataset(const char *pszFilename, RPFTocEntry *tocEntry);
      88             : 
      89           3 :     void SetSize(int rasterXSize, int rasterYSize)
      90             :     {
      91           3 :         nRasterXSize = rasterXSize;
      92           3 :         nRasterYSize = rasterYSize;
      93           3 :     }
      94             : 
      95           0 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
      96             :     {
      97           0 :         if (bGotGeoTransform)
      98             :         {
      99           0 :             gt = m_gt;
     100           0 :             return CE_None;
     101             :         }
     102           0 :         return CE_Failure;
     103             :     }
     104             : 
     105           3 :     CPLErr SetGeoTransform(const GDALGeoTransform &gt) override
     106             :     {
     107           3 :         bGotGeoTransform = TRUE;
     108           3 :         m_gt = gt;
     109           3 :         return CE_None;
     110             :     }
     111             : 
     112           0 :     const OGRSpatialReference *GetSpatialRef() const override
     113             :     {
     114           0 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     115             :     }
     116             : 
     117           3 :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override
     118             :     {
     119           3 :         m_oSRS.Clear();
     120           3 :         if (poSRS)
     121           3 :             m_oSRS = *poSRS;
     122           3 :         return CE_None;
     123             :     }
     124             : 
     125             :     static int IsNITFFileTOC(NITFFile *psFile);
     126             :     static GDALDataset *OpenFileTOC(NITFFile *psFile, const char *pszFilename,
     127             :                                     const char *entryName,
     128             :                                     const char *openInformationName);
     129             : 
     130             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
     131             : };
     132             : 
     133             : /************************************************************************/
     134             : /* ==================================================================== */
     135             : /*                            RPFTOCSubDataset                          */
     136             : /* ==================================================================== */
     137             : /************************************************************************/
     138             : 
     139             : class RPFTOCSubDataset final : public VRTDataset
     140             : {
     141             :     int cachedTileBlockXOff = -1;
     142             :     int cachedTileBlockYOff = -1;
     143             :     void *cachedTileData = nullptr;
     144             :     int cachedTileDataSize = 0;
     145             :     const char *cachedTileFileName = nullptr;
     146             :     char **papszFileList = nullptr;
     147             :     CPL_DISALLOW_COPY_ASSIGN(RPFTOCSubDataset)
     148             : 
     149             :   public:
     150          10 :     RPFTOCSubDataset(int nXSize, int nYSize) : VRTDataset(nXSize, nYSize)
     151             :     {
     152             :         /* Don't try to write a VRT file */
     153          10 :         SetWritable(FALSE);
     154             : 
     155             :         /* The driver is set to VRT in VRTDataset constructor. */
     156             :         /* We have to set it to the expected value ! */
     157          10 :         poDriver = GDALDriver::FromHandle(GDALGetDriverByName("RPFTOC"));
     158          10 :     }
     159             : 
     160             :     ~RPFTOCSubDataset() override;
     161             : 
     162           5 :     char **GetFileList() override
     163             :     {
     164           5 :         return CSLDuplicate(papszFileList);
     165             :     }
     166             : 
     167         288 :     void *GetCachedTile(const char *tileFileName, int nBlockXOff,
     168             :                         int nBlockYOff)
     169             :     {
     170         288 :         if (cachedTileFileName == tileFileName &&
     171           0 :             cachedTileBlockXOff == nBlockXOff &&
     172           0 :             cachedTileBlockYOff == nBlockYOff)
     173             :         {
     174           0 :             return cachedTileData;
     175             :         }
     176             : 
     177         288 :         return nullptr;
     178             :     }
     179             : 
     180         288 :     void SetCachedTile(const char *tileFileName, int nBlockXOff, int nBlockYOff,
     181             :                        const void *pData, int dataSize)
     182             :     {
     183         288 :         if (cachedTileData == nullptr || dataSize > cachedTileDataSize)
     184             :         {
     185           2 :             cachedTileData = CPLRealloc(cachedTileData, dataSize);
     186           2 :             cachedTileDataSize = dataSize;
     187             :         }
     188         288 :         memcpy(cachedTileData, pData, dataSize);
     189         288 :         cachedTileFileName = tileFileName;
     190         288 :         cachedTileBlockXOff = nBlockXOff;
     191         288 :         cachedTileBlockYOff = nBlockYOff;
     192         288 :     }
     193             : 
     194             :     static GDALDataset *CreateDataSetFromTocEntry(
     195             :         const char *openInformationName, const char *pszTOCFileName, int nEntry,
     196             :         const RPFTocEntry *entry, int isRGBA, char **papszMetadataRPFTOCFile);
     197             : };
     198             : 
     199          20 : RPFTOCSubDataset::~RPFTOCSubDataset()
     200             : {
     201          10 :     CSLDestroy(papszFileList);
     202          10 :     CPLFree(cachedTileData);
     203          20 : }
     204             : 
     205             : /************************************************************************/
     206             : /* ==================================================================== */
     207             : /*                        RPFTOCProxyRasterDataSet                       */
     208             : /* ==================================================================== */
     209             : /************************************************************************/
     210             : 
     211             : class RPFTOCProxyRasterDataSet final : public GDALProxyPoolDataset
     212             : {
     213             :     /* The following parameters are only for sanity checking */
     214             :     bool checkDone = false;
     215             :     bool checkOK = false;
     216             :     const double nwLong;
     217             :     const double nwLat;
     218             :     GDALColorTable *colorTableRef = nullptr;
     219             :     int bHasNoDataValue = false;
     220             :     double noDataValue = 0;
     221             :     RPFTOCSubDataset *const subdataset;
     222             : 
     223             :     CPL_DISALLOW_COPY_ASSIGN(RPFTOCProxyRasterDataSet)
     224             : 
     225             :   public:
     226             :     RPFTOCProxyRasterDataSet(RPFTOCSubDataset *subdataset, const char *fileName,
     227             :                              int nRasterXSize, int nRasterYSize,
     228             :                              int nBlockXSize, int nBlockYSize,
     229             :                              const char *projectionRef, double nwLong,
     230             :                              double nwLat, int nBands);
     231             : 
     232           7 :     void SetNoDataValue(double noDataValueIn)
     233             :     {
     234           7 :         this->noDataValue = noDataValueIn;
     235           7 :         bHasNoDataValue = TRUE;
     236           7 :     }
     237             : 
     238           3 :     double GetNoDataValue(int *pbHasNoDataValue)
     239             :     {
     240           3 :         if (pbHasNoDataValue)
     241           3 :             *pbHasNoDataValue = this->bHasNoDataValue;
     242           3 :         return noDataValue;
     243             :     }
     244             : 
     245             :     GDALDataset *RefUnderlyingDataset() const override;
     246             : 
     247         400 :     void UnrefUnderlyingDataset(GDALDataset *poUnderlyingDataset) const override
     248             :     {
     249         400 :         GDALProxyPoolDataset::UnrefUnderlyingDataset(poUnderlyingDataset);
     250         400 :     }
     251             : 
     252           7 :     void SetReferenceColorTable(GDALColorTable *colorTableRefIn)
     253             :     {
     254           7 :         this->colorTableRef = colorTableRefIn;
     255           7 :     }
     256             : 
     257           3 :     const GDALColorTable *GetReferenceColorTable() const
     258             :     {
     259           3 :         return colorTableRef;
     260             :     }
     261             : 
     262             :     int SanityCheckOK(GDALDataset *sourceDS);
     263             : 
     264         576 :     RPFTOCSubDataset *GetSubDataset()
     265             :     {
     266         576 :         return subdataset;
     267             :     }
     268             : };
     269             : 
     270         400 : GDALDataset *RPFTOCProxyRasterDataSet::RefUnderlyingDataset() const
     271             : {
     272         400 :     return GDALProxyPoolDataset::RefUnderlyingDataset();
     273             : }
     274             : 
     275             : /************************************************************************/
     276             : /* ==================================================================== */
     277             : /*                     RPFTOCProxyRasterBandRGBA                        */
     278             : /* ==================================================================== */
     279             : /************************************************************************/
     280             : 
     281             : class RPFTOCProxyRasterBandRGBA final : public GDALPamRasterBand
     282             : {
     283             :     bool initDone = false;
     284             :     std::array<unsigned char, 256> colorTable = {0};
     285             :     int blockByteSize = 0;
     286             : 
     287             :   private:
     288             :     void Expand(void *pImage, const void *srcImage);
     289             : 
     290             :   public:
     291          12 :     RPFTOCProxyRasterBandRGBA(GDALProxyPoolDataset *poDSIn, int nBandIn,
     292             :                               int nBlockXSizeIn, int nBlockYSizeIn)
     293          12 :     {
     294          12 :         this->poDS = poDSIn;
     295          12 :         nRasterXSize = poDSIn->GetRasterXSize();
     296          12 :         nRasterYSize = poDSIn->GetRasterYSize();
     297          12 :         this->nBlockXSize = nBlockXSizeIn;
     298          12 :         this->nBlockYSize = nBlockYSizeIn;
     299          12 :         eDataType = GDT_Byte;
     300          12 :         this->nBand = nBandIn;
     301          12 :         blockByteSize = nBlockXSize * nBlockYSize;
     302          12 :     }
     303             : 
     304           0 :     GDALColorInterp GetColorInterpretation() override
     305             :     {
     306           0 :         return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
     307             :     }
     308             : 
     309             :   protected:
     310             :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
     311             : };
     312             : 
     313             : /************************************************************************/
     314             : /*                    Expand()                                          */
     315             : /************************************************************************/
     316             : 
     317             : /* Expand the  array or indexed colors to an array of their corresponding R,G,B
     318             :  * or A component */
     319         288 : void RPFTOCProxyRasterBandRGBA::Expand(void *pImage, const void *srcImage)
     320             : {
     321             :     // pImage might be equal to srcImage
     322             : 
     323         288 :     if ((blockByteSize & (~3)) != 0)
     324             :     {
     325    18874700 :         for (int i = 0; i < blockByteSize; i++)
     326             :         {
     327    18874400 :             static_cast<unsigned char *>(pImage)[i] =
     328    18874400 :                 colorTable[static_cast<const unsigned char *>(srcImage)[i]];
     329             :         }
     330             :     }
     331             :     else
     332             :     {
     333           0 :         const int nIter = blockByteSize / 4;
     334           0 :         for (int i = 0; i < nIter; i++)
     335             :         {
     336           0 :             const unsigned int four_pixels =
     337           0 :                 static_cast<const unsigned int *>(srcImage)[i];
     338           0 :             static_cast<unsigned int *>(pImage)[i] =
     339           0 :                 (colorTable[four_pixels >> 24] << 24) |
     340           0 :                 (colorTable[(four_pixels >> 16) & 0xFF] << 16) |
     341           0 :                 (colorTable[(four_pixels >> 8) & 0xFF] << 8) |
     342           0 :                 colorTable[four_pixels & 0xFF];
     343             :         }
     344             :     }
     345         288 : }
     346             : 
     347             : /************************************************************************/
     348             : /*                    IReadBlock()                                      */
     349             : /************************************************************************/
     350             : 
     351         288 : CPLErr RPFTOCProxyRasterBandRGBA::IReadBlock(int nBlockXOff, int nBlockYOff,
     352             :                                              void *pImage)
     353             : {
     354             :     CPLErr ret;
     355         288 :     RPFTOCProxyRasterDataSet *proxyDS =
     356             :         reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS);
     357             : 
     358         288 :     GDALDataset *ds = proxyDS->RefUnderlyingDataset();
     359         288 :     if (ds)
     360             :     {
     361         288 :         if (proxyDS->SanityCheckOK(ds) == FALSE)
     362             :         {
     363           0 :             proxyDS->UnrefUnderlyingDataset(ds);
     364           0 :             return CE_Failure;
     365             :         }
     366             : 
     367         288 :         GDALRasterBand *srcBand = ds->GetRasterBand(1);
     368         288 :         if (initDone == FALSE)
     369             :         {
     370           8 :             GDALColorTable *srcColorTable = srcBand->GetColorTable();
     371             :             int bHasNoDataValue;
     372             :             int noDataValue =
     373           8 :                 static_cast<int>(srcBand->GetNoDataValue(&bHasNoDataValue));
     374           8 :             const int nEntries = srcColorTable->GetColorEntryCount();
     375        1744 :             for (int i = 0; i < nEntries; i++)
     376             :             {
     377        1736 :                 const GDALColorEntry *entry = srcColorTable->GetColorEntry(i);
     378        1736 :                 if (nBand == 1)
     379         434 :                     colorTable[i] = static_cast<unsigned char>(entry->c1);
     380        1302 :                 else if (nBand == 2)
     381         434 :                     colorTable[i] = static_cast<unsigned char>(entry->c2);
     382         868 :                 else if (nBand == 3)
     383         434 :                     colorTable[i] = static_cast<unsigned char>(entry->c3);
     384             :                 else
     385             :                 {
     386         866 :                     colorTable[i] = (bHasNoDataValue && i == noDataValue)
     387             :                                         ? 0
     388         432 :                                         : static_cast<unsigned char>(entry->c4);
     389             :                 }
     390             :             }
     391           8 :             if (bHasNoDataValue && nEntries == noDataValue)
     392           0 :                 colorTable[nEntries] = 0;
     393           8 :             initDone = TRUE;
     394             :         }
     395             : 
     396             :         /* We use a 1-tile cache as the same source tile will be consecutively
     397             :          * asked for */
     398             :         /* computing the R tile, the G tile, the B tile and the A tile */
     399         576 :         void *cachedImage = proxyDS->GetSubDataset()->GetCachedTile(
     400         288 :             GetDescription(), nBlockXOff, nBlockYOff);
     401         288 :         if (cachedImage == nullptr)
     402             :         {
     403         288 :             CPLDebug("RPFTOC", "Read (%d, %d) of band %d, of file %s",
     404         288 :                      nBlockXOff, nBlockYOff, nBand, GetDescription());
     405         288 :             ret = srcBand->ReadBlock(nBlockXOff, nBlockYOff, pImage);
     406         288 :             if (ret == CE_None)
     407             :             {
     408         288 :                 proxyDS->GetSubDataset()->SetCachedTile(GetDescription(),
     409             :                                                         nBlockXOff, nBlockYOff,
     410             :                                                         pImage, blockByteSize);
     411         288 :                 Expand(pImage, pImage);
     412             :             }
     413             : 
     414             :             /* -------------------------------------------------------------- */
     415             :             /*  Forcibly load the other bands associated with this scanline.  */
     416             :             /* -------------------------------------------------------------- */
     417         288 :             if (nBand == 1)
     418             :             {
     419             :                 GDALRasterBlock *poBlock =
     420          72 :                     poDS->GetRasterBand(2)->GetLockedBlockRef(nBlockXOff,
     421          72 :                                                               nBlockYOff);
     422          72 :                 if (poBlock)
     423          72 :                     poBlock->DropLock();
     424             : 
     425          72 :                 poBlock = poDS->GetRasterBand(3)->GetLockedBlockRef(nBlockXOff,
     426          72 :                                                                     nBlockYOff);
     427          72 :                 if (poBlock)
     428          72 :                     poBlock->DropLock();
     429             : 
     430          72 :                 poBlock = poDS->GetRasterBand(4)->GetLockedBlockRef(nBlockXOff,
     431          72 :                                                                     nBlockYOff);
     432          72 :                 if (poBlock)
     433          72 :                     poBlock->DropLock();
     434             :             }
     435             :         }
     436             :         else
     437             :         {
     438           0 :             Expand(pImage, cachedImage);
     439           0 :             ret = CE_None;
     440             :         }
     441             :     }
     442             :     else
     443             :     {
     444           0 :         ret = CE_Failure;
     445             :     }
     446             : 
     447         288 :     proxyDS->UnrefUnderlyingDataset(ds);
     448             : 
     449         288 :     return ret;
     450             : }
     451             : 
     452             : /************************************************************************/
     453             : /* ==================================================================== */
     454             : /*                 RPFTOCProxyRasterBandPalette                         */
     455             : /* ==================================================================== */
     456             : /************************************************************************/
     457             : 
     458             : class RPFTOCProxyRasterBandPalette final : public GDALPamRasterBand
     459             : {
     460             :     int initDone;
     461             :     int blockByteSize;
     462             :     int samePalette;
     463             :     unsigned char remapLUT[256];
     464             : 
     465             :   public:
     466           7 :     RPFTOCProxyRasterBandPalette(GDALProxyPoolDataset *poDSIn, int nBandIn,
     467             :                                  int nBlockXSizeIn, int nBlockYSizeIn)
     468          14 :         : initDone(FALSE), blockByteSize(nBlockXSizeIn * nBlockYSizeIn),
     469           7 :           samePalette(0)
     470             :     {
     471           7 :         this->poDS = poDSIn;
     472           7 :         nRasterXSize = poDSIn->GetRasterXSize();
     473           7 :         nRasterYSize = poDSIn->GetRasterYSize();
     474           7 :         this->nBlockXSize = nBlockXSizeIn;
     475           7 :         this->nBlockYSize = nBlockYSizeIn;
     476           7 :         eDataType = GDT_Byte;
     477           7 :         this->nBand = nBandIn;
     478           7 :         memset(remapLUT, 0, sizeof(remapLUT));
     479           7 :     }
     480             : 
     481           3 :     GDALColorInterp GetColorInterpretation() override
     482             :     {
     483           3 :         return GCI_PaletteIndex;
     484             :     }
     485             : 
     486           3 :     double GetNoDataValue(int *bHasNoDataValue) override
     487             :     {
     488           3 :         return (reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS))
     489           3 :             ->GetNoDataValue(bHasNoDataValue);
     490             :     }
     491             : 
     492           3 :     GDALColorTable *GetColorTable() override
     493             :     {
     494             :         // TODO: This casting is a bit scary.
     495             :         return const_cast<GDALColorTable *>(
     496           3 :             reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS)
     497           3 :                 ->GetReferenceColorTable());
     498             :     }
     499             : 
     500             :   protected:
     501             :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) override;
     502             : };
     503             : 
     504             : /************************************************************************/
     505             : /*                    IReadBlock()                                      */
     506             : /************************************************************************/
     507             : 
     508         108 : CPLErr RPFTOCProxyRasterBandPalette::IReadBlock(int nBlockXOff, int nBlockYOff,
     509             :                                                 void *pImage)
     510             : {
     511             :     CPLErr ret;
     512         108 :     RPFTOCProxyRasterDataSet *proxyDS =
     513             :         reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS);
     514         108 :     GDALDataset *ds = proxyDS->RefUnderlyingDataset();
     515         108 :     if (ds)
     516             :     {
     517         108 :         if (proxyDS->SanityCheckOK(ds) == FALSE)
     518             :         {
     519           0 :             proxyDS->UnrefUnderlyingDataset(ds);
     520           0 :             return CE_Failure;
     521             :         }
     522             : 
     523         108 :         GDALRasterBand *srcBand = ds->GetRasterBand(1);
     524         108 :         ret = srcBand->ReadBlock(nBlockXOff, nBlockYOff, pImage);
     525             : 
     526         108 :         if (initDone == FALSE)
     527             :         {
     528             :             int approximateMatching;
     529           3 :             if (srcBand->GetIndexColorTranslationTo(this, remapLUT,
     530           3 :                                                     &approximateMatching))
     531             :             {
     532           0 :                 samePalette = FALSE;
     533           0 :                 if (approximateMatching)
     534             :                 {
     535           0 :                     CPLError(
     536             :                         CE_Failure, CPLE_AppDefined,
     537             :                         "Palette for %s is different from reference palette. "
     538             :                         "Coudln't remap exactly all colors. Trying to find "
     539             :                         "closest matches.\n",
     540           0 :                         GetDescription());
     541             :                 }
     542             :             }
     543             :             else
     544             :             {
     545           3 :                 samePalette = TRUE;
     546             :             }
     547           3 :             initDone = TRUE;
     548             :         }
     549             : 
     550         108 :         if (samePalette == FALSE)
     551             :         {
     552           0 :             unsigned char *data = static_cast<unsigned char *>(pImage);
     553           0 :             for (int i = 0; i < blockByteSize; i++)
     554             :             {
     555           0 :                 data[i] = remapLUT[data[i]];
     556             :             }
     557             :         }
     558             :     }
     559             :     else
     560             :     {
     561           0 :         ret = CE_Failure;
     562             :     }
     563             : 
     564         108 :     proxyDS->UnrefUnderlyingDataset(ds);
     565             : 
     566         108 :     return ret;
     567             : }
     568             : 
     569             : /************************************************************************/
     570             : /*                    RPFTOCProxyRasterDataSet()                         */
     571             : /************************************************************************/
     572             : 
     573          10 : RPFTOCProxyRasterDataSet::RPFTOCProxyRasterDataSet(
     574             :     RPFTOCSubDataset *subdatasetIn, const char *fileNameIn, int nRasterXSizeIn,
     575             :     int nRasterYSizeIn, int nBlockXSizeIn, int nBlockYSizeIn,
     576          10 :     const char *projectionRefIn, double nwLongIn, double nwLatIn, int nBandsIn)
     577             :     :  // Mark as shared since the VRT will take several references if we are in
     578             :        // RGBA mode (4 bands for this dataset).
     579             :       GDALProxyPoolDataset(fileNameIn, nRasterXSizeIn, nRasterYSizeIn,
     580             :                            GA_ReadOnly, TRUE, projectionRefIn),
     581          10 :       nwLong(nwLongIn), nwLat(nwLatIn), subdataset(subdatasetIn)
     582             : {
     583          10 :     if (nBandsIn == 4)
     584             :     {
     585          15 :         for (int i = 0; i < 4; i++)
     586             :         {
     587          12 :             SetBand(i + 1, new RPFTOCProxyRasterBandRGBA(
     588          12 :                                this, i + 1, nBlockXSizeIn, nBlockYSizeIn));
     589             :         }
     590             :     }
     591             :     else
     592             :     {
     593           7 :         SetBand(1, new RPFTOCProxyRasterBandPalette(this, 1, nBlockXSizeIn,
     594           7 :                                                     nBlockYSizeIn));
     595             :     }
     596          10 : }
     597             : 
     598             : /************************************************************************/
     599             : /*                    SanityCheckOK()                                   */
     600             : /************************************************************************/
     601             : 
     602             : #define WARN_ON_FAIL(x)                                                        \
     603             :     do                                                                         \
     604             :     {                                                                          \
     605             :         if (!(x))                                                              \
     606             :         {                                                                      \
     607             :             CPLError(CE_Warning, CPLE_AppDefined,                              \
     608             :                      "For %s, assert '" #x "' failed", GetDescription());      \
     609             :         }                                                                      \
     610             :     } while (false)
     611             : #define ERROR_ON_FAIL(x)                                                       \
     612             :     do                                                                         \
     613             :     {                                                                          \
     614             :         if (!(x))                                                              \
     615             :         {                                                                      \
     616             :             CPLError(CE_Warning, CPLE_AppDefined,                              \
     617             :                      "For %s, assert '" #x "' failed", GetDescription());      \
     618             :             checkOK = FALSE;                                                   \
     619             :         }                                                                      \
     620             :     } while (false)
     621             : 
     622         396 : int RPFTOCProxyRasterDataSet::SanityCheckOK(GDALDataset *sourceDS)
     623             : {
     624         396 :     if (checkDone)
     625         391 :         return checkOK;
     626             : 
     627             :     int src_nBlockXSize;
     628             :     int src_nBlockYSize;
     629             :     int nBlockXSize;
     630             :     int nBlockYSize;
     631           5 :     GDALGeoTransform l_gt;
     632             : 
     633           5 :     checkOK = TRUE;
     634           5 :     checkDone = TRUE;
     635             : 
     636           5 :     sourceDS->GetGeoTransform(l_gt);
     637           5 :     WARN_ON_FAIL(fabs(l_gt[GEOTRSFRM_TOPLEFT_X] - nwLong) < l_gt[1]);
     638           5 :     WARN_ON_FAIL(fabs(l_gt[GEOTRSFRM_TOPLEFT_Y] - nwLat) < fabs(l_gt[5]));
     639           5 :     WARN_ON_FAIL(l_gt[GEOTRSFRM_ROTATION_PARAM1] == 0 &&
     640             :                  l_gt[GEOTRSFRM_ROTATION_PARAM2] == 0); /* No rotation */
     641           5 :     ERROR_ON_FAIL(sourceDS->GetRasterCount() == 1);     /* Just 1 band */
     642           5 :     ERROR_ON_FAIL(sourceDS->GetRasterXSize() == nRasterXSize);
     643           5 :     ERROR_ON_FAIL(sourceDS->GetRasterYSize() == nRasterYSize);
     644           5 :     WARN_ON_FAIL(EQUAL(sourceDS->GetProjectionRef(), GetProjectionRef()));
     645           5 :     sourceDS->GetRasterBand(1)->GetBlockSize(&src_nBlockXSize,
     646             :                                              &src_nBlockYSize);
     647           5 :     GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     648           5 :     ERROR_ON_FAIL(src_nBlockXSize == nBlockXSize);
     649           5 :     ERROR_ON_FAIL(src_nBlockYSize == nBlockYSize);
     650           5 :     WARN_ON_FAIL(sourceDS->GetRasterBand(1)->GetColorInterpretation() ==
     651             :                  GCI_PaletteIndex);
     652           5 :     WARN_ON_FAIL(sourceDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte);
     653             : 
     654           5 :     return checkOK;
     655             : }
     656             : 
     657             : /************************************************************************/
     658             : /*                           MakeTOCEntryName()                         */
     659             : /************************************************************************/
     660             : 
     661          10 : static const char *MakeTOCEntryName(RPFTocEntry *tocEntry)
     662             : {
     663          10 :     char *str = nullptr;
     664          10 :     if (tocEntry->seriesAbbreviation)
     665          10 :         str = const_cast<char *>(CPLSPrintf(
     666          10 :             "%s_%s_%s_%s_%d", tocEntry->type, tocEntry->seriesAbbreviation,
     667          10 :             tocEntry->scale, tocEntry->zone, tocEntry->boundaryId));
     668             :     else
     669           0 :         str = const_cast<char *>(CPLSPrintf("%s_%s_%s_%d", tocEntry->type,
     670           0 :                                             tocEntry->scale, tocEntry->zone,
     671             :                                             tocEntry->boundaryId));
     672          10 :     char *c = str;
     673         240 :     while (*c)
     674             :     {
     675         230 :         if (*c == ':' || *c == ' ')
     676           0 :             *c = '_';
     677         230 :         c++;
     678             :     }
     679          10 :     return str;
     680             : }
     681             : 
     682             : /************************************************************************/
     683             : /*                           AddSubDataset()                            */
     684             : /************************************************************************/
     685             : 
     686           3 : void RPFTOCDataset::AddSubDataset(const char *pszFilename,
     687             :                                   RPFTocEntry *tocEntry)
     688             : 
     689             : {
     690             :     char szName[80];
     691           3 :     const int nCount = CSLCount(papszSubDatasets) / 2;
     692             : 
     693           3 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
     694           3 :     papszSubDatasets =
     695           3 :         CSLSetNameValue(papszSubDatasets, szName,
     696             :                         CPLSPrintf("NITF_TOC_ENTRY:%s:%s",
     697             :                                    MakeTOCEntryName(tocEntry), pszFilename));
     698             : 
     699           3 :     snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
     700           3 :     if (tocEntry->seriesName && tocEntry->seriesAbbreviation)
     701           3 :         papszSubDatasets = CSLSetNameValue(
     702             :             papszSubDatasets, szName,
     703           3 :             CPLSPrintf("%s:%s:%s:%s:%s:%d", tocEntry->type,
     704             :                        tocEntry->seriesAbbreviation, tocEntry->seriesName,
     705           3 :                        tocEntry->scale, tocEntry->zone, tocEntry->boundaryId));
     706             :     else
     707           0 :         papszSubDatasets = CSLSetNameValue(
     708             :             papszSubDatasets, szName,
     709           0 :             CPLSPrintf("%s:%s:%s:%d", tocEntry->type, tocEntry->scale,
     710           0 :                        tocEntry->zone, tocEntry->boundaryId));
     711           3 : }
     712             : 
     713             : /************************************************************************/
     714             : /*                            GetMetadata()                             */
     715             : /************************************************************************/
     716             : 
     717           3 : char **RPFTOCDataset::GetMetadata(const char *pszDomain)
     718             : 
     719             : {
     720           3 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
     721           3 :         return papszSubDatasets;
     722             : 
     723           0 :     return GDALPamDataset::GetMetadata(pszDomain);
     724             : }
     725             : 
     726             : /************************************************************************/
     727             : /*                  NITFCreateVRTDataSetFromTocEntry()                  */
     728             : /************************************************************************/
     729             : 
     730             : #define ASSERT_CREATE_VRT(x)                                                   \
     731             :     do                                                                         \
     732             :     {                                                                          \
     733             :         if (!(x))                                                              \
     734             :         {                                                                      \
     735             :             CPLError(CE_Failure, CPLE_AppDefined,                              \
     736             :                      "For %s, assert '" #x "' failed",                         \
     737             :                      entry->frameEntries[i].fullFilePath);                     \
     738             :             if (poSrcDS)                                                       \
     739             :                 GDALClose(poSrcDS);                                            \
     740             :             CPLFree(projectionRef);                                            \
     741             :             return nullptr;                                                    \
     742             :         }                                                                      \
     743             :     } while (false)
     744             : 
     745             : /* Builds a RPFTOCSubDataset from the set of files of the toc entry */
     746          10 : GDALDataset *RPFTOCSubDataset::CreateDataSetFromTocEntry(
     747             :     const char *openInformationName, const char *pszTOCFileName, int nEntry,
     748             :     const RPFTocEntry *entry, int isRGBA, char **papszMetadataRPFTOCFile)
     749             : {
     750          10 :     GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("VRT");
     751          10 :     if (poDriver == nullptr)
     752           0 :         return nullptr;
     753             : 
     754          10 :     const int N = entry->nVertFrames * entry->nHorizFrames;
     755             : 
     756             :     /* This may not be reliable. See below */
     757          10 :     int sizeX =
     758          10 :         static_cast<int>((entry->seLong - entry->nwLong) /
     759          10 :                              (entry->nHorizFrames * entry->horizInterval) +
     760             :                          0.5);
     761             : 
     762          10 :     int sizeY =
     763          10 :         static_cast<int>((entry->nwLat - entry->seLat) /
     764          10 :                              (entry->nVertFrames * entry->vertInterval) +
     765             :                          0.5);
     766             : 
     767          10 :     if ((EQUAL(entry->type, "CADRG") || (EQUAL(entry->type, "CIB"))))
     768             :     {
     769             :         // for CADRG and CIB the frame size is defined with 1536x1536
     770             :         // CADRG: see MIL-C-89038: 3.5.2 a - Each frame shall comprise a
     771             :         // rectangular array of 1536 by 1536 pixels CIB: see MIL-C-89041: 3.5.2
     772             :         // a - Each frame shall comprise a rectangular array of 1536 by 1536
     773             :         // pixels
     774          10 :         sizeX = 1536;
     775          10 :         sizeY = 1536;
     776             :     }
     777             : 
     778          10 :     int nBlockXSize = 0;
     779          10 :     int nBlockYSize = 0;
     780          10 :     GDALGeoTransform gt;
     781          10 :     char *projectionRef = nullptr;
     782          10 :     int index = 0;
     783             : 
     784          20 :     for (int i = 0; i < N; i++)
     785             :     {
     786          10 :         if (!entry->frameEntries[i].fileExists)
     787           0 :             continue;
     788             : 
     789          10 :         if (index == 0)
     790             :         {
     791             :             /* Open the first available file to get its geotransform, projection
     792             :              * ref and block size */
     793             :             /* Do a few sanity checks too */
     794             :             /* Ideally we should make these sanity checks now on ALL files, but
     795             :              * it would be too slow */
     796             :             /* for large datasets. So these sanity checks will be done at the
     797             :              * time we really need */
     798             :             /* to access the file (see SanityCheckOK method) */
     799          10 :             GDALDataset *poSrcDS = GDALDataset::FromHandle(GDALOpenShared(
     800          10 :                 entry->frameEntries[i].fullFilePath, GA_ReadOnly));
     801          10 :             ASSERT_CREATE_VRT(poSrcDS);
     802          10 :             poSrcDS->GetGeoTransform(gt);
     803          10 :             projectionRef = CPLStrdup(poSrcDS->GetProjectionRef());
     804          10 :             ASSERT_CREATE_VRT(gt[GEOTRSFRM_ROTATION_PARAM1] == 0 &&
     805             :                               gt[GEOTRSFRM_ROTATION_PARAM2] ==
     806             :                                   0);                          /* No rotation */
     807          10 :             ASSERT_CREATE_VRT(poSrcDS->GetRasterCount() == 1); /* Just 1 band */
     808             : 
     809             :             /* Tolerance of 1%... This is necessary for CADRG_L22/RPF/A.TOC for
     810             :              * example */
     811          10 :             ASSERT_CREATE_VRT((entry->horizInterval - gt[GEOTRSFRM_WE_RES]) /
     812             :                                   entry->horizInterval <
     813             :                               0.01); /* X interval same as in TOC */
     814          10 :             ASSERT_CREATE_VRT((entry->vertInterval - (-gt[GEOTRSFRM_NS_RES])) /
     815             :                                   entry->vertInterval <
     816             :                               0.01); /* Y interval same as in TOC */
     817             : 
     818          10 :             const int ds_sizeX = poSrcDS->GetRasterXSize();
     819          10 :             const int ds_sizeY = poSrcDS->GetRasterYSize();
     820             :             /* for polar zone use the sizes from the dataset */
     821          10 :             if ((entry->zone[0] == '9') || (entry->zone[0] == 'J'))
     822             :             {
     823           0 :                 sizeX = ds_sizeX;
     824           0 :                 sizeY = ds_sizeY;
     825             :             }
     826             : 
     827             :             /* In the case the east longitude is 180, there's a great chance
     828             :              * that it is in fact */
     829             :             /* truncated in the A.TOC. Thus, the only reliable way to find out
     830             :              * the tile width, is to */
     831             :             /* read it from the tile dataset itself... */
     832             :             /* This is the case for the GNCJNCN dataset that has world coverage
     833             :              */
     834          10 :             if (entry->seLong == 180.00)
     835           0 :                 sizeX = ds_sizeX;
     836             :             else
     837          10 :                 ASSERT_CREATE_VRT(sizeX == ds_sizeX);
     838             : 
     839          10 :             ASSERT_CREATE_VRT(sizeY == ds_sizeY);
     840          10 :             poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
     841          10 :             ASSERT_CREATE_VRT(
     842             :                 poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
     843             :                 GCI_PaletteIndex);
     844          10 :             ASSERT_CREATE_VRT(poSrcDS->GetRasterBand(1)->GetRasterDataType() ==
     845             :                               GDT_Byte);
     846          10 :             GDALClose(poSrcDS);
     847             :         }
     848             : 
     849          10 :         index++;
     850             :     }
     851             : 
     852          10 :     if (index == 0)
     853           0 :         return nullptr;
     854             : 
     855             :     /* ------------------------------------ */
     856             :     /* Create the VRT with the overall size */
     857             :     /* ------------------------------------ */
     858             :     RPFTOCSubDataset *poVirtualDS = new RPFTOCSubDataset(
     859          10 :         sizeX * entry->nHorizFrames, sizeY * entry->nVertFrames);
     860             : 
     861          10 :     if (papszMetadataRPFTOCFile)
     862           0 :         poVirtualDS->SetMetadata(papszMetadataRPFTOCFile);
     863             : 
     864          10 :     poVirtualDS->SetProjection(projectionRef);
     865             : 
     866          10 :     gt[GEOTRSFRM_TOPLEFT_X] = entry->nwLong;
     867          10 :     gt[GEOTRSFRM_TOPLEFT_Y] = entry->nwLat;
     868          10 :     poVirtualDS->SetGeoTransform(gt);
     869             : 
     870             :     int nBands;
     871             : 
     872             :     /* In most cases, all the files inside a TOC entry share the same */
     873             :     /* palette and we could use it for the VRT. */
     874             :     /* In other cases like for CADRG801_France_250K (TOC entry CADRG_250K_2_2),
     875             :      */
     876             :     /* the file for Corsica and the file for Sardegna do not share the same
     877             :      * palette */
     878             :     /* however they contain the same RGB triplets and are just ordered
     879             :      * differently */
     880             :     /* So we can use the same palette */
     881             :     /* In the unlikely event where palettes would be incompatible, we can use
     882             :      * the RGBA */
     883             :     /* option through the config option RPFTOC_FORCE_RGBA */
     884          10 :     if (isRGBA == FALSE)
     885             :     {
     886           7 :         poVirtualDS->AddBand(GDT_Byte, nullptr);
     887           7 :         GDALRasterBand *poBand = poVirtualDS->GetRasterBand(1);
     888           7 :         poBand->SetColorInterpretation(GCI_PaletteIndex);
     889           7 :         nBands = 1;
     890             : 
     891          14 :         for (int i = 0; i < N; i++)
     892             :         {
     893           7 :             if (!entry->frameEntries[i].fileExists)
     894           0 :                 continue;
     895             : 
     896           7 :             bool bAllBlack = true;
     897           7 :             GDALDataset *poSrcDS = GDALDataset::FromHandle(GDALOpenShared(
     898           7 :                 entry->frameEntries[i].fullFilePath, GA_ReadOnly));
     899           7 :             if (poSrcDS != nullptr)
     900             :             {
     901           7 :                 if (poSrcDS->GetRasterCount() == 1)
     902             :                 {
     903             :                     int bHasNoDataValue;
     904             :                     const double noDataValue =
     905           7 :                         poSrcDS->GetRasterBand(1)->GetNoDataValue(
     906           7 :                             &bHasNoDataValue);
     907           7 :                     if (bHasNoDataValue)
     908           7 :                         poBand->SetNoDataValue(noDataValue);
     909             : 
     910             :                     /* Avoid setting a color table that is all black (which
     911             :                      * might be */
     912             :                     /* the case of the edge tiles of a RPF subdataset) */
     913             :                     GDALColorTable *poCT =
     914           7 :                         poSrcDS->GetRasterBand(1)->GetColorTable();
     915           7 :                     if (poCT != nullptr)
     916             :                     {
     917        1526 :                         for (int iC = 0; iC < poCT->GetColorEntryCount(); iC++)
     918             :                         {
     919        3045 :                             if (bHasNoDataValue &&
     920        1519 :                                 iC == static_cast<int>(noDataValue))
     921           7 :                                 continue;
     922             : 
     923             :                             const GDALColorEntry *psColorEntry =
     924        1512 :                                 poCT->GetColorEntry(iC);
     925        1512 :                             if (psColorEntry->c1 != 0 ||
     926        1512 :                                 psColorEntry->c2 != 0 || psColorEntry->c3 != 0)
     927             :                             {
     928           0 :                                 bAllBlack = false;
     929           0 :                                 break;
     930             :                             }
     931             :                         }
     932             : 
     933             :                         /* Assign it temporarily, in the hope of a better match
     934             :                          */
     935             :                         /* afterwards */
     936           7 :                         poBand->SetColorTable(poCT);
     937           7 :                         if (bAllBlack)
     938             :                         {
     939           7 :                             CPLDebug("RPFTOC",
     940             :                                      "Skipping %s. Its palette is all black.",
     941           7 :                                      poSrcDS->GetDescription());
     942             :                         }
     943             :                     }
     944             :                 }
     945           7 :                 GDALClose(poSrcDS);
     946             :             }
     947           7 :             if (!bAllBlack)
     948           0 :                 break;
     949             :         }
     950             :     }
     951             :     else
     952             :     {
     953          15 :         for (int i = 0; i < 4; i++)
     954             :         {
     955          12 :             poVirtualDS->AddBand(GDT_Byte, nullptr);
     956          12 :             GDALRasterBand *poBand = poVirtualDS->GetRasterBand(i + 1);
     957          12 :             poBand->SetColorInterpretation(
     958          12 :                 static_cast<GDALColorInterp>(GCI_RedBand + i));
     959             :         }
     960           3 :         nBands = 4;
     961             :     }
     962             : 
     963          10 :     CPLFree(projectionRef);
     964          10 :     projectionRef = nullptr;
     965             : 
     966             :     /* -------------------------------------------------------------------- */
     967             :     /*      Check for overviews.                                            */
     968             :     /* -------------------------------------------------------------------- */
     969             : 
     970          10 :     poVirtualDS->oOvManager.Initialize(
     971          20 :         poVirtualDS, CPLString().Printf("%s.%d", pszTOCFileName, nEntry + 1));
     972             : 
     973          10 :     poVirtualDS->SetDescription(pszTOCFileName);
     974          10 :     poVirtualDS->papszFileList = poVirtualDS->GDALDataset::GetFileList();
     975          10 :     poVirtualDS->SetDescription(openInformationName);
     976             : 
     977          10 :     int iFile = 0;
     978          20 :     for (int i = 0; i < N; i++)
     979             :     {
     980          10 :         if (!entry->frameEntries[i].fileExists)
     981           0 :             continue;
     982             : 
     983          10 :         poVirtualDS->SetMetadataItem(CPLSPrintf("FILENAME_%d", iFile),
     984          10 :                                      entry->frameEntries[i].fullFilePath);
     985          20 :         poVirtualDS->papszFileList = CSLAddString(
     986          10 :             poVirtualDS->papszFileList, entry->frameEntries[i].fullFilePath);
     987          10 :         iFile++;
     988             : 
     989             :         /* We create proxy datasets and raster bands */
     990             :         /* Using real datasets and raster bands is possible in theory */
     991             :         /* However for large datasets, a TOC entry can include several hundreds
     992             :          * of files */
     993             :         /* and we finally reach the limit of maximum file descriptors open at
     994             :          * the same time ! */
     995             :         /* So the idea is to warp the datasets into a proxy and open the
     996             :          * underlying dataset only when it is */
     997             :         /* needed (IRasterIO operation). To improve a bit efficiency, we have a
     998             :          * cache of opened */
     999             :         /* underlying datasets */
    1000             :         RPFTOCProxyRasterDataSet *ds = new RPFTOCProxyRasterDataSet(
    1001          10 :             cpl::down_cast<RPFTOCSubDataset *>(poVirtualDS),
    1002          10 :             entry->frameEntries[i].fullFilePath, sizeX, sizeY, nBlockXSize,
    1003          10 :             nBlockYSize, poVirtualDS->GetProjectionRef(),
    1004          10 :             entry->nwLong +
    1005          10 :                 entry->frameEntries[i].frameCol * entry->horizInterval * sizeX,
    1006          10 :             entry->nwLat -
    1007          10 :                 entry->frameEntries[i].frameRow * entry->vertInterval * sizeY,
    1008          10 :             nBands);
    1009             : 
    1010          10 :         if (nBands == 1)
    1011             :         {
    1012           7 :             GDALRasterBand *poBand = poVirtualDS->GetRasterBand(1);
    1013           7 :             ds->SetReferenceColorTable(poBand->GetColorTable());
    1014             :             int bHasNoDataValue;
    1015           7 :             const double noDataValue = poBand->GetNoDataValue(&bHasNoDataValue);
    1016           7 :             if (bHasNoDataValue)
    1017           7 :                 ds->SetNoDataValue(noDataValue);
    1018             :         }
    1019             : 
    1020          29 :         for (int j = 0; j < nBands; j++)
    1021             :         {
    1022             :             VRTSourcedRasterBand *poBand =
    1023             :                 reinterpret_cast<VRTSourcedRasterBand *>(
    1024          19 :                     poVirtualDS->GetRasterBand(j + 1));
    1025             :             /* Place the raster band at the right position in the VRT */
    1026          19 :             poBand->AddSimpleSource(
    1027             :                 ds->GetRasterBand(j + 1), 0, 0, sizeX, sizeY,
    1028          19 :                 entry->frameEntries[i].frameCol * sizeX,
    1029          19 :                 entry->frameEntries[i].frameRow * sizeY, sizeX, sizeY);
    1030             :         }
    1031             : 
    1032             :         /* The RPFTOCProxyRasterDataSet will be destroyed when its last raster
    1033             :          * band will be */
    1034             :         /* destroyed */
    1035          10 :         ds->Dereference();
    1036             :     }
    1037             : 
    1038          10 :     poVirtualDS->SetMetadataItem("NITF_SCALE", entry->scale);
    1039          10 :     poVirtualDS->SetMetadataItem(
    1040             :         "NITF_SERIES_ABBREVIATION",
    1041          10 :         (entry->seriesAbbreviation) ? entry->seriesAbbreviation : "Unknown");
    1042          10 :     poVirtualDS->SetMetadataItem("NITF_SERIES_NAME", (entry->seriesName)
    1043             :                                                          ? entry->seriesName
    1044          10 :                                                          : "Unknown");
    1045             : 
    1046          10 :     return poVirtualDS;
    1047             : }
    1048             : 
    1049             : /************************************************************************/
    1050             : /*                             IsNITFFileTOC()                          */
    1051             : /************************************************************************/
    1052             : 
    1053             : /* Check whether this NITF file is a TOC file */
    1054           0 : int RPFTOCDataset::IsNITFFileTOC(NITFFile *psFile)
    1055             : {
    1056             :     const char *fileTitle =
    1057           0 :         CSLFetchNameValue(psFile->papszMetadata, "NITF_FTITLE");
    1058           0 :     while (fileTitle && *fileTitle)
    1059             :     {
    1060           0 :         if (EQUAL(fileTitle, "A.TOC"))
    1061             :         {
    1062           0 :             return TRUE;
    1063             :         }
    1064           0 :         fileTitle++;
    1065             :     }
    1066           0 :     return FALSE;
    1067             : }
    1068             : 
    1069             : /************************************************************************/
    1070             : /*                                OpenFileTOC()                         */
    1071             : /************************************************************************/
    1072             : 
    1073             : /* Create a dataset from a TOC file */
    1074             : /* If psFile == NULL, the TOC file has no NITF header */
    1075             : /* If entryName != NULL, the dataset will be made just of the entry of the TOC
    1076             :  * file */
    1077          10 : GDALDataset *RPFTOCDataset::OpenFileTOC(NITFFile *psFile,
    1078             :                                         const char *pszFilename,
    1079             :                                         const char *entryName,
    1080             :                                         const char *openInformationName)
    1081             : {
    1082             :     char buffer[48];
    1083          10 :     VSILFILE *fp = nullptr;
    1084          10 :     if (psFile == nullptr)
    1085             :     {
    1086          10 :         fp = VSIFOpenL(pszFilename, "rb");
    1087             : 
    1088          10 :         if (fp == nullptr)
    1089             :         {
    1090           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.",
    1091             :                      pszFilename);
    1092           0 :             return nullptr;
    1093             :         }
    1094          10 :         if (VSIFReadL(buffer, 1, 48, fp) != 48)
    1095             :         {
    1096           0 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
    1097           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1098           0 :             return nullptr;
    1099             :         }
    1100             :     }
    1101             :     const int isRGBA =
    1102          10 :         CPLTestBool(CPLGetConfigOption("RPFTOC_FORCE_RGBA", "NO"));
    1103          10 :     RPFToc *toc = (psFile) ? RPFTOCRead(pszFilename, psFile)
    1104          10 :                            : RPFTOCReadFromBuffer(pszFilename, fp, buffer);
    1105          10 :     if (fp)
    1106          10 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1107          10 :     fp = nullptr;
    1108             : 
    1109          10 :     if (entryName != nullptr)
    1110             :     {
    1111           7 :         if (toc)
    1112             :         {
    1113           7 :             for (int i = 0; i < toc->nEntries; i++)
    1114             :             {
    1115           7 :                 if (EQUAL(entryName, MakeTOCEntryName(&toc->entries[i])))
    1116             :                 {
    1117             :                     GDALDataset *ds =
    1118           7 :                         RPFTOCSubDataset::CreateDataSetFromTocEntry(
    1119             :                             openInformationName, pszFilename, i,
    1120           7 :                             &toc->entries[i], isRGBA,
    1121             :                             (psFile) ? psFile->papszMetadata : nullptr);
    1122             : 
    1123           7 :                     RPFTOCFree(toc);
    1124           7 :                     return ds;
    1125             :                 }
    1126             :             }
    1127           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1128             :                      "The entry %s does not exist in file %s.", entryName,
    1129             :                      pszFilename);
    1130             :         }
    1131           0 :         RPFTOCFree(toc);
    1132           0 :         return nullptr;
    1133             :     }
    1134             : 
    1135           3 :     if (toc)
    1136             :     {
    1137           3 :         RPFTOCDataset *ds = new RPFTOCDataset();
    1138           3 :         if (psFile)
    1139           0 :             ds->SetMetadata(psFile->papszMetadata);
    1140             : 
    1141           3 :         bool ok = false;
    1142           3 :         char *projectionRef = nullptr;
    1143           3 :         double nwLong = 0.0;
    1144           3 :         double nwLat = 0.0;
    1145           3 :         double seLong = 0.0;
    1146           3 :         double seLat = 0.0;
    1147           3 :         GDALGeoTransform gt;
    1148             : 
    1149           3 :         ds->papszFileList = CSLAddString(ds->papszFileList, pszFilename);
    1150             : 
    1151           6 :         for (int i = 0; i < toc->nEntries; i++)
    1152             :         {
    1153           3 :             if (!toc->entries[i].isOverviewOrLegend)
    1154             :             {
    1155             :                 GDALDataset *tmpDS =
    1156           6 :                     RPFTOCSubDataset::CreateDataSetFromTocEntry(
    1157           3 :                         openInformationName, pszFilename, i, &toc->entries[i],
    1158             :                         isRGBA, nullptr);
    1159           3 :                 if (tmpDS)
    1160             :                 {
    1161           3 :                     char **papszSubDatasetFileList = tmpDS->GetFileList();
    1162             :                     /* Yes, begin at 1, since the first is the a.toc */
    1163           6 :                     ds->papszFileList = CSLInsertStrings(
    1164           3 :                         ds->papszFileList, -1, papszSubDatasetFileList + 1);
    1165           3 :                     CSLDestroy(papszSubDatasetFileList);
    1166             : 
    1167           3 :                     tmpDS->GetGeoTransform(gt);
    1168           3 :                     if (projectionRef == nullptr)
    1169             :                     {
    1170           3 :                         ok = true;
    1171           3 :                         projectionRef = CPLStrdup(tmpDS->GetProjectionRef());
    1172           3 :                         nwLong = gt[GEOTRSFRM_TOPLEFT_X];
    1173           3 :                         nwLat = gt[GEOTRSFRM_TOPLEFT_Y];
    1174           3 :                         seLong = nwLong +
    1175           3 :                                  gt[GEOTRSFRM_WE_RES] * tmpDS->GetRasterXSize();
    1176           3 :                         seLat = nwLat +
    1177           3 :                                 gt[GEOTRSFRM_NS_RES] * tmpDS->GetRasterYSize();
    1178             :                     }
    1179           0 :                     else if (ok)
    1180             :                     {
    1181           0 :                         double _nwLong = gt[GEOTRSFRM_TOPLEFT_X];
    1182           0 :                         double _nwLat = gt[GEOTRSFRM_TOPLEFT_Y];
    1183           0 :                         double _seLong = _nwLong + gt[GEOTRSFRM_WE_RES] *
    1184           0 :                                                        tmpDS->GetRasterXSize();
    1185           0 :                         double _seLat = _nwLat + gt[GEOTRSFRM_NS_RES] *
    1186           0 :                                                      tmpDS->GetRasterYSize();
    1187           0 :                         if (!EQUAL(projectionRef, tmpDS->GetProjectionRef()))
    1188           0 :                             ok = false;
    1189           0 :                         if (_nwLong < nwLong)
    1190           0 :                             nwLong = _nwLong;
    1191           0 :                         if (_nwLat > nwLat)
    1192           0 :                             nwLat = _nwLat;
    1193           0 :                         if (_seLong > seLong)
    1194           0 :                             seLong = _seLong;
    1195           0 :                         if (_seLat < seLat)
    1196           0 :                             seLat = _seLat;
    1197             :                     }
    1198           3 :                     delete tmpDS;
    1199           3 :                     ds->AddSubDataset(pszFilename, &toc->entries[i]);
    1200             :                 }
    1201             :             }
    1202             :         }
    1203           3 :         if (ok)
    1204             :         {
    1205           3 :             gt[GEOTRSFRM_TOPLEFT_X] = nwLong;
    1206           3 :             gt[GEOTRSFRM_TOPLEFT_Y] = nwLat;
    1207           6 :             ds->SetSize(
    1208           3 :                 static_cast<int>(0.5 +
    1209           3 :                                  (seLong - nwLong) / gt[GEOTRSFRM_WE_RES]),
    1210           3 :                 static_cast<int>(0.5 + (seLat - nwLat) / gt[GEOTRSFRM_NS_RES]));
    1211             : 
    1212           3 :             ds->SetGeoTransform(gt);
    1213           3 :             ds->SetProjection(projectionRef);
    1214             :         }
    1215           3 :         CPLFree(projectionRef);
    1216           3 :         RPFTOCFree(toc);
    1217             : 
    1218             :         /* --------------------------------------------------------------------
    1219             :          */
    1220             :         /*      Initialize any PAM information. */
    1221             :         /* --------------------------------------------------------------------
    1222             :          */
    1223           3 :         ds->SetDescription(pszFilename);
    1224           3 :         ds->TryLoadXML();
    1225             : 
    1226           3 :         return ds;
    1227             :     }
    1228             : 
    1229           0 :     return nullptr;
    1230             : }
    1231             : 
    1232             : /************************************************************************/
    1233             : /*                                Open()                                */
    1234             : /************************************************************************/
    1235             : 
    1236          10 : GDALDataset *RPFTOCDataset::Open(GDALOpenInfo *poOpenInfo)
    1237             : 
    1238             : {
    1239          10 :     if (!RPFTOCDriverIdentify(poOpenInfo))
    1240           0 :         return nullptr;
    1241             : 
    1242          10 :     const char *pszFilename = poOpenInfo->pszFilename;
    1243          10 :     char *entryName = nullptr;
    1244             : 
    1245          10 :     if (STARTS_WITH_CI(pszFilename, "NITF_TOC_ENTRY:"))
    1246             :     {
    1247           7 :         pszFilename += strlen("NITF_TOC_ENTRY:");
    1248           7 :         entryName = CPLStrdup(pszFilename);
    1249           7 :         char *c = entryName;
    1250         168 :         while (*c != '\0' && *c != ':')
    1251         161 :             c++;
    1252           7 :         if (*c != ':')
    1253             :         {
    1254           0 :             CPLFree(entryName);
    1255           0 :             return nullptr;
    1256             :         }
    1257           7 :         *c = 0;
    1258             : 
    1259         168 :         while (*pszFilename != '\0' && *pszFilename != ':')
    1260         161 :             pszFilename++;
    1261           7 :         pszFilename++;
    1262             :     }
    1263             : 
    1264          10 :     if (RPFTOCIsNonNITFFileTOC((entryName != nullptr) ? nullptr : poOpenInfo,
    1265          10 :                                pszFilename))
    1266             :     {
    1267          20 :         GDALDataset *poDS = OpenFileTOC(nullptr, pszFilename, entryName,
    1268          10 :                                         poOpenInfo->pszFilename);
    1269             : 
    1270          10 :         CPLFree(entryName);
    1271             : 
    1272          10 :         if (poDS && poOpenInfo->eAccess == GA_Update)
    1273             :         {
    1274           0 :             ReportUpdateNotSupportedByDriver("RPFTOC");
    1275           0 :             delete poDS;
    1276           0 :             return nullptr;
    1277             :         }
    1278             : 
    1279          10 :         return poDS;
    1280             :     }
    1281             : 
    1282             :     /* -------------------------------------------------------------------- */
    1283             :     /*      Open the file with library.                                     */
    1284             :     /* -------------------------------------------------------------------- */
    1285           0 :     NITFFile *psFile = NITFOpen(pszFilename, FALSE);
    1286           0 :     if (psFile == nullptr)
    1287             :     {
    1288           0 :         CPLFree(entryName);
    1289           0 :         return nullptr;
    1290             :     }
    1291             : 
    1292             :     /* -------------------------------------------------------------------- */
    1293             :     /*      Check if it is a TOC file .                                     */
    1294             :     /* -------------------------------------------------------------------- */
    1295           0 :     if (IsNITFFileTOC(psFile))
    1296             :     {
    1297           0 :         GDALDataset *poDS = OpenFileTOC(psFile, pszFilename, entryName,
    1298           0 :                                         poOpenInfo->pszFilename);
    1299           0 :         NITFClose(psFile);
    1300           0 :         CPLFree(entryName);
    1301             : 
    1302           0 :         if (poDS && poOpenInfo->eAccess == GA_Update)
    1303             :         {
    1304           0 :             ReportUpdateNotSupportedByDriver("RPFTOC");
    1305           0 :             delete poDS;
    1306           0 :             return nullptr;
    1307             :         }
    1308             : 
    1309           0 :         return poDS;
    1310             :     }
    1311             :     else
    1312             :     {
    1313           0 :         CPLError(CE_Failure, CPLE_AppDefined, "File %s is not a TOC file.",
    1314             :                  pszFilename);
    1315           0 :         NITFClose(psFile);
    1316           0 :         CPLFree(entryName);
    1317           0 :         return nullptr;
    1318             :     }
    1319             : }
    1320             : 
    1321             : /************************************************************************/
    1322             : /*                          GDALRegister_RPFTOC()                       */
    1323             : /************************************************************************/
    1324             : 
    1325        2024 : void GDALRegister_RPFTOC()
    1326             : 
    1327             : {
    1328        2024 :     if (GDALGetDriverByName(RPFTOC_DRIVER_NAME) != nullptr)
    1329         283 :         return;
    1330             : 
    1331        1741 :     GDALDriver *poDriver = new GDALDriver();
    1332        1741 :     RPFTOCDriverSetCommonMetadata(poDriver);
    1333             : 
    1334        1741 :     poDriver->pfnOpen = RPFTOCDataset::Open;
    1335             : 
    1336        1741 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1337             : }

Generated by: LCOV version 1.14