LCOV - code coverage report
Current view: top level - frmts/nitf - rpftocdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 438 554 79.1 %
Date: 2025-07-05 22:54:03 Functions: 36 41 87.8 %

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

Generated by: LCOV version 1.14