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