LCOV - code coverage report
Current view: top level - frmts/nitf - rpftocdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 507 609 83.3 %
Date: 2026-03-05 10:33:42 Functions: 40 44 90.9 %

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

Generated by: LCOV version 1.14