LCOV - code coverage report
Current view: top level - frmts/nitf - rpftocdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 448 563 79.6 %
Date: 2024-11-21 22:18:42 Functions: 38 43 88.4 %

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

Generated by: LCOV version 1.14