Coverage Report

Created: 2026-08-11 08:26

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