Coverage Report

Created: 2025-11-15 08:43

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