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

Generated by: LCOV version 1.14