• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.06
/frmts/nitf/rpftocdataset.cpp
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()
3✔
70
    {
3✔
71
        m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3✔
72
    }
3✔
73

74
    virtual ~RPFTOCDataset()
6✔
75
    {
3✔
76
        CSLDestroy(papszSubDatasets);
3✔
77
        CSLDestroy(papszFileList);
3✔
78
    }
6✔
79

80
    virtual char **GetMetadata(const char *pszDomain = "") override;
81

82
    virtual char **GetFileList() override
×
83
    {
84
        return CSLDuplicate(papszFileList);
×
85
    }
86

87
    void AddSubDataset(const char *pszFilename, RPFTocEntry *tocEntry);
88

89
    void SetSize(int rasterXSize, int rasterYSize)
3✔
90
    {
91
        nRasterXSize = rasterXSize;
3✔
92
        nRasterYSize = rasterYSize;
3✔
93
    }
3✔
94

95
    virtual CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
×
96
    {
97
        if (bGotGeoTransform)
×
98
        {
99
            gt = m_gt;
×
100
            return CE_None;
×
101
        }
102
        return CE_Failure;
×
103
    }
104

105
    virtual CPLErr SetGeoTransform(const GDALGeoTransform &gt) override
3✔
106
    {
107
        bGotGeoTransform = TRUE;
3✔
108
        m_gt = gt;
3✔
109
        return CE_None;
3✔
110
    }
111

112
    const OGRSpatialReference *GetSpatialRef() const override
×
113
    {
114
        return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
×
115
    }
116

117
    CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override
3✔
118
    {
119
        m_oSRS.Clear();
3✔
120
        if (poSRS)
3✔
121
            m_oSRS = *poSRS;
3✔
122
        return CE_None;
3✔
123
    }
124

125
    static int IsNITFFileTOC(NITFFile *psFile);
126
    static GDALDataset *OpenFileTOC(NITFFile *psFile, const char *pszFilename,
127
                                    const char *entryName,
128
                                    const char *openInformationName);
129

130
    static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
131
};
132

133
/************************************************************************/
134
/* ==================================================================== */
135
/*                            RPFTOCSubDataset                          */
136
/* ==================================================================== */
137
/************************************************************************/
138

139
class RPFTOCSubDataset final : public VRTDataset
140
{
141
    int cachedTileBlockXOff = -1;
142
    int cachedTileBlockYOff = -1;
143
    void *cachedTileData = nullptr;
144
    int cachedTileDataSize = 0;
145
    const char *cachedTileFileName = nullptr;
146
    char **papszFileList = nullptr;
147
    CPL_DISALLOW_COPY_ASSIGN(RPFTOCSubDataset)
148

149
  public:
150
    RPFTOCSubDataset(int nXSize, int nYSize) : VRTDataset(nXSize, nYSize)
10✔
151
    {
152
        /* Don't try to write a VRT file */
153
        SetWritable(FALSE);
10✔
154

155
        /* The driver is set to VRT in VRTDataset constructor. */
156
        /* We have to set it to the expected value ! */
157
        poDriver = GDALDriver::FromHandle(GDALGetDriverByName("RPFTOC"));
10✔
158
    }
10✔
159

160
    ~RPFTOCSubDataset() override;
161

162
    virtual char **GetFileList() override
5✔
163
    {
164
        return CSLDuplicate(papszFileList);
5✔
165
    }
166

167
    void *GetCachedTile(const char *tileFileName, int nBlockXOff,
288✔
168
                        int nBlockYOff)
169
    {
170
        if (cachedTileFileName == tileFileName &&
288✔
171
            cachedTileBlockXOff == nBlockXOff &&
×
172
            cachedTileBlockYOff == nBlockYOff)
×
173
        {
174
            return cachedTileData;
×
175
        }
176

177
        return nullptr;
288✔
178
    }
179

180
    void SetCachedTile(const char *tileFileName, int nBlockXOff, int nBlockYOff,
288✔
181
                       const void *pData, int dataSize)
182
    {
183
        if (cachedTileData == nullptr || dataSize > cachedTileDataSize)
288✔
184
        {
185
            cachedTileData = CPLRealloc(cachedTileData, dataSize);
2✔
186
            cachedTileDataSize = dataSize;
2✔
187
        }
188
        memcpy(cachedTileData, pData, dataSize);
288✔
189
        cachedTileFileName = tileFileName;
288✔
190
        cachedTileBlockXOff = nBlockXOff;
288✔
191
        cachedTileBlockYOff = nBlockYOff;
288✔
192
    }
288✔
193

194
    static GDALDataset *CreateDataSetFromTocEntry(
195
        const char *openInformationName, const char *pszTOCFileName, int nEntry,
196
        const RPFTocEntry *entry, int isRGBA, char **papszMetadataRPFTOCFile);
197
};
198

199
RPFTOCSubDataset::~RPFTOCSubDataset()
20✔
200
{
201
    CSLDestroy(papszFileList);
10✔
202
    CPLFree(cachedTileData);
10✔
203
}
20✔
204

205
/************************************************************************/
206
/* ==================================================================== */
207
/*                        RPFTOCProxyRasterDataSet                       */
208
/* ==================================================================== */
209
/************************************************************************/
210

211
class RPFTOCProxyRasterDataSet final : public GDALProxyPoolDataset
212
{
213
    /* The following parameters are only for sanity checking */
214
    bool checkDone = false;
215
    bool checkOK = false;
216
    const double nwLong;
217
    const double nwLat;
218
    GDALColorTable *colorTableRef = nullptr;
219
    int bHasNoDataValue = false;
220
    double noDataValue = 0;
221
    RPFTOCSubDataset *const subdataset;
222

223
    CPL_DISALLOW_COPY_ASSIGN(RPFTOCProxyRasterDataSet)
224

225
  public:
226
    RPFTOCProxyRasterDataSet(RPFTOCSubDataset *subdataset, const char *fileName,
227
                             int nRasterXSize, int nRasterYSize,
228
                             int nBlockXSize, int nBlockYSize,
229
                             const char *projectionRef, double nwLong,
230
                             double nwLat, int nBands);
231

232
    void SetNoDataValue(double noDataValueIn)
7✔
233
    {
234
        this->noDataValue = noDataValueIn;
7✔
235
        bHasNoDataValue = TRUE;
7✔
236
    }
7✔
237

238
    double GetNoDataValue(int *pbHasNoDataValue)
3✔
239
    {
240
        if (pbHasNoDataValue)
3✔
241
            *pbHasNoDataValue = this->bHasNoDataValue;
3✔
242
        return noDataValue;
3✔
243
    }
244

245
    GDALDataset *RefUnderlyingDataset() const override;
246

247
    void UnrefUnderlyingDataset(GDALDataset *poUnderlyingDataset) const override
400✔
248
    {
249
        GDALProxyPoolDataset::UnrefUnderlyingDataset(poUnderlyingDataset);
400✔
250
    }
400✔
251

252
    void SetReferenceColorTable(GDALColorTable *colorTableRefIn)
7✔
253
    {
254
        this->colorTableRef = colorTableRefIn;
7✔
255
    }
7✔
256

257
    const GDALColorTable *GetReferenceColorTable() const
3✔
258
    {
259
        return colorTableRef;
3✔
260
    }
261

262
    int SanityCheckOK(GDALDataset *sourceDS);
263

264
    RPFTOCSubDataset *GetSubDataset()
576✔
265
    {
266
        return subdataset;
576✔
267
    }
268
};
269

270
GDALDataset *RPFTOCProxyRasterDataSet::RefUnderlyingDataset() const
400✔
271
{
272
    return GDALProxyPoolDataset::RefUnderlyingDataset();
400✔
273
}
274

275
/************************************************************************/
276
/* ==================================================================== */
277
/*                     RPFTOCProxyRasterBandRGBA                        */
278
/* ==================================================================== */
279
/************************************************************************/
280

281
class RPFTOCProxyRasterBandRGBA final : public GDALPamRasterBand
282
{
283
    bool initDone = false;
284
    std::array<unsigned char, 256> colorTable = {0};
285
    int blockByteSize = 0;
286

287
  private:
288
    void Expand(void *pImage, const void *srcImage);
289

290
  public:
291
    RPFTOCProxyRasterBandRGBA(GDALProxyPoolDataset *poDSIn, int nBandIn,
12✔
292
                              int nBlockXSizeIn, int nBlockYSizeIn)
293
    {
12✔
294
        this->poDS = poDSIn;
12✔
295
        nRasterXSize = poDSIn->GetRasterXSize();
12✔
296
        nRasterYSize = poDSIn->GetRasterYSize();
12✔
297
        this->nBlockXSize = nBlockXSizeIn;
12✔
298
        this->nBlockYSize = nBlockYSizeIn;
12✔
299
        eDataType = GDT_Byte;
12✔
300
        this->nBand = nBandIn;
12✔
301
        blockByteSize = nBlockXSize * nBlockYSize;
12✔
302
    }
12✔
303

304
    virtual GDALColorInterp GetColorInterpretation() override
×
305
    {
306
        return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
×
307
    }
308

309
  protected:
310
    virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff,
311
                              void *pImage) override;
312
};
313

314
/************************************************************************/
315
/*                    Expand()                                          */
316
/************************************************************************/
317

318
/* Expand the  array or indexed colors to an array of their corresponding R,G,B
319
 * or A component */
320
void RPFTOCProxyRasterBandRGBA::Expand(void *pImage, const void *srcImage)
288✔
321
{
322
    // pImage might be equal to srcImage
323

324
    if ((blockByteSize & (~3)) != 0)
288✔
325
    {
326
        for (int i = 0; i < blockByteSize; i++)
18,874,700✔
327
        {
328
            static_cast<unsigned char *>(pImage)[i] =
18,874,400✔
329
                colorTable[static_cast<const unsigned char *>(srcImage)[i]];
18,874,400✔
330
        }
331
    }
332
    else
333
    {
334
        const int nIter = blockByteSize / 4;
×
335
        for (int i = 0; i < nIter; i++)
×
336
        {
337
            const unsigned int four_pixels =
×
338
                static_cast<const unsigned int *>(srcImage)[i];
×
339
            static_cast<unsigned int *>(pImage)[i] =
×
340
                (colorTable[four_pixels >> 24] << 24) |
×
341
                (colorTable[(four_pixels >> 16) & 0xFF] << 16) |
×
342
                (colorTable[(four_pixels >> 8) & 0xFF] << 8) |
×
343
                colorTable[four_pixels & 0xFF];
×
344
        }
345
    }
346
}
288✔
347

348
/************************************************************************/
349
/*                    IReadBlock()                                      */
350
/************************************************************************/
351

352
CPLErr RPFTOCProxyRasterBandRGBA::IReadBlock(int nBlockXOff, int nBlockYOff,
288✔
353
                                             void *pImage)
354
{
355
    CPLErr ret;
356
    RPFTOCProxyRasterDataSet *proxyDS =
288✔
357
        reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS);
358

359
    GDALDataset *ds = proxyDS->RefUnderlyingDataset();
288✔
360
    if (ds)
288✔
361
    {
362
        if (proxyDS->SanityCheckOK(ds) == FALSE)
288✔
363
        {
364
            proxyDS->UnrefUnderlyingDataset(ds);
×
365
            return CE_Failure;
×
366
        }
367

368
        GDALRasterBand *srcBand = ds->GetRasterBand(1);
288✔
369
        if (initDone == FALSE)
288✔
370
        {
371
            GDALColorTable *srcColorTable = srcBand->GetColorTable();
8✔
372
            int bHasNoDataValue;
373
            int noDataValue =
374
                static_cast<int>(srcBand->GetNoDataValue(&bHasNoDataValue));
8✔
375
            const int nEntries = srcColorTable->GetColorEntryCount();
8✔
376
            for (int i = 0; i < nEntries; i++)
1,744✔
377
            {
378
                const GDALColorEntry *entry = srcColorTable->GetColorEntry(i);
1,736✔
379
                if (nBand == 1)
1,736✔
380
                    colorTable[i] = static_cast<unsigned char>(entry->c1);
434✔
381
                else if (nBand == 2)
1,302✔
382
                    colorTable[i] = static_cast<unsigned char>(entry->c2);
434✔
383
                else if (nBand == 3)
868✔
384
                    colorTable[i] = static_cast<unsigned char>(entry->c3);
434✔
385
                else
386
                {
387
                    colorTable[i] = (bHasNoDataValue && i == noDataValue)
866✔
388
                                        ? 0
389
                                        : static_cast<unsigned char>(entry->c4);
432✔
390
                }
391
            }
392
            if (bHasNoDataValue && nEntries == noDataValue)
8✔
393
                colorTable[nEntries] = 0;
×
394
            initDone = TRUE;
8✔
395
        }
396

397
        /* We use a 1-tile cache as the same source tile will be consecutively
398
         * asked for */
399
        /* computing the R tile, the G tile, the B tile and the A tile */
400
        void *cachedImage = proxyDS->GetSubDataset()->GetCachedTile(
576✔
401
            GetDescription(), nBlockXOff, nBlockYOff);
288✔
402
        if (cachedImage == nullptr)
288✔
403
        {
404
            CPLDebug("RPFTOC", "Read (%d, %d) of band %d, of file %s",
288✔
405
                     nBlockXOff, nBlockYOff, nBand, GetDescription());
288✔
406
            ret = srcBand->ReadBlock(nBlockXOff, nBlockYOff, pImage);
288✔
407
            if (ret == CE_None)
288✔
408
            {
409
                proxyDS->GetSubDataset()->SetCachedTile(GetDescription(),
288✔
410
                                                        nBlockXOff, nBlockYOff,
411
                                                        pImage, blockByteSize);
412
                Expand(pImage, pImage);
288✔
413
            }
414

415
            /* -------------------------------------------------------------- */
416
            /*  Forcibly load the other bands associated with this scanline.  */
417
            /* -------------------------------------------------------------- */
418
            if (nBand == 1)
288✔
419
            {
420
                GDALRasterBlock *poBlock =
421
                    poDS->GetRasterBand(2)->GetLockedBlockRef(nBlockXOff,
72✔
422
                                                              nBlockYOff);
72✔
423
                if (poBlock)
72✔
424
                    poBlock->DropLock();
72✔
425

426
                poBlock = poDS->GetRasterBand(3)->GetLockedBlockRef(nBlockXOff,
72✔
427
                                                                    nBlockYOff);
72✔
428
                if (poBlock)
72✔
429
                    poBlock->DropLock();
72✔
430

431
                poBlock = poDS->GetRasterBand(4)->GetLockedBlockRef(nBlockXOff,
72✔
432
                                                                    nBlockYOff);
72✔
433
                if (poBlock)
72✔
434
                    poBlock->DropLock();
72✔
435
            }
436
        }
437
        else
438
        {
439
            Expand(pImage, cachedImage);
×
440
            ret = CE_None;
×
441
        }
442
    }
443
    else
444
    {
445
        ret = CE_Failure;
×
446
    }
447

448
    proxyDS->UnrefUnderlyingDataset(ds);
288✔
449

450
    return ret;
288✔
451
}
452

453
/************************************************************************/
454
/* ==================================================================== */
455
/*                 RPFTOCProxyRasterBandPalette                         */
456
/* ==================================================================== */
457
/************************************************************************/
458

459
class RPFTOCProxyRasterBandPalette final : public GDALPamRasterBand
460
{
461
    int initDone;
462
    int blockByteSize;
463
    int samePalette;
464
    unsigned char remapLUT[256];
465

466
  public:
467
    RPFTOCProxyRasterBandPalette(GDALProxyPoolDataset *poDSIn, int nBandIn,
7✔
468
                                 int nBlockXSizeIn, int nBlockYSizeIn)
469
        : initDone(FALSE), blockByteSize(nBlockXSizeIn * nBlockYSizeIn),
14✔
470
          samePalette(0)
7✔
471
    {
472
        this->poDS = poDSIn;
7✔
473
        nRasterXSize = poDSIn->GetRasterXSize();
7✔
474
        nRasterYSize = poDSIn->GetRasterYSize();
7✔
475
        this->nBlockXSize = nBlockXSizeIn;
7✔
476
        this->nBlockYSize = nBlockYSizeIn;
7✔
477
        eDataType = GDT_Byte;
7✔
478
        this->nBand = nBandIn;
7✔
479
        memset(remapLUT, 0, sizeof(remapLUT));
7✔
480
    }
7✔
481

482
    virtual GDALColorInterp GetColorInterpretation() override
3✔
483
    {
484
        return GCI_PaletteIndex;
3✔
485
    }
486

487
    virtual double GetNoDataValue(int *bHasNoDataValue) override
3✔
488
    {
489
        return (reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS))
3✔
490
            ->GetNoDataValue(bHasNoDataValue);
3✔
491
    }
492

493
    virtual GDALColorTable *GetColorTable() override
3✔
494
    {
495
        // TODO: This casting is a bit scary.
496
        return const_cast<GDALColorTable *>(
497
            reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS)
3✔
498
                ->GetReferenceColorTable());
3✔
499
    }
500

501
  protected:
502
    virtual CPLErr IReadBlock(int nBlockXOff, int nBlockYOff,
503
                              void *pImage) override;
504
};
505

506
/************************************************************************/
507
/*                    IReadBlock()                                      */
508
/************************************************************************/
509

510
CPLErr RPFTOCProxyRasterBandPalette::IReadBlock(int nBlockXOff, int nBlockYOff,
108✔
511
                                                void *pImage)
512
{
513
    CPLErr ret;
514
    RPFTOCProxyRasterDataSet *proxyDS =
108✔
515
        reinterpret_cast<RPFTOCProxyRasterDataSet *>(poDS);
516
    GDALDataset *ds = proxyDS->RefUnderlyingDataset();
108✔
517
    if (ds)
108✔
518
    {
519
        if (proxyDS->SanityCheckOK(ds) == FALSE)
108✔
520
        {
521
            proxyDS->UnrefUnderlyingDataset(ds);
×
522
            return CE_Failure;
×
523
        }
524

525
        GDALRasterBand *srcBand = ds->GetRasterBand(1);
108✔
526
        ret = srcBand->ReadBlock(nBlockXOff, nBlockYOff, pImage);
108✔
527

528
        if (initDone == FALSE)
108✔
529
        {
530
            int approximateMatching;
531
            if (srcBand->GetIndexColorTranslationTo(this, remapLUT,
3✔
532
                                                    &approximateMatching))
3✔
533
            {
534
                samePalette = FALSE;
×
535
                if (approximateMatching)
×
536
                {
537
                    CPLError(
×
538
                        CE_Failure, CPLE_AppDefined,
539
                        "Palette for %s is different from reference palette. "
540
                        "Coudln't remap exactly all colors. Trying to find "
541
                        "closest matches.\n",
542
                        GetDescription());
×
543
                }
544
            }
545
            else
546
            {
547
                samePalette = TRUE;
3✔
548
            }
549
            initDone = TRUE;
3✔
550
        }
551

552
        if (samePalette == FALSE)
108✔
553
        {
554
            unsigned char *data = static_cast<unsigned char *>(pImage);
×
555
            for (int i = 0; i < blockByteSize; i++)
×
556
            {
557
                data[i] = remapLUT[data[i]];
×
558
            }
559
        }
560
    }
561
    else
562
    {
563
        ret = CE_Failure;
×
564
    }
565

566
    proxyDS->UnrefUnderlyingDataset(ds);
108✔
567

568
    return ret;
108✔
569
}
570

571
/************************************************************************/
572
/*                    RPFTOCProxyRasterDataSet()                         */
573
/************************************************************************/
574

575
RPFTOCProxyRasterDataSet::RPFTOCProxyRasterDataSet(
10✔
576
    RPFTOCSubDataset *subdatasetIn, const char *fileNameIn, int nRasterXSizeIn,
577
    int nRasterYSizeIn, int nBlockXSizeIn, int nBlockYSizeIn,
578
    const char *projectionRefIn, double nwLongIn, double nwLatIn, int nBandsIn)
10✔
579
    :  // Mark as shared since the VRT will take several references if we are in
580
       // RGBA mode (4 bands for this dataset).
581
      GDALProxyPoolDataset(fileNameIn, nRasterXSizeIn, nRasterYSizeIn,
582
                           GA_ReadOnly, TRUE, projectionRefIn),
583
      nwLong(nwLongIn), nwLat(nwLatIn), subdataset(subdatasetIn)
10✔
584
{
585
    if (nBandsIn == 4)
10✔
586
    {
587
        for (int i = 0; i < 4; i++)
15✔
588
        {
589
            SetBand(i + 1, new RPFTOCProxyRasterBandRGBA(
12✔
590
                               this, i + 1, nBlockXSizeIn, nBlockYSizeIn));
12✔
591
        }
592
    }
593
    else
594
    {
595
        SetBand(1, new RPFTOCProxyRasterBandPalette(this, 1, nBlockXSizeIn,
7✔
596
                                                    nBlockYSizeIn));
7✔
597
    }
598
}
10✔
599

600
/************************************************************************/
601
/*                    SanityCheckOK()                                   */
602
/************************************************************************/
603

604
#define WARN_ON_FAIL(x)                                                        \
605
    do                                                                         \
606
    {                                                                          \
607
        if (!(x))                                                              \
608
        {                                                                      \
609
            CPLError(CE_Warning, CPLE_AppDefined,                              \
610
                     "For %s, assert '" #x "' failed", GetDescription());      \
611
        }                                                                      \
612
    } while (false)
613
#define ERROR_ON_FAIL(x)                                                       \
614
    do                                                                         \
615
    {                                                                          \
616
        if (!(x))                                                              \
617
        {                                                                      \
618
            CPLError(CE_Warning, CPLE_AppDefined,                              \
619
                     "For %s, assert '" #x "' failed", GetDescription());      \
620
            checkOK = FALSE;                                                   \
621
        }                                                                      \
622
    } while (false)
623

624
int RPFTOCProxyRasterDataSet::SanityCheckOK(GDALDataset *sourceDS)
396✔
625
{
626
    if (checkDone)
396✔
627
        return checkOK;
391✔
628

629
    int src_nBlockXSize;
630
    int src_nBlockYSize;
631
    int nBlockXSize;
632
    int nBlockYSize;
633
    GDALGeoTransform l_gt;
5✔
634

635
    checkOK = TRUE;
5✔
636
    checkDone = TRUE;
5✔
637

638
    sourceDS->GetGeoTransform(l_gt);
5✔
639
    WARN_ON_FAIL(fabs(l_gt[GEOTRSFRM_TOPLEFT_X] - nwLong) < l_gt[1]);
5✔
640
    WARN_ON_FAIL(fabs(l_gt[GEOTRSFRM_TOPLEFT_Y] - nwLat) < fabs(l_gt[5]));
5✔
641
    WARN_ON_FAIL(l_gt[GEOTRSFRM_ROTATION_PARAM1] == 0 &&
5✔
642
                 l_gt[GEOTRSFRM_ROTATION_PARAM2] == 0); /* No rotation */
643
    ERROR_ON_FAIL(sourceDS->GetRasterCount() == 1);     /* Just 1 band */
5✔
644
    ERROR_ON_FAIL(sourceDS->GetRasterXSize() == nRasterXSize);
5✔
645
    ERROR_ON_FAIL(sourceDS->GetRasterYSize() == nRasterYSize);
5✔
646
    WARN_ON_FAIL(EQUAL(sourceDS->GetProjectionRef(), GetProjectionRef()));
5✔
647
    sourceDS->GetRasterBand(1)->GetBlockSize(&src_nBlockXSize,
5✔
648
                                             &src_nBlockYSize);
649
    GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
5✔
650
    ERROR_ON_FAIL(src_nBlockXSize == nBlockXSize);
5✔
651
    ERROR_ON_FAIL(src_nBlockYSize == nBlockYSize);
5✔
652
    WARN_ON_FAIL(sourceDS->GetRasterBand(1)->GetColorInterpretation() ==
5✔
653
                 GCI_PaletteIndex);
654
    WARN_ON_FAIL(sourceDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte);
5✔
655

656
    return checkOK;
5✔
657
}
658

659
/************************************************************************/
660
/*                           MakeTOCEntryName()                         */
661
/************************************************************************/
662

663
static const char *MakeTOCEntryName(RPFTocEntry *tocEntry)
10✔
664
{
665
    char *str = nullptr;
10✔
666
    if (tocEntry->seriesAbbreviation)
10✔
667
        str = const_cast<char *>(CPLSPrintf(
10✔
668
            "%s_%s_%s_%s_%d", tocEntry->type, tocEntry->seriesAbbreviation,
10✔
669
            tocEntry->scale, tocEntry->zone, tocEntry->boundaryId));
10✔
670
    else
671
        str = const_cast<char *>(CPLSPrintf("%s_%s_%s_%d", tocEntry->type,
×
672
                                            tocEntry->scale, tocEntry->zone,
×
673
                                            tocEntry->boundaryId));
674
    char *c = str;
10✔
675
    while (*c)
240✔
676
    {
677
        if (*c == ':' || *c == ' ')
230✔
678
            *c = '_';
×
679
        c++;
230✔
680
    }
681
    return str;
10✔
682
}
683

684
/************************************************************************/
685
/*                           AddSubDataset()                            */
686
/************************************************************************/
687

688
void RPFTOCDataset::AddSubDataset(const char *pszFilename,
3✔
689
                                  RPFTocEntry *tocEntry)
690

691
{
692
    char szName[80];
693
    const int nCount = CSLCount(papszSubDatasets) / 2;
3✔
694

695
    snprintf(szName, sizeof(szName), "SUBDATASET_%d_NAME", nCount + 1);
3✔
696
    papszSubDatasets =
3✔
697
        CSLSetNameValue(papszSubDatasets, szName,
3✔
698
                        CPLSPrintf("NITF_TOC_ENTRY:%s:%s",
699
                                   MakeTOCEntryName(tocEntry), pszFilename));
700

701
    snprintf(szName, sizeof(szName), "SUBDATASET_%d_DESC", nCount + 1);
3✔
702
    if (tocEntry->seriesName && tocEntry->seriesAbbreviation)
3✔
703
        papszSubDatasets = CSLSetNameValue(
3✔
704
            papszSubDatasets, szName,
705
            CPLSPrintf("%s:%s:%s:%s:%s:%d", tocEntry->type,
3✔
706
                       tocEntry->seriesAbbreviation, tocEntry->seriesName,
707
                       tocEntry->scale, tocEntry->zone, tocEntry->boundaryId));
3✔
708
    else
709
        papszSubDatasets = CSLSetNameValue(
×
710
            papszSubDatasets, szName,
711
            CPLSPrintf("%s:%s:%s:%d", tocEntry->type, tocEntry->scale,
×
712
                       tocEntry->zone, tocEntry->boundaryId));
×
713
}
3✔
714

715
/************************************************************************/
716
/*                            GetMetadata()                             */
717
/************************************************************************/
718

719
char **RPFTOCDataset::GetMetadata(const char *pszDomain)
3✔
720

721
{
722
    if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
3✔
723
        return papszSubDatasets;
3✔
724

725
    return GDALPamDataset::GetMetadata(pszDomain);
×
726
}
727

728
/************************************************************************/
729
/*                  NITFCreateVRTDataSetFromTocEntry()                  */
730
/************************************************************************/
731

732
#define ASSERT_CREATE_VRT(x)                                                   \
733
    do                                                                         \
734
    {                                                                          \
735
        if (!(x))                                                              \
736
        {                                                                      \
737
            CPLError(CE_Failure, CPLE_AppDefined,                              \
738
                     "For %s, assert '" #x "' failed",                         \
739
                     entry->frameEntries[i].fullFilePath);                     \
740
            if (poSrcDS)                                                       \
741
                GDALClose(poSrcDS);                                            \
742
            CPLFree(projectionRef);                                            \
743
            return nullptr;                                                    \
744
        }                                                                      \
745
    } while (false)
746

747
/* Builds a RPFTOCSubDataset from the set of files of the toc entry */
748
GDALDataset *RPFTOCSubDataset::CreateDataSetFromTocEntry(
10✔
749
    const char *openInformationName, const char *pszTOCFileName, int nEntry,
750
    const RPFTocEntry *entry, int isRGBA, char **papszMetadataRPFTOCFile)
751
{
752
    GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("VRT");
10✔
753
    if (poDriver == nullptr)
10✔
754
        return nullptr;
×
755

756
    const int N = entry->nVertFrames * entry->nHorizFrames;
10✔
757

758
    /* This may not be reliable. See below */
759
    int sizeX =
10✔
760
        static_cast<int>((entry->seLong - entry->nwLong) /
10✔
761
                             (entry->nHorizFrames * entry->horizInterval) +
10✔
762
                         0.5);
763

764
    int sizeY =
10✔
765
        static_cast<int>((entry->nwLat - entry->seLat) /
10✔
766
                             (entry->nVertFrames * entry->vertInterval) +
10✔
767
                         0.5);
768

769
    if ((EQUAL(entry->type, "CADRG") || (EQUAL(entry->type, "CIB"))))
10✔
770
    {
771
        // for CADRG and CIB the frame size is defined with 1536x1536
772
        // CADRG: see MIL-C-89038: 3.5.2 a - Each frame shall comprise a
773
        // rectangular array of 1536 by 1536 pixels CIB: see MIL-C-89041: 3.5.2
774
        // a - Each frame shall comprise a rectangular array of 1536 by 1536
775
        // pixels
776
        sizeX = 1536;
10✔
777
        sizeY = 1536;
10✔
778
    }
779

780
    int nBlockXSize = 0;
10✔
781
    int nBlockYSize = 0;
10✔
782
    GDALGeoTransform gt;
10✔
783
    char *projectionRef = nullptr;
10✔
784
    int index = 0;
10✔
785

786
    for (int i = 0; i < N; i++)
20✔
787
    {
788
        if (!entry->frameEntries[i].fileExists)
10✔
789
            continue;
×
790

791
        if (index == 0)
10✔
792
        {
793
            /* Open the first available file to get its geotransform, projection
794
             * ref and block size */
795
            /* Do a few sanity checks too */
796
            /* Ideally we should make these sanity checks now on ALL files, but
797
             * it would be too slow */
798
            /* for large datasets. So these sanity checks will be done at the
799
             * time we really need */
800
            /* to access the file (see SanityCheckOK method) */
801
            GDALDataset *poSrcDS = GDALDataset::FromHandle(GDALOpenShared(
10✔
802
                entry->frameEntries[i].fullFilePath, GA_ReadOnly));
10✔
803
            ASSERT_CREATE_VRT(poSrcDS);
10✔
804
            poSrcDS->GetGeoTransform(gt);
10✔
805
            projectionRef = CPLStrdup(poSrcDS->GetProjectionRef());
10✔
806
            ASSERT_CREATE_VRT(gt[GEOTRSFRM_ROTATION_PARAM1] == 0 &&
10✔
807
                              gt[GEOTRSFRM_ROTATION_PARAM2] ==
808
                                  0);                          /* No rotation */
809
            ASSERT_CREATE_VRT(poSrcDS->GetRasterCount() == 1); /* Just 1 band */
10✔
810

811
            /* Tolerance of 1%... This is necessary for CADRG_L22/RPF/A.TOC for
812
             * example */
813
            ASSERT_CREATE_VRT((entry->horizInterval - gt[GEOTRSFRM_WE_RES]) /
10✔
814
                                  entry->horizInterval <
815
                              0.01); /* X interval same as in TOC */
816
            ASSERT_CREATE_VRT((entry->vertInterval - (-gt[GEOTRSFRM_NS_RES])) /
10✔
817
                                  entry->vertInterval <
818
                              0.01); /* Y interval same as in TOC */
819

820
            const int ds_sizeX = poSrcDS->GetRasterXSize();
10✔
821
            const int ds_sizeY = poSrcDS->GetRasterYSize();
10✔
822
            /* for polar zone use the sizes from the dataset */
823
            if ((entry->zone[0] == '9') || (entry->zone[0] == 'J'))
10✔
824
            {
825
                sizeX = ds_sizeX;
×
826
                sizeY = ds_sizeY;
×
827
            }
828

829
            /* In the case the east longitude is 180, there's a great chance
830
             * that it is in fact */
831
            /* truncated in the A.TOC. Thus, the only reliable way to find out
832
             * the tile width, is to */
833
            /* read it from the tile dataset itself... */
834
            /* This is the case for the GNCJNCN dataset that has world coverage
835
             */
836
            if (entry->seLong == 180.00)
10✔
837
                sizeX = ds_sizeX;
×
838
            else
839
                ASSERT_CREATE_VRT(sizeX == ds_sizeX);
10✔
840

841
            ASSERT_CREATE_VRT(sizeY == ds_sizeY);
10✔
842
            poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
10✔
843
            ASSERT_CREATE_VRT(
10✔
844
                poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
845
                GCI_PaletteIndex);
846
            ASSERT_CREATE_VRT(poSrcDS->GetRasterBand(1)->GetRasterDataType() ==
10✔
847
                              GDT_Byte);
848
            GDALClose(poSrcDS);
10✔
849
        }
850

851
        index++;
10✔
852
    }
853

854
    if (index == 0)
10✔
855
        return nullptr;
×
856

857
    /* ------------------------------------ */
858
    /* Create the VRT with the overall size */
859
    /* ------------------------------------ */
860
    RPFTOCSubDataset *poVirtualDS = new RPFTOCSubDataset(
861
        sizeX * entry->nHorizFrames, sizeY * entry->nVertFrames);
10✔
862

863
    if (papszMetadataRPFTOCFile)
10✔
864
        poVirtualDS->SetMetadata(papszMetadataRPFTOCFile);
×
865

866
    poVirtualDS->SetProjection(projectionRef);
10✔
867

868
    gt[GEOTRSFRM_TOPLEFT_X] = entry->nwLong;
10✔
869
    gt[GEOTRSFRM_TOPLEFT_Y] = entry->nwLat;
10✔
870
    poVirtualDS->SetGeoTransform(gt);
10✔
871

872
    int nBands;
873

874
    /* In most cases, all the files inside a TOC entry share the same */
875
    /* palette and we could use it for the VRT. */
876
    /* In other cases like for CADRG801_France_250K (TOC entry CADRG_250K_2_2),
877
     */
878
    /* the file for Corsica and the file for Sardegna do not share the same
879
     * palette */
880
    /* however they contain the same RGB triplets and are just ordered
881
     * differently */
882
    /* So we can use the same palette */
883
    /* In the unlikely event where palettes would be incompatible, we can use
884
     * the RGBA */
885
    /* option through the config option RPFTOC_FORCE_RGBA */
886
    if (isRGBA == FALSE)
10✔
887
    {
888
        poVirtualDS->AddBand(GDT_Byte, nullptr);
7✔
889
        GDALRasterBand *poBand = poVirtualDS->GetRasterBand(1);
7✔
890
        poBand->SetColorInterpretation(GCI_PaletteIndex);
7✔
891
        nBands = 1;
7✔
892

893
        for (int i = 0; i < N; i++)
14✔
894
        {
895
            if (!entry->frameEntries[i].fileExists)
7✔
896
                continue;
×
897

898
            bool bAllBlack = true;
7✔
899
            GDALDataset *poSrcDS = GDALDataset::FromHandle(GDALOpenShared(
7✔
900
                entry->frameEntries[i].fullFilePath, GA_ReadOnly));
7✔
901
            if (poSrcDS != nullptr)
7✔
902
            {
903
                if (poSrcDS->GetRasterCount() == 1)
7✔
904
                {
905
                    int bHasNoDataValue;
906
                    const double noDataValue =
907
                        poSrcDS->GetRasterBand(1)->GetNoDataValue(
7✔
908
                            &bHasNoDataValue);
7✔
909
                    if (bHasNoDataValue)
7✔
910
                        poBand->SetNoDataValue(noDataValue);
7✔
911

912
                    /* Avoid setting a color table that is all black (which
913
                     * might be */
914
                    /* the case of the edge tiles of a RPF subdataset) */
915
                    GDALColorTable *poCT =
916
                        poSrcDS->GetRasterBand(1)->GetColorTable();
7✔
917
                    if (poCT != nullptr)
7✔
918
                    {
919
                        for (int iC = 0; iC < poCT->GetColorEntryCount(); iC++)
1,526✔
920
                        {
921
                            if (bHasNoDataValue &&
3,045✔
922
                                iC == static_cast<int>(noDataValue))
1,519✔
923
                                continue;
7✔
924

925
                            const GDALColorEntry *psColorEntry =
926
                                poCT->GetColorEntry(iC);
1,512✔
927
                            if (psColorEntry->c1 != 0 ||
1,512✔
928
                                psColorEntry->c2 != 0 || psColorEntry->c3 != 0)
1,512✔
929
                            {
930
                                bAllBlack = false;
×
931
                                break;
×
932
                            }
933
                        }
934

935
                        /* Assign it temporarily, in the hope of a better match
936
                         */
937
                        /* afterwards */
938
                        poBand->SetColorTable(poCT);
7✔
939
                        if (bAllBlack)
7✔
940
                        {
941
                            CPLDebug("RPFTOC",
7✔
942
                                     "Skipping %s. Its palette is all black.",
943
                                     poSrcDS->GetDescription());
7✔
944
                        }
945
                    }
946
                }
947
                GDALClose(poSrcDS);
7✔
948
            }
949
            if (!bAllBlack)
7✔
950
                break;
×
951
        }
952
    }
953
    else
954
    {
955
        for (int i = 0; i < 4; i++)
15✔
956
        {
957
            poVirtualDS->AddBand(GDT_Byte, nullptr);
12✔
958
            GDALRasterBand *poBand = poVirtualDS->GetRasterBand(i + 1);
12✔
959
            poBand->SetColorInterpretation(
12✔
960
                static_cast<GDALColorInterp>(GCI_RedBand + i));
12✔
961
        }
962
        nBands = 4;
3✔
963
    }
964

965
    CPLFree(projectionRef);
10✔
966
    projectionRef = nullptr;
10✔
967

968
    /* -------------------------------------------------------------------- */
969
    /*      Check for overviews.                                            */
970
    /* -------------------------------------------------------------------- */
971

972
    poVirtualDS->oOvManager.Initialize(
10✔
973
        poVirtualDS, CPLString().Printf("%s.%d", pszTOCFileName, nEntry + 1));
20✔
974

975
    poVirtualDS->SetDescription(pszTOCFileName);
10✔
976
    poVirtualDS->papszFileList = poVirtualDS->GDALDataset::GetFileList();
10✔
977
    poVirtualDS->SetDescription(openInformationName);
10✔
978

979
    int iFile = 0;
10✔
980
    for (int i = 0; i < N; i++)
20✔
981
    {
982
        if (!entry->frameEntries[i].fileExists)
10✔
983
            continue;
×
984

985
        poVirtualDS->SetMetadataItem(CPLSPrintf("FILENAME_%d", iFile),
10✔
986
                                     entry->frameEntries[i].fullFilePath);
10✔
987
        poVirtualDS->papszFileList = CSLAddString(
20✔
988
            poVirtualDS->papszFileList, entry->frameEntries[i].fullFilePath);
10✔
989
        iFile++;
10✔
990

991
        /* We create proxy datasets and raster bands */
992
        /* Using real datasets and raster bands is possible in theory */
993
        /* However for large datasets, a TOC entry can include several hundreds
994
         * of files */
995
        /* and we finally reach the limit of maximum file descriptors open at
996
         * the same time ! */
997
        /* So the idea is to warp the datasets into a proxy and open the
998
         * underlying dataset only when it is */
999
        /* needed (IRasterIO operation). To improve a bit efficiency, we have a
1000
         * cache of opened */
1001
        /* underlying datasets */
1002
        RPFTOCProxyRasterDataSet *ds = new RPFTOCProxyRasterDataSet(
1003
            reinterpret_cast<RPFTOCSubDataset *>(poVirtualDS),
1004
            entry->frameEntries[i].fullFilePath, sizeX, sizeY, nBlockXSize,
10✔
1005
            nBlockYSize, poVirtualDS->GetProjectionRef(),
10✔
1006
            entry->nwLong +
10✔
1007
                entry->frameEntries[i].frameCol * entry->horizInterval * sizeX,
10✔
1008
            entry->nwLat -
10✔
1009
                entry->frameEntries[i].frameRow * entry->vertInterval * sizeY,
10✔
1010
            nBands);
10✔
1011

1012
        if (nBands == 1)
10✔
1013
        {
1014
            GDALRasterBand *poBand = poVirtualDS->GetRasterBand(1);
7✔
1015
            ds->SetReferenceColorTable(poBand->GetColorTable());
7✔
1016
            int bHasNoDataValue;
1017
            const double noDataValue = poBand->GetNoDataValue(&bHasNoDataValue);
7✔
1018
            if (bHasNoDataValue)
7✔
1019
                ds->SetNoDataValue(noDataValue);
7✔
1020
        }
1021

1022
        for (int j = 0; j < nBands; j++)
29✔
1023
        {
1024
            VRTSourcedRasterBand *poBand =
1025
                reinterpret_cast<VRTSourcedRasterBand *>(
1026
                    poVirtualDS->GetRasterBand(j + 1));
19✔
1027
            /* Place the raster band at the right position in the VRT */
1028
            poBand->AddSimpleSource(
19✔
1029
                ds->GetRasterBand(j + 1), 0, 0, sizeX, sizeY,
1030
                entry->frameEntries[i].frameCol * sizeX,
19✔
1031
                entry->frameEntries[i].frameRow * sizeY, sizeX, sizeY);
19✔
1032
        }
1033

1034
        /* The RPFTOCProxyRasterDataSet will be destroyed when its last raster
1035
         * band will be */
1036
        /* destroyed */
1037
        ds->Dereference();
10✔
1038
    }
1039

1040
    poVirtualDS->SetMetadataItem("NITF_SCALE", entry->scale);
10✔
1041
    poVirtualDS->SetMetadataItem(
10✔
1042
        "NITF_SERIES_ABBREVIATION",
1043
        (entry->seriesAbbreviation) ? entry->seriesAbbreviation : "Unknown");
10✔
1044
    poVirtualDS->SetMetadataItem("NITF_SERIES_NAME", (entry->seriesName)
10✔
1045
                                                         ? entry->seriesName
1046
                                                         : "Unknown");
10✔
1047

1048
    return poVirtualDS;
10✔
1049
}
1050

1051
/************************************************************************/
1052
/*                             IsNITFFileTOC()                          */
1053
/************************************************************************/
1054

1055
/* Check whether this NITF file is a TOC file */
1056
int RPFTOCDataset::IsNITFFileTOC(NITFFile *psFile)
×
1057
{
1058
    const char *fileTitle =
1059
        CSLFetchNameValue(psFile->papszMetadata, "NITF_FTITLE");
×
1060
    while (fileTitle && *fileTitle)
×
1061
    {
1062
        if (EQUAL(fileTitle, "A.TOC"))
×
1063
        {
1064
            return TRUE;
×
1065
        }
1066
        fileTitle++;
×
1067
    }
1068
    return FALSE;
×
1069
}
1070

1071
/************************************************************************/
1072
/*                                OpenFileTOC()                         */
1073
/************************************************************************/
1074

1075
/* Create a dataset from a TOC file */
1076
/* If psFile == NULL, the TOC file has no NITF header */
1077
/* If entryName != NULL, the dataset will be made just of the entry of the TOC
1078
 * file */
1079
GDALDataset *RPFTOCDataset::OpenFileTOC(NITFFile *psFile,
10✔
1080
                                        const char *pszFilename,
1081
                                        const char *entryName,
1082
                                        const char *openInformationName)
1083
{
1084
    char buffer[48];
1085
    VSILFILE *fp = nullptr;
10✔
1086
    if (psFile == nullptr)
10✔
1087
    {
1088
        fp = VSIFOpenL(pszFilename, "rb");
10✔
1089

1090
        if (fp == nullptr)
10✔
1091
        {
1092
            CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.",
×
1093
                     pszFilename);
1094
            return nullptr;
×
1095
        }
1096
        if (VSIFReadL(buffer, 1, 48, fp) != 48)
10✔
1097
        {
1098
            CPLError(CE_Failure, CPLE_FileIO, "I/O error");
×
1099
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
×
1100
            return nullptr;
×
1101
        }
1102
    }
1103
    const int isRGBA =
1104
        CPLTestBool(CPLGetConfigOption("RPFTOC_FORCE_RGBA", "NO"));
10✔
1105
    RPFToc *toc = (psFile) ? RPFTOCRead(pszFilename, psFile)
10✔
1106
                           : RPFTOCReadFromBuffer(pszFilename, fp, buffer);
10✔
1107
    if (fp)
10✔
1108
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
10✔
1109
    fp = nullptr;
10✔
1110

1111
    if (entryName != nullptr)
10✔
1112
    {
1113
        if (toc)
7✔
1114
        {
1115
            for (int i = 0; i < toc->nEntries; i++)
7✔
1116
            {
1117
                if (EQUAL(entryName, MakeTOCEntryName(&toc->entries[i])))
7✔
1118
                {
1119
                    GDALDataset *ds =
1120
                        RPFTOCSubDataset::CreateDataSetFromTocEntry(
7✔
1121
                            openInformationName, pszFilename, i,
1122
                            &toc->entries[i], isRGBA,
7✔
1123
                            (psFile) ? psFile->papszMetadata : nullptr);
1124

1125
                    RPFTOCFree(toc);
7✔
1126
                    return ds;
7✔
1127
                }
1128
            }
1129
            CPLError(CE_Failure, CPLE_AppDefined,
×
1130
                     "The entry %s does not exist in file %s.", entryName,
1131
                     pszFilename);
1132
        }
1133
        RPFTOCFree(toc);
×
1134
        return nullptr;
×
1135
    }
1136

1137
    if (toc)
3✔
1138
    {
1139
        RPFTOCDataset *ds = new RPFTOCDataset();
3✔
1140
        if (psFile)
3✔
1141
            ds->SetMetadata(psFile->papszMetadata);
×
1142

1143
        bool ok = false;
3✔
1144
        char *projectionRef = nullptr;
3✔
1145
        double nwLong = 0.0;
3✔
1146
        double nwLat = 0.0;
3✔
1147
        double seLong = 0.0;
3✔
1148
        double seLat = 0.0;
3✔
1149
        GDALGeoTransform gt;
3✔
1150

1151
        ds->papszFileList = CSLAddString(ds->papszFileList, pszFilename);
3✔
1152

1153
        for (int i = 0; i < toc->nEntries; i++)
6✔
1154
        {
1155
            if (!toc->entries[i].isOverviewOrLegend)
3✔
1156
            {
1157
                GDALDataset *tmpDS =
1158
                    RPFTOCSubDataset::CreateDataSetFromTocEntry(
6✔
1159
                        openInformationName, pszFilename, i, &toc->entries[i],
3✔
1160
                        isRGBA, nullptr);
1161
                if (tmpDS)
3✔
1162
                {
1163
                    char **papszSubDatasetFileList = tmpDS->GetFileList();
3✔
1164
                    /* Yes, begin at 1, since the first is the a.toc */
1165
                    ds->papszFileList = CSLInsertStrings(
6✔
1166
                        ds->papszFileList, -1, papszSubDatasetFileList + 1);
3✔
1167
                    CSLDestroy(papszSubDatasetFileList);
3✔
1168

1169
                    tmpDS->GetGeoTransform(gt);
3✔
1170
                    if (projectionRef == nullptr)
3✔
1171
                    {
1172
                        ok = true;
3✔
1173
                        projectionRef = CPLStrdup(tmpDS->GetProjectionRef());
3✔
1174
                        nwLong = gt[GEOTRSFRM_TOPLEFT_X];
3✔
1175
                        nwLat = gt[GEOTRSFRM_TOPLEFT_Y];
3✔
1176
                        seLong = nwLong +
3✔
1177
                                 gt[GEOTRSFRM_WE_RES] * tmpDS->GetRasterXSize();
3✔
1178
                        seLat = nwLat +
3✔
1179
                                gt[GEOTRSFRM_NS_RES] * tmpDS->GetRasterYSize();
3✔
1180
                    }
1181
                    else if (ok)
×
1182
                    {
1183
                        double _nwLong = gt[GEOTRSFRM_TOPLEFT_X];
×
1184
                        double _nwLat = gt[GEOTRSFRM_TOPLEFT_Y];
×
1185
                        double _seLong = _nwLong + gt[GEOTRSFRM_WE_RES] *
×
1186
                                                       tmpDS->GetRasterXSize();
×
1187
                        double _seLat = _nwLat + gt[GEOTRSFRM_NS_RES] *
×
1188
                                                     tmpDS->GetRasterYSize();
×
1189
                        if (!EQUAL(projectionRef, tmpDS->GetProjectionRef()))
×
1190
                            ok = false;
×
1191
                        if (_nwLong < nwLong)
×
1192
                            nwLong = _nwLong;
×
1193
                        if (_nwLat > nwLat)
×
1194
                            nwLat = _nwLat;
×
1195
                        if (_seLong > seLong)
×
1196
                            seLong = _seLong;
×
1197
                        if (_seLat < seLat)
×
1198
                            seLat = _seLat;
×
1199
                    }
1200
                    delete tmpDS;
3✔
1201
                    ds->AddSubDataset(pszFilename, &toc->entries[i]);
3✔
1202
                }
1203
            }
1204
        }
1205
        if (ok)
3✔
1206
        {
1207
            gt[GEOTRSFRM_TOPLEFT_X] = nwLong;
3✔
1208
            gt[GEOTRSFRM_TOPLEFT_Y] = nwLat;
3✔
1209
            ds->SetSize(
6✔
1210
                static_cast<int>(0.5 +
3✔
1211
                                 (seLong - nwLong) / gt[GEOTRSFRM_WE_RES]),
3✔
1212
                static_cast<int>(0.5 + (seLat - nwLat) / gt[GEOTRSFRM_NS_RES]));
3✔
1213

1214
            ds->SetGeoTransform(gt);
3✔
1215
            ds->SetProjection(projectionRef);
3✔
1216
        }
1217
        CPLFree(projectionRef);
3✔
1218
        RPFTOCFree(toc);
3✔
1219

1220
        /* --------------------------------------------------------------------
1221
         */
1222
        /*      Initialize any PAM information. */
1223
        /* --------------------------------------------------------------------
1224
         */
1225
        ds->SetDescription(pszFilename);
3✔
1226
        ds->TryLoadXML();
3✔
1227

1228
        return ds;
3✔
1229
    }
1230

1231
    return nullptr;
×
1232
}
1233

1234
/************************************************************************/
1235
/*                                Open()                                */
1236
/************************************************************************/
1237

1238
GDALDataset *RPFTOCDataset::Open(GDALOpenInfo *poOpenInfo)
10✔
1239

1240
{
1241
    if (!RPFTOCDriverIdentify(poOpenInfo))
10✔
1242
        return nullptr;
×
1243

1244
    const char *pszFilename = poOpenInfo->pszFilename;
10✔
1245
    char *entryName = nullptr;
10✔
1246

1247
    if (STARTS_WITH_CI(pszFilename, "NITF_TOC_ENTRY:"))
10✔
1248
    {
1249
        pszFilename += strlen("NITF_TOC_ENTRY:");
7✔
1250
        entryName = CPLStrdup(pszFilename);
7✔
1251
        char *c = entryName;
7✔
1252
        while (*c != '\0' && *c != ':')
168✔
1253
            c++;
161✔
1254
        if (*c != ':')
7✔
1255
        {
1256
            CPLFree(entryName);
×
1257
            return nullptr;
×
1258
        }
1259
        *c = 0;
7✔
1260

1261
        while (*pszFilename != '\0' && *pszFilename != ':')
168✔
1262
            pszFilename++;
161✔
1263
        pszFilename++;
7✔
1264
    }
1265

1266
    if (RPFTOCIsNonNITFFileTOC((entryName != nullptr) ? nullptr : poOpenInfo,
10✔
1267
                               pszFilename))
10✔
1268
    {
1269
        GDALDataset *poDS = OpenFileTOC(nullptr, pszFilename, entryName,
20✔
1270
                                        poOpenInfo->pszFilename);
10✔
1271

1272
        CPLFree(entryName);
10✔
1273

1274
        if (poDS && poOpenInfo->eAccess == GA_Update)
10✔
1275
        {
1276
            ReportUpdateNotSupportedByDriver("RPFTOC");
×
1277
            delete poDS;
×
1278
            return nullptr;
×
1279
        }
1280

1281
        return poDS;
10✔
1282
    }
1283

1284
    /* -------------------------------------------------------------------- */
1285
    /*      Open the file with library.                                     */
1286
    /* -------------------------------------------------------------------- */
1287
    NITFFile *psFile = NITFOpen(pszFilename, FALSE);
×
1288
    if (psFile == nullptr)
×
1289
    {
1290
        CPLFree(entryName);
×
1291
        return nullptr;
×
1292
    }
1293

1294
    /* -------------------------------------------------------------------- */
1295
    /*      Check if it is a TOC file .                                     */
1296
    /* -------------------------------------------------------------------- */
1297
    if (IsNITFFileTOC(psFile))
×
1298
    {
1299
        GDALDataset *poDS = OpenFileTOC(psFile, pszFilename, entryName,
×
1300
                                        poOpenInfo->pszFilename);
×
1301
        NITFClose(psFile);
×
1302
        CPLFree(entryName);
×
1303

1304
        if (poDS && poOpenInfo->eAccess == GA_Update)
×
1305
        {
1306
            ReportUpdateNotSupportedByDriver("RPFTOC");
×
1307
            delete poDS;
×
1308
            return nullptr;
×
1309
        }
1310

1311
        return poDS;
×
1312
    }
1313
    else
1314
    {
1315
        CPLError(CE_Failure, CPLE_AppDefined, "File %s is not a TOC file.",
×
1316
                 pszFilename);
1317
        NITFClose(psFile);
×
1318
        CPLFree(entryName);
×
1319
        return nullptr;
×
1320
    }
1321
}
1322

1323
/************************************************************************/
1324
/*                          GDALRegister_RPFTOC()                       */
1325
/************************************************************************/
1326

1327
void GDALRegister_RPFTOC()
1,911✔
1328

1329
{
1330
    if (GDALGetDriverByName(RPFTOC_DRIVER_NAME) != nullptr)
1,911✔
1331
        return;
282✔
1332

1333
    GDALDriver *poDriver = new GDALDriver();
1,629✔
1334
    RPFTOCDriverSetCommonMetadata(poDriver);
1,629✔
1335

1336
    poDriver->pfnOpen = RPFTOCDataset::Open;
1,629✔
1337

1338
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
1339
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc