Coverage Report

Created: 2026-05-16 08:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/nitf/nitfdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  NITF Read/Write Translator
4
 * Purpose:  NITFDataset and driver related implementations.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2002, Frank Warmerdam
9
 * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * Portions Copyright (c) Her majesty the Queen in right of Canada as
12
 * represented by the Minister of National Defence, 2006, 2020
13
 *
14
 * SPDX-License-Identifier: MIT
15
 ****************************************************************************/
16
17
#include "cpl_port.h"
18
#include "nitfdataset.h"
19
#include "nitfdrivercore.h"
20
21
#include "gdal_mdreader.h"
22
23
#include <algorithm>
24
#include <cmath>
25
#include <cstdio>
26
#include <cstdlib>
27
#include <cstring>
28
#include <memory>
29
#include <mutex>
30
#include <string>
31
#include <vector>
32
33
#include "cpl_conv.h"
34
#include "cpl_csv.h"
35
#include "cpl_error.h"
36
#include "cpl_minixml.h"
37
#include "cpl_progress.h"
38
#include "cpl_string.h"
39
#include "cpl_vsi.h"
40
#include "gdal.h"
41
#include "gdal_frmts.h"
42
#include "gdal_priv.h"
43
#include "ogr_api.h"
44
#include "ogr_core.h"
45
#include "ogr_srs_api.h"
46
47
#ifdef EMBED_RESOURCE_FILES
48
#include "embedded_resources.h"
49
#endif
50
51
#include "offsetpatcher.h"
52
#include "rpfframewriter.h"
53
54
static bool NITFPatchImageLength(const char *pszFilename, VSILFILE *fp,
55
                                 int nIMIndex, GUIntBig nImageOffset,
56
                                 GIntBig nPixelCount, const char *pszIC,
57
                                 vsi_l_offset nICOffset,
58
                                 CSLConstList papszCreationOptions);
59
static bool
60
NITFWriteExtraSegments(const char *pszFilename, VSILFILE *fpIn,
61
                       CSLConstList papszCgmMD, CSLConstList papszTextMD,
62
                       GDALOffsetPatcher::OffsetPatcher *offsetPatcher,
63
                       const CPLStringList &aosOptions, int nReciprocalScale);
64
65
#ifdef JPEG_SUPPORTED
66
static bool NITFWriteJPEGImage(GDALDataset *, VSILFILE *, vsi_l_offset,
67
                               CSLConstList, GDALProgressFunc pfnProgress,
68
                               void *pProgressData);
69
#endif
70
71
static void SetBandMetadata(NITFImage *psImage, GDALRasterBand *poBand,
72
                            int nBand, bool bReportISUBCAT);
73
74
static bool NITFWriteCGMSegments(const char *pszFilename, VSILFILE *&fpVSIL,
75
                                 CSLConstList papszList);
76
static bool NITFWriteTextSegments(const char *pszFilename, VSILFILE *&fpVSIL,
77
                                  CSLConstList papszList);
78
static bool UpdateFileLength(VSILFILE *fp);
79
80
/************************************************************************/
81
/* ==================================================================== */
82
/*                             NITFDataset                              */
83
/* ==================================================================== */
84
/************************************************************************/
85
86
/************************************************************************/
87
/*                            NITFDataset()                             */
88
/************************************************************************/
89
90
NITFDataset::NITFDataset()
91
914
{
92
914
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
93
914
    m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
94
95
914
    poDriver = GDALDriver::FromHandle(GDALGetDriverByName("NITF"));
96
914
}
97
98
/************************************************************************/
99
/*                            ~NITFDataset()                            */
100
/************************************************************************/
101
102
NITFDataset::~NITFDataset()
103
104
914
{
105
914
    NITFDataset::Close();
106
107
    /* -------------------------------------------------------------------- */
108
    /*      Free datastructures.                                            */
109
    /* -------------------------------------------------------------------- */
110
111
914
    GDALDeinitGCPs(nGCPCount, pasGCPList);
112
914
    CPLFree(pasGCPList);
113
114
914
    CPLFree(panJPEGBlockOffset);
115
914
    CPLFree(pabyJPEGBlock);
116
914
}
117
118
/************************************************************************/
119
/*                               Close()                                */
120
/************************************************************************/
121
122
CPLErr NITFDataset::Close(GDALProgressFunc, void *)
123
1.45k
{
124
1.45k
    int bHasDroppedRef = FALSE;
125
1.45k
    return NITFDataset::Close(bHasDroppedRef);
126
1.45k
}
127
128
CPLErr NITFDataset::Close(int &bHasDroppedRef)
129
1.45k
{
130
1.45k
    CPLErr eErr = CE_None;
131
1.45k
    bHasDroppedRef = FALSE;
132
1.45k
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
133
914
    {
134
914
        eErr = NITFDataset::FlushCache(true);
135
136
914
        bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
137
138
        /* -------------------------------------------------------------------- */
139
        /*      If we have been writing to a JPEG2000 file, check if the        */
140
        /*      color interpretations were set.  If so, apply the settings      */
141
        /*      to the NITF file.                                               */
142
        /* -------------------------------------------------------------------- */
143
914
        if (poJ2KDataset != nullptr && bJP2Writing)
144
0
        {
145
0
            for (int i = 0; i < nBands && papoBands != nullptr; i++)
146
0
            {
147
0
                if (papoBands[i]->GetColorInterpretation() != GCI_Undefined)
148
0
                    NITFSetColorInterpretation(
149
0
                        psImage, i + 1, papoBands[i]->GetColorInterpretation());
150
0
            }
151
0
        }
152
153
        /* -------------------------------------------------------------------- */
154
        /*      Close the underlying NITF file.                                 */
155
        /* -------------------------------------------------------------------- */
156
914
        if (psFile != nullptr)
157
914
        {
158
914
            eErr = GDAL::Combine(eErr, NITFClose(psFile));
159
914
            psFile = nullptr;
160
914
        }
161
162
        /* -------------------------------------------------------------------- */
163
        /*      If we have a jpeg2000 output file, make sure it gets closed     */
164
        /*      and flushed out.                                                */
165
        /* -------------------------------------------------------------------- */
166
914
        if (poJ2KDataset != nullptr)
167
0
        {
168
0
            eErr = GDAL::Combine(eErr, poJ2KDataset->Close());
169
0
            poJ2KDataset.reset();
170
0
            bHasDroppedRef = TRUE;
171
0
        }
172
173
        /* -------------------------------------------------------------------- */
174
        /*      Update file length, and COMRAT for JPEG2000 files we are        */
175
        /*      writing to.                                                     */
176
        /* -------------------------------------------------------------------- */
177
914
        if (bJP2Writing)
178
0
        {
179
0
            const GIntBig nPixelCount =
180
0
                static_cast<GIntBig>(nRasterXSize) * nRasterYSize * nBands;
181
182
0
            eErr = GDAL::Combine(
183
0
                eErr, NITFPatchImageLength(
184
0
                          GetDescription(), nullptr, m_nIMIndex, m_nImageOffset,
185
0
                          nPixelCount, "C8", m_nICOffset, nullptr));
186
0
        }
187
188
914
        bJP2Writing = FALSE;
189
190
        /* -------------------------------------------------------------------- */
191
        /*      If we have a jpeg output file, make sure it gets closed         */
192
        /*      and flushed out.                                                */
193
        /* -------------------------------------------------------------------- */
194
914
        if (poJPEGDataset != nullptr)
195
14
        {
196
14
            eErr = GDAL::Combine(eErr, poJPEGDataset->Close());
197
14
            poJPEGDataset.reset();
198
14
            bHasDroppedRef = TRUE;
199
14
        }
200
201
        /* -------------------------------------------------------------------- */
202
        /*      If the dataset was opened by Create(), we may need to write     */
203
        /*      the CGM and TEXT segments                                       */
204
        /* -------------------------------------------------------------------- */
205
914
        if (m_nIMIndex + 1 == m_nImageCount)
206
0
        {
207
0
            eErr = GDAL::Combine(
208
0
                eErr, NITFWriteExtraSegments(
209
0
                          GetDescription(), nullptr, papszCgmMDToWrite,
210
0
                          papszTextMDToWrite, nullptr, aosCreationOptions, 0));
211
0
        }
212
213
914
        CSLDestroy(papszTextMDToWrite);
214
914
        papszTextMDToWrite = nullptr;
215
914
        CSLDestroy(papszCgmMDToWrite);
216
914
        papszCgmMDToWrite = nullptr;
217
218
914
        eErr = GDAL::Combine(eErr, GDALPamDataset::Close());
219
220
        /* -------------------------------------------------------------------- */
221
        /*      Destroy the raster bands if they exist.                         */
222
        /* We must do it now since the rasterbands can be NITFWrapperRasterBand */
223
        /* that derive from the GDALProxyRasterBand object, which keeps         */
224
        /* a reference on the JPEG/JP2K dataset, so any later call to           */
225
        /* FlushCache() would result in FlushCache() being called on a          */
226
        /* already destroyed object                                             */
227
        /* -------------------------------------------------------------------- */
228
14.7k
        for (int iBand = 0; iBand < nBands; iBand++)
229
13.8k
        {
230
13.8k
            delete papoBands[iBand];
231
13.8k
        }
232
914
        nBands = 0;
233
914
    }
234
1.45k
    return eErr;
235
1.45k
}
236
237
/************************************************************************/
238
/*                       CloseDependentDatasets()                       */
239
/************************************************************************/
240
241
int NITFDataset::CloseDependentDatasets()
242
0
{
243
0
    int bHasDroppedRef = FALSE;
244
0
    Close(bHasDroppedRef);
245
0
    return bHasDroppedRef;
246
0
}
247
248
/************************************************************************/
249
/*                             FlushCache()                             */
250
/************************************************************************/
251
252
CPLErr NITFDataset::FlushCache(bool bAtClosing)
253
254
930
{
255
    // If the JPEG/JP2K dataset has dirty pam info, then we should consider
256
    // ourselves to as well.
257
930
    if (poJPEGDataset != nullptr &&
258
14
        (poJPEGDataset->GetMOFlags() & GMO_PAM_CLASS) &&
259
14
        (cpl::down_cast<GDALPamDataset *>(poJPEGDataset.get())->GetPamFlags() &
260
14
         GPF_DIRTY))
261
0
        MarkPamDirty();
262
263
930
    if (poJ2KDataset != nullptr &&
264
0
        (poJ2KDataset->GetMOFlags() & GMO_PAM_CLASS) &&
265
0
        (cpl::down_cast<GDALPamDataset *>(poJ2KDataset.get())->GetPamFlags() &
266
0
         GPF_DIRTY))
267
0
        MarkPamDirty();
268
269
930
    CPLErr eErr = CE_None;
270
930
    if (poJ2KDataset != nullptr && bJP2Writing)
271
0
        eErr = poJ2KDataset->FlushCache(bAtClosing);
272
273
930
    if (GDALPamDataset::FlushCache(bAtClosing) != CE_None)
274
0
        eErr = CE_Failure;
275
930
    return eErr;
276
930
}
277
278
#ifdef ESRI_BUILD
279
280
/************************************************************************/
281
/*                           ExtractEsriMD()                            */
282
/*                                                                      */
283
/*      Extracts ESRI-specific required meta data from metadata         */
284
/*      string list papszStrList.                                       */
285
/************************************************************************/
286
287
static char **ExtractEsriMD(char **papszMD)
288
{
289
    char **papszEsriMD = NULL;
290
291
    if (papszMD)
292
    {
293
        // These are the current generic ESRI metadata.
294
        const char *const pEsriMDAcquisitionDate = "ESRI_MD_ACQUISITION_DATE";
295
        const char *const pEsriMDAngleToNorth = "ESRI_MD_ANGLE_TO_NORTH";
296
        const char *const pEsriMDCircularError = "ESRI_MD_CE";
297
        const char *const pEsriMDDataType = "ESRI_MD_DATA_TYPE";
298
        const char *const pEsriMDIsCloudCover = "ESRI_MD_ISCLOUDCOVER";
299
        const char *const pEsriMDLinearError = "ESRI_MD_LE";
300
        const char *const pEsriMDOffNaDir = "ESRI_MD_OFF_NADIR";
301
        const char *const pEsriMDPercentCloudCover =
302
            "ESRI_MD_PERCENT_CLOUD_COVER";
303
        const char *const pEsriMDProductName = "ESRI_MD_PRODUCT_NAME";
304
        const char *const pEsriMDSensorAzimuth = "ESRI_MD_SENSOR_AZIMUTH";
305
        const char *const pEsriMDSensorElevation = "ESRI_MD_SENSOR_ELEVATION";
306
        const char *const pEsriMDSensorName = "ESRI_MD_SENSOR_NAME";
307
        const char *const pEsriMDSunAzimuth = "ESRI_MD_SUN_AZIMUTH";
308
        const char *const pEsriMDSunElevation = "ESRI_MD_SUN_ELEVATION";
309
310
        const char *pCCImageSegment = CSLFetchNameValue(papszMD, "NITF_IID1");
311
        std::string ccSegment("false");
312
313
        if ((pCCImageSegment != NULL) && (strlen(pCCImageSegment) <= 10))
314
        {
315
            char szField[11] = {0};
316
            strncpy(szField, pCCImageSegment, strlen(pCCImageSegment));
317
            szField[strlen(pCCImageSegment)] = '\0';
318
319
            // Trim white off tag.
320
            while ((strlen(szField) > 0) &&
321
                   (szField[strlen(szField) - 1] == ' '))
322
                szField[strlen(szField) - 1] = '\0';
323
324
            if ((strlen(szField) == 2) && (STARTS_WITH_CI(szField, "CC")))
325
                ccSegment.assign("true");
326
        }
327
328
        const char *pAcquisitionDate = CSLFetchNameValue(papszMD, "NITF_FDT");
329
        const char *pAngleToNorth =
330
            CSLFetchNameValue(papszMD, "NITF_CSEXRA_ANGLE_TO_NORTH");
331
        const char *pCircularError = CSLFetchNameValue(
332
            papszMD, "NITF_CSEXRA_CIRCL_ERR");  // Unit in feet.
333
        const char *pLinearError = CSLFetchNameValue(
334
            papszMD, "NITF_CSEXRA_LINEAR_ERR");  // Unit in feet.
335
        const char *pPercentCloudCover =
336
            CSLFetchNameValue(papszMD, "NITF_PIAIMC_CLOUDCVR");
337
        const char *pProductName =
338
            CSLFetchNameValue(papszMD, "NITF_CSDIDA_PRODUCT_ID");
339
        const char *pSensorName =
340
            CSLFetchNameValue(papszMD, "NITF_PIAIMC_SENSNAME");
341
        const char *pSunAzimuth =
342
            CSLFetchNameValue(papszMD, "NITF_CSEXRA_SUN_AZIMUTH");
343
        const char *pSunElevation =
344
            CSLFetchNameValue(papszMD, "NITF_CSEXRA_SUN_ELEVATION");
345
346
        // Get ESRI_MD_DATA_TYPE.
347
        const char *pImgSegFieldICAT = CSLFetchNameValue(papszMD, "NITF_ICAT");
348
349
        const char *pDataType = NULL;
350
        if ((pImgSegFieldICAT != NULL) &&
351
            (STARTS_WITH_CI(pImgSegFieldICAT, "DTEM")))
352
            pDataType = "Elevation";
353
        else
354
            pDataType = "Generic";
355
356
        if (pAngleToNorth == NULL)
357
            pAngleToNorth =
358
                CSLFetchNameValue(papszMD, "NITF_USE00A_ANGLE_TO_NORTH");
359
360
        // Percent cloud cover == 999 means that the information is not
361
        // available.
362
        if ((pPercentCloudCover != NULL) &&
363
            (STARTS_WITH_CI(pPercentCloudCover, "999")))
364
            pPercentCloudCover = NULL;
365
366
        pAngleToNorth =
367
            CSLFetchNameValue(papszMD, "NITF_USE00A_ANGLE_TO_NORTH");
368
369
        if (pSunAzimuth == NULL)
370
            pSunAzimuth = CSLFetchNameValue(papszMD, "NITF_USE00A_SUN_AZ");
371
372
        if (pSunElevation == NULL)
373
            pSunElevation = CSLFetchNameValue(papszMD, "NITF_USE00A_SUN_EL");
374
375
        // CSLAddNameValue will not add the key/value pair if the value is NULL.
376
        papszEsriMD = CSLAddNameValue(papszEsriMD, pEsriMDAcquisitionDate,
377
                                      pAcquisitionDate);
378
        papszEsriMD =
379
            CSLAddNameValue(papszEsriMD, pEsriMDAngleToNorth, pAngleToNorth);
380
        papszEsriMD =
381
            CSLAddNameValue(papszEsriMD, pEsriMDCircularError, pCircularError);
382
        papszEsriMD = CSLAddNameValue(papszEsriMD, pEsriMDDataType, pDataType);
383
        papszEsriMD = CSLAddNameValue(papszEsriMD, pEsriMDIsCloudCover,
384
                                      ccSegment.c_str());
385
        papszEsriMD =
386
            CSLAddNameValue(papszEsriMD, pEsriMDLinearError, pLinearError);
387
        papszEsriMD =
388
            CSLAddNameValue(papszEsriMD, pEsriMDProductName, pProductName);
389
        papszEsriMD = CSLAddNameValue(papszEsriMD, pEsriMDPercentCloudCover,
390
                                      pPercentCloudCover);
391
        papszEsriMD =
392
            CSLAddNameValue(papszEsriMD, pEsriMDSensorName, pSensorName);
393
        papszEsriMD =
394
            CSLAddNameValue(papszEsriMD, pEsriMDSunAzimuth, pSunAzimuth);
395
        papszEsriMD =
396
            CSLAddNameValue(papszEsriMD, pEsriMDSunElevation, pSunElevation);
397
    }
398
399
    return papszEsriMD;
400
}
401
402
#endif /* def ESRI_BUILD */
403
404
/************************************************************************/
405
/*                          SetBandMetadata()                           */
406
/************************************************************************/
407
408
static void SetBandMetadata(NITFImage *psImage, GDALRasterBand *poBand,
409
                            int nBand, bool bReportISUBCAT)
410
13.8k
{
411
13.8k
    const NITFBandInfo *psBandInfo = psImage->pasBandInfo + nBand - 1;
412
413
    /* The ISUBCAT is particularly valuable for interpreting SAR bands */
414
13.8k
    if (bReportISUBCAT && strlen(psBandInfo->szISUBCAT) > 0)
415
7.86k
    {
416
7.86k
        poBand->SetMetadataItem("NITF_ISUBCAT", psBandInfo->szISUBCAT);
417
7.86k
    }
418
13.8k
}
419
420
/************************************************************************/
421
/*                                Open()                                */
422
/************************************************************************/
423
424
GDALDataset *NITFDataset::Open(GDALOpenInfo *poOpenInfo)
425
2.08k
{
426
2.08k
    return OpenInternal(poOpenInfo, nullptr, false, -1);
427
2.08k
}
428
429
NITFDataset *NITFDataset::OpenInternal(GDALOpenInfo *poOpenInfo,
430
                                       GDALDataset *poWritableJ2KDataset,
431
                                       bool bOpenForCreate, int nIMIndex)
432
433
2.10k
{
434
2.10k
    if (!NITFDriverIdentify(poOpenInfo))
435
0
        return nullptr;
436
437
2.10k
    const char *pszFilename = poOpenInfo->pszFilename;
438
439
    /* -------------------------------------------------------------------- */
440
    /*      Select a specific subdataset.                                   */
441
    /* -------------------------------------------------------------------- */
442
2.10k
    if (STARTS_WITH_CI(pszFilename, "NITF_IM:"))
443
0
    {
444
0
        pszFilename += 8;
445
0
        nIMIndex = atoi(pszFilename);
446
447
0
        while (*pszFilename != '\0' && *pszFilename != ':')
448
0
            pszFilename++;
449
450
0
        if (*pszFilename == ':')
451
0
            pszFilename++;
452
0
    }
453
454
    /* -------------------------------------------------------------------- */
455
    /*      Open the file with library.                                     */
456
    /* -------------------------------------------------------------------- */
457
2.10k
    NITFFile *psFile = nullptr;
458
459
2.10k
    if (poOpenInfo->fpL)
460
2.10k
    {
461
2.10k
        VSILFILE *fpL = poOpenInfo->fpL;
462
2.10k
        poOpenInfo->fpL = nullptr;
463
2.10k
        psFile = NITFOpenEx(fpL, pszFilename);
464
2.10k
    }
465
0
    else
466
0
        psFile = NITFOpen(pszFilename, poOpenInfo->eAccess == GA_Update);
467
2.10k
    if (psFile == nullptr)
468
370
    {
469
370
        return nullptr;
470
370
    }
471
472
1.73k
    if (!bOpenForCreate)
473
1.71k
    {
474
1.71k
        NITFCollectAttachments(psFile);
475
1.71k
        NITFReconcileAttachments(psFile);
476
1.71k
    }
477
478
    /* -------------------------------------------------------------------- */
479
    /*      Is there an image to operate on?                                */
480
    /* -------------------------------------------------------------------- */
481
1.73k
    int nThisIM = 0;
482
1.73k
    NITFImage *psImage = nullptr;
483
484
1.73k
    int iSegment = 0;  // Used after for loop.
485
51.2k
    for (; iSegment < psFile->nSegmentCount; iSegment++)
486
51.0k
    {
487
51.0k
        if (EQUAL(psFile->pasSegmentInfo[iSegment].szSegmentType, "IM") &&
488
1.55k
            (nThisIM++ == nIMIndex || nIMIndex == -1))
489
1.55k
        {
490
1.55k
            psImage = NITFImageAccess(psFile, iSegment);
491
1.55k
            if (psImage == nullptr)
492
820
            {
493
820
                NITFClose(psFile);
494
820
                return nullptr;
495
820
            }
496
736
            break;
497
1.55k
        }
498
51.0k
    }
499
500
    /* -------------------------------------------------------------------- */
501
    /*      If no image segments found report this to the user.             */
502
    /* -------------------------------------------------------------------- */
503
914
    if (psImage == nullptr)
504
178
    {
505
178
        CPLError(CE_Warning, CPLE_AppDefined,
506
178
                 "The file %s appears to be an NITF file, but no image "
507
178
                 "blocks were found on it.",
508
178
                 poOpenInfo->pszFilename);
509
178
    }
510
736
    else if (psImage->nBitsPerSample > 16 &&
511
53
             (EQUAL(psImage->szIC, "C3") || EQUAL(psImage->szIC, "M3")))
512
0
    {
513
        // Early rejection of JPEG compressed images with invalid bit depth
514
        // Otherwise this will cause potentially heap buffer overflows
515
        // as ReadJPEGBlock() assumes that the data type size is no larger
516
        // than 2 bytes.
517
0
        CPLError(CE_Failure, CPLE_NotSupported,
518
0
                 "IC=%s and ABPP=%d are not supported", psImage->szIC,
519
0
                 psImage->nBitsPerSample);
520
0
        NITFClose(psFile);
521
0
        return nullptr;
522
0
    }
523
524
    /* -------------------------------------------------------------------- */
525
    /*      Create a corresponding GDALDataset.                             */
526
    /* -------------------------------------------------------------------- */
527
914
    NITFDataset *poDS = new NITFDataset();
528
529
914
    poDS->psFile = psFile;
530
914
    poDS->psImage = psImage;
531
914
    poDS->eAccess = poOpenInfo->eAccess;
532
914
    poDS->osNITFFilename = pszFilename;
533
914
    poDS->nIMIndex = nIMIndex;
534
535
914
    if (psImage)
536
736
    {
537
736
        if (psImage->nCols <= 0 || psImage->nRows <= 0 ||
538
580
            psImage->nBlockWidth <= 0 || psImage->nBlockHeight <= 0)
539
156
        {
540
156
            CPLError(CE_Failure, CPLE_AppDefined,
541
156
                     "Bad values in NITF image : nCols=%d, nRows=%d, "
542
156
                     "nBlockWidth=%d, nBlockHeight=%d",
543
156
                     psImage->nCols, psImage->nRows, psImage->nBlockWidth,
544
156
                     psImage->nBlockHeight);
545
156
            delete poDS;
546
156
            return nullptr;
547
156
        }
548
549
580
        poDS->nRasterXSize = psImage->nCols;
550
580
        poDS->nRasterYSize = psImage->nRows;
551
580
    }
552
178
    else
553
178
    {
554
178
        poDS->nRasterXSize = 1;
555
178
        poDS->nRasterYSize = 1;
556
178
    }
557
558
    /* Can be set to NO to avoid opening the underlying JPEG2000/JPEG */
559
    /* stream. Might speed up operations when just metadata is needed */
560
758
    bool bOpenUnderlyingDS =
561
758
        CPLTestBool(CPLGetConfigOption("NITF_OPEN_UNDERLYING_DS", "YES"));
562
563
    /* -------------------------------------------------------------------- */
564
    /*      If the image is JPEG2000 (C8) compressed, we will need to       */
565
    /*      open the image data as a JPEG2000 dataset.                      */
566
    /* -------------------------------------------------------------------- */
567
758
    int nUsableBands = 0;
568
758
    bool bSetColorInterpretation = true;
569
758
    bool bSetColorTable = false;
570
571
758
    if (psImage)
572
580
        nUsableBands = psImage->nBands;
573
574
758
    if (bOpenUnderlyingDS && psImage != nullptr && EQUAL(psImage->szIC, "C8"))
575
1
    {
576
1
        CPLString osDSName;
577
578
1
        osDSName.Printf("/vsisubfile/" CPL_FRMT_GUIB "_" CPL_FRMT_GUIB ",%s",
579
1
                        psFile->pasSegmentInfo[iSegment].nSegmentStart,
580
1
                        psFile->pasSegmentInfo[iSegment].nSegmentSize,
581
1
                        pszFilename);
582
583
1
        if (poWritableJ2KDataset != nullptr)
584
0
        {
585
0
            poDS->poJ2KDataset.reset(poWritableJ2KDataset);
586
0
            poDS->bJP2Writing = TRUE;
587
0
            poWritableJ2KDataset = nullptr;
588
0
        }
589
1
        else
590
1
        {
591
            // We explicitly list the allowed drivers to avoid hostile content
592
            // to be opened by a random driver.
593
1
            static const char *const apszDrivers[] = {
594
1
                "JP2KAK", "JP2ECW", "JP2MRSID", "JP2OPENJPEG", nullptr};
595
1
            poDS->poJ2KDataset.reset(GDALDataset::Open(
596
1
                osDSName, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, apszDrivers,
597
1
                nullptr, nullptr));
598
599
1
            if (poDS->poJ2KDataset == nullptr)
600
1
            {
601
1
                bool bFoundJPEG2000Driver = false;
602
5
                for (int iDriver = 0; apszDrivers[iDriver] != nullptr;
603
4
                     iDriver++)
604
4
                {
605
4
                    if (GDALGetDriverByName(apszDrivers[iDriver]) != nullptr)
606
0
                        bFoundJPEG2000Driver = true;
607
4
                }
608
609
1
                CPLError(
610
1
                    CE_Failure, CPLE_AppDefined,
611
1
                    "Unable to open JPEG2000 image within NITF file.\n%s\n%s",
612
1
                    !bFoundJPEG2000Driver
613
1
                        ? "No JPEG2000 capable driver (JP2KAK, JP2ECW, "
614
1
                          "JP2MRSID, "
615
1
                          "JP2OPENJPEG, etc...) is available."
616
1
                        : "One or several JPEG2000 capable drivers are "
617
0
                          "available but "
618
0
                          "the datastream could not be opened successfully.",
619
1
                    "You can define the NITF_OPEN_UNDERLYING_DS configuration "
620
1
                    "option to NO, in order to just get the metadata.");
621
1
                delete poDS;
622
1
                return nullptr;
623
1
            }
624
625
0
            if (poDS->poJ2KDataset->GetMOFlags() & GMO_PAM_CLASS)
626
0
            {
627
0
                cpl::down_cast<GDALPamDataset *>(poDS->poJ2KDataset.get())
628
0
                    ->SetPamFlags(reinterpret_cast<GDALPamDataset *>(
629
0
                                      poDS->poJ2KDataset.get())
630
0
                                      ->GetPamFlags() |
631
0
                                  GPF_NOSAVE);
632
0
            }
633
0
        }
634
635
0
        if (poDS->GetRasterXSize() != poDS->poJ2KDataset->GetRasterXSize() ||
636
0
            poDS->GetRasterYSize() != poDS->poJ2KDataset->GetRasterYSize())
637
0
        {
638
0
            CPLError(CE_Failure, CPLE_AppDefined,
639
0
                     "JPEG2000 data stream has not the same dimensions as "
640
0
                     "the NITF file.");
641
0
            delete poDS;
642
0
            return nullptr;
643
0
        }
644
645
0
        if (nUsableBands == 1)
646
0
        {
647
0
            const char *pszIREP =
648
0
                CSLFetchNameValue(psImage->papszMetadata, "NITF_IREP");
649
0
            if (pszIREP != nullptr && EQUAL(pszIREP, "RGB/LUT"))
650
0
            {
651
0
                if (poDS->poJ2KDataset->GetRasterCount() == 3)
652
0
                {
653
                    // Test case:
654
                    // http://www.gwg.nga.mil/ntb/baseline/software/testfile/Jpeg2000/jp2_09/file9_jp2_2places.ntf
655
                    /* 256-entry palette/LUT in both JP2 Header and image
656
                     * Subheader */
657
                    /* In this case, the JPEG2000 driver will probably do the
658
                     * RGB expansion. */
659
0
                    nUsableBands = 3;
660
0
                    bSetColorInterpretation = false;
661
0
                }
662
0
                else if (poDS->poJ2KDataset->GetRasterCount() == 1 &&
663
0
                         psImage->pasBandInfo[0].nSignificantLUTEntries > 0)
664
0
                {
665
                    // Test case:
666
                    // http://www.gwg.nga.mil/ntb/baseline/software/testfile/Jpeg2000/jp2_09/file9_j2c.ntf
667
668
                    // 256-entry/LUT in Image Subheader, JP2 header completely
669
                    // removed. The JPEG2000 driver will decode it as a grey
670
                    // band So we must set the color table on the wrapper band
671
                    // or for file9_jp2_2places.ntf as well if the J2K driver
672
                    // does do RGB expansion
673
0
                    bSetColorTable = true;
674
0
                }
675
0
            }
676
0
        }
677
678
0
        if (poDS->poJ2KDataset->GetRasterCount() < nUsableBands)
679
0
        {
680
0
            CPLError(CE_Warning, CPLE_AppDefined,
681
0
                     "JPEG2000 data stream has less useful bands than "
682
0
                     "expected, likely because some channels have "
683
0
                     "differing resolutions.");
684
685
0
            nUsableBands = poDS->poJ2KDataset->GetRasterCount();
686
0
        }
687
0
    }
688
689
    /* -------------------------------------------------------------------- */
690
    /*      If the image is JPEG (C3) compressed, we will need to open      */
691
    /*      the image data as a JPEG dataset.                               */
692
    /* -------------------------------------------------------------------- */
693
757
    else if (bOpenUnderlyingDS && psImage != nullptr &&
694
579
             EQUAL(psImage->szIC, "C3") && psImage->nBlocksPerRow == 1 &&
695
24
             psImage->nBlocksPerColumn == 1)
696
18
    {
697
18
        GUIntBig nJPEGStart = psFile->pasSegmentInfo[iSegment].nSegmentStart;
698
699
18
        bool bError = false;
700
18
        poDS->nQLevel = poDS->ScanJPEGQLevel(&nJPEGStart, &bError);
701
702
18
        CPLString osDSName;
703
704
18
        if (psFile->pasSegmentInfo[iSegment].nSegmentSize <
705
18
            nJPEGStart - psFile->pasSegmentInfo[iSegment].nSegmentStart)
706
0
        {
707
0
            CPLError(CE_Failure, CPLE_AppDefined, "Corrupted segment size");
708
0
            delete poDS;
709
0
            return nullptr;
710
0
        }
711
712
18
        osDSName.Printf(
713
18
            "JPEG_SUBFILE:Q%d," CPL_FRMT_GUIB "," CPL_FRMT_GUIB ",%s",
714
18
            poDS->nQLevel, nJPEGStart,
715
18
            psFile->pasSegmentInfo[iSegment].nSegmentSize -
716
18
                (nJPEGStart - psFile->pasSegmentInfo[iSegment].nSegmentStart),
717
18
            pszFilename);
718
719
18
        CPLDebug("GDAL", "NITFDataset::Open() as IC=C3 (JPEG compressed)\n");
720
721
18
        poDS->poJPEGDataset.reset(GDALDataset::Open(
722
18
            osDSName, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
723
18
        if (poDS->poJPEGDataset == nullptr)
724
4
        {
725
4
            const bool bFoundJPEGDriver =
726
4
                GDALGetDriverByName("JPEG") != nullptr;
727
4
            CPLError(CE_Failure, CPLE_AppDefined,
728
4
                     "Unable to open JPEG image within NITF file.\n%s\n%s",
729
4
                     (!bFoundJPEGDriver)
730
4
                         ? "The JPEG driver is not available."
731
4
                         : "The JPEG driver is available but the datastream "
732
4
                           "could not be opened successfully.",
733
4
                     "You can define the NITF_OPEN_UNDERLYING_DS configuration "
734
4
                     "option to NO, in order to just get the metadata.");
735
4
            delete poDS;
736
4
            return nullptr;
737
4
        }
738
739
        /* In some circumstances, the JPEG image can be larger than the NITF */
740
        /* (NCOLS, NROWS) dimensions (#5001), so accept it as a valid case */
741
        /* But reject when it is smaller than the NITF dimensions. */
742
14
        if (poDS->GetRasterXSize() > poDS->poJPEGDataset->GetRasterXSize() ||
743
14
            poDS->GetRasterYSize() > poDS->poJPEGDataset->GetRasterYSize())
744
0
        {
745
0
            CPLError(
746
0
                CE_Failure, CPLE_AppDefined,
747
0
                "JPEG data stream has smaller dimensions than the NITF file.");
748
0
            delete poDS;
749
0
            return nullptr;
750
0
        }
751
752
14
        if (poDS->poJPEGDataset->GetMOFlags() & GMO_PAM_CLASS)
753
14
        {
754
14
            (cpl::down_cast<GDALPamDataset *>(poDS->poJPEGDataset.get()))
755
14
                ->SetPamFlags((reinterpret_cast<GDALPamDataset *>(
756
14
                                   poDS->poJPEGDataset.get()))
757
14
                                  ->GetPamFlags() |
758
14
                              GPF_NOSAVE);
759
14
        }
760
761
14
        if (poDS->poJPEGDataset->GetRasterCount() < nUsableBands)
762
0
        {
763
0
            CPLError(
764
0
                CE_Warning, CPLE_AppDefined,
765
0
                "JPEG data stream has less useful bands than expected, likely\n"
766
0
                "because some channels have differing resolutions.");
767
768
0
            nUsableBands = poDS->poJPEGDataset->GetRasterCount();
769
0
        }
770
14
    }
771
772
    /* -------------------------------------------------------------------- */
773
    /*      Create band information objects.                                */
774
    /* -------------------------------------------------------------------- */
775
776
    /* Keep temporary non-based dataset bands */
777
753
    bool bIsTempBandUsed = false;
778
753
    GDALDataType dtFirstBand = GDT_Unknown;
779
753
    GDALDataType dtSecondBand = GDT_Unknown;
780
753
    std::vector<GDALRasterBand *> apoNewBands(nUsableBands);
781
782
753
    GDALDataset *poBaseDS = nullptr;
783
753
    if (poDS->poJ2KDataset != nullptr)
784
0
        poBaseDS = poDS->poJ2KDataset.get();
785
753
    else if (poDS->poJPEGDataset != nullptr)
786
14
        poBaseDS = poDS->poJPEGDataset.get();
787
788
14.5k
    for (int iBand = 0; iBand < nUsableBands; iBand++)
789
13.8k
    {
790
13.8k
        if (poBaseDS != nullptr)
791
14
        {
792
14
            GDALRasterBand *poBaseBand = poBaseDS->GetRasterBand(iBand + 1);
793
794
14
            SetBandMetadata(psImage, poBaseBand, iBand + 1, true);
795
796
14
            NITFWrapperRasterBand *poBand =
797
14
                new NITFWrapperRasterBand(poDS, poBaseBand, iBand + 1);
798
799
14
            NITFBandInfo *psBandInfo = psImage->pasBandInfo + iBand;
800
14
            if (bSetColorInterpretation)
801
14
            {
802
                /* FIXME? Does it make sense if the JPEG/JPEG2000 driver decodes
803
                 */
804
                /* YCbCr data as RGB. We probably don't want to set */
805
                /* the color interpretation as Y, Cb, Cr */
806
14
                if (EQUAL(psBandInfo->szIREPBAND, "R"))
807
0
                    poBand->SetColorInterpretation(GCI_RedBand);
808
14
                if (EQUAL(psBandInfo->szIREPBAND, "G"))
809
0
                    poBand->SetColorInterpretation(GCI_GreenBand);
810
14
                if (EQUAL(psBandInfo->szIREPBAND, "B"))
811
0
                    poBand->SetColorInterpretation(GCI_BlueBand);
812
14
                if (EQUAL(psBandInfo->szIREPBAND, "M"))
813
0
                    poBand->SetColorInterpretation(GCI_GrayIndex);
814
14
                if (EQUAL(psBandInfo->szIREPBAND, "Y"))
815
0
                    poBand->SetColorInterpretation(GCI_YCbCr_YBand);
816
14
                if (EQUAL(psBandInfo->szIREPBAND, "Cb"))
817
0
                    poBand->SetColorInterpretation(GCI_YCbCr_CbBand);
818
14
                if (EQUAL(psBandInfo->szIREPBAND, "Cr"))
819
0
                    poBand->SetColorInterpretation(GCI_YCbCr_CrBand);
820
14
            }
821
14
            if (bSetColorTable)
822
0
            {
823
0
                poBand->SetColorTableFromNITFBandInfo();
824
0
                poBand->SetColorInterpretation(GCI_PaletteIndex);
825
0
            }
826
827
14
            poDS->SetBand(iBand + 1, poBand);
828
829
14
            if (iBand == 0)
830
14
                dtFirstBand = poBand->GetRasterDataType();
831
0
            else if (iBand == 1)
832
0
                dtSecondBand = poBand->GetRasterDataType();
833
14
        }
834
13.8k
        else
835
13.8k
        {
836
13.8k
            bIsTempBandUsed = true;
837
838
13.8k
            NITFRasterBand *poBand = new NITFRasterBand(poDS, iBand + 1);
839
13.8k
            if (poBand->GetRasterDataType() == GDT_Unknown)
840
61
            {
841
61
                for (auto *poOtherBand : apoNewBands)
842
1.36k
                    delete poOtherBand;
843
61
                delete poBand;
844
61
                delete poDS;
845
61
                return nullptr;
846
61
            }
847
848
13.7k
            apoNewBands[iBand] = poBand;
849
850
13.7k
            if (iBand == 0)
851
500
                dtFirstBand = poBand->GetRasterDataType();
852
13.7k
            if (iBand == 1)
853
114
                dtSecondBand = poBand->GetRasterDataType();
854
13.7k
        }
855
13.8k
    }
856
857
    /* -------------------------------------------------------------------- */
858
    /*      SAR images may store complex data in 2 bands (I and Q)          */
859
    /*      Map onto a GDAL complex raster band                             */
860
    /* -------------------------------------------------------------------- */
861
692
    bool bIsTempBandSet = false;
862
692
    if (!bOpenForCreate && psImage &&
863
494
        EQUAL(psImage->szICAT, "SAR")  //SAR image...
864
0
        && bIsTempBandUsed &&
865
0
        nUsableBands == psImage->nBands
866
        //...with 2 bands ... (modified to allow an even number - spec seems to indicate only 2 bands allowed?)
867
0
        && (nUsableBands % 2) == 0 &&
868
0
        dtFirstBand == dtSecondBand  //...that have the same datatype...
869
0
        && !GDALDataTypeIsComplex(dtFirstBand)  //...and are not complex...
870
        //..and can be mapped directly to a complex type
871
0
        && (dtFirstBand == GDT_Int16 || dtFirstBand == GDT_Int32 ||
872
0
            dtFirstBand == GDT_Float32 || dtFirstBand == GDT_Float64) &&
873
0
        CPLTestBool(CPLGetConfigOption("NITF_SAR_AS_COMPLEX_TYPE", "YES")))
874
0
    {
875
0
        bool allBandsIQ = true;
876
0
        for (int i = 0; i < nUsableBands; i += 2)
877
0
        {
878
0
            const NITFBandInfo *psBandInfo1 = psImage->pasBandInfo + i;
879
0
            const NITFBandInfo *psBandInfo2 = psImage->pasBandInfo + i + 1;
880
881
            //check that the ISUBCAT is labelled "I" and "Q" on the 2 bands
882
0
            if (!EQUAL(psBandInfo1->szISUBCAT, "I") ||
883
0
                !EQUAL(psBandInfo2->szISUBCAT, "Q"))
884
0
            {
885
0
                allBandsIQ = false;
886
0
                break;
887
0
            }
888
0
        }
889
890
0
        if (allBandsIQ)
891
0
        {
892
0
            poDS->m_bHasComplexRasterBand = true;
893
0
            for (int i = 0; i < (nUsableBands / 2); i++)
894
0
            {
895
                //wrap the I and Q bands into a single complex band
896
0
                const int iBandIndex = 2 * i;
897
0
                const int qBandIndex = 2 * i + 1;
898
0
                NITFComplexRasterBand *poBand = new NITFComplexRasterBand(
899
0
                    poDS, apoNewBands[iBandIndex], apoNewBands[qBandIndex],
900
0
                    iBandIndex + 1, qBandIndex + 1);
901
0
                SetBandMetadata(psImage, poBand, i + 1, false);
902
0
                poDS->SetBand(i + 1, poBand);
903
0
                bIsTempBandSet = true;
904
0
            }
905
0
        }
906
0
    }
907
908
692
    if (bIsTempBandUsed && !bIsTempBandSet)
909
500
    {
910
        // Reset properly bands that are not complex
911
14.2k
        for (int iBand = 0; iBand < nUsableBands; iBand++)
912
13.7k
        {
913
13.7k
            GDALRasterBand *poBand = apoNewBands[iBand];
914
13.7k
            SetBandMetadata(psImage, poBand, iBand + 1, true);
915
13.7k
            poDS->SetBand(iBand + 1, poBand);
916
13.7k
        }
917
500
    }
918
919
    /* -------------------------------------------------------------------- */
920
    /*      Report problems with odd bit sizes.                             */
921
    /* -------------------------------------------------------------------- */
922
692
    if (poOpenInfo->eAccess == GA_Update && psImage != nullptr &&
923
20
        (psImage->nBitsPerSample % 8 != 0) && poDS->poJPEGDataset == nullptr &&
924
0
        poDS->poJ2KDataset == nullptr)
925
0
    {
926
0
        CPLError(
927
0
            CE_Warning, CPLE_AppDefined,
928
0
            "Image with %d bits per sample cannot be opened in update mode.",
929
0
            psImage->nBitsPerSample);
930
0
        delete poDS;
931
0
        return nullptr;
932
0
    }
933
934
    /* -------------------------------------------------------------------- */
935
    /*      Process the projection from the ICORDS.                         */
936
    /* -------------------------------------------------------------------- */
937
692
    if (psImage == nullptr)
938
178
    {
939
        /* nothing */
940
178
    }
941
514
    else if (psImage->chICORDS == 'G' || psImage->chICORDS == 'D')
942
36
    {
943
36
        poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
944
36
    }
945
478
    else if (psImage->chICORDS == 'C')
946
5
    {
947
5
        poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
948
949
        /* convert latitudes from geocentric to geodetic form. */
950
951
5
        psImage->dfULY =
952
5
            NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(psImage->dfULY);
953
5
        psImage->dfLLY =
954
5
            NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(psImage->dfLLY);
955
5
        psImage->dfURY =
956
5
            NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(psImage->dfURY);
957
5
        psImage->dfLRY =
958
5
            NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(psImage->dfLRY);
959
5
    }
960
473
    else if (psImage->chICORDS == 'S' || psImage->chICORDS == 'N')
961
45
    {
962
        // in open-for-create mode, we don't have a valid UTM zone, which
963
        // would make PROJ unhappy
964
45
        if (!bOpenForCreate)
965
39
        {
966
39
            poDS->m_oSRS.SetUTM(psImage->nZone, psImage->chICORDS == 'N');
967
39
            poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
968
39
        }
969
45
    }
970
428
    else if (psImage->chICORDS == 'U' && psImage->nZone != 0)
971
0
    {
972
0
        poDS->m_oSRS.SetUTM(std::abs(psImage->nZone), psImage->nZone > 0);
973
0
        poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
974
0
    }
975
976
    /* -------------------------------------------------------------------- */
977
    /*      Try looking for a .nfw file.                                    */
978
    /* -------------------------------------------------------------------- */
979
692
    if (psImage && GDALReadWorldFile2(pszFilename, "nfw", poDS->m_gt.data(),
980
514
                                      poOpenInfo->GetSiblingFiles(), nullptr))
981
0
    {
982
0
        int isNorth;
983
0
        int zone;
984
985
0
        poDS->bGotGeoTransform = TRUE;
986
987
        /* If nfw found, try looking for a header with projection info */
988
        /* in space imaging style format                               */
989
0
        std::string osHDR = CPLResetExtensionSafe(pszFilename, "hdr");
990
991
0
        VSILFILE *fpHDR = VSIFOpenL(osHDR.c_str(), "rt");
992
993
0
        if (fpHDR == nullptr && VSIIsCaseSensitiveFS(osHDR.c_str()))
994
0
        {
995
0
            osHDR = CPLResetExtensionSafe(pszFilename, "HDR");
996
0
            fpHDR = VSIFOpenL(osHDR.c_str(), "rt");
997
0
        }
998
999
0
        if (fpHDR != nullptr)
1000
0
        {
1001
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpHDR));
1002
0
            char **papszLines = CSLLoad2(osHDR.c_str(), 16, 200, nullptr);
1003
0
            if (CSLCount(papszLines) == 16)
1004
0
            {
1005
1006
0
                if (psImage->chICORDS == 'N')
1007
0
                    isNorth = 1;
1008
0
                else if (psImage->chICORDS == 'S')
1009
0
                    isNorth = 0;
1010
0
                else if (psImage->chICORDS == 'G' || psImage->chICORDS == 'D' ||
1011
0
                         psImage->chICORDS == 'C')
1012
0
                {
1013
0
                    if (psImage->dfLLY + psImage->dfLRY + psImage->dfULY +
1014
0
                            psImage->dfURY <
1015
0
                        0)
1016
0
                        isNorth = 0;
1017
0
                    else
1018
0
                        isNorth = 1;
1019
0
                }
1020
0
                else if (psImage->chICORDS == 'U')
1021
0
                {
1022
0
                    isNorth = psImage->nZone >= 0;
1023
0
                }
1024
0
                else
1025
0
                {
1026
                    // Arbitrarily suppose we are in northern hemisphere.
1027
0
                    isNorth = 1;
1028
1029
                    /* unless we have other information to determine the
1030
                     * hemisphere */
1031
0
                    char **papszUSE00A_MD = NITFReadSTDIDC(psImage);
1032
0
                    if (papszUSE00A_MD != nullptr)
1033
0
                    {
1034
0
                        const char *pszLocation = CSLFetchNameValue(
1035
0
                            papszUSE00A_MD, "NITF_STDIDC_LOCATION");
1036
0
                        if (pszLocation && strlen(pszLocation) == 11)
1037
0
                        {
1038
0
                            isNorth = (pszLocation[4] == 'N');
1039
0
                        }
1040
0
                        CSLDestroy(papszUSE00A_MD);
1041
0
                    }
1042
0
                    else
1043
0
                    {
1044
0
                        NITFRPC00BInfo sRPCInfo;
1045
0
                        if (NITFReadRPC00B(psImage, &sRPCInfo) &&
1046
0
                            sRPCInfo.SUCCESS)
1047
0
                        {
1048
0
                            isNorth = (sRPCInfo.LAT_OFF >= 0);
1049
0
                        }
1050
0
                    }
1051
0
                }
1052
1053
0
                if ((STARTS_WITH_CI(papszLines[7],
1054
0
                                    "Selected Projection: Universal Transverse "
1055
0
                                    "Mercator")) &&
1056
0
                    (STARTS_WITH_CI(papszLines[8], "Zone: ")) &&
1057
0
                    (strlen(papszLines[8]) >= 7))
1058
0
                {
1059
0
                    zone = atoi(&(papszLines[8][6]));
1060
0
                    poDS->m_oSRS.Clear();
1061
0
                    poDS->m_oSRS.SetUTM(zone, isNorth);
1062
0
                    poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
1063
0
                }
1064
0
                else
1065
0
                {
1066
                    /* Couldn't find associated projection info.
1067
                       Go back to original file for geotransform.
1068
                    */
1069
0
                    poDS->bGotGeoTransform = FALSE;
1070
0
                }
1071
0
            }
1072
0
            else
1073
0
                poDS->bGotGeoTransform = FALSE;
1074
0
            CSLDestroy(papszLines);
1075
0
        }
1076
0
        else
1077
0
            poDS->bGotGeoTransform = FALSE;
1078
0
    }
1079
1080
    /* -------------------------------------------------------------------- */
1081
    /*      Does this look like a CADRG polar tile ? (#2940)                */
1082
    /* -------------------------------------------------------------------- */
1083
692
    const char *pszIID1 =
1084
692
        (psImage) ? CSLFetchNameValue(psImage->papszMetadata, "NITF_IID1")
1085
692
                  : nullptr;
1086
692
    const char *pszITITLE =
1087
692
        (psImage) ? CSLFetchNameValue(psImage->papszMetadata, "NITF_ITITLE")
1088
692
                  : nullptr;
1089
692
    if (psImage != nullptr && !poDS->bGotGeoTransform &&
1090
514
        (psImage->chICORDS == 'G' || psImage->chICORDS == 'D') &&
1091
36
        pszIID1 != nullptr && EQUAL(pszIID1, "CADRG") && pszITITLE != nullptr &&
1092
18
        strlen(pszITITLE) >= 12 &&
1093
18
        (pszITITLE[strlen(pszITITLE) - 1] == '9' ||
1094
15
         pszITITLE[strlen(pszITITLE) - 1] == 'J'))
1095
3
    {
1096
3
        OGRSpatialReference oSRS_AEQD, oSRS_WGS84;
1097
1098
3
        const char *pszPolarProjection = (psImage->dfULY > 0)
1099
3
                                             ? pszNorthPolarProjection
1100
3
                                             : pszSouthPolarProjection;
1101
1102
3
        oSRS_AEQD.importFromWkt(pszPolarProjection);
1103
1104
3
        oSRS_WGS84.SetWellKnownGeogCS("WGS84");
1105
3
        oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1106
1107
3
        CPLPushErrorHandler(CPLQuietErrorHandler);
1108
3
        auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
1109
3
            OGRCreateCoordinateTransformation(&oSRS_WGS84, &oSRS_AEQD));
1110
3
        CPLPopErrorHandler();
1111
3
        if (poCT)
1112
3
        {
1113
3
            double dfULX_AEQD = psImage->dfULX;
1114
3
            double dfULY_AEQD = psImage->dfULY;
1115
3
            double dfURX_AEQD = psImage->dfURX;
1116
3
            double dfURY_AEQD = psImage->dfURY;
1117
3
            double dfLLX_AEQD = psImage->dfLLX;
1118
3
            double dfLLY_AEQD = psImage->dfLLY;
1119
3
            double dfLRX_AEQD = psImage->dfLRX;
1120
3
            double dfLRY_AEQD = psImage->dfLRY;
1121
3
            double z = 0;
1122
3
            int bSuccess = TRUE;
1123
3
            bSuccess &= poCT->Transform(1, &dfULX_AEQD, &dfULY_AEQD, &z);
1124
3
            bSuccess &= poCT->Transform(1, &dfURX_AEQD, &dfURY_AEQD, &z);
1125
3
            bSuccess &= poCT->Transform(1, &dfLLX_AEQD, &dfLLY_AEQD, &z);
1126
3
            bSuccess &= poCT->Transform(1, &dfLRX_AEQD, &dfLRY_AEQD, &z);
1127
3
            if (bSuccess)
1128
3
            {
1129
                /* Check that the coordinates of the 4 corners in Azimuthal
1130
                 * Equidistant projection */
1131
                /* are a rectangle */
1132
3
                if (fabs(dfULX_AEQD - dfLLX_AEQD) < 1e-6 * fabs(dfLLX_AEQD) &&
1133
2
                    fabs(dfURX_AEQD - dfLRX_AEQD) < 1e-6 * fabs(dfLRX_AEQD) &&
1134
2
                    fabs(dfULY_AEQD - dfURY_AEQD) < 1e-6 * fabs(dfURY_AEQD) &&
1135
2
                    fabs(dfLLY_AEQD - dfLRY_AEQD) < 1e-6 * fabs(dfLRY_AEQD))
1136
2
                {
1137
2
                    poDS->m_oSRS = std::move(oSRS_AEQD);
1138
1139
2
                    poDS->bGotGeoTransform = TRUE;
1140
2
                    poDS->m_gt.xorig = dfULX_AEQD;
1141
2
                    poDS->m_gt.xscale =
1142
2
                        (dfURX_AEQD - dfULX_AEQD) / poDS->nRasterXSize;
1143
2
                    poDS->m_gt.xrot = 0;
1144
2
                    poDS->m_gt.yorig = dfULY_AEQD;
1145
2
                    poDS->m_gt.yrot = 0;
1146
2
                    poDS->m_gt.yscale =
1147
2
                        (dfLLY_AEQD - dfULY_AEQD) / poDS->nRasterYSize;
1148
2
                }
1149
3
            }
1150
3
        }
1151
0
        else
1152
0
        {
1153
            // if we cannot instantiate the transformer, then we
1154
            // will at least attempt to record what we believe the
1155
            // natural coordinate system of the image is.  This is
1156
            // primarily used by ArcGIS (#3337)
1157
1158
0
            CPLErrorReset();
1159
1160
0
            CPLError(CE_Warning, CPLE_AppDefined,
1161
0
                     "Failed to instantiate coordinate system transformer, "
1162
0
                     "likely PROJ.DLL/libproj.so is not available.  Returning "
1163
0
                     "image corners as lat/long GCPs as a fallback.");
1164
1165
0
            char *pszAEQD = nullptr;
1166
0
            oSRS_AEQD.exportToWkt(&(pszAEQD));
1167
0
            poDS->SetMetadataItem("GCPPROJECTIONX", pszAEQD, "IMAGE_STRUCTURE");
1168
0
            CPLFree(pszAEQD);
1169
0
        }
1170
3
    }
1171
1172
    /* -------------------------------------------------------------------- */
1173
    /*      Do we have RPCs?                                                */
1174
    /* -------------------------------------------------------------------- */
1175
692
    bool bHasRPC00 = false;
1176
692
    NITFRPC00BInfo sRPCInfo;
1177
692
    memset(&sRPCInfo, 0,
1178
692
           sizeof(sRPCInfo)); /* To avoid warnings from not clever compilers */
1179
1180
692
    if (psImage && NITFReadRPC00B(psImage, &sRPCInfo) && sRPCInfo.SUCCESS)
1181
0
        bHasRPC00 = true;
1182
1183
    /* -------------------------------------------------------------------- */
1184
    /*      Do we have IGEOLO data that can be treated as a                 */
1185
    /*      geotransform?  Our approach should support images in an         */
1186
    /*      affine rotated frame of reference.                              */
1187
    /* -------------------------------------------------------------------- */
1188
692
    int nGCPCount = 0;
1189
692
    GDAL_GCP *psGCPs = nullptr;
1190
1191
692
    if (psImage && !poDS->bGotGeoTransform && psImage->chICORDS != ' ')
1192
272
    {
1193
272
        nGCPCount = 4;
1194
1195
272
        psGCPs = reinterpret_cast<GDAL_GCP *>(
1196
272
            CPLMalloc(sizeof(GDAL_GCP) * nGCPCount));
1197
272
        GDALInitGCPs(nGCPCount, psGCPs);
1198
1199
272
        if (psImage->bIsBoxCenterOfPixel)
1200
257
        {
1201
257
            psGCPs[0].dfGCPPixel = 0.5;
1202
257
            psGCPs[0].dfGCPLine = 0.5;
1203
257
            psGCPs[1].dfGCPPixel = poDS->nRasterXSize - 0.5;
1204
257
            psGCPs[1].dfGCPLine = 0.5;
1205
257
            psGCPs[2].dfGCPPixel = poDS->nRasterXSize - 0.5;
1206
257
            psGCPs[2].dfGCPLine = poDS->nRasterYSize - 0.5;
1207
257
            psGCPs[3].dfGCPPixel = 0.5;
1208
257
            psGCPs[3].dfGCPLine = poDS->nRasterYSize - 0.5;
1209
257
        }
1210
15
        else
1211
15
        {
1212
15
            psGCPs[0].dfGCPPixel = 0.0;
1213
15
            psGCPs[0].dfGCPLine = 0.0;
1214
15
            psGCPs[1].dfGCPPixel = poDS->nRasterXSize;
1215
15
            psGCPs[1].dfGCPLine = 0.0;
1216
15
            psGCPs[2].dfGCPPixel = poDS->nRasterXSize;
1217
15
            psGCPs[2].dfGCPLine = poDS->nRasterYSize;
1218
15
            psGCPs[3].dfGCPPixel = 0.0;
1219
15
            psGCPs[3].dfGCPLine = poDS->nRasterYSize;
1220
15
        }
1221
1222
272
        psGCPs[0].dfGCPX = psImage->dfULX;
1223
272
        psGCPs[0].dfGCPY = psImage->dfULY;
1224
1225
272
        psGCPs[1].dfGCPX = psImage->dfURX;
1226
272
        psGCPs[1].dfGCPY = psImage->dfURY;
1227
1228
272
        psGCPs[2].dfGCPX = psImage->dfLRX;
1229
272
        psGCPs[2].dfGCPY = psImage->dfLRY;
1230
1231
272
        psGCPs[3].dfGCPX = psImage->dfLLX;
1232
272
        psGCPs[3].dfGCPY = psImage->dfLLY;
1233
1234
/* -------------------------------------------------------------------- */
1235
/*      ESRI desires to use the RPCs to produce a denser and more       */
1236
/*      accurate set of GCPs in this case.  Details are unclear at      */
1237
/*      this time.                                                      */
1238
/* -------------------------------------------------------------------- */
1239
#ifdef ESRI_BUILD
1240
        if (bHasRPC00 &&
1241
            ((psImage->chICORDS == 'G') || (psImage->chICORDS == 'C')))
1242
        {
1243
            if (nGCPCount == 4)
1244
                NITFDensifyGCPs(&psGCPs, &nGCPCount);
1245
1246
            NITFUpdateGCPsWithRPC(&sRPCInfo, psGCPs, &nGCPCount);
1247
        }
1248
#endif /* def ESRI_BUILD */
1249
272
    }
1250
1251
    /* -------------------------------------------------------------------- */
1252
    /*      Convert the GCPs into a geotransform definition, if possible.   */
1253
    /* -------------------------------------------------------------------- */
1254
692
    if (!psImage)
1255
178
    {
1256
        /* nothing */
1257
178
    }
1258
514
    else if (poDS->bGotGeoTransform == FALSE && nGCPCount > 0 &&
1259
272
             GDALGCPsToGeoTransform(nGCPCount, psGCPs, poDS->m_gt.data(),
1260
272
                                    FALSE))
1261
23
    {
1262
23
        poDS->bGotGeoTransform = TRUE;
1263
23
    }
1264
1265
    /* -------------------------------------------------------------------- */
1266
    /*      If we have IGEOLO that isn't north up, return it as GCPs.       */
1267
    /* -------------------------------------------------------------------- */
1268
491
    else if ((psImage->dfULX != 0 || psImage->dfURX != 0 ||
1269
476
              psImage->dfLRX != 0 || psImage->dfLLX != 0) &&
1270
25
             psImage->chICORDS != ' ' && (poDS->bGotGeoTransform == FALSE) &&
1271
23
             nGCPCount >= 4)
1272
23
    {
1273
23
        CPLDebug("GDAL",
1274
23
                 "NITFDataset::Open() was not able to derive a first order\n"
1275
23
                 "geotransform.  It will be returned as GCPs.");
1276
1277
23
        poDS->nGCPCount = nGCPCount;
1278
23
        poDS->pasGCPList = psGCPs;
1279
1280
23
        psGCPs = nullptr;
1281
23
        nGCPCount = 0;
1282
1283
23
        CPLFree(poDS->pasGCPList[0].pszId);
1284
23
        poDS->pasGCPList[0].pszId = CPLStrdup("UpperLeft");
1285
1286
23
        CPLFree(poDS->pasGCPList[1].pszId);
1287
23
        poDS->pasGCPList[1].pszId = CPLStrdup("UpperRight");
1288
1289
23
        CPLFree(poDS->pasGCPList[2].pszId);
1290
23
        poDS->pasGCPList[2].pszId = CPLStrdup("LowerRight");
1291
1292
23
        CPLFree(poDS->pasGCPList[3].pszId);
1293
23
        poDS->pasGCPList[3].pszId = CPLStrdup("LowerLeft");
1294
1295
23
        poDS->m_oGCPSRS = poDS->m_oSRS;
1296
23
    }
1297
1298
    // This cleans up the original copy of the GCPs used to test if
1299
    // this IGEOLO could be used for a geotransform if we did not
1300
    // steal the to use as primary gcps.
1301
692
    if (nGCPCount > 0)
1302
249
    {
1303
249
        GDALDeinitGCPs(nGCPCount, psGCPs);
1304
249
        CPLFree(psGCPs);
1305
249
    }
1306
1307
    /* -------------------------------------------------------------------- */
1308
    /*      Do we have PRJPSB and MAPLOB TREs to get better                 */
1309
    /*      georeferencing from?                                            */
1310
    /* -------------------------------------------------------------------- */
1311
692
    if (psImage)
1312
514
        poDS->CheckGeoSDEInfo();
1313
1314
    /* -------------------------------------------------------------------- */
1315
    /*      Do we have metadata.                                            */
1316
    /* -------------------------------------------------------------------- */
1317
1318
    // File and Image level metadata.
1319
692
    char **papszMergedMD = CSLDuplicate(poDS->psFile->papszMetadata);
1320
1321
692
    if (psImage)
1322
514
    {
1323
514
        papszMergedMD = CSLInsertStrings(papszMergedMD, CSLCount(papszMergedMD),
1324
514
                                         psImage->papszMetadata);
1325
1326
        // Comments.
1327
514
        if (psImage->pszComments != nullptr &&
1328
514
            strlen(psImage->pszComments) != 0)
1329
319
            papszMergedMD = CSLSetNameValue(
1330
319
                papszMergedMD, "NITF_IMAGE_COMMENTS", psImage->pszComments);
1331
1332
        // Compression code.
1333
514
        papszMergedMD =
1334
514
            CSLSetNameValue(papszMergedMD, "NITF_IC", psImage->szIC);
1335
1336
        // IMODE
1337
514
        char szIMODE[2];
1338
514
        szIMODE[0] = psImage->chIMODE;
1339
514
        szIMODE[1] = '\0';
1340
514
        papszMergedMD = CSLSetNameValue(papszMergedMD, "NITF_IMODE", szIMODE);
1341
1342
        // ILOC/Attachment info
1343
514
        if (psImage->nIDLVL != 0)
1344
479
        {
1345
479
            NITFSegmentInfo *psSegInfo =
1346
479
                psFile->pasSegmentInfo + psImage->iSegment;
1347
1348
479
            papszMergedMD =
1349
479
                CSLSetNameValue(papszMergedMD, "NITF_IDLVL",
1350
479
                                CPLString().Printf("%d", psImage->nIDLVL));
1351
479
            papszMergedMD =
1352
479
                CSLSetNameValue(papszMergedMD, "NITF_IALVL",
1353
479
                                CPLString().Printf("%d", psImage->nIALVL));
1354
479
            papszMergedMD =
1355
479
                CSLSetNameValue(papszMergedMD, "NITF_ILOC_ROW",
1356
479
                                CPLString().Printf("%d", psImage->nILOCRow));
1357
479
            papszMergedMD =
1358
479
                CSLSetNameValue(papszMergedMD, "NITF_ILOC_COLUMN",
1359
479
                                CPLString().Printf("%d", psImage->nILOCColumn));
1360
479
            papszMergedMD =
1361
479
                CSLSetNameValue(papszMergedMD, "NITF_CCS_ROW",
1362
479
                                CPLString().Printf("%d", psSegInfo->nCCS_R));
1363
479
            papszMergedMD =
1364
479
                CSLSetNameValue(papszMergedMD, "NITF_CCS_COLUMN",
1365
479
                                CPLString().Printf("%d", psSegInfo->nCCS_C));
1366
479
            papszMergedMD =
1367
479
                CSLSetNameValue(papszMergedMD, "NITF_IMAG", psImage->szIMAG);
1368
479
        }
1369
1370
514
        papszMergedMD =
1371
514
            NITFGenericMetadataRead(papszMergedMD, psFile, psImage, nullptr);
1372
1373
        // BLOCKA
1374
514
        char **papszTRE_MD = NITFReadBLOCKA(psImage);
1375
514
        if (papszTRE_MD != nullptr)
1376
10
        {
1377
10
            papszMergedMD = CSLInsertStrings(
1378
10
                papszMergedMD, CSLCount(papszTRE_MD), papszTRE_MD);
1379
10
            CSLDestroy(papszTRE_MD);
1380
10
        }
1381
514
    }
1382
1383
#ifdef ESRI_BUILD
1384
    // Extract ESRI generic metadata.
1385
    char **papszESRI_MD = ExtractEsriMD(papszMergedMD);
1386
    if (papszESRI_MD != NULL)
1387
    {
1388
        papszMergedMD = CSLInsertStrings(papszMergedMD, CSLCount(papszESRI_MD),
1389
                                         papszESRI_MD);
1390
        CSLDestroy(papszESRI_MD);
1391
    }
1392
#endif
1393
1394
692
    poDS->SetMetadata(papszMergedMD);
1395
692
    CSLDestroy(papszMergedMD);
1396
1397
    /* -------------------------------------------------------------------- */
1398
    /*      Image structure metadata.                                       */
1399
    /* -------------------------------------------------------------------- */
1400
692
    if (psImage == nullptr)
1401
178
        /* do nothing */;
1402
514
    else if (psImage->szIC[1] == '1')
1403
121
        poDS->SetMetadataItem("COMPRESSION", "BILEVEL", "IMAGE_STRUCTURE");
1404
393
    else if (psImage->szIC[1] == '2')
1405
7
        poDS->SetMetadataItem("COMPRESSION", "ARIDPCM", "IMAGE_STRUCTURE");
1406
386
    else if (psImage->szIC[1] == '3')
1407
29
        poDS->SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
1408
357
    else if (psImage->szIC[1] == '4')
1409
26
        poDS->SetMetadataItem("COMPRESSION", "VECTOR QUANTIZATION",
1410
26
                              "IMAGE_STRUCTURE");
1411
331
    else if (psImage->szIC[1] == '5')
1412
0
        poDS->SetMetadataItem("COMPRESSION", "LOSSLESS JPEG",
1413
0
                              "IMAGE_STRUCTURE");
1414
331
    else if (psImage->szIC[1] == '8')
1415
20
        poDS->SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
1416
1417
    /* -------------------------------------------------------------------- */
1418
    /*      Do we have RPC info.                                            */
1419
    /* -------------------------------------------------------------------- */
1420
1421
    // get _rpc.txt file
1422
692
    const std::string osDirName = CPLGetDirnameSafe(pszFilename);
1423
692
    const std::string osBaseName = CPLGetBasenameSafe(pszFilename);
1424
692
    std::string osRPCTXTFilename = CPLFormFilenameSafe(
1425
692
        osDirName.c_str(), std::string(osBaseName).append("_rpc").c_str(),
1426
692
        "txt");
1427
692
    if (CPLCheckForFile(osRPCTXTFilename.data(), poOpenInfo->GetSiblingFiles()))
1428
0
    {
1429
0
        poDS->m_osRPCTXTFilename = osRPCTXTFilename;
1430
0
    }
1431
692
    else
1432
692
    {
1433
692
        osRPCTXTFilename = CPLFormFilenameSafe(
1434
692
            osDirName.c_str(), std::string(osBaseName).append("_RPC").c_str(),
1435
692
            "TXT");
1436
692
        CPL_IGNORE_RET_VAL(osBaseName);
1437
692
        if (CPLCheckForFile(osRPCTXTFilename.data(),
1438
692
                            poOpenInfo->GetSiblingFiles()))
1439
0
        {
1440
0
            poDS->m_osRPCTXTFilename = osRPCTXTFilename;
1441
0
        }
1442
692
    }
1443
692
    bool bHasLoadedRPCTXT = false;
1444
692
    if (!poDS->m_osRPCTXTFilename.empty())
1445
0
    {
1446
0
        char **papszMD = GDALLoadRPCFile(poDS->m_osRPCTXTFilename);
1447
0
        if (papszMD != nullptr)
1448
0
        {
1449
0
            bHasLoadedRPCTXT = true;
1450
0
            poDS->SetMetadata(papszMD, "RPC");
1451
0
            CSLDestroy(papszMD);
1452
0
        }
1453
0
        else
1454
0
        {
1455
0
            poDS->m_osRPCTXTFilename.clear();
1456
0
        }
1457
0
    }
1458
1459
692
    if (psImage && bHasRPC00 && !bHasLoadedRPCTXT)
1460
0
    {
1461
0
        char szValue[1280];
1462
1463
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.ERR_BIAS);
1464
0
        poDS->SetMetadataItem("ERR_BIAS", szValue, "RPC");
1465
1466
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.ERR_RAND);
1467
0
        poDS->SetMetadataItem("ERR_RAND", szValue, "RPC");
1468
1469
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LINE_OFF);
1470
0
        poDS->SetMetadataItem("LINE_OFF", szValue, "RPC");
1471
1472
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LINE_SCALE);
1473
0
        poDS->SetMetadataItem("LINE_SCALE", szValue, "RPC");
1474
1475
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.SAMP_OFF);
1476
0
        poDS->SetMetadataItem("SAMP_OFF", szValue, "RPC");
1477
1478
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.SAMP_SCALE);
1479
0
        poDS->SetMetadataItem("SAMP_SCALE", szValue, "RPC");
1480
1481
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LONG_OFF);
1482
0
        poDS->SetMetadataItem("LONG_OFF", szValue, "RPC");
1483
1484
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LONG_SCALE);
1485
0
        poDS->SetMetadataItem("LONG_SCALE", szValue, "RPC");
1486
1487
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LAT_OFF);
1488
0
        poDS->SetMetadataItem("LAT_OFF", szValue, "RPC");
1489
1490
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LAT_SCALE);
1491
0
        poDS->SetMetadataItem("LAT_SCALE", szValue, "RPC");
1492
1493
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.HEIGHT_OFF);
1494
0
        poDS->SetMetadataItem("HEIGHT_OFF", szValue, "RPC");
1495
1496
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.HEIGHT_SCALE);
1497
0
        poDS->SetMetadataItem("HEIGHT_SCALE", szValue, "RPC");
1498
1499
0
        szValue[0] = '\0';
1500
0
        for (int i = 0; i < 20; i++)
1501
0
            CPLsnprintf(szValue + strlen(szValue),
1502
0
                        sizeof(szValue) - strlen(szValue), "%.16g ",
1503
0
                        sRPCInfo.LINE_NUM_COEFF[i]);
1504
0
        poDS->SetMetadataItem("LINE_NUM_COEFF", szValue, "RPC");
1505
1506
0
        szValue[0] = '\0';
1507
0
        for (int i = 0; i < 20; i++)
1508
0
            CPLsnprintf(szValue + strlen(szValue),
1509
0
                        sizeof(szValue) - strlen(szValue), "%.16g ",
1510
0
                        sRPCInfo.LINE_DEN_COEFF[i]);
1511
0
        poDS->SetMetadataItem("LINE_DEN_COEFF", szValue, "RPC");
1512
1513
0
        szValue[0] = '\0';
1514
0
        for (int i = 0; i < 20; i++)
1515
0
            CPLsnprintf(szValue + strlen(szValue),
1516
0
                        sizeof(szValue) - strlen(szValue), "%.16g ",
1517
0
                        sRPCInfo.SAMP_NUM_COEFF[i]);
1518
0
        poDS->SetMetadataItem("SAMP_NUM_COEFF", szValue, "RPC");
1519
1520
0
        szValue[0] = '\0';
1521
0
        for (int i = 0; i < 20; i++)
1522
0
            CPLsnprintf(szValue + strlen(szValue),
1523
0
                        sizeof(szValue) - strlen(szValue), "%.16g ",
1524
0
                        sRPCInfo.SAMP_DEN_COEFF[i]);
1525
0
        poDS->SetMetadataItem("SAMP_DEN_COEFF", szValue, "RPC");
1526
1527
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g",
1528
0
                    sRPCInfo.LONG_OFF - sRPCInfo.LONG_SCALE);
1529
0
        poDS->SetMetadataItem("MIN_LONG", szValue, "RPC");
1530
1531
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g",
1532
0
                    sRPCInfo.LONG_OFF + sRPCInfo.LONG_SCALE);
1533
0
        poDS->SetMetadataItem("MAX_LONG", szValue, "RPC");
1534
1535
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g",
1536
0
                    sRPCInfo.LAT_OFF - sRPCInfo.LAT_SCALE);
1537
0
        poDS->SetMetadataItem("MIN_LAT", szValue, "RPC");
1538
1539
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g",
1540
0
                    sRPCInfo.LAT_OFF + sRPCInfo.LAT_SCALE);
1541
0
        poDS->SetMetadataItem("MAX_LAT", szValue, "RPC");
1542
0
    }
1543
1544
    /* -------------------------------------------------------------------- */
1545
    /*      Do we have Chip info?                                            */
1546
    /* -------------------------------------------------------------------- */
1547
692
    NITFICHIPBInfo sChipInfo;
1548
1549
692
    if (psImage && NITFReadICHIPB(psImage, &sChipInfo) &&
1550
0
        sChipInfo.XFRM_FLAG == 0)
1551
0
    {
1552
0
        char szValue[1280];
1553
1554
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.SCALE_FACTOR);
1555
0
        poDS->SetMetadataItem("ICHIP_SCALE_FACTOR", szValue);
1556
1557
        // TODO: Why do these two not use CPLsnprintf?
1558
0
        snprintf(szValue, sizeof(szValue), "%d", sChipInfo.ANAMORPH_CORR);
1559
0
        poDS->SetMetadataItem("ICHIP_ANAMORPH_CORR", szValue);
1560
1561
0
        snprintf(szValue, sizeof(szValue), "%d", sChipInfo.SCANBLK_NUM);
1562
0
        poDS->SetMetadataItem("ICHIP_SCANBLK_NUM", szValue);
1563
1564
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_ROW_11);
1565
0
        poDS->SetMetadataItem("ICHIP_OP_ROW_11", szValue);
1566
1567
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_COL_11);
1568
0
        poDS->SetMetadataItem("ICHIP_OP_COL_11", szValue);
1569
1570
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_ROW_12);
1571
0
        poDS->SetMetadataItem("ICHIP_OP_ROW_12", szValue);
1572
1573
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_COL_12);
1574
0
        poDS->SetMetadataItem("ICHIP_OP_COL_12", szValue);
1575
1576
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_ROW_21);
1577
0
        poDS->SetMetadataItem("ICHIP_OP_ROW_21", szValue);
1578
1579
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_COL_21);
1580
0
        poDS->SetMetadataItem("ICHIP_OP_COL_21", szValue);
1581
1582
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_ROW_22);
1583
0
        poDS->SetMetadataItem("ICHIP_OP_ROW_22", szValue);
1584
1585
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_COL_22);
1586
0
        poDS->SetMetadataItem("ICHIP_OP_COL_22", szValue);
1587
1588
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_ROW_11);
1589
0
        poDS->SetMetadataItem("ICHIP_FI_ROW_11", szValue);
1590
1591
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_COL_11);
1592
0
        poDS->SetMetadataItem("ICHIP_FI_COL_11", szValue);
1593
1594
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_ROW_12);
1595
0
        poDS->SetMetadataItem("ICHIP_FI_ROW_12", szValue);
1596
1597
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_COL_12);
1598
0
        poDS->SetMetadataItem("ICHIP_FI_COL_12", szValue);
1599
1600
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_ROW_21);
1601
0
        poDS->SetMetadataItem("ICHIP_FI_ROW_21", szValue);
1602
1603
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_COL_21);
1604
0
        poDS->SetMetadataItem("ICHIP_FI_COL_21", szValue);
1605
1606
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_ROW_22);
1607
0
        poDS->SetMetadataItem("ICHIP_FI_ROW_22", szValue);
1608
1609
0
        CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_COL_22);
1610
0
        poDS->SetMetadataItem("ICHIP_FI_COL_22", szValue);
1611
1612
        // Why not CPLsnprintf?
1613
0
        snprintf(szValue, sizeof(szValue), "%d", sChipInfo.FI_ROW);
1614
0
        poDS->SetMetadataItem("ICHIP_FI_ROW", szValue);
1615
1616
0
        snprintf(szValue, sizeof(szValue), "%d", sChipInfo.FI_COL);
1617
0
        poDS->SetMetadataItem("ICHIP_FI_COL", szValue);
1618
0
    }
1619
1620
692
    const NITFSeries *series = NITFGetSeriesInfo(pszFilename);
1621
692
    if (series)
1622
14
    {
1623
14
        poDS->SetMetadataItem("NITF_SERIES_ABBREVIATION",
1624
14
                              (series->abbreviation) ? series->abbreviation
1625
14
                                                     : "Unknown");
1626
14
        poDS->SetMetadataItem("NITF_SERIES_NAME",
1627
14
                              (series->name) ? series->name : "Unknown");
1628
14
    }
1629
1630
    /* -------------------------------------------------------------------- */
1631
    /*      If there are multiple image segments, and no specific one is    */
1632
    /*      asker for, then setup the subdataset metadata.                  */
1633
    /* -------------------------------------------------------------------- */
1634
692
    int nSubDSCount = 0;
1635
1636
692
    {
1637
692
        char **papszSubdatasets = nullptr;
1638
1639
106k
        for (iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
1640
105k
        {
1641
105k
            if (EQUAL(psFile->pasSegmentInfo[iSegment].szSegmentType, "IM"))
1642
53.2k
            {
1643
53.2k
                CPLString oName;
1644
53.2k
                CPLString oValue;
1645
1646
53.2k
                if (nIMIndex == -1)
1647
53.2k
                {
1648
53.2k
                    oName.Printf("SUBDATASET_%d_NAME", nSubDSCount + 1);
1649
53.2k
                    oValue.Printf("NITF_IM:%d:%s", nSubDSCount, pszFilename);
1650
53.2k
                    papszSubdatasets =
1651
53.2k
                        CSLSetNameValue(papszSubdatasets, oName, oValue);
1652
1653
53.2k
                    oName.Printf("SUBDATASET_%d_DESC", nSubDSCount + 1);
1654
53.2k
                    oValue.Printf("Image %d of %s", nSubDSCount + 1,
1655
53.2k
                                  pszFilename);
1656
53.2k
                    papszSubdatasets =
1657
53.2k
                        CSLSetNameValue(papszSubdatasets, oName, oValue);
1658
53.2k
                }
1659
1660
53.2k
                nSubDSCount++;
1661
53.2k
            }
1662
105k
        }
1663
1664
692
        if (nIMIndex == -1 && nSubDSCount > 1)
1665
384
        {
1666
384
            poDS->GDALMajorObject::SetMetadata(papszSubdatasets, "SUBDATASETS");
1667
384
        }
1668
1669
692
        CSLDestroy(papszSubdatasets);
1670
692
    }
1671
1672
    /* -------------------------------------------------------------------- */
1673
    /*      Initialize any PAM information.                                 */
1674
    /* -------------------------------------------------------------------- */
1675
692
    poDS->SetDescription(poOpenInfo->pszFilename);
1676
692
    poDS->SetPhysicalFilename(pszFilename);
1677
1678
692
    if (nSubDSCount > 1 || nIMIndex != -1)
1679
384
    {
1680
384
        if (nIMIndex == -1)
1681
384
        {
1682
384
            nIMIndex = 0;
1683
384
        }
1684
0
        else if (nIMIndex == 0 && nSubDSCount == 1)
1685
0
        {
1686
            // If subdataset 0 is explicitly specified, and there's a single
1687
            // subdataset, and that PAM .aux.xml doesn't have a Subdataset node,
1688
            // then don't set the subdataset name to get metadata from the
1689
            // top PAM node.
1690
0
            const char *pszPAMFilename = poDS->BuildPamFilename();
1691
0
            VSIStatBufL sStatBuf;
1692
0
            if (pszPAMFilename != nullptr &&
1693
0
                VSIStatExL(pszPAMFilename, &sStatBuf,
1694
0
                           VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
1695
0
                VSI_ISREG(sStatBuf.st_mode))
1696
0
            {
1697
0
                CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1698
0
                CPLXMLNode *psTree = CPLParseXMLFile(pszPAMFilename);
1699
0
                if (psTree)
1700
0
                {
1701
0
                    if (CPLGetXMLNode(psTree, "=PAMDataset.Subdataset") ==
1702
0
                        nullptr)
1703
0
                    {
1704
0
                        nIMIndex = -1;
1705
0
                    }
1706
0
                }
1707
0
                CPLDestroyXMLNode(psTree);
1708
0
            }
1709
0
        }
1710
1711
384
        if (nIMIndex >= 0)
1712
384
        {
1713
384
            poDS->SetSubdatasetName(CPLString().Printf("%d", nIMIndex));
1714
384
        }
1715
384
    }
1716
308
    else if (/* nIMIndex == -1 && */ nSubDSCount == 1)
1717
130
    {
1718
        // GDAL 3.4.0 to 3.5.0 used to save the PAM metadata if a Subdataset
1719
        // node, even if there was one single subdataset.
1720
        // Detect that situation to automatically read it even if not explicitly
1721
        // specifying that single subdataset.
1722
130
        const char *pszPAMFilename = poDS->BuildPamFilename();
1723
130
        VSIStatBufL sStatBuf;
1724
130
        if (pszPAMFilename != nullptr &&
1725
130
            VSIStatExL(pszPAMFilename, &sStatBuf,
1726
130
                       VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
1727
73
            VSI_ISREG(sStatBuf.st_mode))
1728
73
        {
1729
73
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1730
73
            CPLXMLNode *psTree = CPLParseXMLFile(pszPAMFilename);
1731
73
            if (psTree)
1732
73
            {
1733
73
                const auto psSubdatasetNode =
1734
73
                    CPLGetXMLNode(psTree, "=PAMDataset.Subdataset");
1735
73
                if (psSubdatasetNode != nullptr &&
1736
0
                    strcmp(CPLGetXMLValue(psSubdatasetNode, "name", ""), "0") ==
1737
0
                        0)
1738
0
                {
1739
0
                    poDS->SetSubdatasetName("0");
1740
0
                    poDS->SetPhysicalFilename(pszFilename);
1741
0
                }
1742
73
                CPLDestroyXMLNode(psTree);
1743
73
            }
1744
73
        }
1745
130
    }
1746
1747
692
    poDS->bInLoadXML = TRUE;
1748
692
    poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
1749
692
    poDS->bInLoadXML = FALSE;
1750
1751
    /* -------------------------------------------------------------------- */
1752
    /*      Do we have a special overview file?  If not, do we have         */
1753
    /*      RSets that should be treated as an overview file?               */
1754
    /* -------------------------------------------------------------------- */
1755
692
    const char *pszOverviewFile =
1756
692
        poDS->GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS");
1757
1758
692
    if (pszOverviewFile == nullptr)
1759
692
    {
1760
692
        if (poDS->CheckForRSets(pszFilename, poOpenInfo->GetSiblingFiles()))
1761
0
            pszOverviewFile = poDS->osRSetVRT;
1762
692
    }
1763
1764
    /* -------------------------------------------------------------------- */
1765
    /*      If we have jpeg or jpeg2000 bands we may need to set the        */
1766
    /*      overview file on their dataset. (#3276)                         */
1767
    /* -------------------------------------------------------------------- */
1768
692
    GDALDataset *poSubDS = poDS->poJ2KDataset.get();
1769
692
    if (poDS->poJPEGDataset)
1770
14
        poSubDS = poDS->poJPEGDataset.get();
1771
1772
692
    if (poSubDS && pszOverviewFile != nullptr)
1773
0
    {
1774
0
        poSubDS->SetMetadataItem("OVERVIEW_FILE", pszOverviewFile, "OVERVIEWS");
1775
0
    }
1776
1777
    /* -------------------------------------------------------------------- */
1778
    /*      If we have jpeg, or jpeg2000 bands we may need to clear         */
1779
    /*      their PAM dirty flag too.                                       */
1780
    /* -------------------------------------------------------------------- */
1781
692
    if (poDS->poJ2KDataset != nullptr &&
1782
0
        (poDS->poJ2KDataset->GetMOFlags() & GMO_PAM_CLASS))
1783
0
        (cpl::down_cast<GDALPamDataset *>(poDS->poJ2KDataset.get()))
1784
0
            ->SetPamFlags(
1785
0
                (cpl::down_cast<GDALPamDataset *>(poDS->poJ2KDataset.get()))
1786
0
                    ->GetPamFlags() &
1787
0
                ~GPF_DIRTY);
1788
692
    if (poDS->poJPEGDataset != nullptr &&
1789
14
        (poDS->poJPEGDataset->GetMOFlags() & GMO_PAM_CLASS))
1790
14
        (cpl::down_cast<GDALPamDataset *>(poDS->poJPEGDataset.get()))
1791
14
            ->SetPamFlags(
1792
14
                (cpl::down_cast<GDALPamDataset *>(poDS->poJPEGDataset.get()))
1793
14
                    ->GetPamFlags() &
1794
14
                ~GPF_DIRTY);
1795
1796
    /* -------------------------------------------------------------------- */
1797
    /*      Check for overviews.                                            */
1798
    /* -------------------------------------------------------------------- */
1799
692
    if (!EQUAL(poOpenInfo->pszFilename, pszFilename))
1800
0
        poDS->oOvManager.Initialize(poDS, ":::VIRTUAL:::");
1801
692
    else
1802
692
        poDS->oOvManager.Initialize(poDS, pszFilename,
1803
692
                                    poOpenInfo->GetSiblingFiles());
1804
1805
    /* If there are PAM overviews, don't expose the underlying JPEG dataset */
1806
    /* overviews (in case of monoblock C3) */
1807
692
    if (poDS->GetRasterCount() > 0 && poDS->GetRasterBand(1) != nullptr)
1808
514
        poDS->bExposeUnderlyingJPEGDatasetOverviews =
1809
514
            (reinterpret_cast<GDALPamRasterBand *>(poDS->GetRasterBand(1)))
1810
514
                ->GDALPamRasterBand::GetOverviewCount() == 0;
1811
1812
692
    if (CPLFetchBool(poOpenInfo->papszOpenOptions, "VALIDATE", false))
1813
0
    {
1814
0
        if (!poDS->Validate() &&
1815
0
            CPLFetchBool(poOpenInfo->papszOpenOptions,
1816
0
                         "FAIL_IF_VALIDATION_ERROR", false))
1817
0
        {
1818
0
            delete poDS;
1819
0
            poDS = nullptr;
1820
0
        }
1821
0
    }
1822
1823
692
    return poDS;
1824
692
}
1825
1826
/************************************************************************/
1827
/*                              Validate()                              */
1828
/************************************************************************/
1829
1830
bool NITFDataset::Validate()
1831
0
{
1832
0
    bool bSuccess = InitializeTREMetadata(true);
1833
0
    if (!InitializeNITFDESs(true))
1834
0
        bSuccess = false;
1835
0
    return bSuccess;
1836
0
}
1837
1838
/************************************************************************/
1839
/*                            LoadDODDatum()                            */
1840
/*                                                                      */
1841
/*      Try to turn a US military datum name into a datum definition.   */
1842
/************************************************************************/
1843
1844
static OGRErr LoadDODDatum(OGRSpatialReference *poSRS, const char *pszDatumName)
1845
1846
0
{
1847
    /* -------------------------------------------------------------------- */
1848
    /*      The most common case...                                         */
1849
    /* -------------------------------------------------------------------- */
1850
0
    if (STARTS_WITH_CI(pszDatumName, "WGE "))
1851
0
    {
1852
0
        poSRS->SetWellKnownGeogCS("WGS84");
1853
0
        return OGRERR_NONE;
1854
0
    }
1855
1856
#if defined(USE_ONLY_EMBEDDED_RESOURCE_FILES) && !defined(EMBED_RESOURCE_FILES)
1857
    return OGRERR_FAILURE;
1858
#else
1859
1860
    /* -------------------------------------------------------------------- */
1861
    /*      All the rest we will try and load from gt_datum.csv             */
1862
    /*      (Geotrans datum file).                                          */
1863
    /* -------------------------------------------------------------------- */
1864
0
    char szExpanded[6];
1865
0
    const char *pszGTDatum = nullptr;
1866
0
    CPL_IGNORE_RET_VAL(pszGTDatum);
1867
0
#ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
1868
0
    pszGTDatum = CSVFilename("gt_datum.csv");
1869
0
#endif
1870
0
#ifdef EMBED_RESOURCE_FILES
1871
0
    std::string osTmpFilename;
1872
    // CSVFilename() returns the same content as pszFilename if it does not
1873
    // find the file.
1874
0
    if (!pszGTDatum || strcmp(pszGTDatum, "gt_datum.csv") == 0)
1875
0
    {
1876
0
        osTmpFilename = VSIMemGenerateHiddenFilename("gt_datum.csv");
1877
0
        const char *pszFileContent = NITFGetGTDatum();
1878
0
        VSIFCloseL(VSIFileFromMemBuffer(
1879
0
            osTmpFilename.c_str(),
1880
0
            const_cast<GByte *>(
1881
0
                reinterpret_cast<const GByte *>(pszFileContent)),
1882
0
            static_cast<int>(strlen(pszFileContent)),
1883
0
            /* bTakeOwnership = */ false));
1884
0
        pszGTDatum = osTmpFilename.c_str();
1885
0
    }
1886
0
#endif
1887
1888
0
    strncpy(szExpanded, pszDatumName, 3);
1889
0
    szExpanded[3] = '\0';
1890
0
    if (pszDatumName[3] != ' ')
1891
0
    {
1892
0
        size_t nLen;
1893
0
        strcat(szExpanded, "-");
1894
0
        nLen = strlen(szExpanded);
1895
0
        szExpanded[nLen] = pszDatumName[3];
1896
0
        szExpanded[nLen + 1] = '\0';
1897
0
    }
1898
1899
0
    CPLString osDName =
1900
0
        CSVGetField(pszGTDatum, "CODE", szExpanded, CC_ApproxString, "NAME");
1901
0
    if (osDName.empty())
1902
0
    {
1903
0
        CPLError(CE_Failure, CPLE_AppDefined,
1904
0
                 "Failed to find datum %s/%s in gt_datum.csv.", pszDatumName,
1905
0
                 szExpanded);
1906
1907
0
#ifdef EMBED_RESOURCE_FILES
1908
0
        if (!osTmpFilename.empty())
1909
0
        {
1910
0
            CSVDeaccess(osTmpFilename.c_str());
1911
0
            VSIUnlink(osTmpFilename.c_str());
1912
0
        }
1913
0
#endif
1914
0
        return OGRERR_FAILURE;
1915
0
    }
1916
1917
0
    CPLString osEllipseCode = CSVGetField(pszGTDatum, "CODE", szExpanded,
1918
0
                                          CC_ApproxString, "ELLIPSOID");
1919
0
    double dfDeltaX = CPLAtof(
1920
0
        CSVGetField(pszGTDatum, "CODE", szExpanded, CC_ApproxString, "DELTAX"));
1921
0
    double dfDeltaY = CPLAtof(
1922
0
        CSVGetField(pszGTDatum, "CODE", szExpanded, CC_ApproxString, "DELTAY"));
1923
0
    double dfDeltaZ = CPLAtof(
1924
0
        CSVGetField(pszGTDatum, "CODE", szExpanded, CC_ApproxString, "DELTAZ"));
1925
1926
0
#ifdef EMBED_RESOURCE_FILES
1927
0
    if (!osTmpFilename.empty())
1928
0
    {
1929
0
        CSVDeaccess(osTmpFilename.c_str());
1930
0
        VSIUnlink(osTmpFilename.c_str());
1931
0
        osTmpFilename.clear();
1932
0
    }
1933
0
#endif
1934
1935
    /* -------------------------------------------------------------------- */
1936
    /*      Lookup the ellipse code.                                        */
1937
    /* -------------------------------------------------------------------- */
1938
0
    const char *pszGTEllipse = nullptr;
1939
0
    CPL_IGNORE_RET_VAL(pszGTEllipse);
1940
0
#ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
1941
0
    pszGTEllipse = CSVFilename("gt_ellips.csv");
1942
0
#endif
1943
1944
0
#ifdef EMBED_RESOURCE_FILES
1945
    // CSVFilename() returns the same content as pszFilename if it does not
1946
    // find the file.
1947
0
    if (!pszGTEllipse || strcmp(pszGTEllipse, "gt_ellips.csv") == 0)
1948
0
    {
1949
0
        osTmpFilename = VSIMemGenerateHiddenFilename("gt_ellips");
1950
0
        const char *pszFileContent = NITFGetGTEllips();
1951
0
        VSIFCloseL(VSIFileFromMemBuffer(
1952
0
            osTmpFilename.c_str(),
1953
0
            const_cast<GByte *>(
1954
0
                reinterpret_cast<const GByte *>(pszFileContent)),
1955
0
            static_cast<int>(strlen(pszFileContent)),
1956
0
            /* bTakeOwnership = */ false));
1957
0
        pszGTEllipse = osTmpFilename.c_str();
1958
0
    }
1959
0
#endif
1960
1961
0
    CPLString osEName = CSVGetField(pszGTEllipse, "CODE", osEllipseCode,
1962
0
                                    CC_ApproxString, "NAME");
1963
0
    osEName = osEName.Trim();
1964
0
    if (osEName.empty())
1965
0
    {
1966
0
        CPLError(CE_Failure, CPLE_AppDefined,
1967
0
                 "Failed to find datum %s in gt_ellips.csv.",
1968
0
                 osEllipseCode.c_str());
1969
1970
0
#ifdef EMBED_RESOURCE_FILES
1971
0
        if (!osTmpFilename.empty())
1972
0
        {
1973
0
            CSVDeaccess(osTmpFilename.c_str());
1974
0
            VSIUnlink(osTmpFilename.c_str());
1975
0
        }
1976
0
#endif
1977
0
        return OGRERR_FAILURE;
1978
0
    }
1979
1980
0
    double dfA = CPLAtof(
1981
0
        CSVGetField(pszGTEllipse, "CODE", osEllipseCode, CC_ApproxString, "A"));
1982
0
    double dfInvF = CPLAtof(CSVGetField(pszGTEllipse, "CODE", osEllipseCode,
1983
0
                                        CC_ApproxString, "RF"));
1984
1985
    /* -------------------------------------------------------------------- */
1986
    /*      Create geographic coordinate system.                            */
1987
    /* -------------------------------------------------------------------- */
1988
0
    poSRS->SetGeogCS(osDName, osDName, osEName, dfA, dfInvF);
1989
1990
0
    poSRS->SetTOWGS84(dfDeltaX, dfDeltaY, dfDeltaZ);
1991
1992
0
#ifdef EMBED_RESOURCE_FILES
1993
0
    if (!osTmpFilename.empty())
1994
0
    {
1995
0
        CSVDeaccess(osTmpFilename.c_str());
1996
0
        VSIUnlink(osTmpFilename.c_str());
1997
0
        osTmpFilename.clear();
1998
0
    }
1999
0
#endif
2000
2001
0
    return OGRERR_NONE;
2002
0
#endif
2003
0
}
2004
2005
/************************************************************************/
2006
/*                          CheckGeoSDEInfo()                           */
2007
/*                                                                      */
2008
/*      Check for GeoSDE TREs (GEOPSB/PRJPSB and MAPLOB).  If we        */
2009
/*      have them, use them to override our coordinate system and       */
2010
/*      geotransform info.                                              */
2011
/************************************************************************/
2012
2013
void NITFDataset::CheckGeoSDEInfo()
2014
2015
514
{
2016
514
    if (!psImage)
2017
0
        return;
2018
2019
    /* -------------------------------------------------------------------- */
2020
    /*      Do we have the required TREs?                                   */
2021
    /* -------------------------------------------------------------------- */
2022
514
    int nGEOPSBSize, nPRJPSBSize, nMAPLOBSize;
2023
2024
514
    const char *pszGEOPSB =
2025
514
        NITFFindTRE(psFile->pachTRE, psFile->nTREBytes, "GEOPSB", &nGEOPSBSize);
2026
514
    const char *pszPRJPSB =
2027
514
        NITFFindTRE(psFile->pachTRE, psFile->nTREBytes, "PRJPSB", &nPRJPSBSize);
2028
514
    const char *pszMAPLOB = NITFFindTRE(psImage->pachTRE, psImage->nTREBytes,
2029
514
                                        "MAPLOB", &nMAPLOBSize);
2030
2031
514
    if (pszGEOPSB == nullptr || pszPRJPSB == nullptr || pszMAPLOB == nullptr)
2032
514
        return;
2033
2034
    /* -------------------------------------------------------------------- */
2035
    /*      Collect projection parameters.                                  */
2036
    /* -------------------------------------------------------------------- */
2037
2038
0
    char szParam[16];
2039
0
    if (nPRJPSBSize < 82 + 1)
2040
0
    {
2041
0
        CPLError(CE_Failure, CPLE_AppDefined,
2042
0
                 "Cannot read PRJPSB TRE. Not enough bytes");
2043
0
        return;
2044
0
    }
2045
0
    const int nParamCount = atoi(NITFGetField(szParam, pszPRJPSB, 82, 1));
2046
0
    if (nPRJPSBSize < 83 + 15 * nParamCount + 15 + 15)
2047
0
    {
2048
0
        CPLError(CE_Failure, CPLE_AppDefined,
2049
0
                 "Cannot read PRJPSB TRE. Not enough bytes");
2050
0
        return;
2051
0
    }
2052
2053
0
    double adfParam[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
2054
0
    for (int i = 0; i < nParamCount; i++)
2055
0
        adfParam[i] =
2056
0
            CPLAtof(NITFGetField(szParam, pszPRJPSB, 83 + 15 * i, 15));
2057
2058
0
    const double dfFE =
2059
0
        CPLAtof(NITFGetField(szParam, pszPRJPSB, 83 + 15 * nParamCount, 15));
2060
0
    const double dfFN = CPLAtof(
2061
0
        NITFGetField(szParam, pszPRJPSB, 83 + 15 * nParamCount + 15, 15));
2062
2063
    /* -------------------------------------------------------------------- */
2064
    /*      Try to handle the projection.                                   */
2065
    /* -------------------------------------------------------------------- */
2066
0
    OGRSpatialReference oSRS;
2067
0
    oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2068
2069
0
    if (STARTS_WITH_CI(pszPRJPSB + 80, "AC"))
2070
0
        oSRS.SetACEA(adfParam[1], adfParam[2], adfParam[3], adfParam[0], dfFE,
2071
0
                     dfFN);
2072
2073
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "AK"))
2074
0
        oSRS.SetLAEA(adfParam[1], adfParam[0], dfFE, dfFN);
2075
2076
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "AL"))
2077
0
        oSRS.SetAE(adfParam[1], adfParam[0], dfFE, dfFN);
2078
2079
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "BF"))
2080
0
        oSRS.SetBonne(adfParam[1], adfParam[0], dfFE, dfFN);
2081
2082
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "CP"))
2083
0
        oSRS.SetEquirectangular(adfParam[1], adfParam[0], dfFE, dfFN);
2084
2085
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "CS"))
2086
0
        oSRS.SetCS(adfParam[1], adfParam[0], dfFE, dfFN);
2087
2088
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "EF"))
2089
0
        oSRS.SetEckertIV(adfParam[0], dfFE, dfFN);
2090
2091
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "ED"))
2092
0
        oSRS.SetEckertVI(adfParam[0], dfFE, dfFN);
2093
2094
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "GN"))
2095
0
        oSRS.SetGnomonic(adfParam[1], adfParam[0], dfFE, dfFN);
2096
2097
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "HX"))
2098
0
        oSRS.SetHOM2PNO(adfParam[1], adfParam[3], adfParam[2], adfParam[5],
2099
0
                        adfParam[4], adfParam[0], dfFE, dfFN);
2100
2101
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "KA"))
2102
0
        oSRS.SetEC(adfParam[1], adfParam[2], adfParam[3], adfParam[0], dfFE,
2103
0
                   dfFN);
2104
2105
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "LE"))
2106
0
        oSRS.SetLCC(adfParam[1], adfParam[2], adfParam[3], adfParam[0], dfFE,
2107
0
                    dfFN);
2108
2109
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "LI"))
2110
0
        oSRS.SetCEA(adfParam[1], adfParam[0], dfFE, dfFN);
2111
2112
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "MC"))
2113
0
        oSRS.SetMercator(adfParam[2], adfParam[1], 1.0, dfFE, dfFN);
2114
2115
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "MH"))
2116
0
        oSRS.SetMC(0.0, adfParam[1], dfFE, dfFN);
2117
2118
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "MP"))
2119
0
        oSRS.SetMollweide(adfParam[0], dfFE, dfFN);
2120
2121
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "NT"))
2122
0
        oSRS.SetNZMG(adfParam[1], adfParam[0], dfFE, dfFN);
2123
2124
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "OD"))
2125
0
        oSRS.SetOrthographic(adfParam[1], adfParam[0], dfFE, dfFN);
2126
2127
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "PC"))
2128
0
        oSRS.SetPolyconic(adfParam[1], adfParam[0], dfFE, dfFN);
2129
2130
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "PG"))
2131
0
        oSRS.SetPS(adfParam[1], adfParam[0], 1.0, dfFE, dfFN);
2132
2133
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "RX"))
2134
0
        oSRS.SetRobinson(adfParam[0], dfFE, dfFN);
2135
2136
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "SA"))
2137
0
        oSRS.SetSinusoidal(adfParam[0], dfFE, dfFN);
2138
2139
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "TC"))
2140
0
        oSRS.SetTM(adfParam[2], adfParam[0], adfParam[1], dfFE, dfFN);
2141
2142
0
    else if (STARTS_WITH_CI(pszPRJPSB + 80, "VA"))
2143
0
        oSRS.SetVDG(adfParam[0], dfFE, dfFN);
2144
2145
0
    else
2146
0
    {
2147
0
        char szName[81];
2148
0
        oSRS.SetLocalCS(NITFGetField(szName, pszPRJPSB, 0, 80));
2149
0
    }
2150
2151
    /* -------------------------------------------------------------------- */
2152
    /*      Try to apply the datum.                                         */
2153
    /* -------------------------------------------------------------------- */
2154
0
    if (nGEOPSBSize < 86 + 4)
2155
0
    {
2156
0
        CPLError(CE_Failure, CPLE_AppDefined,
2157
0
                 "Cannot read GEOPSB TRE. Not enough bytes");
2158
0
        return;
2159
0
    }
2160
0
    LoadDODDatum(&oSRS, NITFGetField(szParam, pszGEOPSB, 86, 4));
2161
2162
    /* -------------------------------------------------------------------- */
2163
    /*      Get the geotransform                                            */
2164
    /* -------------------------------------------------------------------- */
2165
0
    if (nMAPLOBSize < 28 + 15)
2166
0
    {
2167
0
        CPLError(CE_Failure, CPLE_AppDefined,
2168
0
                 "Cannot read MAPLOB TRE. Not enough bytes");
2169
0
        return;
2170
0
    }
2171
2172
0
    double dfMeterPerUnit = 1.0;
2173
0
    if (STARTS_WITH_CI(pszMAPLOB + 0, "DM "))
2174
0
        dfMeterPerUnit = 0.1;
2175
0
    else if (STARTS_WITH_CI(pszMAPLOB + 0, "CM "))
2176
0
        dfMeterPerUnit = 0.01;
2177
0
    else if (STARTS_WITH_CI(pszMAPLOB + 0, "MM "))
2178
0
        dfMeterPerUnit = 0.001;
2179
0
    else if (STARTS_WITH_CI(pszMAPLOB + 0, "UM "))
2180
0
        dfMeterPerUnit = 0.000001;
2181
0
    else if (STARTS_WITH_CI(pszMAPLOB + 0, "KM "))
2182
0
        dfMeterPerUnit = 1000.0;
2183
0
    else if (STARTS_WITH_CI(pszMAPLOB + 0, "M  "))
2184
0
        dfMeterPerUnit = 1.0;
2185
0
    else
2186
0
    {
2187
0
        CPLError(CE_Warning, CPLE_AppDefined,
2188
0
                 "MAPLOB Unit=%3.3s not recognized, geolocation may be wrong.",
2189
0
                 pszMAPLOB + 0);
2190
0
    }
2191
2192
0
    m_gt.xorig = CPLAtof(NITFGetField(szParam, pszMAPLOB, 13, 15));
2193
0
    m_gt.xscale =
2194
0
        CPLAtof(NITFGetField(szParam, pszMAPLOB, 3, 5)) * dfMeterPerUnit;
2195
0
    m_gt.xrot = 0.0;
2196
0
    m_gt.yorig = CPLAtof(NITFGetField(szParam, pszMAPLOB, 28, 15));
2197
0
    m_gt.yrot = 0.0;
2198
0
    m_gt.yscale =
2199
0
        -CPLAtof(NITFGetField(szParam, pszMAPLOB, 8, 5)) * dfMeterPerUnit;
2200
2201
0
    m_oSRS = std::move(oSRS);
2202
2203
0
    bGotGeoTransform = TRUE;
2204
0
}
2205
2206
/************************************************************************/
2207
/*                             AdviseRead()                             */
2208
/************************************************************************/
2209
2210
CPLErr NITFDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
2211
                               int nBufXSize, int nBufYSize, GDALDataType eDT,
2212
                               int nBandCount, int *panBandList,
2213
                               CSLConstList papszOptions)
2214
2215
8
{
2216
    //go through GDALDataset::AdviseRead for the complex SAR
2217
8
    if (poJ2KDataset == nullptr || m_bHasComplexRasterBand)
2218
8
        return GDALDataset::AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize,
2219
8
                                       nBufYSize, eDT, nBandCount, panBandList,
2220
8
                                       papszOptions);
2221
0
    else if (poJPEGDataset != nullptr)
2222
0
        return poJPEGDataset->AdviseRead(nXOff, nYOff, nXSize, nYSize,
2223
0
                                         nBufXSize, nBufYSize, eDT, nBandCount,
2224
0
                                         panBandList, papszOptions);
2225
0
    else
2226
0
        return poJ2KDataset->AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize,
2227
0
                                        nBufYSize, eDT, nBandCount, panBandList,
2228
0
                                        papszOptions);
2229
8
}
2230
2231
/************************************************************************/
2232
/*                             IRasterIO()                              */
2233
/************************************************************************/
2234
2235
CPLErr NITFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2236
                              int nXSize, int nYSize, void *pData,
2237
                              int nBufXSize, int nBufYSize,
2238
                              GDALDataType eBufType, int nBandCount,
2239
                              BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
2240
                              GSpacing nLineSpace, GSpacing nBandSpace,
2241
                              GDALRasterIOExtraArg *psExtraArg)
2242
2243
0
{
2244
    //go through GDALDataset::IRasterIO for the complex SAR
2245
0
    if (poJ2KDataset != nullptr && !m_bHasComplexRasterBand)
2246
0
        return poJ2KDataset->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2247
0
                                      pData, nBufXSize, nBufYSize, eBufType,
2248
0
                                      nBandCount, panBandMap, nPixelSpace,
2249
0
                                      nLineSpace, nBandSpace, psExtraArg);
2250
0
    else if (poJPEGDataset != nullptr && !m_bHasComplexRasterBand)
2251
0
        return poJPEGDataset->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2252
0
                                       pData, nBufXSize, nBufYSize, eBufType,
2253
0
                                       nBandCount, panBandMap, nPixelSpace,
2254
0
                                       nLineSpace, nBandSpace, psExtraArg);
2255
0
    else
2256
0
        return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2257
0
                                      pData, nBufXSize, nBufYSize, eBufType,
2258
0
                                      nBandCount, panBandMap, nPixelSpace,
2259
0
                                      nLineSpace, nBandSpace, psExtraArg);
2260
0
}
2261
2262
/************************************************************************/
2263
/*                          GetGeoTransform()                           */
2264
/************************************************************************/
2265
2266
CPLErr NITFDataset::GetGeoTransform(GDALGeoTransform &gt) const
2267
2268
523
{
2269
523
    gt = m_gt;
2270
2271
523
    if (bGotGeoTransform)
2272
32
        return CE_None;
2273
2274
491
    return GDALPamDataset::GetGeoTransform(gt);
2275
523
}
2276
2277
/************************************************************************/
2278
/*                          SetGeoTransform()                           */
2279
/************************************************************************/
2280
2281
CPLErr NITFDataset::SetGeoTransform(const GDALGeoTransform &gt)
2282
2283
7
{
2284
7
    bGotGeoTransform = TRUE;
2285
7
    m_gt = gt;
2286
2287
7
    double dfIGEOLOULX = m_gt.xorig + 0.5 * m_gt.xscale + 0.5 * m_gt.xrot;
2288
7
    double dfIGEOLOULY = m_gt.yorig + 0.5 * m_gt.yrot + 0.5 * m_gt.yscale;
2289
7
    double dfIGEOLOURX = dfIGEOLOULX + m_gt.xscale * (nRasterXSize - 1);
2290
7
    double dfIGEOLOURY = dfIGEOLOULY + m_gt.yrot * (nRasterXSize - 1);
2291
7
    double dfIGEOLOLRX = dfIGEOLOULX + m_gt.xscale * (nRasterXSize - 1) +
2292
7
                         m_gt.xrot * (nRasterYSize - 1);
2293
7
    double dfIGEOLOLRY = dfIGEOLOULY + m_gt.yrot * (nRasterXSize - 1) +
2294
7
                         m_gt.yscale * (nRasterYSize - 1);
2295
7
    double dfIGEOLOLLX = dfIGEOLOULX + m_gt.xrot * (nRasterYSize - 1);
2296
7
    double dfIGEOLOLLY = dfIGEOLOULY + m_gt.yscale * (nRasterYSize - 1);
2297
2298
7
    if (psImage != nullptr &&
2299
7
        NITFWriteIGEOLO(psImage, psImage->chICORDS, psImage->nZone, dfIGEOLOULX,
2300
7
                        dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY, dfIGEOLOLRX,
2301
7
                        dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY))
2302
3
        return CE_None;
2303
2304
4
    return GDALPamDataset::SetGeoTransform(gt);
2305
7
}
2306
2307
/************************************************************************/
2308
/*                              SetGCPs()                               */
2309
/************************************************************************/
2310
2311
CPLErr NITFDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
2312
                            const OGRSpatialReference *poGCPSRSIn)
2313
1
{
2314
1
    if (nGCPCountIn != 4)
2315
1
    {
2316
1
        CPLError(CE_Failure, CPLE_NotSupported,
2317
1
                 "NITF only supports writing 4 GCPs.");
2318
1
        return CE_Failure;
2319
1
    }
2320
2321
    /* Free previous GCPs */
2322
0
    GDALDeinitGCPs(nGCPCount, pasGCPList);
2323
0
    CPLFree(pasGCPList);
2324
2325
    /* Duplicate in GCPs */
2326
0
    nGCPCount = nGCPCountIn;
2327
0
    pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
2328
2329
0
    m_oGCPSRS.Clear();
2330
0
    if (poGCPSRSIn)
2331
0
        m_oGCPSRS = *poGCPSRSIn;
2332
2333
0
    int iUL = -1;
2334
0
    int iUR = -1;
2335
0
    int iLR = -1;
2336
0
    int iLL = -1;
2337
2338
0
#define EPS_GCP 1e-5
2339
0
    for (int i = 0; i < 4; i++)
2340
0
    {
2341
0
        if (fabs(pasGCPList[i].dfGCPPixel - 0.5) < EPS_GCP &&
2342
0
            fabs(pasGCPList[i].dfGCPLine - 0.5) < EPS_GCP)
2343
0
            iUL = i;
2344
2345
0
        else if (fabs(pasGCPList[i].dfGCPPixel - (nRasterXSize - 0.5)) <
2346
0
                     EPS_GCP &&
2347
0
                 fabs(pasGCPList[i].dfGCPLine - 0.5) < EPS_GCP)
2348
0
            iUR = i;
2349
2350
0
        else if (fabs(pasGCPList[i].dfGCPPixel - (nRasterXSize - 0.5)) <
2351
0
                     EPS_GCP &&
2352
0
                 fabs(pasGCPList[i].dfGCPLine - (nRasterYSize - 0.5)) < EPS_GCP)
2353
0
            iLR = i;
2354
2355
0
        else if (fabs(pasGCPList[i].dfGCPPixel - 0.5) < EPS_GCP &&
2356
0
                 fabs(pasGCPList[i].dfGCPLine - (nRasterYSize - 0.5)) < EPS_GCP)
2357
0
            iLL = i;
2358
0
    }
2359
2360
0
    if (iUL < 0 || iUR < 0 || iLR < 0 || iLL < 0)
2361
0
    {
2362
0
        CPLError(CE_Failure, CPLE_NotSupported,
2363
0
                 "The 4 GCPs image coordinates must be exactly "
2364
0
                 "at the *center* of the 4 corners of the image "
2365
0
                 "( (%.1f, %.1f), (%.1f %.1f), (%.1f %.1f), (%.1f %.1f) ).",
2366
0
                 0.5, 0.5, nRasterYSize - 0.5, 0.5, nRasterXSize - 0.5,
2367
0
                 nRasterYSize - 0.5, nRasterXSize - 0.5, 0.5);
2368
0
        return CE_Failure;
2369
0
    }
2370
2371
0
    double dfIGEOLOULX = pasGCPList[iUL].dfGCPX;
2372
0
    double dfIGEOLOULY = pasGCPList[iUL].dfGCPY;
2373
0
    double dfIGEOLOURX = pasGCPList[iUR].dfGCPX;
2374
0
    double dfIGEOLOURY = pasGCPList[iUR].dfGCPY;
2375
0
    double dfIGEOLOLRX = pasGCPList[iLR].dfGCPX;
2376
0
    double dfIGEOLOLRY = pasGCPList[iLR].dfGCPY;
2377
0
    double dfIGEOLOLLX = pasGCPList[iLL].dfGCPX;
2378
0
    double dfIGEOLOLLY = pasGCPList[iLL].dfGCPY;
2379
2380
    /* To recompute the zone */
2381
0
    OGRSpatialReference oSRSBackup = m_oSRS;
2382
0
    CPLErr eErr = SetSpatialRef(&m_oGCPSRS);
2383
0
    m_oSRS = std::move(oSRSBackup);
2384
2385
0
    if (eErr != CE_None)
2386
0
        return eErr;
2387
2388
0
    if (NITFWriteIGEOLO(psImage, psImage->chICORDS, psImage->nZone, dfIGEOLOULX,
2389
0
                        dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY, dfIGEOLOLRX,
2390
0
                        dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY))
2391
0
        return CE_None;
2392
2393
0
    return CE_Failure;
2394
0
}
2395
2396
/************************************************************************/
2397
/*                           GetSpatialRef()                            */
2398
/************************************************************************/
2399
2400
const OGRSpatialReference *NITFDataset::GetSpatialRef() const
2401
2402
528
{
2403
528
    if (bGotGeoTransform)
2404
32
        return &m_oSRS;
2405
2406
496
    return GDALPamDataset::GetSpatialRef();
2407
528
}
2408
2409
/************************************************************************/
2410
/*                           SetSpatialRef()                            */
2411
/************************************************************************/
2412
2413
CPLErr NITFDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
2414
2415
5
{
2416
5
    int bNorth;
2417
5
    OGRSpatialReference oSRS, oSRS_WGS84;
2418
2419
5
    if (poSRS == nullptr)
2420
0
        return CE_Failure;
2421
2422
5
    oSRS_WGS84.SetWellKnownGeogCS("WGS84");
2423
5
    if (poSRS->IsSameGeogCS(&oSRS_WGS84) == FALSE)
2424
4
    {
2425
4
        CPLError(CE_Failure, CPLE_NotSupported,
2426
4
                 "NITF only supports WGS84 geographic and UTM projections.");
2427
4
        return CE_Failure;
2428
4
    }
2429
2430
1
    if (poSRS->IsGeographic() && poSRS->GetPrimeMeridian() == 0.0)
2431
0
    {
2432
0
        if (psImage->chICORDS != 'G' && psImage->chICORDS != 'D')
2433
0
        {
2434
0
            CPLError(CE_Failure, CPLE_NotSupported,
2435
0
                     "NITF file should have been created with creation option "
2436
0
                     "'ICORDS=G' (or 'ICORDS=D').");
2437
0
            return CE_Failure;
2438
0
        }
2439
0
    }
2440
1
    else if (poSRS->GetUTMZone(&bNorth) > 0)
2441
0
    {
2442
0
        if (bNorth && psImage->chICORDS != 'N')
2443
0
        {
2444
0
            CPLError(CE_Failure, CPLE_NotSupported,
2445
0
                     "NITF file should have been created with creation option "
2446
0
                     "'ICORDS=N'.");
2447
0
            return CE_Failure;
2448
0
        }
2449
0
        else if (!bNorth && psImage->chICORDS != 'S')
2450
0
        {
2451
0
            CPLError(CE_Failure, CPLE_NotSupported,
2452
0
                     "NITF file should have been created with creation option "
2453
0
                     "'ICORDS=S'.");
2454
0
            return CE_Failure;
2455
0
        }
2456
2457
0
        psImage->nZone = poSRS->GetUTMZone(nullptr);
2458
0
    }
2459
1
    else
2460
1
    {
2461
1
        CPLError(CE_Failure, CPLE_NotSupported,
2462
1
                 "NITF only supports WGS84 geographic and UTM projections.");
2463
1
        return CE_Failure;
2464
1
    }
2465
2466
0
    m_oSRS = *poSRS;
2467
2468
0
    if (bGotGeoTransform)
2469
0
        SetGeoTransform(m_gt);
2470
2471
0
    return CE_None;
2472
1
}
2473
2474
#ifdef ESRI_BUILD
2475
/************************************************************************/
2476
/*                     InitializeNITFDESMetadata()                      */
2477
/************************************************************************/
2478
2479
void NITFDataset::InitializeNITFDESMetadata()
2480
{
2481
    static const char *const pszDESMetadataDomain = "NITF_DES_METADATA";
2482
    static const char *const pszDESsDomain = "xml:DES";
2483
    static const char *const pszMDXmlDataContentDESDATA =
2484
        "NITF_DES_XML_DATA_CONTENT_DESDATA";
2485
    static const char *const pszXmlDataContent = "XML_DATA_CONTENT";
2486
    constexpr int idxXmlDataContentDESDATA = 973;
2487
    static const int sizeXmlDataContent =
2488
        static_cast<int>(strlen(pszXmlDataContent));
2489
2490
    char **ppszDESMetadataList = oSpecialMD.GetMetadata(pszDESMetadataDomain);
2491
2492
    if (ppszDESMetadataList != NULL)
2493
        return;
2494
2495
    char **ppszDESsList = this->GetMetadata(pszDESsDomain);
2496
2497
    if (ppszDESsList == NULL)
2498
        return;
2499
2500
    bool foundXmlDataContent = false;
2501
    char *pachNITFDES = NULL;
2502
2503
    // Set metadata "NITF_DES_XML_DATA_CONTENT_DESDATA".
2504
    // NOTE: There should only be one instance of XML_DATA_CONTENT DES.
2505
2506
    while (((pachNITFDES = *ppszDESsList) != NULL) && (!foundXmlDataContent))
2507
    {
2508
        // The data stream has been Base64 encoded, need to decode it.
2509
        // NOTE: The actual length of the DES data stream is appended at the
2510
        // beginning of the encoded
2511
        //       data and is separated by a space.
2512
2513
        const char *pszSpace = strchr(pachNITFDES, ' ');
2514
2515
        char *pszData = NULL;
2516
        int nDataLen = 0;
2517
        if (pszSpace)
2518
        {
2519
            pszData = CPLStrdup(pszSpace + 1);
2520
            nDataLen =
2521
                CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pszData));
2522
            pszData[nDataLen] = 0;
2523
        }
2524
2525
        if (nDataLen > 2 + sizeXmlDataContent && STARTS_WITH_CI(pszData, "DE"))
2526
        {
2527
            // Check to see if this is a XML_DATA_CONTENT DES.
2528
            if (EQUALN(pszData + 2, pszXmlDataContent, sizeXmlDataContent) &&
2529
                nDataLen > idxXmlDataContentDESDATA)
2530
            {
2531
                foundXmlDataContent = true;
2532
2533
                // Get the value of the DESDATA field and set metadata
2534
                // "NITF_DES_XML_DATA_CONTENT_DESDATA".
2535
                const char *pszXML = pszData + idxXmlDataContentDESDATA;
2536
2537
                // Set the metadata.
2538
                oSpecialMD.SetMetadataItem(pszMDXmlDataContentDESDATA, pszXML,
2539
                                           pszDESMetadataDomain);
2540
            }
2541
        }
2542
2543
        CPLFree(pszData);
2544
2545
        pachNITFDES = NULL;
2546
        ppszDESsList += 1;
2547
    }
2548
}
2549
2550
/************************************************************************/
2551
/*                         InitializeNITFTREs()                         */
2552
/************************************************************************/
2553
2554
void NITFDataset::InitializeNITFTREs()
2555
{
2556
    static const char *const pszFileHeaderTREsDomain = "NITF_FILE_HEADER_TRES";
2557
    static const char *const pszImageSegmentTREsDomain =
2558
        "NITF_IMAGE_SEGMENT_TRES";
2559
2560
    char **ppszFileHeaderTREsList =
2561
        oSpecialMD.GetMetadata(pszFileHeaderTREsDomain);
2562
    char **ppszImageSegmentTREsList =
2563
        oSpecialMD.GetMetadata(pszImageSegmentTREsDomain);
2564
2565
    if ((ppszFileHeaderTREsList != NULL) && (ppszImageSegmentTREsList != NULL))
2566
        return;
2567
2568
    /* -------------------------------------------------------------------- */
2569
    /*      Loop over TRE sources (file and image).                         */
2570
    /* -------------------------------------------------------------------- */
2571
2572
    for (int nTRESrc = 0; nTRESrc < 2; nTRESrc++)
2573
    {
2574
        int nTREBytes = 0;
2575
        char *pszTREData = NULL;
2576
        const char *pszTREsDomain = NULL;
2577
        CPLStringList aosList;
2578
2579
        /* --------------------------------------------------------------------
2580
         */
2581
        /*      Extract file header or image segment TREs. */
2582
        /* --------------------------------------------------------------------
2583
         */
2584
2585
        if (nTRESrc == 0)
2586
        {
2587
            if (ppszFileHeaderTREsList != NULL)
2588
                continue;
2589
2590
            nTREBytes = psFile->nTREBytes;
2591
            pszTREData = psFile->pachTRE;
2592
            pszTREsDomain = pszFileHeaderTREsDomain;
2593
        }
2594
        else
2595
        {
2596
            if (ppszImageSegmentTREsList != NULL)
2597
                continue;
2598
2599
            if (psImage)
2600
            {
2601
                nTREBytes = psImage->nTREBytes;
2602
                pszTREData = psImage->pachTRE;
2603
                pszTREsDomain = pszImageSegmentTREsDomain;
2604
            }
2605
            else
2606
            {
2607
                nTREBytes = 0;
2608
                pszTREData = NULL;
2609
            }
2610
        }
2611
2612
        /* --------------------------------------------------------------------
2613
         */
2614
        /*      Loop over TREs. */
2615
        /* --------------------------------------------------------------------
2616
         */
2617
2618
        while (nTREBytes >= 11)
2619
        {
2620
            char szTemp[100];
2621
            char szTag[7];
2622
            char *pszEscapedData = NULL;
2623
            int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5));
2624
2625
            if (nThisTRESize < 0)
2626
            {
2627
                NITFGetField(szTemp, pszTREData, 0, 6);
2628
                CPLError(CE_Failure, CPLE_AppDefined,
2629
                         "Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
2630
                return;
2631
            }
2632
2633
            if (nThisTRESize > nTREBytes - 11)
2634
            {
2635
                CPLError(CE_Failure, CPLE_AppDefined,
2636
                         "Not enough bytes in TRE");
2637
                return;
2638
            }
2639
2640
            strncpy(szTag, pszTREData, 6);
2641
            szTag[6] = '\0';
2642
2643
            // trim white off tag.
2644
            while (strlen(szTag) > 0 && szTag[strlen(szTag) - 1] == ' ')
2645
                szTag[strlen(szTag) - 1] = '\0';
2646
2647
            // escape data.
2648
            pszEscapedData = CPLEscapeString(pszTREData + 6, nThisTRESize + 5,
2649
                                             CPLES_BackslashQuotable);
2650
2651
            const size_t nLineLen = strlen(szTag) + strlen(pszEscapedData) + 2;
2652
            char *pszLine = static_cast<char *>(CPLMalloc(nLineLen));
2653
            snprintf(pszLine, nLineLen, "%s=%s", szTag, pszEscapedData);
2654
            aosList.AddString(pszLine);
2655
            CPLFree(pszLine);
2656
            pszLine = NULL;
2657
2658
            CPLFree(pszEscapedData);
2659
            pszEscapedData = NULL;
2660
2661
            nTREBytes -= (nThisTRESize + 11);
2662
            pszTREData += (nThisTRESize + 11);
2663
        }
2664
2665
        if (!aosList.empty())
2666
            oSpecialMD.SetMetadata(aosList.List(), pszTREsDomain);
2667
    }
2668
}
2669
#endif
2670
2671
/************************************************************************/
2672
/*                         InitializeNITFDESs()                         */
2673
/************************************************************************/
2674
2675
bool NITFDataset::InitializeNITFDESs(bool bValidate)
2676
0
{
2677
0
    char **papszDESsList = oSpecialMD.GetMetadata("xml:DES");
2678
2679
0
    if (papszDESsList != nullptr)
2680
0
    {
2681
0
        return true;
2682
0
    }
2683
2684
0
    bool bSuccess = true;
2685
0
    CPLXMLNode *psDesListNode =
2686
0
        CPLCreateXMLNode(nullptr, CXT_Element, "des_list");
2687
2688
0
    for (int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
2689
0
    {
2690
0
        NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
2691
2692
0
        if (EQUAL(psSegInfo->szSegmentType, "DE"))
2693
0
        {
2694
0
            bool bGotError = false;
2695
0
            CPLXMLNode *psDesNode =
2696
0
                NITFDESGetXml(psFile, iSegment, bValidate, &bGotError);
2697
0
            if (bGotError)
2698
0
                bSuccess = false;
2699
2700
0
            if (psDesNode != nullptr)
2701
0
            {
2702
0
                CPLAddXMLChild(psDesListNode, psDesNode);
2703
0
            }
2704
0
        }
2705
0
    }
2706
2707
0
    if (psDesListNode->psChild != nullptr)
2708
0
    {
2709
0
        char *pszXML = CPLSerializeXMLTree(psDesListNode);
2710
0
        char *apszMD[2] = {pszXML, nullptr};
2711
0
        oSpecialMD.SetMetadata(apszMD, "xml:DES");
2712
0
        CPLFree(pszXML);
2713
0
    }
2714
0
    CPLDestroyXMLNode(psDesListNode);
2715
0
    return bSuccess;
2716
0
}
2717
2718
/************************************************************************/
2719
/*                       InitializeNITFMetadata()                       */
2720
/************************************************************************/
2721
2722
void NITFDataset::InitializeNITFMetadata()
2723
2724
0
{
2725
0
    static const char *const pszDomainName = "NITF_METADATA";
2726
0
    static const char *const pszTagNITFFileHeader = "NITFFileHeader";
2727
0
    static const char *const pszTagNITFImageSubheader = "NITFImageSubheader";
2728
2729
0
    if (oSpecialMD.GetMetadata(pszDomainName) != nullptr)
2730
0
        return;
2731
2732
    // nHeaderLenOffset is the number of bytes to skip from the beginning of the
2733
    // NITF file header in order to get to the field HL (NITF file header
2734
    // length).
2735
2736
0
    int nHeaderLen = 0;
2737
0
    int nHeaderLenOffset = 0;
2738
2739
    // Get the NITF file header length.
2740
2741
0
    if (psFile->pachHeader != nullptr)
2742
0
    {
2743
0
        if ((STARTS_WITH(psFile->pachHeader, "NITF02.10")) ||
2744
0
            (STARTS_WITH(psFile->pachHeader, "NSIF01.00")))
2745
0
            nHeaderLenOffset = 354;
2746
0
        else if ((STARTS_WITH(psFile->pachHeader, "NITF01.10")) ||
2747
0
                 (STARTS_WITH(psFile->pachHeader, "NITF02.00")))
2748
0
            nHeaderLenOffset =
2749
0
                (STARTS_WITH((psFile->pachHeader + 280), "999998")) ? 394 : 354;
2750
0
    }
2751
2752
0
    char fieldHL[7];
2753
2754
0
    if (nHeaderLenOffset > 0)
2755
0
    {
2756
0
        char *pszFieldHL = psFile->pachHeader + nHeaderLenOffset;
2757
2758
0
        memcpy(fieldHL, pszFieldHL, 6);
2759
0
        fieldHL[6] = '\0';
2760
0
        nHeaderLen = atoi(fieldHL);
2761
0
    }
2762
2763
0
    if (nHeaderLen <= 0)
2764
0
    {
2765
0
        CPLError(CE_Failure, CPLE_AppDefined, "Zero length NITF file header!");
2766
0
        return;
2767
0
    }
2768
2769
0
    char *encodedHeader = CPLBase64Encode(
2770
0
        nHeaderLen, reinterpret_cast<GByte *>(psFile->pachHeader));
2771
2772
0
    if (encodedHeader == nullptr || strlen(encodedHeader) == 0)
2773
0
    {
2774
0
        CPLError(CE_Failure, CPLE_AppDefined,
2775
0
                 "Failed to encode NITF file header!");
2776
0
        CPLFree(encodedHeader);
2777
0
        return;
2778
0
    }
2779
2780
    // The length of the NITF file header plus a space is append to the
2781
    // beginning of the encoded string so that we can recover the length of the
2782
    // NITF file header when we decode it without having to pull it out the HL
2783
    // field again.
2784
2785
0
    std::string nitfFileheaderStr(fieldHL);
2786
0
    nitfFileheaderStr.append(" ");
2787
0
    nitfFileheaderStr.append(encodedHeader);
2788
2789
0
    CPLFree(encodedHeader);
2790
2791
0
    oSpecialMD.SetMetadataItem(pszTagNITFFileHeader, nitfFileheaderStr.c_str(),
2792
0
                               pszDomainName);
2793
2794
    // Get the image subheader length.
2795
2796
0
    int nImageSubheaderLen = 0;
2797
2798
0
    if (psImage != nullptr &&
2799
0
        STARTS_WITH(psFile->pasSegmentInfo[psImage->iSegment].szSegmentType,
2800
0
                    "IM"))
2801
0
    {
2802
0
        nImageSubheaderLen =
2803
0
            psFile->pasSegmentInfo[psImage->iSegment].nSegmentHeaderSize;
2804
0
    }
2805
2806
0
    if (nImageSubheaderLen < 0)
2807
0
    {
2808
0
        CPLError(CE_Failure, CPLE_AppDefined,
2809
0
                 "Invalid length NITF image subheader!");
2810
0
        return;
2811
0
    }
2812
2813
0
    if (nImageSubheaderLen > 0)
2814
0
    {
2815
0
        char *encodedImageSubheader = CPLBase64Encode(
2816
0
            nImageSubheaderLen, reinterpret_cast<GByte *>(psImage->pachHeader));
2817
2818
0
        if (encodedImageSubheader == nullptr ||
2819
0
            strlen(encodedImageSubheader) == 0)
2820
0
        {
2821
0
            CPLError(CE_Failure, CPLE_AppDefined,
2822
0
                     "Failed to encode image subheader!");
2823
0
            CPLFree(encodedImageSubheader);
2824
0
            return;
2825
0
        }
2826
2827
        // The length of the image subheader plus a space is append to the
2828
        // beginning of the encoded string so that we can recover the actual
2829
        // length of the image subheader when we decode it.
2830
2831
0
        char buffer[20];
2832
2833
0
        snprintf(buffer, sizeof(buffer), "%d", nImageSubheaderLen);
2834
2835
0
        std::string imageSubheaderStr(buffer);
2836
0
        imageSubheaderStr.append(" ");
2837
0
        imageSubheaderStr.append(encodedImageSubheader);
2838
2839
0
        CPLFree(encodedImageSubheader);
2840
2841
0
        oSpecialMD.SetMetadataItem(pszTagNITFImageSubheader,
2842
0
                                   imageSubheaderStr.c_str(), pszDomainName);
2843
0
    }
2844
0
}
2845
2846
/************************************************************************/
2847
/*                       InitializeCGMMetadata()                        */
2848
/************************************************************************/
2849
2850
void NITFDataset::InitializeCGMMetadata()
2851
2852
0
{
2853
0
    if (oSpecialMD.GetMetadataItem("SEGMENT_COUNT", "CGM") != nullptr)
2854
0
        return;
2855
2856
0
    int iCGM = 0;
2857
0
    char **papszCGMMetadata = CSLSetNameValue(nullptr, "SEGMENT_COUNT", "0");
2858
2859
    /* ==================================================================== */
2860
    /*      Process all graphics segments.                                  */
2861
    /* ==================================================================== */
2862
0
    for (int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
2863
0
    {
2864
0
        NITFSegmentInfo *psSegment = psFile->pasSegmentInfo + iSegment;
2865
2866
0
        if (!EQUAL(psSegment->szSegmentType, "GR") &&
2867
0
            !EQUAL(psSegment->szSegmentType, "SY"))
2868
0
            continue;
2869
2870
0
        papszCGMMetadata = CSLSetNameValue(
2871
0
            papszCGMMetadata, CPLString().Printf("SEGMENT_%d_SLOC_ROW", iCGM),
2872
0
            CPLString().Printf("%d", psSegment->nLOC_R));
2873
0
        papszCGMMetadata = CSLSetNameValue(
2874
0
            papszCGMMetadata, CPLString().Printf("SEGMENT_%d_SLOC_COL", iCGM),
2875
0
            CPLString().Printf("%d", psSegment->nLOC_C));
2876
2877
0
        papszCGMMetadata = CSLSetNameValue(
2878
0
            papszCGMMetadata, CPLString().Printf("SEGMENT_%d_CCS_ROW", iCGM),
2879
0
            CPLString().Printf("%d", psSegment->nCCS_R));
2880
0
        papszCGMMetadata = CSLSetNameValue(
2881
0
            papszCGMMetadata, CPLString().Printf("SEGMENT_%d_CCS_COL", iCGM),
2882
0
            CPLString().Printf("%d", psSegment->nCCS_C));
2883
2884
0
        papszCGMMetadata = CSLSetNameValue(
2885
0
            papszCGMMetadata, CPLString().Printf("SEGMENT_%d_SDLVL", iCGM),
2886
0
            CPLString().Printf("%d", psSegment->nDLVL));
2887
0
        papszCGMMetadata = CSLSetNameValue(
2888
0
            papszCGMMetadata, CPLString().Printf("SEGMENT_%d_SALVL", iCGM),
2889
0
            CPLString().Printf("%d", psSegment->nALVL));
2890
2891
        /* --------------------------------------------------------------------
2892
         */
2893
        /*      Load the raw CGM data itself. */
2894
        /* --------------------------------------------------------------------
2895
         */
2896
2897
0
        char *pabyCGMData = static_cast<char *>(VSI_CALLOC_VERBOSE(
2898
0
            1, static_cast<size_t>(psSegment->nSegmentSize)));
2899
0
        if (pabyCGMData == nullptr)
2900
0
        {
2901
0
            CSLDestroy(papszCGMMetadata);
2902
0
            return;
2903
0
        }
2904
0
        if (VSIFSeekL(psFile->fp, psSegment->nSegmentStart, SEEK_SET) != 0 ||
2905
0
            VSIFReadL(pabyCGMData, 1,
2906
0
                      static_cast<size_t>(psSegment->nSegmentSize),
2907
0
                      psFile->fp) != psSegment->nSegmentSize)
2908
0
        {
2909
0
            CPLError(CE_Warning, CPLE_FileIO,
2910
0
                     "Failed to read " CPL_FRMT_GUIB
2911
0
                     " bytes of graphic data at " CPL_FRMT_GUIB ".",
2912
0
                     psSegment->nSegmentSize, psSegment->nSegmentStart);
2913
0
            CPLFree(pabyCGMData);
2914
0
            CSLDestroy(papszCGMMetadata);
2915
0
            return;
2916
0
        }
2917
2918
0
        char *pszEscapedCGMData = CPLEscapeString(
2919
0
            pabyCGMData, static_cast<int>(psSegment->nSegmentSize),
2920
0
            CPLES_BackslashQuotable);
2921
2922
0
        if (pszEscapedCGMData == nullptr)
2923
0
        {
2924
0
            CPLFree(pabyCGMData);
2925
0
            CSLDestroy(papszCGMMetadata);
2926
0
            return;
2927
0
        }
2928
2929
0
        papszCGMMetadata = CSLSetNameValue(
2930
0
            papszCGMMetadata, CPLString().Printf("SEGMENT_%d_DATA", iCGM),
2931
0
            pszEscapedCGMData);
2932
0
        CPLFree(pszEscapedCGMData);
2933
0
        CPLFree(pabyCGMData);
2934
2935
0
        iCGM++;
2936
0
    }
2937
2938
    /* -------------------------------------------------------------------- */
2939
    /*      Record the CGM segment count.                                   */
2940
    /* -------------------------------------------------------------------- */
2941
0
    papszCGMMetadata = CSLSetNameValue(papszCGMMetadata, "SEGMENT_COUNT",
2942
0
                                       CPLString().Printf("%d", iCGM));
2943
2944
0
    oSpecialMD.SetMetadata(papszCGMMetadata, "CGM");
2945
2946
0
    CSLDestroy(papszCGMMetadata);
2947
0
}
2948
2949
/************************************************************************/
2950
/*                       InitializeTextMetadata()                       */
2951
/************************************************************************/
2952
2953
void NITFDataset::InitializeTextMetadata()
2954
2955
0
{
2956
0
    if (oSpecialMD.GetMetadata("TEXT") != nullptr)
2957
0
        return;
2958
2959
0
    int iText = 0;
2960
2961
    /* ==================================================================== */
2962
    /*      Process all text segments.                                  */
2963
    /* ==================================================================== */
2964
0
    for (int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
2965
0
    {
2966
0
        NITFSegmentInfo *psSegment = psFile->pasSegmentInfo + iSegment;
2967
2968
0
        if (!EQUAL(psSegment->szSegmentType, "TX"))
2969
0
            continue;
2970
2971
        /* --------------------------------------------------------------------
2972
         */
2973
        /*      Load the text header */
2974
        /* --------------------------------------------------------------------
2975
         */
2976
2977
        /* Allocate one extra byte for the NULL terminating character */
2978
0
        char *pabyHeaderData = static_cast<char *>(CPLCalloc(
2979
0
            1, static_cast<size_t>(psSegment->nSegmentHeaderSize + 1)));
2980
0
        if (VSIFSeekL(psFile->fp, psSegment->nSegmentHeaderStart, SEEK_SET) !=
2981
0
                0 ||
2982
0
            VSIFReadL(pabyHeaderData, 1,
2983
0
                      static_cast<size_t>(psSegment->nSegmentHeaderSize),
2984
0
                      psFile->fp) != psSegment->nSegmentHeaderSize)
2985
0
        {
2986
0
            CPLError(
2987
0
                CE_Warning, CPLE_FileIO,
2988
0
                "Failed to read %d bytes of text header data at " CPL_FRMT_GUIB
2989
0
                ".",
2990
0
                psSegment->nSegmentHeaderSize, psSegment->nSegmentHeaderStart);
2991
0
            CPLFree(pabyHeaderData);
2992
0
            return;
2993
0
        }
2994
2995
0
        oSpecialMD.SetMetadataItem(CPLString().Printf("HEADER_%d", iText),
2996
0
                                   pabyHeaderData, "TEXT");
2997
0
        CPLFree(pabyHeaderData);
2998
2999
        /* --------------------------------------------------------------------
3000
         */
3001
        /*      Load the raw TEXT data itself. */
3002
        /* --------------------------------------------------------------------
3003
         */
3004
        /* Allocate one extra byte for the NULL terminating character */
3005
0
        char *pabyTextData = static_cast<char *>(VSI_CALLOC_VERBOSE(
3006
0
            1, static_cast<size_t>(psSegment->nSegmentSize) + 1));
3007
0
        if (pabyTextData == nullptr)
3008
0
        {
3009
0
            return;
3010
0
        }
3011
0
        if (VSIFSeekL(psFile->fp, psSegment->nSegmentStart, SEEK_SET) != 0 ||
3012
0
            VSIFReadL(pabyTextData, 1,
3013
0
                      static_cast<size_t>(psSegment->nSegmentSize),
3014
0
                      psFile->fp) != psSegment->nSegmentSize)
3015
0
        {
3016
0
            CPLError(CE_Warning, CPLE_FileIO,
3017
0
                     "Failed to read " CPL_FRMT_GUIB
3018
0
                     " bytes of text data at " CPL_FRMT_GUIB ".",
3019
0
                     psSegment->nSegmentSize, psSegment->nSegmentStart);
3020
0
            CPLFree(pabyTextData);
3021
0
            return;
3022
0
        }
3023
3024
0
        oSpecialMD.SetMetadataItem(CPLString().Printf("DATA_%d", iText),
3025
0
                                   pabyTextData, "TEXT");
3026
0
        CPLFree(pabyTextData);
3027
3028
0
        iText++;
3029
0
    }
3030
0
}
3031
3032
/************************************************************************/
3033
/*                       InitializeTREMetadata()                        */
3034
/************************************************************************/
3035
3036
bool NITFDataset::InitializeTREMetadata(bool bValidate)
3037
3038
0
{
3039
0
    if (oSpecialMD.GetMetadata("TRE") != nullptr ||
3040
0
        oSpecialMD.GetMetadata("xml:TRE") != nullptr)
3041
0
        return true;
3042
3043
0
    bool bGotError = false;
3044
0
    CPLXMLNode *psTresNode = CPLCreateXMLNode(nullptr, CXT_Element, "tres");
3045
0
    bool bFoundRPFIMG = false;
3046
3047
    /* -------------------------------------------------------------------- */
3048
    /*      Loop over TRE sources (file and image).                         */
3049
    /* -------------------------------------------------------------------- */
3050
0
    for (int nTRESrc = 0; nTRESrc < 2; nTRESrc++)
3051
0
    {
3052
0
        int nTREBytes = 0;
3053
0
        char *pszTREData = nullptr;
3054
3055
0
        if (nTRESrc == 0)
3056
0
        {
3057
0
            nTREBytes = psFile->nTREBytes;
3058
0
            pszTREData = psFile->pachTRE;
3059
0
        }
3060
0
        else
3061
0
        {
3062
0
            if (psImage)
3063
0
            {
3064
0
                nTREBytes = psImage->nTREBytes;
3065
0
                pszTREData = psImage->pachTRE;
3066
0
            }
3067
0
            else
3068
0
            {
3069
0
                nTREBytes = 0;
3070
0
                pszTREData = nullptr;
3071
0
            }
3072
0
        }
3073
3074
        /* --------------------------------------------------------------------
3075
         */
3076
        /*      Loop over TREs. */
3077
        /* --------------------------------------------------------------------
3078
         */
3079
3080
0
        while (nTREBytes >= 11)
3081
0
        {
3082
0
            char szTemp[100];
3083
0
            char szTag[7];
3084
0
            const int nThisTRESize =
3085
0
                atoi(NITFGetField(szTemp, pszTREData, 6, 5));
3086
3087
0
            if (nThisTRESize < 0)
3088
0
            {
3089
0
                NITFGetField(szTemp, pszTREData, 0, 6);
3090
0
                CPLError(CE_Failure, CPLE_AppDefined,
3091
0
                         "Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
3092
0
                CPLDestroyXMLNode(psTresNode);
3093
0
                bGotError = true;
3094
0
                return bGotError;
3095
0
            }
3096
0
            if (nThisTRESize > nTREBytes - 11)
3097
0
            {
3098
0
                CPLError(CE_Failure, CPLE_AppDefined,
3099
0
                         "Not enough bytes in TRE");
3100
0
                CPLDestroyXMLNode(psTresNode);
3101
0
                bGotError = true;
3102
0
                return bGotError;
3103
0
            }
3104
3105
0
            strncpy(szTag, pszTREData, 6);
3106
0
            szTag[6] = '\0';
3107
3108
            // trim white off tag.
3109
0
            while (strlen(szTag) > 0 && szTag[strlen(szTag) - 1] == ' ')
3110
0
                szTag[strlen(szTag) - 1] = '\0';
3111
3112
0
            if (strcmp(szTag, "RPFIMG") == 0)
3113
0
                bFoundRPFIMG = true;
3114
3115
0
            CPLXMLNode *psTreNode =
3116
0
                NITFCreateXMLTre(psFile, szTag, pszTREData + 11, nThisTRESize,
3117
0
                                 bValidate, &bGotError);
3118
0
            if (psTreNode)
3119
0
            {
3120
0
                CPLCreateXMLNode(
3121
0
                    CPLCreateXMLNode(psTreNode, CXT_Attribute, "location"),
3122
0
                    CXT_Text, nTRESrc == 0 ? "file" : "image");
3123
0
                CPLAddXMLChild(psTresNode, psTreNode);
3124
0
            }
3125
3126
            // escape data.
3127
0
            char *pszEscapedData = CPLEscapeString(
3128
0
                pszTREData + 11, nThisTRESize, CPLES_BackslashQuotable);
3129
0
            if (pszEscapedData == nullptr)
3130
0
            {
3131
0
                bGotError = true;
3132
0
            }
3133
0
            else
3134
0
            {
3135
0
                char szUniqueTag[32];
3136
0
                strcpy(szUniqueTag, szTag);
3137
0
                int nCountUnique = 2;
3138
0
                while (oSpecialMD.GetMetadataItem(szUniqueTag, "TRE") !=
3139
0
                       nullptr)
3140
0
                {
3141
0
                    snprintf(szUniqueTag, sizeof(szUniqueTag), "%s_%d", szTag,
3142
0
                             nCountUnique);
3143
0
                    nCountUnique++;
3144
0
                }
3145
0
                oSpecialMD.SetMetadataItem(szUniqueTag, pszEscapedData, "TRE");
3146
0
                CPLFree(pszEscapedData);
3147
0
            }
3148
3149
0
            nTREBytes -= (nThisTRESize + 11);
3150
0
            pszTREData += (nThisTRESize + 11);
3151
0
        }
3152
0
    }
3153
3154
    /* -------------------------------------------------------------------- */
3155
    /*      Loop over TRE in DES                                            */
3156
    /* -------------------------------------------------------------------- */
3157
0
    for (int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
3158
0
    {
3159
0
        NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
3160
0
        if (!EQUAL(psSegInfo->szSegmentType, "DE"))
3161
0
            continue;
3162
3163
0
        NITFDES *psDES = NITFDESAccess(psFile, iSegment);
3164
0
        if (psDES == nullptr)
3165
0
            continue;
3166
3167
0
        char *pabyTREData = nullptr;
3168
0
        int nOffset = 0;
3169
0
        char szTREName[7];
3170
0
        int nThisTRESize;
3171
3172
0
        while (NITFDESGetTRE(psDES, nOffset, szTREName, &pabyTREData,
3173
0
                             &nThisTRESize))
3174
0
        {
3175
0
            if (nThisTRESize == 0)
3176
0
            {
3177
0
                CPLError(CE_Warning, CPLE_AppDefined,
3178
0
                         "Invalid size=0 for TRE %s", szTREName);
3179
0
                break;
3180
0
            }
3181
3182
0
            if (!(bFoundRPFIMG && strcmp(szTREName, "RPFDES") == 0))
3183
0
            {
3184
0
                char *pszEscapedData = CPLEscapeString(
3185
0
                    pabyTREData, nThisTRESize, CPLES_BackslashQuotable);
3186
0
                if (pszEscapedData == nullptr)
3187
0
                {
3188
0
                    NITFDESFreeTREData(pabyTREData);
3189
0
                    bGotError = true;
3190
0
                    break;
3191
0
                }
3192
3193
                // trim white off tag.
3194
0
                while (strlen(szTREName) > 0 &&
3195
0
                       szTREName[strlen(szTREName) - 1] == ' ')
3196
0
                    szTREName[strlen(szTREName) - 1] = '\0';
3197
3198
0
                CPLXMLNode *psTreNode =
3199
0
                    NITFCreateXMLTre(psFile, szTREName, pabyTREData,
3200
0
                                     nThisTRESize, bValidate, &bGotError);
3201
0
                if (psTreNode)
3202
0
                {
3203
0
                    const char *pszDESID =
3204
0
                        CSLFetchNameValue(psDES->papszMetadata, "DESID");
3205
0
                    CPLCreateXMLNode(
3206
0
                        CPLCreateXMLNode(psTreNode, CXT_Attribute, "location"),
3207
0
                        CXT_Text,
3208
0
                        pszDESID ? CPLSPrintf("des %s", pszDESID) : "des");
3209
0
                    CPLAddXMLChild(psTresNode, psTreNode);
3210
0
                }
3211
3212
0
                char szUniqueTag[32];
3213
0
                strcpy(szUniqueTag, szTREName);
3214
0
                int nCountUnique = 2;
3215
0
                while (oSpecialMD.GetMetadataItem(szUniqueTag, "TRE") !=
3216
0
                       nullptr)
3217
0
                {
3218
0
                    snprintf(szUniqueTag, sizeof(szUniqueTag), "%s_%d",
3219
0
                             szTREName, nCountUnique);
3220
0
                    nCountUnique++;
3221
0
                }
3222
0
                oSpecialMD.SetMetadataItem(szUniqueTag, pszEscapedData, "TRE");
3223
3224
0
                CPLFree(pszEscapedData);
3225
0
            }
3226
3227
0
            nOffset += 11 + nThisTRESize;
3228
3229
0
            NITFDESFreeTREData(pabyTREData);
3230
0
        }
3231
3232
0
        NITFDESDeaccess(psDES);
3233
0
    }
3234
3235
0
    if (psTresNode->psChild != nullptr)
3236
0
    {
3237
0
        char *pszXML = CPLSerializeXMLTree(psTresNode);
3238
0
        char *apszMD[2] = {pszXML, nullptr};
3239
0
        oSpecialMD.SetMetadata(apszMD, "xml:TRE");
3240
0
        CPLFree(pszXML);
3241
0
    }
3242
0
    CPLDestroyXMLNode(psTresNode);
3243
3244
0
    return !bGotError;
3245
0
}
3246
3247
/************************************************************************/
3248
/*                       GetMetadataDomainList()                        */
3249
/************************************************************************/
3250
3251
char **NITFDataset::GetMetadataDomainList()
3252
0
{
3253
0
    return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
3254
0
                                   TRUE, "NITF_METADATA", "xml:DES",
3255
0
                                   "NITF_DES_METADATA", "NITF_FILE_HEADER_TRES",
3256
0
                                   "NITF_IMAGE_SEGMENT_TRES", "CGM", "TEXT",
3257
0
                                   "TRE", "xml:TRE", "OVERVIEWS", nullptr);
3258
0
}
3259
3260
/************************************************************************/
3261
/*                  InitializeImageStructureMetadata()                  */
3262
/************************************************************************/
3263
3264
void NITFDataset::InitializeImageStructureMetadata()
3265
0
{
3266
0
    if (oSpecialMD.GetMetadata("IMAGE_STRUCTURE") != nullptr)
3267
0
        return;
3268
3269
0
    oSpecialMD.SetMetadata(GDALPamDataset::GetMetadata("IMAGE_STRUCTURE"),
3270
0
                           "IMAGE_STRUCTURE");
3271
0
    if (poJ2KDataset)
3272
0
    {
3273
0
        const char *pszReversibility = poJ2KDataset->GetMetadataItem(
3274
0
            "COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE");
3275
0
        if (pszReversibility)
3276
0
        {
3277
0
            oSpecialMD.SetMetadataItem("COMPRESSION_REVERSIBILITY",
3278
0
                                       pszReversibility, "IMAGE_STRUCTURE");
3279
0
        }
3280
0
    }
3281
0
}
3282
3283
/************************************************************************/
3284
/*                            GetMetadata()                             */
3285
/************************************************************************/
3286
3287
CSLConstList NITFDataset::GetMetadata(const char *pszDomain)
3288
3289
237
{
3290
237
    if (pszDomain != nullptr && EQUAL(pszDomain, "NITF_METADATA"))
3291
0
    {
3292
        // InitializeNITFMetadata retrieves the NITF file header and all image
3293
        // segment file headers. (NOTE: The returned strings are
3294
        // base64-encoded).
3295
3296
0
        InitializeNITFMetadata();
3297
0
        return oSpecialMD.GetMetadata(pszDomain);
3298
0
    }
3299
3300
237
    if (pszDomain != nullptr && EQUAL(pszDomain, "xml:DES"))
3301
0
    {
3302
        // InitializeNITFDESs retrieves all the DES file headers (NOTE: The
3303
        // returned strings are base64-encoded).
3304
3305
0
        InitializeNITFDESs(false);
3306
0
        return oSpecialMD.GetMetadata(pszDomain);
3307
0
    }
3308
3309
#ifdef ESRI_BUILD
3310
    if (pszDomain != NULL && EQUAL(pszDomain, "NITF_DES_METADATA"))
3311
    {
3312
        // InitializeNITFDESs retrieves all the DES file headers (NOTE: The
3313
        // returned strings are base64-encoded).
3314
3315
        InitializeNITFDESMetadata(false);
3316
        return oSpecialMD.GetMetadata(pszDomain);
3317
    }
3318
3319
    if (pszDomain != NULL && EQUAL(pszDomain, "NITF_FILE_HEADER_TRES"))
3320
    {
3321
        // InitializeNITFTREs retrieves all the TREs that are resides in the
3322
        // NITF file header and all the TREs that are resides in the current
3323
        // image segment. NOTE: the returned strings are backslash-escaped
3324
3325
        InitializeNITFTREs();
3326
        return oSpecialMD.GetMetadata(pszDomain);
3327
    }
3328
3329
    if (pszDomain != NULL && EQUAL(pszDomain, "NITF_IMAGE_SEGMENT_TRES"))
3330
    {
3331
        // InitializeNITFTREs retrieves all the TREs that are resides in the
3332
        // NITF file header and all the TREs that are resides in the current
3333
        // image segment. NOTE: the returned strings are backslash-escaped
3334
3335
        InitializeNITFTREs();
3336
        return oSpecialMD.GetMetadata(pszDomain);
3337
    }
3338
#endif
3339
3340
237
    if (pszDomain != nullptr && EQUAL(pszDomain, "CGM"))
3341
0
    {
3342
0
        InitializeCGMMetadata();
3343
0
        return oSpecialMD.GetMetadata(pszDomain);
3344
0
    }
3345
3346
237
    if (pszDomain != nullptr && EQUAL(pszDomain, "TEXT"))
3347
0
    {
3348
0
        InitializeTextMetadata();
3349
0
        return oSpecialMD.GetMetadata(pszDomain);
3350
0
    }
3351
3352
237
    if (pszDomain != nullptr && EQUAL(pszDomain, "TRE"))
3353
0
    {
3354
0
        InitializeTREMetadata(false);
3355
0
        return oSpecialMD.GetMetadata(pszDomain);
3356
0
    }
3357
3358
237
    if (pszDomain != nullptr && EQUAL(pszDomain, "xml:TRE"))
3359
0
    {
3360
0
        InitializeTREMetadata(false);
3361
0
        return oSpecialMD.GetMetadata(pszDomain);
3362
0
    }
3363
3364
237
    if (pszDomain != nullptr && EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
3365
0
        poJ2KDataset)
3366
0
    {
3367
0
        InitializeImageStructureMetadata();
3368
0
        return oSpecialMD.GetMetadata(pszDomain);
3369
0
    }
3370
3371
237
    return GDALPamDataset::GetMetadata(pszDomain);
3372
237
}
3373
3374
/************************************************************************/
3375
/*                          GetMetadataItem()                           */
3376
/************************************************************************/
3377
3378
const char *NITFDataset::GetMetadataItem(const char *pszName,
3379
                                         const char *pszDomain)
3380
3381
1.73k
{
3382
1.73k
    if (pszDomain != nullptr && EQUAL(pszDomain, "NITF_METADATA"))
3383
0
    {
3384
        // InitializeNITFMetadata retrieves the NITF file header and all image
3385
        // segment file headers. (NOTE: The returned strings are
3386
        // base64-encoded).
3387
3388
0
        InitializeNITFMetadata();
3389
0
        return oSpecialMD.GetMetadataItem(pszName, pszDomain);
3390
0
    }
3391
3392
#ifdef ESRI_BUILD
3393
    if (pszDomain != NULL && EQUAL(pszDomain, "NITF_DES_METADATA"))
3394
    {
3395
        // InitializeNITFDESs retrieves all the DES file headers (NOTE: The
3396
        // returned strings are base64-encoded).
3397
3398
        InitializeNITFDESMetadata(false);
3399
        return oSpecialMD.GetMetadataItem(pszName, pszDomain);
3400
    }
3401
3402
    if (pszDomain != NULL && EQUAL(pszDomain, "NITF_FILE_HEADER_TRES"))
3403
    {
3404
        // InitializeNITFTREs retrieves all the TREs that are resides in the
3405
        // NITF file header and all the TREs that are resides in the current
3406
        // image segment. NOTE: the returned strings are backslash-escaped
3407
3408
        InitializeNITFTREs();
3409
        return oSpecialMD.GetMetadataItem(pszName, pszDomain);
3410
    }
3411
3412
    if (pszDomain != NULL && EQUAL(pszDomain, "NITF_IMAGE_SEGMENT_TRES"))
3413
    {
3414
        // InitializeNITFTREs retrieves all the TREs that are resides in the
3415
        // NITF file header and all the TREs that are resides in the current
3416
        // image segment. NOTE: the returned strings are backslash-escaped
3417
3418
        InitializeNITFTREs();
3419
        return oSpecialMD.GetMetadataItem(pszName, pszDomain);
3420
    }
3421
#endif
3422
3423
1.73k
    if (pszDomain != nullptr && EQUAL(pszDomain, "CGM"))
3424
0
    {
3425
0
        InitializeCGMMetadata();
3426
0
        return oSpecialMD.GetMetadataItem(pszName, pszDomain);
3427
0
    }
3428
3429
1.73k
    if (pszDomain != nullptr && EQUAL(pszDomain, "TEXT"))
3430
0
    {
3431
0
        InitializeTextMetadata();
3432
0
        return oSpecialMD.GetMetadataItem(pszName, pszDomain);
3433
0
    }
3434
3435
1.73k
    if (pszDomain != nullptr && EQUAL(pszDomain, "TRE"))
3436
0
    {
3437
0
        InitializeTREMetadata(false);
3438
0
        return oSpecialMD.GetMetadataItem(pszName, pszDomain);
3439
0
    }
3440
3441
1.73k
    if (pszDomain != nullptr && EQUAL(pszDomain, "OVERVIEWS") &&
3442
964
        !osRSetVRT.empty())
3443
0
        return osRSetVRT;
3444
3445
1.73k
    if (pszDomain != nullptr && EQUAL(pszDomain, "IMAGE_STRUCTURE") &&
3446
406
        poJ2KDataset && EQUAL(pszName, "COMPRESSION_REVERSIBILITY"))
3447
0
    {
3448
0
        InitializeImageStructureMetadata();
3449
0
        return oSpecialMD.GetMetadataItem(pszName, pszDomain);
3450
0
    }
3451
3452
    // For unit test purposes
3453
1.73k
    if (pszDomain != nullptr && EQUAL(pszDomain, "DEBUG") &&
3454
0
        EQUAL(pszName, "JPEG2000_DATASET_NAME") && poJ2KDataset)
3455
0
        return poJ2KDataset->GetDescription();
3456
3457
    // For unit test purposes
3458
1.73k
    if (pszDomain != nullptr && EQUAL(pszDomain, "DEBUG") &&
3459
0
        EQUAL(pszName, "COMRAT") && psImage)
3460
0
        return psImage->szCOMRAT;
3461
3462
1.73k
    return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
3463
1.73k
}
3464
3465
/************************************************************************/
3466
/*                            GetGCPCount()                             */
3467
/************************************************************************/
3468
3469
int NITFDataset::GetGCPCount()
3470
3471
218
{
3472
218
    return nGCPCount;
3473
218
}
3474
3475
/************************************************************************/
3476
/*                          GetGCPSpatialRef()                          */
3477
/************************************************************************/
3478
3479
const OGRSpatialReference *NITFDataset::GetGCPSpatialRef() const
3480
3481
215
{
3482
215
    if (nGCPCount > 0 && !m_oGCPSRS.IsEmpty())
3483
16
        return &m_oGCPSRS;
3484
3485
199
    return nullptr;
3486
215
}
3487
3488
/************************************************************************/
3489
/*                               GetGCP()                               */
3490
/************************************************************************/
3491
3492
const GDAL_GCP *NITFDataset::GetGCPs()
3493
3494
215
{
3495
215
    return pasGCPList;
3496
215
}
3497
3498
/************************************************************************/
3499
/*                           CheckForRSets()                            */
3500
/*                                                                      */
3501
/*      Check for reduced resolution images in .r<n> files and if       */
3502
/*      found return filename for a virtual file wrapping them as an    */
3503
/*      overview file. (#3457)                                          */
3504
/************************************************************************/
3505
3506
int NITFDataset::CheckForRSets(const char *pszNITFFilename,
3507
                               char **papszSiblingFiles)
3508
3509
692
{
3510
692
    bool isR0File = EQUAL(CPLGetExtensionSafe(pszNITFFilename).c_str(), "r0");
3511
3512
    /* -------------------------------------------------------------------- */
3513
    /*      Check to see if we have RSets.                                  */
3514
    /* -------------------------------------------------------------------- */
3515
692
    std::vector<CPLString> aosRSetFilenames;
3516
3517
692
    for (int i = 1; i <= 5; i++)
3518
692
    {
3519
692
        CPLString osTarget;
3520
692
        VSIStatBufL sStat;
3521
3522
692
        if (isR0File)
3523
0
        {
3524
0
            osTarget = pszNITFFilename;
3525
0
            osTarget.back() = static_cast<char>('0' + i);
3526
0
        }
3527
692
        else
3528
692
            osTarget.Printf("%s.r%d", pszNITFFilename, i);
3529
3530
692
        if (papszSiblingFiles == nullptr)
3531
14
        {
3532
14
            if (VSIStatL(osTarget, &sStat) != 0)
3533
14
                break;
3534
14
        }
3535
678
        else
3536
678
        {
3537
678
            if (CSLFindStringCaseSensitive(papszSiblingFiles,
3538
678
                                           CPLGetFilename(osTarget)) < 0)
3539
678
                break;
3540
678
        }
3541
3542
0
        aosRSetFilenames.push_back(std::move(osTarget));
3543
0
    }
3544
3545
692
    if (aosRSetFilenames.empty())
3546
692
    {
3547
        //try for remoteview RRDS (with .rv%d extension)
3548
692
        for (int i = 1; i <= 7; i++)
3549
692
        {
3550
692
            CPLString osTarget;
3551
692
            VSIStatBufL sStat;
3552
3553
692
            osTarget.Printf("%s.rv%d", pszNITFFilename, i);
3554
3555
692
            if (VSIStatL(osTarget, &sStat) != 0)
3556
692
                break;
3557
3558
0
            aosRSetFilenames.push_back(std::move(osTarget));
3559
0
        }
3560
3561
692
        if (aosRSetFilenames.empty())
3562
692
            return FALSE;
3563
692
    }
3564
3565
    /* -------------------------------------------------------------------- */
3566
    /*      We do, so try to create a wrapping VRT file.                    */
3567
    /* -------------------------------------------------------------------- */
3568
0
    CPLString osFragment;
3569
3570
0
    osRSetVRT.Printf("<VRTDataset rasterXSize=\"%d\" rasterYSize=\"%d\">\n",
3571
0
                     GetRasterXSize() / 2, GetRasterYSize() / 2);
3572
3573
0
    for (int iBand = 0; iBand < GetRasterCount(); iBand++)
3574
0
    {
3575
0
        GDALRasterBand *poBand = GetRasterBand(iBand + 1);
3576
3577
0
        osRSetVRT += osFragment.Printf(
3578
0
            "  <VRTRasterBand dataType=\"%s\" band=\"%d\">\n",
3579
0
            GDALGetDataTypeName(poBand->GetRasterDataType()), iBand + 1);
3580
3581
0
        for (int i = 0; i < static_cast<int>(aosRSetFilenames.size()); i++)
3582
0
        {
3583
0
            char *pszEscaped =
3584
0
                CPLEscapeString(aosRSetFilenames[i].c_str(), -1, CPLES_XML);
3585
0
            if (i == 0)
3586
0
                osRSetVRT +=
3587
0
                    osFragment.Printf("    "
3588
0
                                      "<SimpleSource><SourceFilename>%s</"
3589
0
                                      "SourceFilename><SourceBand>%d</"
3590
0
                                      "SourceBand></SimpleSource>\n",
3591
0
                                      pszEscaped, iBand + 1);
3592
0
            else
3593
0
                osRSetVRT += osFragment.Printf(
3594
0
                    "    "
3595
0
                    "<Overview><SourceFilename>%s</"
3596
0
                    "SourceFilename><SourceBand>%d</SourceBand></Overview>\n",
3597
0
                    pszEscaped, iBand + 1);
3598
0
            CPLFree(pszEscaped);
3599
0
        }
3600
0
        osRSetVRT += osFragment.Printf("  </VRTRasterBand>\n");
3601
0
    }
3602
3603
0
    osRSetVRT += "</VRTDataset>\n";
3604
3605
0
    return TRUE;
3606
692
}
3607
3608
/************************************************************************/
3609
/*                          IBuildOverviews()                           */
3610
/************************************************************************/
3611
3612
CPLErr NITFDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
3613
                                    const int *panOverviewList, int nListBands,
3614
                                    const int *panBandList,
3615
                                    GDALProgressFunc pfnProgress,
3616
                                    void *pProgressData,
3617
                                    CSLConstList papszOptions)
3618
3619
0
{
3620
    /* -------------------------------------------------------------------- */
3621
    /*      If we have been using RSets we will need to clear them first.   */
3622
    /* -------------------------------------------------------------------- */
3623
0
    if (!osRSetVRT.empty())
3624
0
    {
3625
0
        oOvManager.CleanOverviews();
3626
0
        osRSetVRT = "";
3627
0
    }
3628
3629
0
    bExposeUnderlyingJPEGDatasetOverviews = FALSE;
3630
3631
    /* -------------------------------------------------------------------- */
3632
    /*      If we have an underlying JPEG2000 dataset (hopefully via        */
3633
    /*      JP2KAK) we will try and build zero overviews as a way of        */
3634
    /*      tricking it into clearing existing overviews-from-jpeg2000.     */
3635
    /* -------------------------------------------------------------------- */
3636
0
    if (poJ2KDataset != nullptr &&
3637
0
        !poJ2KDataset->GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS"))
3638
0
        poJ2KDataset->BuildOverviews(pszResampling, 0, nullptr, nListBands,
3639
0
                                     panBandList, GDALDummyProgress, nullptr,
3640
0
                                     /* papszOptions = */ nullptr);
3641
3642
    /* -------------------------------------------------------------------- */
3643
    /*      Use the overview manager to build requested overviews.          */
3644
    /* -------------------------------------------------------------------- */
3645
0
    CPLErr eErr = GDALPamDataset::IBuildOverviews(
3646
0
        pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
3647
0
        pfnProgress, pProgressData, papszOptions);
3648
3649
    /* -------------------------------------------------------------------- */
3650
    /*      If we are working with jpeg or jpeg2000, let the underlying     */
3651
    /*      dataset know about the overview file.                           */
3652
    /* -------------------------------------------------------------------- */
3653
0
    GDALDataset *poSubDS = poJ2KDataset.get();
3654
0
    if (poJPEGDataset)
3655
0
        poSubDS = poJPEGDataset.get();
3656
3657
0
    const char *pszOverviewFile = GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS");
3658
3659
0
    if (poSubDS && pszOverviewFile != nullptr && eErr == CE_None &&
3660
0
        poSubDS->GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS") == nullptr)
3661
0
    {
3662
0
        poSubDS->SetMetadataItem("OVERVIEW_FILE", pszOverviewFile, "OVERVIEWS");
3663
0
    }
3664
3665
0
    return eErr;
3666
0
}
3667
3668
/************************************************************************/
3669
/*                           ScanJPEGQLevel()                           */
3670
/*                                                                      */
3671
/*      Search the NITF APP header in the jpeg data stream to find      */
3672
/*      out what predefined Q level tables should be used (or -1 if     */
3673
/*      they are inline).                                               */
3674
/************************************************************************/
3675
3676
int NITFDataset::ScanJPEGQLevel(GUIntBig *pnDataStart, bool *pbError)
3677
3678
25
{
3679
25
    if (VSIFSeekL(psFile->fp, *pnDataStart, SEEK_SET) != 0)
3680
0
    {
3681
0
        CPLError(CE_Failure, CPLE_FileIO, "Seek error to jpeg data stream.");
3682
0
        *pbError = true;
3683
0
        return 0;
3684
0
    }
3685
3686
25
    GByte abyHeader[100];
3687
25
    if (VSIFReadL(abyHeader, 1, sizeof(abyHeader), psFile->fp) <
3688
25
        sizeof(abyHeader))
3689
0
    {
3690
0
        CPLError(CE_Failure, CPLE_FileIO, "Read error to jpeg data stream.");
3691
0
        *pbError = true;
3692
0
        return 0;
3693
0
    }
3694
3695
    /* -------------------------------------------------------------------- */
3696
    /*      Scan ahead for jpeg magic code.  In some files (eg. NSIF)       */
3697
    /*      there seems to be some extra junk before the image data stream. */
3698
    /* -------------------------------------------------------------------- */
3699
25
    GUInt32 nOffset = 0;
3700
290
    while (nOffset < sizeof(abyHeader) - 23 &&
3701
287
           (abyHeader[nOffset + 0] != 0xff || abyHeader[nOffset + 1] != 0xd8 ||
3702
23
            abyHeader[nOffset + 2] != 0xff))
3703
265
        nOffset++;
3704
3705
25
    if (nOffset >= sizeof(abyHeader) - 23)
3706
3
    {
3707
3
        *pbError = true;
3708
3
        return 0;
3709
3
    }
3710
3711
22
    *pbError = false;
3712
22
    *pnDataStart += nOffset;
3713
3714
22
    if (nOffset > 0)
3715
1
        CPLDebug("NITF",
3716
1
                 "JPEG data stream at offset %d from start of data segment, "
3717
1
                 "NSIF?",
3718
1
                 nOffset);
3719
3720
    /* -------------------------------------------------------------------- */
3721
    /*      Do we have an NITF app tag?  If so, pull out the Q level.       */
3722
    /* -------------------------------------------------------------------- */
3723
22
    if (memcmp(abyHeader + nOffset + 6, "NITF\0", 5) != 0)
3724
2
        return 0;
3725
3726
20
    return abyHeader[22 + nOffset];
3727
22
}
3728
3729
/************************************************************************/
3730
/*                           ScanJPEGBlocks()                           */
3731
/************************************************************************/
3732
3733
CPLErr NITFDataset::ScanJPEGBlocks()
3734
3735
7
{
3736
7
    GUIntBig nJPEGStart =
3737
7
        psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart;
3738
7
    bool bError = false;
3739
7
    nQLevel = ScanJPEGQLevel(&nJPEGStart, &bError);
3740
7
    if (bError)
3741
1
    {
3742
1
        return CE_Failure;
3743
1
    }
3744
3745
    /* -------------------------------------------------------------------- */
3746
    /*      Allocate offset array                                           */
3747
    /* -------------------------------------------------------------------- */
3748
6
    panJPEGBlockOffset = static_cast<vsi_l_offset *>(VSI_CALLOC_VERBOSE(
3749
6
        sizeof(vsi_l_offset), static_cast<size_t>(psImage->nBlocksPerRow) *
3750
6
                                  psImage->nBlocksPerColumn));
3751
6
    if (panJPEGBlockOffset == nullptr)
3752
0
    {
3753
0
        return CE_Failure;
3754
0
    }
3755
6
    panJPEGBlockOffset[0] = nJPEGStart;
3756
3757
6
    if (psImage->nBlocksPerRow * psImage->nBlocksPerColumn == 1)
3758
0
        return CE_None;
3759
3760
6
    for (int iBlock = psImage->nBlocksPerRow * psImage->nBlocksPerColumn - 1;
3761
5.12k
         iBlock > 0; iBlock--)
3762
5.12k
        panJPEGBlockOffset[iBlock] = -1;
3763
3764
    /* -------------------------------------------------------------------- */
3765
    /*      Scan through the whole image data stream identifying all        */
3766
    /*      block boundaries.  Each block starts with 0xFFD8 (SOI).         */
3767
    /*      They also end with 0xFFD9, but we don't currently look for      */
3768
    /*      that.                                                           */
3769
    /* -------------------------------------------------------------------- */
3770
6
    int iNextBlock = 1;
3771
6
    GIntBig iSegOffset = 2;
3772
6
    if (psFile->pasSegmentInfo[psImage->iSegment].nSegmentSize <
3773
6
        nJPEGStart - psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart)
3774
0
        return CE_Failure;
3775
6
    GIntBig iSegSize =
3776
6
        psFile->pasSegmentInfo[psImage->iSegment].nSegmentSize -
3777
6
        (nJPEGStart - psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart);
3778
6
    GByte abyBlock[512];
3779
6
    int ignoreBytes = 0;
3780
3781
48
    while (iSegOffset < iSegSize - 1)
3782
47
    {
3783
47
        const size_t nReadSize = std::min(
3784
47
            sizeof(abyBlock), static_cast<size_t>(iSegSize - iSegOffset));
3785
3786
47
        if (VSIFSeekL(psFile->fp, panJPEGBlockOffset[0] + iSegOffset,
3787
47
                      SEEK_SET) != 0)
3788
0
        {
3789
0
            CPLError(CE_Failure, CPLE_FileIO,
3790
0
                     "Seek error to jpeg data stream.");
3791
0
            return CE_Failure;
3792
0
        }
3793
3794
47
        if (VSIFReadL(abyBlock, 1, nReadSize, psFile->fp) < nReadSize)
3795
5
        {
3796
5
            CPLError(CE_Failure, CPLE_FileIO,
3797
5
                     "Read error to jpeg data stream.");
3798
5
            return CE_Failure;
3799
5
        }
3800
3801
21.3k
        for (size_t i = 0; i < nReadSize - 1; i++)
3802
21.3k
        {
3803
21.3k
            if (ignoreBytes == 0)
3804
18.1k
            {
3805
18.1k
                if (abyBlock[i] == 0xff)
3806
226
                {
3807
                    /* start-of-image marker */
3808
226
                    if (abyBlock[i + 1] == 0xd8)
3809
4
                    {
3810
4
                        panJPEGBlockOffset[iNextBlock++] =
3811
4
                            panJPEGBlockOffset[0] + iSegOffset + i;
3812
3813
4
                        if (iNextBlock ==
3814
4
                            psImage->nBlocksPerRow * psImage->nBlocksPerColumn)
3815
0
                        {
3816
0
                            return CE_None;
3817
0
                        }
3818
4
                    }
3819
                    /* Skip application-specific data to avoid false positive
3820
                     * while detecting */
3821
                    /* start-of-image markers (#2927). The size of the
3822
                     * application data is */
3823
                    /* found in the two following bytes */
3824
                    /* We need this complex mechanism of ignoreBytes for dealing
3825
                     * with */
3826
                    /* application data crossing several abyBlock ... */
3827
222
                    else if (abyBlock[i + 1] >= 0xe0 && abyBlock[i + 1] < 0xf0)
3828
6
                    {
3829
6
                        ignoreBytes = -2;
3830
6
                    }
3831
226
                }
3832
18.1k
            }
3833
3.18k
            else if (ignoreBytes < 0)
3834
12
            {
3835
12
                if (ignoreBytes == -1)
3836
6
                {
3837
                    /* Size of the application data */
3838
6
                    ignoreBytes = abyBlock[i] * 256 + abyBlock[i + 1];
3839
6
                }
3840
6
                else
3841
6
                    ignoreBytes++;
3842
12
            }
3843
3.17k
            else
3844
3.17k
            {
3845
3.17k
                ignoreBytes--;
3846
3.17k
            }
3847
21.3k
        }
3848
3849
42
        iSegOffset += nReadSize - 1;
3850
42
    }
3851
3852
1
    return CE_None;
3853
6
}
3854
3855
/************************************************************************/
3856
/*                           ReadJPEGBlock()                            */
3857
/************************************************************************/
3858
3859
CPLErr NITFDataset::ReadJPEGBlock(int iBlockX, int iBlockY)
3860
3861
7
{
3862
7
    CPLErr eErr;
3863
3864
    /* -------------------------------------------------------------------- */
3865
    /*      If this is our first request, do a scan for block boundaries.   */
3866
    /* -------------------------------------------------------------------- */
3867
7
    if (panJPEGBlockOffset == nullptr)
3868
7
    {
3869
7
        if (EQUAL(psImage->szIC, "M3"))
3870
0
        {
3871
            /* --------------------------------------------------------------------
3872
             */
3873
            /*      When a data mask subheader is present, we don't need to scan
3874
             */
3875
            /*      the whole file. We just use the psImage->panBlockStart table
3876
             */
3877
            /* --------------------------------------------------------------------
3878
             */
3879
0
            panJPEGBlockOffset = static_cast<vsi_l_offset *>(
3880
0
                VSI_CALLOC_VERBOSE(sizeof(vsi_l_offset),
3881
0
                                   static_cast<size_t>(psImage->nBlocksPerRow) *
3882
0
                                       psImage->nBlocksPerColumn));
3883
0
            if (panJPEGBlockOffset == nullptr)
3884
0
            {
3885
0
                return CE_Failure;
3886
0
            }
3887
0
            for (int i = 0;
3888
0
                 i < psImage->nBlocksPerRow * psImage->nBlocksPerColumn; i++)
3889
0
            {
3890
0
                panJPEGBlockOffset[i] = psImage->panBlockStart[i];
3891
0
                if (panJPEGBlockOffset[i] != static_cast<vsi_l_offset>(-1) &&
3892
0
                    panJPEGBlockOffset[i] != UINT_MAX)
3893
0
                {
3894
0
                    vsi_l_offset nOffset = panJPEGBlockOffset[i];
3895
0
                    bool bError = false;
3896
0
                    nQLevel = ScanJPEGQLevel(&nOffset, &bError);
3897
                    /* The beginning of the JPEG stream should be the offset */
3898
                    /* from the panBlockStart table */
3899
0
                    if (bError || nOffset != panJPEGBlockOffset[i])
3900
0
                    {
3901
0
                        CPLError(CE_Failure, CPLE_AppDefined,
3902
0
                                 "JPEG block doesn't start at expected offset");
3903
0
                        return CE_Failure;
3904
0
                    }
3905
0
                }
3906
0
            }
3907
0
        }
3908
7
        else /* 'C3' case */
3909
7
        {
3910
            /* --------------------------------------------------------------------
3911
             */
3912
            /*      Scan through the whole image data stream identifying all */
3913
            /*      block boundaries. */
3914
            /* --------------------------------------------------------------------
3915
             */
3916
7
            eErr = ScanJPEGBlocks();
3917
7
            if (eErr != CE_None)
3918
6
                return eErr;
3919
7
        }
3920
7
    }
3921
3922
    /* -------------------------------------------------------------------- */
3923
    /*    Allocate image data block (where the uncompressed image will go)  */
3924
    /* -------------------------------------------------------------------- */
3925
1
    if (pabyJPEGBlock == nullptr)
3926
1
    {
3927
        /* Allocate enough memory to hold 12bit JPEG data */
3928
1
        pabyJPEGBlock = static_cast<GByte *>(VSI_CALLOC_VERBOSE(
3929
1
            psImage->nBands, static_cast<size_t>(psImage->nBlockWidth) *
3930
1
                                 psImage->nBlockHeight * 2));
3931
1
        if (pabyJPEGBlock == nullptr)
3932
0
        {
3933
0
            return CE_Failure;
3934
0
        }
3935
1
    }
3936
3937
    /* -------------------------------------------------------------------- */
3938
    /*      Read JPEG Chunk.                                                */
3939
    /* -------------------------------------------------------------------- */
3940
1
    const int iBlock = iBlockX + iBlockY * psImage->nBlocksPerRow;
3941
3942
1
    if (panJPEGBlockOffset[iBlock] == static_cast<vsi_l_offset>(-1) ||
3943
1
        panJPEGBlockOffset[iBlock] == UINT_MAX)
3944
0
    {
3945
0
        memset(pabyJPEGBlock, 0,
3946
0
               static_cast<size_t>(psImage->nBands) * psImage->nBlockWidth *
3947
0
                   psImage->nBlockHeight * 2);
3948
0
        return CE_None;
3949
0
    }
3950
3951
1
    CPLString osFilename;
3952
1
    osFilename.Printf("JPEG_SUBFILE:Q%d," CPL_FRMT_GUIB ",%d,%s", nQLevel,
3953
1
                      panJPEGBlockOffset[iBlock], 0, osNITFFilename.c_str());
3954
3955
1
    GDALDataset *poDS =
3956
1
        GDALDataset::FromHandle(GDALOpen(osFilename, GA_ReadOnly));
3957
1
    if (poDS == nullptr)
3958
0
        return CE_Failure;
3959
3960
1
    if (poDS->GetRasterXSize() != psImage->nBlockWidth ||
3961
1
        poDS->GetRasterYSize() != psImage->nBlockHeight)
3962
0
    {
3963
0
        CPLError(CE_Failure, CPLE_AppDefined,
3964
0
                 "JPEG block %d not same size as NITF blocksize.", iBlock);
3965
0
        delete poDS;
3966
0
        return CE_Failure;
3967
0
    }
3968
3969
1
    if (poDS->GetRasterCount() < psImage->nBands)
3970
0
    {
3971
0
        CPLError(CE_Failure, CPLE_AppDefined,
3972
0
                 "JPEG block %d has not enough bands.", iBlock);
3973
0
        delete poDS;
3974
0
        return CE_Failure;
3975
0
    }
3976
3977
1
    if (poDS->GetRasterBand(1)->GetRasterDataType() !=
3978
1
        GetRasterBand(1)->GetRasterDataType())
3979
0
    {
3980
0
        CPLError(
3981
0
            CE_Failure, CPLE_AppDefined,
3982
0
            "JPEG block %d data type (%s) not consistent with band data type "
3983
0
            "(%s).",
3984
0
            iBlock,
3985
0
            GDALGetDataTypeName(poDS->GetRasterBand(1)->GetRasterDataType()),
3986
0
            GDALGetDataTypeName(GetRasterBand(1)->GetRasterDataType()));
3987
0
        delete poDS;
3988
0
        return CE_Failure;
3989
0
    }
3990
3991
1
    int anBands[3] = {1, 2, 3};
3992
1
    eErr = poDS->RasterIO(GF_Read, 0, 0, psImage->nBlockWidth,
3993
1
                          psImage->nBlockHeight, pabyJPEGBlock,
3994
1
                          psImage->nBlockWidth, psImage->nBlockHeight,
3995
1
                          GetRasterBand(1)->GetRasterDataType(),
3996
1
                          psImage->nBands, anBands, 0, 0, 0, nullptr);
3997
3998
1
    delete poDS;
3999
4000
1
    return eErr;
4001
1
}
4002
4003
/************************************************************************/
4004
/*                            GetFileList()                             */
4005
/************************************************************************/
4006
4007
char **NITFDataset::GetFileList()
4008
4009
432
{
4010
432
    char **papszFileList = GDALPamDataset::GetFileList();
4011
4012
    // Small optimization to avoid useless file probing.
4013
432
    if (CSLCount(papszFileList) == 0)
4014
0
        return papszFileList;
4015
4016
    /* -------------------------------------------------------------------- */
4017
    /*      Check for .imd file.                                            */
4018
    /* -------------------------------------------------------------------- */
4019
432
    papszFileList = AddFile(papszFileList, "IMD", "imd");
4020
4021
    /* -------------------------------------------------------------------- */
4022
    /*      Check for .rpb file.                                            */
4023
    /* -------------------------------------------------------------------- */
4024
432
    papszFileList = AddFile(papszFileList, "RPB", "rpb");
4025
4026
432
    if (!m_osRPCTXTFilename.empty())
4027
0
        papszFileList = CSLAddString(papszFileList, m_osRPCTXTFilename);
4028
4029
    /* -------------------------------------------------------------------- */
4030
    /*      Check for other files.                                          */
4031
    /* -------------------------------------------------------------------- */
4032
432
    papszFileList = AddFile(papszFileList, "ATT", "att");
4033
432
    papszFileList = AddFile(papszFileList, "EPH", "eph");
4034
432
    papszFileList = AddFile(papszFileList, "GEO", "geo");
4035
432
    papszFileList = AddFile(papszFileList, "XML", "xml");
4036
4037
432
    return papszFileList;
4038
432
}
4039
4040
/************************************************************************/
4041
/*                              AddFile()                               */
4042
/*                                                                      */
4043
/*      Helper method for GetFileList()                                 */
4044
/************************************************************************/
4045
char **NITFDataset::AddFile(char **papszFileList, const char *EXTENSION,
4046
                            const char *extension)
4047
2.59k
{
4048
2.59k
    VSIStatBufL sStatBuf;
4049
2.59k
    CPLString osTarget = CPLResetExtensionSafe(osNITFFilename, EXTENSION);
4050
2.59k
    if (oOvManager.GetSiblingFiles() != nullptr)
4051
2.59k
    {
4052
2.59k
        if (CSLFindStringCaseSensitive(oOvManager.GetSiblingFiles(),
4053
2.59k
                                       CPLGetFilename(osTarget)) >= 0)
4054
0
            papszFileList = CSLAddString(papszFileList, osTarget);
4055
2.59k
        else
4056
2.59k
        {
4057
2.59k
            osTarget = CPLResetExtensionSafe(osNITFFilename, extension);
4058
2.59k
            if (CSLFindStringCaseSensitive(oOvManager.GetSiblingFiles(),
4059
2.59k
                                           CPLGetFilename(osTarget)) >= 0)
4060
4
                papszFileList = CSLAddString(papszFileList, osTarget);
4061
2.59k
        }
4062
2.59k
    }
4063
0
    else
4064
0
    {
4065
0
        if (VSIStatL(osTarget, &sStatBuf) == 0)
4066
0
            papszFileList = CSLAddString(papszFileList, osTarget);
4067
0
        else
4068
0
        {
4069
0
            osTarget = CPLResetExtensionSafe(osNITFFilename, extension);
4070
0
            if (VSIStatL(osTarget, &sStatBuf) == 0)
4071
0
                papszFileList = CSLAddString(papszFileList, osTarget);
4072
0
        }
4073
0
    }
4074
4075
2.59k
    return papszFileList;
4076
2.59k
}
4077
4078
/************************************************************************/
4079
/*                         GDALToNITFDataType()                         */
4080
/************************************************************************/
4081
4082
static const char *GDALToNITFDataType(GDALDataType eType)
4083
4084
20
{
4085
20
    const char *pszPVType = nullptr;
4086
4087
20
    switch (eType)
4088
20
    {
4089
9
        case GDT_UInt8:
4090
15
        case GDT_UInt16:
4091
15
        case GDT_UInt32:
4092
15
            pszPVType = "INT";
4093
15
            break;
4094
4095
1
        case GDT_Int16:
4096
1
        case GDT_Int32:
4097
1
            pszPVType = "SI";
4098
1
            break;
4099
4100
1
        case GDT_Float32:
4101
4
        case GDT_Float64:
4102
4
            pszPVType = "R";
4103
4
            break;
4104
4105
0
        case GDT_CInt16:
4106
0
        case GDT_CInt32:
4107
0
            CPLError(CE_Failure, CPLE_AppDefined,
4108
0
                     "NITF format does not support complex integer data.");
4109
0
            return nullptr;
4110
4111
0
        case GDT_CFloat32:
4112
0
            pszPVType = "C";
4113
0
            break;
4114
4115
0
        default:
4116
0
            CPLError(CE_Failure, CPLE_AppDefined,
4117
0
                     "Unsupported raster pixel type (%s).",
4118
0
                     GDALGetDataTypeName(eType));
4119
0
            return nullptr;
4120
20
    }
4121
4122
20
    return pszPVType;
4123
20
}
4124
4125
/************************************************************************/
4126
/*                          NITFJP2ECWOptions()                         */
4127
/*                                                                      */
4128
/*      Prepare JP2-in-NITF creation options based in part of the       */
4129
/*      NITF creation options.                                          */
4130
/************************************************************************/
4131
4132
static CPLStringList NITFJP2ECWOptions(const CPLStringList &aosOptionsIn)
4133
4134
0
{
4135
0
    CPLStringList aoJP2ECWOptions;
4136
0
    aoJP2ECWOptions.AddString("PROFILE=NPJE");
4137
0
    aoJP2ECWOptions.AddString("CODESTREAM_ONLY=TRUE");
4138
4139
0
    for (int i = 0; i < aosOptionsIn.size(); ++i)
4140
0
    {
4141
0
        if (STARTS_WITH_CI(aosOptionsIn[i], "PROFILE="))
4142
0
        {
4143
0
            aoJP2ECWOptions.SetNameValue("PROFILE",
4144
0
                                         aosOptionsIn[i] + strlen("PROFILE="));
4145
0
        }
4146
0
        else if (STARTS_WITH_CI(aosOptionsIn[i], "TARGET="))
4147
0
            aoJP2ECWOptions.AddString(aosOptionsIn[i]);
4148
0
    }
4149
4150
0
    return aoJP2ECWOptions;
4151
0
}
4152
4153
/************************************************************************/
4154
/*                           NITFJP2KAKOptions()                        */
4155
/*                                                                      */
4156
/*      Prepare JP2-in-NITF creation options based in part of the       */
4157
/*      NITF creation options.                                          */
4158
/************************************************************************/
4159
4160
static CPLStringList NITFJP2KAKOptions(const CPLStringList &aosOptionsIn,
4161
                                       int nABPP)
4162
4163
0
{
4164
0
    CPLStringList aoJP2KAKOptions;
4165
0
    aoJP2KAKOptions.AddString("CODEC=J2K");
4166
4167
0
    for (int i = 0; i < aosOptionsIn.size(); ++i)
4168
0
    {
4169
0
        if (STARTS_WITH_CI(aosOptionsIn[i], "QUALITY=") ||
4170
0
            STARTS_WITH_CI(aosOptionsIn[i], "BLOCKXSIZE=") ||
4171
0
            STARTS_WITH_CI(aosOptionsIn[i], "BLOCKYSIZE=") ||
4172
0
            STARTS_WITH_CI(aosOptionsIn[i], "LAYERS=") ||
4173
0
            STARTS_WITH_CI(aosOptionsIn[i], "ROI="))
4174
0
        {
4175
0
            aoJP2KAKOptions.AddString(aosOptionsIn[i]);
4176
0
        }
4177
0
    }
4178
4179
0
    aoJP2KAKOptions.SetNameValue("NBITS", CPLSPrintf("%d", nABPP));
4180
4181
0
    return aoJP2KAKOptions;
4182
0
}
4183
4184
/************************************************************************/
4185
/*                      NITFJP2OPENJPEGOptions()                        */
4186
/*                                                                      */
4187
/*      Prepare JP2-in-NITF creation options based in part of the       */
4188
/*      NITF creation options.                                          */
4189
/************************************************************************/
4190
4191
static CPLStringList NITFJP2OPENJPEGOptions(GDALDriver *poJ2KDriver,
4192
                                            const CPLStringList &aosOptionsIn,
4193
                                            int nABPP)
4194
4195
0
{
4196
0
    CPLStringList aoJP2OJPOptions;
4197
0
    aoJP2OJPOptions.AddString("CODEC=J2K");
4198
4199
0
    const char *pszQuality = aosOptionsIn.FetchNameValue("QUALITY");
4200
0
    double dfQuality = 0;
4201
0
    if (pszQuality)
4202
0
    {
4203
0
        for (const char *pszVal :
4204
0
             CPLStringList(CSLTokenizeString2(pszQuality, ",", 0)))
4205
0
            dfQuality = std::max(dfQuality, CPLAtof(pszVal));
4206
0
    }
4207
4208
0
    double dfTarget = CPLAtof(aosOptionsIn.FetchNameValueDef("TARGET", "0"));
4209
4210
0
    if (dfTarget > 0 && dfTarget < 100)
4211
0
        dfQuality = 100. - dfTarget;
4212
4213
0
    for (int i = 0; i < aosOptionsIn.size(); ++i)
4214
0
    {
4215
0
        if (STARTS_WITH_CI(aosOptionsIn[i], "BLOCKXSIZE=") ||
4216
0
            STARTS_WITH_CI(aosOptionsIn[i], "BLOCKYSIZE="))
4217
0
        {
4218
0
            aoJP2OJPOptions.AddString(aosOptionsIn[i]);
4219
0
        }
4220
0
    }
4221
4222
    // Set it now before the NPJE profiles have a chance to override it
4223
0
    if (pszQuality)
4224
0
    {
4225
0
        aoJP2OJPOptions.SetNameValue("QUALITY", pszQuality);
4226
0
    }
4227
4228
0
    const char *pszProfile = aosOptionsIn.FetchNameValueDef("PROFILE", "");
4229
0
    if (STARTS_WITH_CI(pszProfile, "NPJE"))
4230
0
    {
4231
        // Follow STDI-0006 NCDRD "2.3 Data Compression - JPEG 2000" and
4232
        // ISO/IEC BIIF Profile BPJ2K01.10
4233
        // (https://nsgreg.nga.mil/doc/view?i=2031&month=3&day=22&year=2021),
4234
        // for NPJE (Appendix D ) profile
4235
4236
0
        if (pszQuality && strchr(pszQuality, ','))
4237
0
        {
4238
0
            CPLError(CE_Warning, CPLE_AppDefined,
4239
0
                     "Only largest value of QUALITY used when PROFILE=%s "
4240
0
                     "is specified",
4241
0
                     pszProfile);
4242
0
        }
4243
4244
0
        aoJP2OJPOptions.AddString("@BLOCKSIZE_STRICT=YES");
4245
4246
        // Empty PRECINCTS option to ask for no custom precincts
4247
0
        aoJP2OJPOptions.AddString("PRECINCTS=");
4248
4249
0
#if defined(__GNUC__)
4250
0
#pragma GCC diagnostic push
4251
0
#pragma GCC diagnostic ignored "-Warray-bounds"
4252
0
#endif
4253
        // See Table 2.3-3 - Target Bit Rates for Each Tile in Panchromatic
4254
        // Image Segments of STDI-0006
4255
0
        std::vector<double> adfBPP = {
4256
0
            0.03125, 0.0625, 0.125, 0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
4257
0
            1.1,     1.2,    1.3,   1.5,  1.7, 2.0, 2.3, 3.5, 3.9};
4258
0
        if (STARTS_WITH_CI(pszProfile, "NPJE_NUMERICALLY_LOSSLESS"))
4259
0
        {
4260
            // given that we consider a compression ratio afterwards, we
4261
            // arbitrarily consider a Byte datatype, and thus lossless quality
4262
            // is achieved at worse with 8 bpp
4263
0
            adfBPP.push_back(8.0);
4264
4265
            // Lossless 5x3 wavelet
4266
0
            aoJP2OJPOptions.AddString("REVERSIBLE=YES");
4267
0
        }
4268
0
#if defined(__GNUC__)
4269
0
#pragma GCC diagnostic pop
4270
0
#endif
4271
4272
0
        std::string osQuality;
4273
0
        for (double dfBPP : adfBPP)
4274
0
        {
4275
0
            if (!osQuality.empty())
4276
0
                osQuality += ',';
4277
            // the JP2OPENJPEG QUALITY setting is 100. / compression_ratio
4278
            // and compression_ratio = 8 / bpp
4279
0
            double dfLayerQuality = 100.0 / (8.0 / dfBPP);
4280
0
            if (dfLayerQuality > dfQuality && dfQuality != 0.0)
4281
0
            {
4282
0
                osQuality += CPLSPrintf("%f", dfQuality);
4283
0
                break;
4284
0
            }
4285
0
            osQuality += CPLSPrintf("%f", dfLayerQuality);
4286
0
        }
4287
0
        aoJP2OJPOptions.SetNameValue("QUALITY", osQuality.c_str());
4288
4289
0
        aoJP2OJPOptions.AddString("PROGRESSION=LRCP");
4290
4291
        // Disable MCT
4292
0
        aoJP2OJPOptions.AddString("YCC=NO");
4293
4294
        // TLM option added in OpenJPEG 2.5
4295
0
        if (strstr(poJ2KDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
4296
0
                   "TLM") != nullptr)
4297
0
        {
4298
0
            aoJP2OJPOptions.AddString("PLT=YES");
4299
0
            aoJP2OJPOptions.AddString("TLM=YES");
4300
0
        }
4301
0
        else
4302
0
        {
4303
0
            CPLError(CE_Warning, CPLE_AppDefined,
4304
0
                     "TLM option not available in JP2OPENJPEG driver. "
4305
0
                     "Use OpenJPEG 2.5 or later");
4306
0
        }
4307
4308
0
        aoJP2OJPOptions.AddString("RESOLUTIONS=6");
4309
0
    }
4310
0
    else if (EQUAL(pszProfile, "PROFILE_1"))
4311
0
    {
4312
0
        aoJP2OJPOptions.AddString("PROFILE=PROFILE_1");
4313
0
    }
4314
0
    else if (EQUAL(pszProfile, "PROFILE_2"))
4315
0
    {
4316
0
        aoJP2OJPOptions.AddString("PROFILE=UNRESTRICTED");
4317
0
    }
4318
4319
0
    aoJP2OJPOptions.SetNameValue("NBITS", CPLSPrintf("%d", nABPP));
4320
4321
0
    return aoJP2OJPOptions;
4322
0
}
4323
4324
/************************************************************************/
4325
/*                NITFExtractTEXTAndCGMCreationOption()                 */
4326
/************************************************************************/
4327
4328
static CPLStringList NITFExtractTEXTAndCGMCreationOption(
4329
    GDALDataset *poSrcDS, CSLConstList papszOptions, CPLStringList &aosTextMD,
4330
    CPLStringList &aosCgmMD)
4331
20
{
4332
20
    CPLStringList aosOptions(CSLDuplicate(papszOptions));
4333
4334
    /* -------------------------------------------------------------------- */
4335
    /*      Prepare for text segments.                                      */
4336
    /* -------------------------------------------------------------------- */
4337
20
    aosTextMD = CSLFetchNameValueMultiple(papszOptions, "TEXT");
4338
    // Notice: CSLFetchNameValueMultiple remove the leading "TEXT=" when
4339
    // returning the list, which is what we want.
4340
4341
    // Use TEXT information from original image if no creation option is passed
4342
    // in.
4343
20
    if (poSrcDS != nullptr && aosTextMD.empty())
4344
20
    {
4345
        // Read CGM adata from original image, duplicate the list because
4346
        // we frees papszCgmMD at end of the function.
4347
20
        aosTextMD = CSLDuplicate(poSrcDS->GetMetadata("TEXT"));
4348
20
    }
4349
4350
20
    int nNUMT = 0;
4351
20
    for (int iOpt = 0; iOpt < aosTextMD.size(); iOpt++)
4352
0
    {
4353
0
        if (!STARTS_WITH_CI(aosTextMD[iOpt], "DATA_"))
4354
0
            continue;
4355
4356
0
        nNUMT++;
4357
0
    }
4358
4359
20
    if (nNUMT > 0)
4360
0
    {
4361
0
        aosOptions.SetNameValue("NUMT", std::to_string(nNUMT).c_str());
4362
0
    }
4363
4364
    /* -------------------------------------------------------------------- */
4365
    /*      Prepare for CGM segments.                                       */
4366
    /* -------------------------------------------------------------------- */
4367
20
    aosCgmMD = CSLFetchNameValueMultiple(papszOptions, "CGM");
4368
    // Notice: CSLFetchNameValueMultiple remove the leading "CGM=" when
4369
    // returning the list, which is what we want.
4370
4371
    // Use CGM information from original image if no creation option is passed
4372
    // in.
4373
20
    if (poSrcDS != nullptr && aosCgmMD.empty())
4374
20
    {
4375
        // Read CGM adata from original image, duplicate the list because
4376
        // we frees papszCgmMD at end of the function.
4377
20
        aosCgmMD = CSLDuplicate(poSrcDS->GetMetadata("CGM"));
4378
20
    }
4379
4380
    // Set NUMS based on the number of segments
4381
20
    const char *pszNUMS;  // graphic segment option string
4382
20
    int nNUMS = 0;
4383
20
    if (!aosCgmMD.empty())
4384
0
    {
4385
0
        pszNUMS = aosCgmMD.FetchNameValue("SEGMENT_COUNT");
4386
4387
0
        if (pszNUMS != nullptr)
4388
0
        {
4389
0
            nNUMS = atoi(pszNUMS);
4390
0
        }
4391
0
        aosOptions.SetNameValue("NUMS", std::to_string(nNUMS).c_str());
4392
0
    }
4393
4394
20
    return aosOptions;
4395
20
}
4396
4397
/************************************************************************/
4398
/*                         NITFDatasetCreate()                          */
4399
/************************************************************************/
4400
4401
GDALDataset *NITFDataset::NITFDatasetCreate(const char *pszFilename, int nXSize,
4402
                                            int nYSize, int nBandsIn,
4403
                                            GDALDataType eType,
4404
                                            CSLConstList papszOptions)
4405
4406
0
{
4407
0
    const char *pszPVType = GDALToNITFDataType(eType);
4408
0
    if (pszPVType == nullptr)
4409
0
        return nullptr;
4410
4411
0
    const char *pszProduct = CSLFetchNameValue(papszOptions, "PRODUCT_TYPE");
4412
0
    if (pszProduct && EQUAL(pszProduct, "CADRG"))
4413
0
    {
4414
0
        CPLError(CE_Failure, CPLE_NotSupported,
4415
0
                 "CADRG creation only supported in CreateCopy()");
4416
0
        return nullptr;
4417
0
    }
4418
4419
0
    const char *pszIC = CSLFetchNameValue(papszOptions, "IC");
4420
4421
    /* -------------------------------------------------------------------- */
4422
    /*      We disallow any IC value except NC when creating this way.      */
4423
    /* -------------------------------------------------------------------- */
4424
0
    GDALDriver *poJ2KDriver = nullptr;
4425
4426
0
    if (pszIC != nullptr && EQUAL(pszIC, "C8"))
4427
0
    {
4428
0
        bool bHasCreate = false;
4429
4430
0
        poJ2KDriver = GetGDALDriverManager()->GetDriverByName("JP2ECW");
4431
0
        if (poJ2KDriver != nullptr)
4432
0
            bHasCreate = poJ2KDriver->GetMetadataItem(GDAL_DCAP_CREATE,
4433
0
                                                      nullptr) != nullptr;
4434
0
        if (!bHasCreate)
4435
0
        {
4436
0
            CPLError(
4437
0
                CE_Failure, CPLE_AppDefined,
4438
0
                "Unable to create JPEG2000 encoded NITF files.  The\n"
4439
0
                "JP2ECW driver is unavailable, or missing Create support.");
4440
0
            return nullptr;
4441
0
        }
4442
4443
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "J2KLRA", "NO")))
4444
0
        {
4445
0
            CPLError(CE_Warning, CPLE_NotSupported,
4446
0
                     "J2KLRA TRE can only be written in CreateCopy() mode, and "
4447
0
                     "when using the JP2OPENJPEG driver in NPJE profiles");
4448
0
        }
4449
0
    }
4450
4451
0
    else if (pszIC != nullptr && !EQUAL(pszIC, "NC"))
4452
0
    {
4453
0
        CPLError(CE_Failure, CPLE_AppDefined,
4454
0
                 "Unsupported compression (IC=%s) used in direct\n"
4455
0
                 "NITF File creation",
4456
0
                 pszIC);
4457
0
        return nullptr;
4458
0
    }
4459
4460
0
    const char *const apszIgnoredOptions[] = {"SDE_TRE", "RPC00B", "RPCTXT",
4461
0
                                              nullptr};
4462
0
    for (int i = 0; apszIgnoredOptions[i] != nullptr; ++i)
4463
0
    {
4464
0
        if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
4465
0
        {
4466
0
            CPLError(CE_Warning, CPLE_AppDefined,
4467
0
                     "%s creation option ignored by Create() method "
4468
0
                     "(only valid in CreateCopy())",
4469
0
                     apszIgnoredOptions[i]);
4470
0
        }
4471
0
    }
4472
4473
    /* -------------------------------------------------------------------- */
4474
    /*      Prepare for text and CGM segments.                              */
4475
    /* -------------------------------------------------------------------- */
4476
0
    CPLStringList aosTextMD, aosCgmMD;
4477
0
    CPLStringList aosOptions(NITFExtractTEXTAndCGMCreationOption(
4478
0
        nullptr, papszOptions, aosTextMD, aosCgmMD));
4479
4480
0
    const char *pszBlockSize = aosOptions.FetchNameValue("BLOCKSIZE");
4481
0
    if (pszBlockSize != nullptr &&
4482
0
        aosOptions.FetchNameValue("BLOCKXSIZE") == nullptr)
4483
0
    {
4484
0
        aosOptions.SetNameValue("BLOCKXSIZE", pszBlockSize);
4485
0
    }
4486
0
    if (pszBlockSize != nullptr &&
4487
0
        aosOptions.FetchNameValue("BLOCKYSIZE") == nullptr)
4488
0
    {
4489
0
        aosOptions.SetNameValue("BLOCKYSIZE", pszBlockSize);
4490
0
    }
4491
4492
0
    if (const char *pszNBITS = aosOptions.FetchNameValue("NBITS"))
4493
0
    {
4494
0
        aosOptions.SetNameValue("ABPP", pszNBITS);
4495
0
    }
4496
4497
    /* -------------------------------------------------------------------- */
4498
    /*      Create the file.                                                */
4499
    /* -------------------------------------------------------------------- */
4500
4501
0
    int nIMIndex = 0;
4502
0
    int nImageCount = 0;
4503
0
    vsi_l_offset nImageOffset = 0;
4504
0
    vsi_l_offset nICOffset = 0;
4505
0
    if (!NITFCreateEx(pszFilename, nXSize, nYSize, nBandsIn,
4506
0
                      GDALGetDataTypeSizeBits(eType), pszPVType,
4507
0
                      aosOptions.List(), &nIMIndex, &nImageCount, &nImageOffset,
4508
0
                      &nICOffset, nullptr))
4509
0
    {
4510
0
        return nullptr;
4511
0
    }
4512
4513
    /* -------------------------------------------------------------------- */
4514
    /*      Various special hacks related to JPEG2000 encoded files.        */
4515
    /* -------------------------------------------------------------------- */
4516
0
    GDALDataset *poWritableJ2KDataset = nullptr;
4517
0
    if (poJ2KDriver)
4518
0
    {
4519
0
        CPLString osDSName;
4520
4521
0
        osDSName.Printf("/vsisubfile/" CPL_FRMT_GUIB "_%d,%s",
4522
0
                        static_cast<GUIntBig>(nImageOffset), -1, pszFilename);
4523
4524
0
        poWritableJ2KDataset =
4525
0
            poJ2KDriver->Create(osDSName, nXSize, nYSize, nBandsIn, eType,
4526
0
                                NITFJP2ECWOptions(aosOptions).List());
4527
4528
0
        if (poWritableJ2KDataset == nullptr)
4529
0
        {
4530
0
            return nullptr;
4531
0
        }
4532
0
    }
4533
4534
    /* -------------------------------------------------------------------- */
4535
    /*      Open the dataset in update mode.                                */
4536
    /* -------------------------------------------------------------------- */
4537
0
    GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
4538
0
    NITFDataset *poDS = NITFDataset::OpenInternal(
4539
0
        &oOpenInfo, poWritableJ2KDataset, true, nIMIndex);
4540
0
    if (poDS)
4541
0
    {
4542
0
        poDS->m_nImageOffset = nImageOffset;
4543
0
        poDS->m_nIMIndex = nIMIndex;
4544
0
        poDS->m_nImageCount = nImageCount;
4545
0
        poDS->m_nICOffset = nICOffset;
4546
0
        poDS->papszTextMDToWrite = aosTextMD.StealList();
4547
0
        poDS->papszCgmMDToWrite = aosCgmMD.StealList();
4548
0
        poDS->aosCreationOptions.Assign(CSLDuplicate(papszOptions), true);
4549
0
    }
4550
0
    return poDS;
4551
0
}
4552
4553
/************************************************************************/
4554
/*                           NITFCreateCopy()                           */
4555
/************************************************************************/
4556
4557
GDALDataset *NITFDataset::NITFCreateCopy(const char *pszFilename,
4558
                                         GDALDataset *poSrcDS, int bStrict,
4559
                                         CSLConstList papszOptions,
4560
                                         GDALProgressFunc pfnProgress,
4561
                                         void *pProgressData)
4562
21
{
4563
21
    CADRGCreateCopyContext copyContext;
4564
21
    return CreateCopy(pszFilename, poSrcDS, bStrict, papszOptions, pfnProgress,
4565
21
                      pProgressData, /* nRecLevel = */ 0, &copyContext)
4566
21
        .release();
4567
21
}
4568
4569
/************************************************************************/
4570
/*                      NITFDataset::CreateCopy()                       */
4571
/************************************************************************/
4572
4573
std::unique_ptr<GDALDataset>
4574
NITFDataset::CreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
4575
                        int bStrict, CSLConstList papszOptions,
4576
                        GDALProgressFunc pfnProgress, void *pProgressData,
4577
                        int nRecLevel, CADRGCreateCopyContext *copyContext)
4578
4579
21
{
4580
21
    if (nRecLevel == 3)
4581
0
    {
4582
0
        CPLError(CE_Failure, CPLE_AppDefined,
4583
0
                 "NITFDataset::CreateCopy(): programming error: too deep "
4584
0
                 "recursion");
4585
0
        return nullptr;
4586
0
    }
4587
4588
21
    int nBands = poSrcDS->GetRasterCount();
4589
21
    if (nBands == 0)
4590
1
    {
4591
1
        CPLError(CE_Failure, CPLE_NotSupported,
4592
1
                 "Unable to export files with zero bands.");
4593
1
        return nullptr;
4594
1
    }
4595
4596
20
    GDALRasterBand *poBand1 = poSrcDS->GetRasterBand(1);
4597
20
    if (poBand1 == nullptr)
4598
0
    {
4599
0
        return nullptr;
4600
0
    }
4601
4602
20
    const char *pszProductType =
4603
20
        CSLFetchNameValue(papszOptions, "PRODUCT_TYPE");
4604
20
    const bool bIsCADRG = (pszProductType && EQUAL(pszProductType, "CADRG"));
4605
4606
    /* -------------------------------------------------------------------- */
4607
    /*      Only allow supported compression values.                        */
4608
    /* -------------------------------------------------------------------- */
4609
20
    bool bJPEG2000 = false;
4610
20
    bool bJPEG = false;
4611
20
    GDALDriver *poJ2KDriver = nullptr;
4612
20
    const char *pszJPEG2000_DRIVER =
4613
20
        CSLFetchNameValue(papszOptions, "JPEG2000_DRIVER");
4614
20
    if (pszJPEG2000_DRIVER != nullptr)
4615
0
        poJ2KDriver =
4616
0
            GetGDALDriverManager()->GetDriverByName(pszJPEG2000_DRIVER);
4617
4618
20
    const char *pszIC = CSLFetchNameValue(papszOptions, "IC");
4619
20
    if (pszIC != nullptr)
4620
0
    {
4621
0
        if (bIsCADRG)
4622
0
        {
4623
0
            if (!EQUAL(pszIC, "C4"))
4624
0
            {
4625
0
                CPLError(CE_Failure, CPLE_NotSupported,
4626
0
                         "CADRG only supports IC=C4");
4627
0
                return nullptr;
4628
0
            }
4629
0
        }
4630
0
        else if (EQUAL(pszIC, "C4"))
4631
0
        {
4632
0
            CPLError(CE_Failure, CPLE_NotSupported,
4633
0
                     "IC=C4 only supported for PRODUCT_TYPE=CADRG");
4634
0
            return nullptr;
4635
0
        }
4636
4637
0
        if (EQUAL(pszIC, "NC"))
4638
0
            /* ok */;
4639
0
        else if (EQUAL(pszIC, "C8"))
4640
0
        {
4641
0
            if (pszJPEG2000_DRIVER == nullptr)
4642
0
            {
4643
0
                poJ2KDriver = GetGDALDriverManager()->GetDriverByName("JP2ECW");
4644
0
                if (poJ2KDriver == nullptr ||
4645
0
                    poJ2KDriver->GetMetadataItem(GDAL_DCAP_CREATECOPY,
4646
0
                                                 nullptr) == nullptr)
4647
0
                {
4648
                    /* Try with  JP2KAK as an alternate driver */
4649
0
                    poJ2KDriver =
4650
0
                        GetGDALDriverManager()->GetDriverByName("JP2KAK");
4651
0
                }
4652
0
                if (poJ2KDriver == nullptr)
4653
0
                {
4654
                    /* Try with JP2OPENJPEG as an alternate driver */
4655
0
                    poJ2KDriver =
4656
0
                        GetGDALDriverManager()->GetDriverByName("JP2OPENJPEG");
4657
0
                }
4658
0
            }
4659
0
            if (poJ2KDriver == nullptr)
4660
0
            {
4661
0
                CPLError(CE_Failure, CPLE_AppDefined,
4662
0
                         "Unable to write JPEG2000 compressed NITF file.\n"
4663
0
                         "No 'subfile' JPEG2000 write supporting drivers are\n"
4664
0
                         "configured.");
4665
0
                return nullptr;
4666
0
            }
4667
4668
0
            if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "J2KLRA", "NO")))
4669
0
            {
4670
0
                if (!EQUAL(poJ2KDriver->GetDescription(), "JP2OPENJPEG"))
4671
0
                {
4672
0
                    CPLError(
4673
0
                        CE_Warning, CPLE_NotSupported,
4674
0
                        "J2KLRA TRE can only be written "
4675
0
                        "when using the JP2OPENJPEG driver in NPJE profiles");
4676
0
                }
4677
0
                else if (!STARTS_WITH_CI(
4678
0
                             CSLFetchNameValueDef(papszOptions, "PROFILE", ""),
4679
0
                             "NPJE"))
4680
0
                {
4681
0
                    CPLError(CE_Warning, CPLE_NotSupported,
4682
0
                             "J2KLRA TRE can only be written in NPJE profiles");
4683
0
                }
4684
0
            }
4685
0
            bJPEG2000 = TRUE;
4686
0
        }
4687
0
        else if (EQUAL(pszIC, "C3") || EQUAL(pszIC, "M3"))
4688
0
        {
4689
0
            bJPEG = TRUE;
4690
#ifndef JPEG_SUPPORTED
4691
            CPLError(CE_Failure, CPLE_AppDefined,
4692
                     "Unable to write JPEG compressed NITF file.\n"
4693
                     "Libjpeg is not configured into build.");
4694
            return nullptr;
4695
#endif
4696
0
        }
4697
0
        else
4698
0
        {
4699
0
            CPLError(CE_Failure, CPLE_AppDefined,
4700
0
                     "Only IC=NC (uncompressed), IC=C3/M3 (JPEG) and IC=C8 "
4701
0
                     "(JPEG2000)\n"
4702
0
                     "allowed with NITF CreateCopy method.");
4703
0
            return nullptr;
4704
0
        }
4705
0
    }
4706
4707
    /* -------------------------------------------------------------------- */
4708
    /*      Get the data type.  Complex integers isn't supported by         */
4709
    /*      NITF, so map that to complex float if we aren't in strict       */
4710
    /*      mode.                                                           */
4711
    /* -------------------------------------------------------------------- */
4712
20
    GDALDataType eType = poBand1->GetRasterDataType();
4713
20
    if (!bStrict && (eType == GDT_CInt16 || eType == GDT_CInt32))
4714
0
        eType = GDT_CFloat32;
4715
4716
    /* -------------------------------------------------------------------- */
4717
    /*      CADRG related checks and pre-processing                         */
4718
    /* -------------------------------------------------------------------- */
4719
20
    if (bIsCADRG)
4720
0
    {
4721
0
        auto ret =
4722
0
            CADRGCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
4723
0
                            pfnProgress, pProgressData, nRecLevel, copyContext);
4724
0
        if (std::holds_alternative<std::unique_ptr<GDALDataset>>(ret))
4725
0
        {
4726
0
            return std::move(std::get<std::unique_ptr<GDALDataset>>(ret));
4727
0
        }
4728
0
        CPLAssert(std::holds_alternative<bool>(ret));
4729
0
        const bool bGoOn = std::get<bool>(ret);
4730
0
        if (!bGoOn)
4731
0
        {
4732
0
            return nullptr;
4733
0
        }
4734
0
    }
4735
4736
    /* -------------------------------------------------------------------- */
4737
    /*      Prepare for text and CGM segments.                              */
4738
    /* -------------------------------------------------------------------- */
4739
20
    CPLStringList aosTextMD, aosCgmMD;
4740
20
    CPLStringList aosOptions(NITFExtractTEXTAndCGMCreationOption(
4741
20
        poSrcDS, papszOptions, aosTextMD, aosCgmMD));
4742
4743
20
    const char *pszBlockSize = aosOptions.FetchNameValue("BLOCKSIZE");
4744
20
    if (bIsCADRG)
4745
0
    {
4746
0
        if (pszBlockSize && atoi(pszBlockSize) != 256)
4747
0
        {
4748
0
            CPLError(CE_Failure, CPLE_AppDefined,
4749
0
                     "CADRG only supports BLOCKSIZE=256");
4750
0
            return nullptr;
4751
0
        }
4752
0
        else
4753
0
        {
4754
0
            pszBlockSize = "256";
4755
0
        }
4756
0
    }
4757
20
    if (pszBlockSize != nullptr &&
4758
0
        aosOptions.FetchNameValue("BLOCKXSIZE") == nullptr)
4759
0
    {
4760
0
        aosOptions.SetNameValue("BLOCKXSIZE", pszBlockSize);
4761
0
    }
4762
20
    if (pszBlockSize != nullptr &&
4763
0
        aosOptions.FetchNameValue("BLOCKYSIZE") == nullptr)
4764
0
    {
4765
0
        aosOptions.SetNameValue("BLOCKYSIZE", pszBlockSize);
4766
0
    }
4767
4768
    /* -------------------------------------------------------------------- */
4769
    /*      Copy over other source metadata items as creation options       */
4770
    /*      that seem useful, unless they are already set as creation       */
4771
    /*      options.                                                        */
4772
    /* -------------------------------------------------------------------- */
4773
20
    const bool bUseSrcNITFMetadata =
4774
20
        CPLFetchBool(papszOptions, "USE_SRC_NITF_METADATA", true);
4775
20
    CSLConstList papszSrcMD = poSrcDS->GetMetadata();
4776
4777
240
    for (int iMD = 0; bUseSrcNITFMetadata && papszSrcMD && papszSrcMD[iMD];
4778
220
         iMD++)
4779
220
    {
4780
220
        bool bPreserveSrcMDAsCreationOption = false;
4781
220
        if (STARTS_WITH_CI(papszSrcMD[iMD], "NITF_BLOCKA"))
4782
0
        {
4783
0
            bPreserveSrcMDAsCreationOption =
4784
0
                CSLPartialFindString(papszOptions, "BLOCKA_") < 0 &&
4785
0
                CSLPartialFindString(papszOptions, "TRE=BLOCKA=") < 0;
4786
0
        }
4787
220
        else if (STARTS_WITH_CI(papszSrcMD[iMD], "NITF_FHDR"))
4788
0
        {
4789
0
            bPreserveSrcMDAsCreationOption =
4790
0
                CSLFetchNameValue(papszOptions, "FHDR") == nullptr;
4791
0
        }
4792
220
        if (bPreserveSrcMDAsCreationOption)
4793
0
        {
4794
0
            char *pszName = nullptr;
4795
0
            const char *pszValue = CPLParseNameValue(papszSrcMD[iMD], &pszName);
4796
0
            if (pszName && aosOptions.FetchNameValue(pszName + 5) == nullptr)
4797
0
            {
4798
0
                aosOptions.SetNameValue(pszName + 5, pszValue);
4799
0
            }
4800
0
            CPLFree(pszName);
4801
0
        }
4802
220
    }
4803
4804
    /* -------------------------------------------------------------------- */
4805
    /*      Copy TRE definitions as creation options, unless they are       */
4806
    /*      already set as creation options.                                */
4807
    /* -------------------------------------------------------------------- */
4808
20
    papszSrcMD = poSrcDS->GetMetadata("TRE");
4809
4810
20
    for (int iMD = 0; bUseSrcNITFMetadata && papszSrcMD && papszSrcMD[iMD];
4811
20
         iMD++)
4812
0
    {
4813
0
        CPLString osTRE;
4814
4815
0
        if (STARTS_WITH_CI(papszSrcMD[iMD], "RPFHDR") ||
4816
0
            STARTS_WITH_CI(papszSrcMD[iMD], "RPFIMG") ||
4817
0
            STARTS_WITH_CI(papszSrcMD[iMD], "RPFDES"))
4818
0
        {
4819
            /* Do not copy RPF TRE. They contain absolute offsets */
4820
            /* No chance that they make sense in the new NITF file */
4821
0
            continue;
4822
0
        }
4823
0
        if (STARTS_WITH_CI(papszSrcMD[iMD], "BLOCKA") &&
4824
0
            CSLPartialFindString(papszOptions, "BLOCKA_") >= 0)
4825
0
        {
4826
            /* Do not copy BLOCKA TRE if there are BLOCKA_ creation options */
4827
0
            continue;
4828
0
        }
4829
4830
0
        osTRE = "TRE=";
4831
0
        osTRE += papszSrcMD[iMD];
4832
4833
0
        char *pszName = nullptr;
4834
0
        CPLParseNameValue(papszSrcMD[iMD], &pszName);
4835
0
        if (pszName != nullptr &&
4836
0
            CSLPartialFindString(papszOptions, CPLSPrintf("TRE=%s", pszName)) <
4837
0
                0)
4838
0
        {
4839
0
            aosOptions.AddString(osTRE);
4840
0
        }
4841
0
        CPLFree(pszName);
4842
0
    }
4843
4844
    /* -------------------------------------------------------------------- */
4845
    /*      Set if we can set IREP.                                         */
4846
    /* -------------------------------------------------------------------- */
4847
20
    if (aosOptions.FetchNameValue("IREP") == nullptr)
4848
20
    {
4849
20
        if (((poSrcDS->GetRasterCount() == 3 && bJPEG) ||
4850
20
             (poSrcDS->GetRasterCount() >= 3 && !bJPEG)) &&
4851
19
            poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
4852
19
                GCI_RedBand &&
4853
5
            poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
4854
5
                GCI_GreenBand &&
4855
0
            poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
4856
0
        {
4857
0
            if (bJPEG)
4858
0
                aosOptions.SetNameValue("IREP", "YCbCr601");
4859
0
            else
4860
0
                aosOptions.SetNameValue("IREP", "RGB");
4861
0
        }
4862
20
        else if (poSrcDS->GetRasterCount() >= 3 && !bJPEG &&
4863
19
                 poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
4864
19
                     GCI_BlueBand &&
4865
0
                 poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
4866
0
                     GCI_GreenBand &&
4867
0
                 poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
4868
0
                     GCI_RedBand &&
4869
0
                 aosOptions.FetchNameValue("IREPBAND") == nullptr)
4870
0
        {
4871
0
            aosOptions.SetNameValue("IREP", "MULTI");
4872
0
            std::string osIREPBAND = "B,G,R";
4873
0
            for (int i = 4; i <= poSrcDS->GetRasterCount(); ++i)
4874
0
                osIREPBAND += ",M";
4875
0
            aosOptions.SetNameValue("IREPBAND", osIREPBAND.c_str());
4876
0
        }
4877
20
        else if (poSrcDS->GetRasterCount() == 1 && eType == GDT_UInt8 &&
4878
0
                 poBand1->GetColorTable() != nullptr)
4879
0
        {
4880
0
            aosOptions.SetNameValue("IREP", "RGB/LUT");
4881
0
            aosOptions.SetNameValue(
4882
0
                "LUT_SIZE",
4883
0
                CPLString().Printf(
4884
0
                    "%d", poBand1->GetColorTable()->GetColorEntryCount()));
4885
0
        }
4886
20
        else if (GDALDataTypeIsComplex(eType))
4887
0
            aosOptions.SetNameValue("IREP", "NODISPLY");
4888
4889
20
        else
4890
20
            aosOptions.SetNameValue("IREP", "MONO");
4891
20
    }
4892
4893
    /* -------------------------------------------------------------------- */
4894
    /*      Do we have lat/long georeferencing information?                 */
4895
    /* -------------------------------------------------------------------- */
4896
20
    GDALGeoTransform gt;
4897
20
    bool bWriteGeoTransform = false;
4898
20
    bool bWriteGCPs = false;
4899
20
    int nZone = 0;
4900
4901
20
    int nGCIFFlags = GCIF_PAM_DEFAULT;
4902
20
    double dfIGEOLOULX = 0;
4903
20
    double dfIGEOLOULY = 0;
4904
20
    double dfIGEOLOURX = 0;
4905
20
    double dfIGEOLOURY = 0;
4906
20
    double dfIGEOLOLRX = 0;
4907
20
    double dfIGEOLOLRY = 0;
4908
20
    double dfIGEOLOLLX = 0;
4909
20
    double dfIGEOLOLLY = 0;
4910
20
    bool bManualWriteOfIGEOLO = false;
4911
4912
20
    const OGRSpatialReference *poSrcSRS = poSrcDS->GetSpatialRef();
4913
20
    if (!poSrcSRS)
4914
4
        poSrcSRS = poSrcDS->GetGCPSpatialRef();
4915
20
    if (poSrcSRS)
4916
17
    {
4917
17
        OGRSpatialReference oSRS_WGS84;
4918
17
        oSRS_WGS84.SetWellKnownGeogCS("WGS84");
4919
17
        oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
4920
4921
17
        const bool bIsCADRGPolarAzimuthalEquidistant =
4922
17
            bIsCADRG && poSrcSRS->IsProjected() &&
4923
0
            EQUAL(poSrcSRS->GetAttrValue("PROJECTION"),
4924
17
                  SRS_PT_AZIMUTHAL_EQUIDISTANT) &&
4925
0
            poSrcSRS->GetSemiMajor() == SRS_WGS84_SEMIMAJOR &&
4926
0
            poSrcSRS->GetInvFlattening() == 0.0 &&
4927
0
            poSrcSRS->GetPrimeMeridian() == 0.0 &&
4928
0
            poSrcSRS->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) == 0.0 &&
4929
0
            std::fabs(poSrcSRS->GetProjParm(SRS_PP_LATITUDE_OF_CENTER)) == 90.0;
4930
17
        if (bIsCADRG && !poSrcSRS->IsGeographic() &&
4931
0
            !bIsCADRGPolarAzimuthalEquidistant)
4932
0
        {
4933
0
            CPLError(CE_Failure, CPLE_NotSupported,
4934
0
                     "CADRG only support geographic CRS or polar azimuthal "
4935
0
                     "equidistant");
4936
0
            return nullptr;
4937
0
        }
4938
4939
17
        if (!bIsCADRGPolarAzimuthalEquidistant)
4940
17
        {
4941
            /* NITF is only WGS84 */
4942
17
            if (!poSrcSRS->IsSameGeogCS(&oSRS_WGS84))
4943
16
            {
4944
16
                CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
4945
16
                         "NITF only supports WGS84 geographic and UTM "
4946
16
                         "projections.");
4947
16
                if (bStrict)
4948
0
                {
4949
0
                    return nullptr;
4950
0
                }
4951
16
            }
4952
17
        }
4953
4954
17
        const char *pszICORDS = aosOptions.FetchNameValue("ICORDS");
4955
4956
        /* --------------------------------------------------------------------
4957
         */
4958
        /*      Should we write DIGEST Spatial Data Extension TRE ? */
4959
        /* --------------------------------------------------------------------
4960
         */
4961
17
        const char *pszSDE_TRE = aosOptions.FetchNameValue("SDE_TRE");
4962
17
        const bool bSDE_TRE = pszSDE_TRE && CPLTestBool(pszSDE_TRE);
4963
17
        if (bSDE_TRE)
4964
0
        {
4965
0
            if (poSrcSRS->IsGeographic() &&
4966
0
                poSrcSRS->GetPrimeMeridian() == 0.0 &&
4967
0
                poSrcDS->GetGeoTransform(gt) == CE_None && gt.xrot == 0.0 &&
4968
0
                gt.yrot == 0.0 && gt.yscale < 0.0)
4969
0
            {
4970
                /* Override ICORDS to G if necessary */
4971
0
                if (pszICORDS != nullptr && EQUAL(pszICORDS, "D"))
4972
0
                {
4973
0
                    aosOptions.SetNameValue("ICORDS", "G");
4974
0
                    CPLError(CE_Warning, CPLE_AppDefined,
4975
0
                             "Forcing ICORDS=G when writing GEOLOB");
4976
0
                }
4977
0
                else
4978
0
                {
4979
                    /* Code a bit below will complain with other ICORDS value */
4980
0
                }
4981
4982
0
                if (CSLPartialFindString(aosOptions.List(), "TRE=GEOLOB=") !=
4983
0
                    -1)
4984
0
                {
4985
0
                    CPLDebug("NITF", "GEOLOB TRE was explicitly defined "
4986
0
                                     "before.  Overriding it with current "
4987
0
                                     "georeferencing info.");
4988
0
                }
4989
4990
                /* Structure of SDE TRE documented here */
4991
                // http://www.gwg.nga.mil/ntb/baseline/docs/digest/part2_annex_d.pdf
4992
4993
                /* --------------------------------------------------------------------
4994
                 */
4995
                /*      Write GEOLOB TRE */
4996
                /* --------------------------------------------------------------------
4997
                 */
4998
                // Extra (useless) bytes to avoid CLang 18 erroneous -Wformat-truncation
4999
0
                constexpr int MARGIN_FOR_CLANG_18 = 2;
5000
0
                char szGEOLOB[48 + 1 + MARGIN_FOR_CLANG_18];
5001
0
                const double dfARV = 360.0 / gt.xscale;
5002
0
                const double dfBRV = 360.0 / -gt.yscale;
5003
0
                const double dfLSO = gt.xorig;
5004
0
                const double dfPSO = gt.yorig;
5005
0
                CPLsnprintf(szGEOLOB, sizeof(szGEOLOB),
5006
0
                            "%09d%09d%#+015.10f%#+015.10f",
5007
0
                            static_cast<int>(dfARV + 0.5),
5008
0
                            static_cast<int>(dfBRV + 0.5), dfLSO, dfPSO);
5009
5010
0
                CPLString osGEOLOB("TRE=GEOLOB=");
5011
0
                osGEOLOB += szGEOLOB;
5012
0
                aosOptions.AddString(osGEOLOB);
5013
5014
                /* --------------------------------------------------------------------
5015
                 */
5016
                /*      Write GEOPSB TRE if not already explicitly provided */
5017
                /* --------------------------------------------------------------------
5018
                 */
5019
0
                if (CSLPartialFindString(aosOptions.List(),
5020
0
                                         "FILE_TRE=GEOPSB=") == -1 &&
5021
0
                    CSLPartialFindString(aosOptions.List(), "TRE=GEOPSB=") ==
5022
0
                        -1)
5023
0
                {
5024
0
                    char szGEOPSB[443 + 1];
5025
0
                    memset(szGEOPSB, ' ', 443);
5026
0
                    szGEOPSB[443] = 0;
5027
0
#define WRITE_STR_NOSZ(dst, src) memcpy(dst, src, strlen(src))
5028
0
                    char *pszGEOPSB = szGEOPSB;
5029
0
                    WRITE_STR_NOSZ(pszGEOPSB, "GEO");
5030
0
                    pszGEOPSB += 3;
5031
0
                    WRITE_STR_NOSZ(pszGEOPSB, "DEG");
5032
0
                    pszGEOPSB += 3;
5033
0
                    WRITE_STR_NOSZ(pszGEOPSB, "World Geodetic System 1984");
5034
0
                    pszGEOPSB += 80;
5035
0
                    WRITE_STR_NOSZ(pszGEOPSB, "WGE");
5036
0
                    pszGEOPSB += 4;
5037
0
                    WRITE_STR_NOSZ(pszGEOPSB, "World Geodetic System 1984");
5038
0
                    pszGEOPSB += 80;
5039
0
                    WRITE_STR_NOSZ(pszGEOPSB, "WE");
5040
0
                    pszGEOPSB += 3;
5041
0
                    WRITE_STR_NOSZ(pszGEOPSB, "Geodetic");
5042
0
                    pszGEOPSB += 80; /* DVR */
5043
0
                    WRITE_STR_NOSZ(pszGEOPSB, "GEOD");
5044
0
                    pszGEOPSB += 4; /* VDCDVR */
5045
0
                    WRITE_STR_NOSZ(pszGEOPSB, "Mean Sea");
5046
0
                    pszGEOPSB += 80; /* SDA */
5047
0
                    WRITE_STR_NOSZ(pszGEOPSB, "MSL");
5048
0
                    pszGEOPSB += 4; /* VDCSDA */
5049
0
                    WRITE_STR_NOSZ(pszGEOPSB, "000000000000000");
5050
0
                    pszGEOPSB += 15; /* ZOR */
5051
0
                    pszGEOPSB += 3;  /* GRD */
5052
0
                    pszGEOPSB += 80; /* GRN */
5053
0
                    WRITE_STR_NOSZ(pszGEOPSB, "0000");
5054
0
                    pszGEOPSB += 4; /* ZNA */
5055
0
                    CPL_IGNORE_RET_VAL(pszGEOPSB);
5056
0
                    CPLAssert(pszGEOPSB == szGEOPSB + 443);
5057
5058
0
                    CPLString osGEOPSB("FILE_TRE=GEOPSB=");
5059
0
                    osGEOPSB += szGEOPSB;
5060
0
                    aosOptions.AddString(osGEOPSB);
5061
0
                }
5062
0
                else
5063
0
                {
5064
0
                    CPLDebug("NITF", "GEOPSB TRE was explicitly defined "
5065
0
                                     "before. Keeping it.");
5066
0
                }
5067
0
            }
5068
0
            else
5069
0
            {
5070
0
                CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
5071
0
                         "Georeferencing info isn't compatible with writing a "
5072
0
                         "GEOLOB TRE (only geographic SRS handled for now)");
5073
0
                if (bStrict)
5074
0
                {
5075
0
                    return nullptr;
5076
0
                }
5077
0
            }
5078
0
        }
5079
5080
17
        bWriteGeoTransform = (poSrcDS->GetGeoTransform(gt) == CE_None);
5081
17
        bWriteGCPs = (!bWriteGeoTransform && poSrcDS->GetGCPCount() == 4);
5082
5083
17
        int bNorth;
5084
17
        const bool bHasIGEOLO = aosOptions.FetchNameValue("IGEOLO") != nullptr;
5085
17
        if (bHasIGEOLO && pszICORDS == nullptr)
5086
0
        {
5087
0
            CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_AppDefined,
5088
0
                     "IGEOLO specified, but ICORDS not.%s",
5089
0
                     bStrict ? "" : " Ignoring IGEOLO");
5090
0
            if (bStrict)
5091
0
            {
5092
0
                return nullptr;
5093
0
            }
5094
0
        }
5095
5096
17
        if (aosOptions.FetchNameValue("IGEOLO") != nullptr &&
5097
0
            pszICORDS != nullptr)
5098
0
        {
5099
            // if both IGEOLO and ICORDS are specified, do not try to write
5100
            // computed values
5101
5102
0
            bWriteGeoTransform = false;
5103
0
            bWriteGCPs = false;
5104
0
            nGCIFFlags &= ~GCIF_PROJECTION;
5105
0
            nGCIFFlags &= ~GCIF_GEOTRANSFORM;
5106
0
        }
5107
17
        else if (poSrcSRS->IsGeographic() &&
5108
4
                 poSrcSRS->GetPrimeMeridian() == 0.0)
5109
4
        {
5110
4
            if (pszICORDS == nullptr)
5111
4
            {
5112
4
                aosOptions.SetNameValue("ICORDS", "G");
5113
4
            }
5114
0
            else if (EQUAL(pszICORDS, "G") || EQUAL(pszICORDS, "D"))
5115
0
            {
5116
                /* Do nothing */
5117
0
            }
5118
0
            else
5119
0
            {
5120
0
                CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
5121
0
                         "Inconsistent ICORDS value with SRS : %s%s.\n",
5122
0
                         pszICORDS,
5123
0
                         (!bStrict) ? ". Setting it to G instead" : "");
5124
0
                if (bStrict)
5125
0
                {
5126
0
                    return nullptr;
5127
0
                }
5128
0
                aosOptions.SetNameValue("ICORDS", "G");
5129
0
            }
5130
4
        }
5131
5132
13
        else if (poSrcSRS->GetUTMZone(&bNorth) > 0)
5133
6
        {
5134
6
            const char *pszComputedICORDS = bNorth ? "N" : "S";
5135
6
            nZone = poSrcSRS->GetUTMZone(nullptr);
5136
6
            if (pszICORDS == nullptr)
5137
6
            {
5138
6
                aosOptions.SetNameValue("ICORDS", pszComputedICORDS);
5139
6
            }
5140
0
            else if (EQUAL(pszICORDS, pszComputedICORDS))
5141
0
            {
5142
                // ok
5143
0
            }
5144
0
            else if ((EQUAL(pszICORDS, "G") || EQUAL(pszICORDS, "D")) &&
5145
0
                     bWriteGeoTransform)
5146
0
            {
5147
                // Reproject UTM corner coordinates to geographic.
5148
                // This can be used when there is no way to write an
5149
                // equatorial image whose one of the northing value is below
5150
                // -1e6
5151
5152
0
                const int nXSize = poSrcDS->GetRasterXSize();
5153
0
                const int nYSize = poSrcDS->GetRasterYSize();
5154
5155
0
                dfIGEOLOULX = gt.xorig + 0.5 * gt.xscale + 0.5 * gt.xrot;
5156
0
                dfIGEOLOULY = gt.yorig + 0.5 * gt.yrot + 0.5 * gt.yscale;
5157
0
                dfIGEOLOURX = dfIGEOLOULX + gt.xscale * (nXSize - 1);
5158
0
                dfIGEOLOURY = dfIGEOLOULY + gt.yrot * (nXSize - 1);
5159
0
                dfIGEOLOLRX = dfIGEOLOULX + gt.xscale * (nXSize - 1) +
5160
0
                              gt.xrot * (nYSize - 1);
5161
0
                dfIGEOLOLRY = dfIGEOLOULY + gt.yrot * (nXSize - 1) +
5162
0
                              gt.yscale * (nYSize - 1);
5163
0
                dfIGEOLOLLX = dfIGEOLOULX + gt.xrot * (nYSize - 1);
5164
0
                dfIGEOLOLLY = dfIGEOLOULY + gt.yscale * (nYSize - 1);
5165
5166
0
                auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
5167
0
                    OGRCreateCoordinateTransformation(poSrcSRS, &oSRS_WGS84));
5168
0
                if (poCT && poCT->Transform(1, &dfIGEOLOULX, &dfIGEOLOULY) &&
5169
0
                    poCT->Transform(1, &dfIGEOLOURX, &dfIGEOLOURY) &&
5170
0
                    poCT->Transform(1, &dfIGEOLOLRX, &dfIGEOLOLRY) &&
5171
0
                    poCT->Transform(1, &dfIGEOLOLLX, &dfIGEOLOLLY))
5172
0
                {
5173
0
                    nZone = 0;
5174
0
                    bWriteGeoTransform = false;
5175
0
                    bManualWriteOfIGEOLO = true;
5176
0
                    nGCIFFlags &= ~GCIF_PROJECTION;
5177
0
                    nGCIFFlags &= ~GCIF_GEOTRANSFORM;
5178
0
                }
5179
0
                else
5180
0
                {
5181
0
                    CPLError(
5182
0
                        CE_Failure, CPLE_AppDefined,
5183
0
                        "Cannot reproject UTM coordinates to geographic ones");
5184
0
                    return nullptr;
5185
0
                }
5186
0
            }
5187
0
            else
5188
0
            {
5189
0
                CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
5190
0
                         "Inconsistent ICORDS value with SRS : %s.", pszICORDS);
5191
0
                if (bStrict)
5192
0
                {
5193
0
                    return nullptr;
5194
0
                }
5195
0
            }
5196
6
        }
5197
7
        else if (bIsCADRGPolarAzimuthalEquidistant)
5198
0
        {
5199
0
            OGREnvelope sExtent;
5200
0
            poSrcDS->GetExtent(&sExtent);
5201
5202
0
            aosOptions.SetNameValue("ICORDS", "G");
5203
0
            dfIGEOLOULX = sExtent.MinX;
5204
0
            dfIGEOLOULY = sExtent.MaxY;
5205
0
            dfIGEOLOURX = sExtent.MaxX;
5206
0
            dfIGEOLOURY = sExtent.MaxY;
5207
0
            dfIGEOLOLLX = sExtent.MinX;
5208
0
            dfIGEOLOLLY = sExtent.MinY;
5209
0
            dfIGEOLOLRX = sExtent.MaxX;
5210
0
            dfIGEOLOLRY = sExtent.MinY;
5211
0
            auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
5212
0
                OGRCreateCoordinateTransformation(poSrcSRS, &oSRS_WGS84));
5213
0
            if (poCT && poCT->Transform(1, &dfIGEOLOULX, &dfIGEOLOULY) &&
5214
0
                poCT->Transform(1, &dfIGEOLOURX, &dfIGEOLOURY) &&
5215
0
                poCT->Transform(1, &dfIGEOLOLRX, &dfIGEOLOLRY) &&
5216
0
                poCT->Transform(1, &dfIGEOLOLLX, &dfIGEOLOLLY))
5217
0
            {
5218
0
                nZone = 0;
5219
0
                bWriteGeoTransform = false;
5220
0
                bManualWriteOfIGEOLO = true;
5221
0
                nGCIFFlags &= ~GCIF_PROJECTION;
5222
0
                nGCIFFlags &= ~GCIF_GEOTRANSFORM;
5223
0
            }
5224
0
            else
5225
0
            {
5226
0
                CPLError(CE_Failure, CPLE_AppDefined,
5227
0
                         "Cannot reproject azimuthal equidistant coordinates "
5228
0
                         "to geographic ones");
5229
0
                return nullptr;
5230
0
            }
5231
0
        }
5232
7
        else
5233
7
        {
5234
7
            CPLError(
5235
7
                (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
5236
7
                "NITF only supports WGS84 geographic and UTM projections.");
5237
7
            if (bStrict)
5238
0
            {
5239
0
                return nullptr;
5240
0
            }
5241
7
        }
5242
17
    }
5243
5244
    /* -------------------------------------------------------------------- */
5245
    /*      Do we have RPC information?                                     */
5246
    /* -------------------------------------------------------------------- */
5247
20
    if (!bUseSrcNITFMetadata)
5248
0
        nGCIFFlags &= ~GCIF_METADATA;
5249
5250
20
    CSLConstList papszRPC = poSrcDS->GetMetadata("RPC");
5251
20
    if (papszRPC != nullptr && bUseSrcNITFMetadata &&
5252
0
        CPLFetchBool(aosOptions.List(), "RPC00B", true))
5253
0
    {
5254
0
        if (CSLPartialFindString(aosOptions.List(), "TRE=RPC00B=") >= 0)
5255
0
        {
5256
0
            CPLDebug("NITF",
5257
0
                     "Both TRE=RPC00B and RPC metadata are available. "
5258
0
                     "Ignoring RPC metadata and re-using source TRE=RPC00B");
5259
0
        }
5260
0
        else
5261
0
        {
5262
0
            int bPrecisionLoss = FALSE;
5263
0
            char *pszRPC =
5264
0
                NITFFormatRPC00BFromMetadata(papszRPC, &bPrecisionLoss);
5265
0
            if (pszRPC == nullptr)
5266
0
            {
5267
0
                CPLError(
5268
0
                    (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
5269
0
                    "Cannot format a valid RPC00B TRE from the RPC metadata");
5270
0
                if (bStrict)
5271
0
                {
5272
0
                    return nullptr;
5273
0
                }
5274
0
            }
5275
0
            else
5276
0
            {
5277
0
                CPLString osRPC00B("TRE=RPC00B=");
5278
0
                osRPC00B += pszRPC;
5279
0
                aosOptions.AddString(osRPC00B);
5280
5281
                // If no precision loss occurred during RPC conversion, then
5282
                // we can suppress it from PAM
5283
0
                if (!bPrecisionLoss)
5284
0
                    nGCIFFlags &= ~GCIF_METADATA;
5285
0
            }
5286
0
            CPLFree(pszRPC);
5287
0
        }
5288
0
    }
5289
20
    else if (!CPLFetchBool(aosOptions.List(), "RPC00B", true))
5290
0
    {
5291
0
        int nIdx = CSLPartialFindString(aosOptions.List(), "TRE=RPC00B=");
5292
0
        if (nIdx >= 0)
5293
0
        {
5294
0
            aosOptions =
5295
0
                CSLRemoveStrings(aosOptions.StealList(), nIdx, 1, nullptr);
5296
0
        }
5297
0
    }
5298
5299
20
    if (papszRPC != nullptr && CPLFetchBool(aosOptions.List(), "RPCTXT", false))
5300
0
    {
5301
0
        GDALWriteRPCTXTFile(pszFilename, papszRPC);
5302
0
    }
5303
5304
    /* -------------------------------------------------------------------- */
5305
    /*      Create the output file.                                         */
5306
    /* -------------------------------------------------------------------- */
5307
20
    const int nXSize = poSrcDS->GetRasterXSize();
5308
20
    const int nYSize = poSrcDS->GetRasterYSize();
5309
20
    const char *pszPVType = GDALToNITFDataType(eType);
5310
5311
20
    if (pszPVType == nullptr)
5312
0
    {
5313
0
        return nullptr;
5314
0
    }
5315
5316
20
    int nABPP = GDALGetDataTypeSizeBits(eType);
5317
20
    if (const char *pszABPP = aosOptions.FetchNameValue("ABPP"))
5318
0
    {
5319
0
        nABPP = atoi(pszABPP);
5320
0
    }
5321
20
    else if (const char *pszNBITS = aosOptions.FetchNameValueDef(
5322
20
                 "NBITS", poBand1->GetMetadataItem("NBITS", "IMAGE_STRUCTURE")))
5323
5
    {
5324
5
        aosOptions.SetNameValue("ABPP", pszNBITS);
5325
5
        nABPP = atoi(pszNBITS);
5326
5
    }
5327
5328
20
    if (poJ2KDriver != nullptr &&
5329
0
        EQUAL(poJ2KDriver->GetDescription(), "JP2ECW"))
5330
0
    {
5331
0
        if (STARTS_WITH_CI(aosOptions.FetchNameValueDef("PROFILE", "NPJE"),
5332
0
                           "NPJE") &&
5333
0
            (nXSize >= 1024 || nYSize >= 1024))
5334
0
        {
5335
0
            int nBlockXSize =
5336
0
                atoi(aosOptions.FetchNameValueDef("BLOCKXSIZE", "0"));
5337
0
            int nBlockYSize =
5338
0
                atoi(aosOptions.FetchNameValueDef("BLOCKYSIZE", "0"));
5339
0
            if (nBlockXSize > 0 && nBlockXSize != 1024)
5340
0
            {
5341
0
                CPLError(CE_Warning, CPLE_AppDefined,
5342
0
                         "BLOCKXSIZE != 1024 inconsistent with PROFILE=NPJE");
5343
0
            }
5344
0
            if (nBlockYSize > 0 && nBlockYSize != 1024)
5345
0
            {
5346
0
                CPLError(CE_Warning, CPLE_AppDefined,
5347
0
                         "BLOCKYSIZE != 1024 inconsistent with PROFILE=NPJE");
5348
0
            }
5349
0
            if (nBlockXSize == 0)
5350
0
            {
5351
0
                aosOptions.SetNameValue("BLOCKXSIZE", "1024");
5352
0
            }
5353
0
            if (nBlockYSize == 0)
5354
0
            {
5355
0
                aosOptions.SetNameValue("BLOCKYSIZE", "1024");
5356
0
            }
5357
0
        }
5358
0
    }
5359
20
    else if (poJ2KDriver != nullptr &&
5360
0
             EQUAL(poJ2KDriver->GetDescription(), "JP2OPENJPEG"))
5361
0
    {
5362
0
        const char *pszProfile = aosOptions.FetchNameValue("PROFILE");
5363
0
        if (pszProfile && EQUAL(pszProfile, "EPJE"))
5364
0
        {
5365
0
            CPLError(CE_Warning, CPLE_AppDefined,
5366
0
                     "PROFILE=EPJE not handled by JP2OPENJPEG driver");
5367
0
        }
5368
5369
0
        int nBlockXSize = atoi(aosOptions.FetchNameValueDef("BLOCKXSIZE", "0"));
5370
0
        int nBlockYSize = atoi(aosOptions.FetchNameValueDef("BLOCKYSIZE", "0"));
5371
0
        if (pszProfile && STARTS_WITH_CI(pszProfile, "NPJE") &&
5372
0
            ((nBlockXSize != 0 && nBlockXSize != 1024) ||
5373
0
             (nBlockYSize != 0 && nBlockYSize != 1024)))
5374
0
        {
5375
0
            CPLError(CE_Warning, CPLE_AppDefined,
5376
0
                     "PROFILE=NPJE implies 1024x1024 tiles");
5377
0
        }
5378
5379
0
        if (nXSize >= 1024 || nYSize >= 1024 ||
5380
0
            (pszProfile && STARTS_WITH_CI(pszProfile, "NPJE")))
5381
0
        {
5382
            // The JP2OPENJPEG driver uses 1024 block size by default. Set it
5383
            // explicitly for NITFCreate() purposes.
5384
0
            if (nBlockXSize == 0)
5385
0
            {
5386
0
                aosOptions.SetNameValue("BLOCKXSIZE", "1024");
5387
0
            }
5388
0
            if (nBlockYSize == 0)
5389
0
            {
5390
0
                aosOptions.SetNameValue("BLOCKYSIZE", "1024");
5391
0
            }
5392
0
        }
5393
5394
        // Compose J2KLRA TRE for NPJE profiles
5395
0
        if (pszProfile && STARTS_WITH_CI(pszProfile, "NPJE") &&
5396
0
            CPLTestBool(aosOptions.FetchNameValueDef("J2KLRA", "YES")))
5397
0
        {
5398
0
#if defined(__GNUC__)
5399
0
#pragma GCC diagnostic push
5400
0
#pragma GCC diagnostic ignored "-Warray-bounds"
5401
0
#endif
5402
            // See Table 2.3-3 - Target Bit Rates for Each Tile in Panchromatic
5403
            // Image Segments of STDI-0006
5404
0
            std::vector<double> adfBPP = {
5405
0
                0.03125, 0.0625, 0.125, 0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
5406
0
                1.1,     1.2,    1.3,   1.5,  1.7, 2.0, 2.3, 3.5, 3.9};
5407
5408
0
            if (EQUAL(pszProfile, "NPJE") ||
5409
0
                EQUAL(pszProfile, "NPJE_NUMERICALLY_LOSSLESS"))
5410
0
            {
5411
0
                adfBPP.push_back(nABPP);
5412
0
            }
5413
0
#if defined(__GNUC__)
5414
0
#pragma GCC diagnostic pop
5415
0
#endif
5416
5417
0
            double dfQuality =
5418
0
                CPLAtof(aosOptions.FetchNameValueDef("QUALITY", "0"));
5419
0
            double dfTarget =
5420
0
                CPLAtof(aosOptions.FetchNameValueDef("TARGET", "0"));
5421
0
            if (dfTarget > 0 && dfTarget < 100)
5422
0
                dfQuality = 100. - dfTarget;
5423
5424
0
            if (dfQuality != 0.0)
5425
0
            {
5426
0
                for (size_t i = 0; i < adfBPP.size(); ++i)
5427
0
                {
5428
                    // the JP2OPENJPEG QUALITY setting is 100. /
5429
                    // compression_ratio and compression_ratio = 8 / bpp
5430
0
                    double dfLayerQuality = 100.0 / (8.0 / adfBPP[i]);
5431
0
                    if (dfLayerQuality > dfQuality)
5432
0
                    {
5433
0
                        adfBPP[i] = dfQuality / 100.0 * nABPP;
5434
0
                        adfBPP.resize(i + 1);
5435
0
                        break;
5436
0
                    }
5437
0
                }
5438
0
            }
5439
5440
0
            CPLString osJ2KLRA("TRE=J2KLRA=");
5441
0
            osJ2KLRA += '0';   // ORIG: 0=Original NPJE
5442
0
            osJ2KLRA += "05";  // Number of wavelets decompositions.
5443
                               // This corresponds to the value of the
5444
                               // RESOLUTIONS JP2OPENJPEG creation option - 1
5445
0
            osJ2KLRA += CPLSPrintf("%05d", poSrcDS->GetRasterCount());
5446
0
            osJ2KLRA += CPLSPrintf("%03d", static_cast<int>(adfBPP.size()));
5447
0
            for (size_t i = 0; i < adfBPP.size(); ++i)
5448
0
            {
5449
0
                osJ2KLRA += CPLSPrintf("%03d", static_cast<int>(i));
5450
0
                osJ2KLRA += CPLSPrintf("%09.6f", adfBPP[i]);
5451
0
            }
5452
0
            aosOptions.AddString(osJ2KLRA);
5453
0
        }
5454
0
    }
5455
5456
20
    GDALOffsetPatcher::OffsetPatcher offsetPatcher;
5457
20
    std::unique_ptr<CADRGInformation> CADRGInfo;
5458
5459
20
    if (bIsCADRG)
5460
0
    {
5461
0
        aosOptions.SetNameValue("FHDR", "NITF02.00");
5462
0
        pszIC = "C4";
5463
0
        aosOptions.SetNameValue("IC", pszIC);
5464
0
        if (aosOptions.FetchNameValue("IID1") == nullptr)
5465
0
        {
5466
0
            aosOptions.SetNameValue("IID1", "CADRG");
5467
0
        }
5468
0
        if (aosOptions.FetchNameValue("FTITLE") == nullptr)
5469
0
        {
5470
0
            aosOptions.SetNameValue("FTITLE", CPLGetFilename(pszFilename));
5471
0
        }
5472
0
        if (aosOptions.FetchNameValue("ITITLE") == nullptr)
5473
0
        {
5474
0
            aosOptions.SetNameValue("ITITLE", CPLGetFilename(pszFilename));
5475
0
        }
5476
0
        if (aosOptions.FetchNameValue("ICAT") == nullptr)
5477
0
        {
5478
0
            aosOptions.SetNameValue("ICAT", "MAP");
5479
0
        }
5480
0
        if (aosOptions.FetchNameValue("IMAG") == nullptr)
5481
0
        {
5482
            // 0.67 = 100 (microns ADRG) / 150 (microns CADRG)
5483
0
            aosOptions.SetNameValue("IMAG", "0.67");
5484
0
        }
5485
5486
0
        const int nRPFDESIdx = aosOptions.PartialFindString("DES=RPFDES=");
5487
0
        if (nRPFDESIdx >= 0)
5488
0
            aosOptions.RemoveStrings(nRPFDESIdx);
5489
        // +1 for the created RPFDES
5490
0
        const int nDES =
5491
0
            1 +
5492
0
            CPLStringList(CSLFetchNameValueMultiple(aosOptions.List(), "DES"))
5493
0
                .size();
5494
0
        aosOptions.SetNameValue("NUMDES", CPLSPrintf("%d", nDES));
5495
5496
0
        if (copyContext->nReciprocalScale == 0)
5497
0
        {
5498
0
            bool bGotDPI = false;
5499
0
            {
5500
0
                CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5501
0
                copyContext->nReciprocalScale =
5502
0
                    RPFGetCADRGClosestReciprocalScale(poSrcDS, 0, bGotDPI);
5503
0
            }
5504
0
            if (!bGotDPI)
5505
0
            {
5506
                // the one from ADRG typically
5507
0
                constexpr double DEFAULT_DPI = 254;
5508
0
                copyContext->nReciprocalScale =
5509
0
                    RPFGetCADRGClosestReciprocalScale(poSrcDS, DEFAULT_DPI,
5510
0
                                                      bGotDPI);
5511
0
                if (!copyContext->nReciprocalScale)
5512
0
                    return nullptr;
5513
0
            }
5514
0
        }
5515
5516
0
        CADRGInfo = RPFFrameCreateCADRG_TREs(&offsetPatcher, pszFilename,
5517
0
                                             poSrcDS, aosOptions, *copyContext);
5518
0
        if (!CADRGInfo)
5519
0
        {
5520
0
            return nullptr;
5521
0
        }
5522
5523
0
        aosOptions.SetNameValue(
5524
0
            "LUT_SIZE",
5525
0
            CPLSPrintf("%d", CADRG_MAX_COLOR_ENTRY_COUNT +
5526
0
                                 (CADRGInfo->HasTransparentPixels() ? 1 : 0)));
5527
0
    }
5528
5529
20
    int nIMIndex = 0;
5530
20
    int nImageCount = 0;
5531
20
    vsi_l_offset nImageOffset = 0;
5532
20
    vsi_l_offset nICOffset = 0;
5533
20
    if (!NITFCreateEx(pszFilename, nXSize, nYSize, poSrcDS->GetRasterCount(),
5534
20
                      GDALGetDataTypeSizeBits(eType), pszPVType,
5535
20
                      aosOptions.List(), &nIMIndex, &nImageCount, &nImageOffset,
5536
20
                      &nICOffset, &offsetPatcher))
5537
0
    {
5538
0
        return nullptr;
5539
0
    }
5540
5541
    /* ==================================================================== */
5542
    /*      JPEG2000 case.  We need to write the data through a J2K         */
5543
    /*      driver in pixel interleaved form.                               */
5544
    /* ==================================================================== */
5545
20
    std::unique_ptr<NITFDataset> poDstDS;
5546
5547
20
    if (bJPEG2000)
5548
0
    {
5549
0
        CPLString osDSName;
5550
0
        osDSName.Printf("/vsisubfile/" CPL_FRMT_GUIB "_%d,%s",
5551
0
                        static_cast<GUIntBig>(nImageOffset), -1, pszFilename);
5552
5553
0
        std::unique_ptr<GDALDataset> poJ2KDataset;
5554
0
        if (EQUAL(poJ2KDriver->GetDescription(), "JP2ECW"))
5555
0
        {
5556
0
            poJ2KDataset.reset(poJ2KDriver->CreateCopy(
5557
0
                osDSName, poSrcDS, FALSE, NITFJP2ECWOptions(aosOptions).List(),
5558
0
                pfnProgress, pProgressData));
5559
0
        }
5560
0
        else if (EQUAL(poJ2KDriver->GetDescription(), "JP2KAK"))
5561
0
        {
5562
0
            poJ2KDataset.reset(poJ2KDriver->CreateCopy(
5563
0
                osDSName, poSrcDS, FALSE,
5564
0
                NITFJP2KAKOptions(aosOptions, nABPP).List(), pfnProgress,
5565
0
                pProgressData));
5566
0
        }
5567
0
        else if (EQUAL(poJ2KDriver->GetDescription(), "JP2OPENJPEG"))
5568
0
        {
5569
0
            poJ2KDataset.reset(poJ2KDriver->CreateCopy(
5570
0
                osDSName, poSrcDS, FALSE,
5571
0
                NITFJP2OPENJPEGOptions(poJ2KDriver, aosOptions, nABPP).List(),
5572
0
                pfnProgress, pProgressData));
5573
0
        }
5574
0
        if (poJ2KDataset == nullptr)
5575
0
        {
5576
0
            return nullptr;
5577
0
        }
5578
5579
0
        poJ2KDataset.reset();
5580
5581
        // Now we need to figure out the actual length of the file
5582
        // and correct the image segment size information.
5583
0
        const GIntBig nPixelCount =
5584
0
            static_cast<GIntBig>(nXSize) * nYSize * poSrcDS->GetRasterCount();
5585
5586
0
        bool bOK = NITFPatchImageLength(pszFilename, nullptr, nIMIndex,
5587
0
                                        nImageOffset, nPixelCount, "C8",
5588
0
                                        nICOffset, aosOptions.List());
5589
0
        if (nIMIndex + 1 == nImageCount)
5590
0
        {
5591
0
            bOK &= NITFWriteExtraSegments(pszFilename, nullptr, aosCgmMD.List(),
5592
0
                                          aosTextMD.List(), nullptr, aosOptions,
5593
0
                                          0);
5594
0
        }
5595
0
        if (!bOK)
5596
0
        {
5597
0
            return nullptr;
5598
0
        }
5599
5600
0
        GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
5601
0
        poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true,
5602
0
                                   nImageCount == 1 ? -1 : nIMIndex));
5603
5604
0
        if (poDstDS == nullptr)
5605
0
        {
5606
0
            return nullptr;
5607
0
        }
5608
0
    }
5609
5610
    /* ==================================================================== */
5611
    /*      Loop copying bands to an uncompressed file.                     */
5612
    /* ==================================================================== */
5613
20
    else if (bJPEG)
5614
0
    {
5615
0
#ifdef JPEG_SUPPORTED
5616
0
        std::unique_ptr<NITFFile, decltype(&NITFClose)> psFile(
5617
0
            NITFOpen(pszFilename, TRUE), NITFClose);
5618
0
        if (psFile == nullptr)
5619
0
        {
5620
0
            return nullptr;
5621
0
        }
5622
5623
0
        const bool bSuccess =
5624
0
            NITFWriteJPEGImage(poSrcDS, psFile->fp, nImageOffset,
5625
0
                               aosOptions.List(), pfnProgress, pProgressData);
5626
5627
0
        if (!bSuccess)
5628
0
        {
5629
0
            return nullptr;
5630
0
        }
5631
5632
        // Now we need to figure out the actual length of the file
5633
        // and correct the image segment size information.
5634
0
        const GIntBig nPixelCount =
5635
0
            static_cast<GIntBig>(nXSize) * nYSize * poSrcDS->GetRasterCount();
5636
5637
0
        psFile.reset();
5638
5639
0
        bool bOK = NITFPatchImageLength(pszFilename, nullptr, nIMIndex,
5640
0
                                        nImageOffset, nPixelCount, pszIC,
5641
0
                                        nICOffset, aosOptions.List());
5642
0
        if (nIMIndex + 1 == nImageCount)
5643
0
        {
5644
0
            bOK &= NITFWriteExtraSegments(pszFilename, nullptr, aosCgmMD.List(),
5645
0
                                          aosTextMD.List(), nullptr, aosOptions,
5646
0
                                          0);
5647
0
        }
5648
0
        if (!bOK)
5649
0
        {
5650
0
            return nullptr;
5651
0
        }
5652
5653
0
        GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
5654
0
        poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true,
5655
0
                                   nImageCount == 1 ? -1 : nIMIndex));
5656
5657
0
        if (poDstDS == nullptr)
5658
0
        {
5659
0
            return nullptr;
5660
0
        }
5661
0
#endif /* def JPEG_SUPPORTED */
5662
0
    }
5663
5664
20
    else if (bIsCADRG)
5665
0
    {
5666
0
        auto fp = VSIFilesystemHandler::OpenStatic(pszFilename, "rb+");
5667
0
        if (!fp)
5668
0
        {
5669
0
            CPLError(CE_Failure, CPLE_FileIO,
5670
0
                     "Cannot re-open %s in read/write mode", pszFilename);
5671
0
            return nullptr;
5672
0
        }
5673
5674
0
        if (!RPFFrameWriteCADRG_ImageContent(&offsetPatcher, fp.get(), poSrcDS,
5675
0
                                             CADRGInfo.get()))
5676
0
        {
5677
0
            CPLError(CE_Failure, CPLE_AppDefined,
5678
0
                     "RPFFrameWriteCADRG_ImageContent() failed");
5679
0
            return nullptr;
5680
0
        }
5681
5682
        // Now we need to figure out the actual length of the file
5683
        // and correct the image segment size information.
5684
0
        const GIntBig nPixelCount =
5685
0
            static_cast<GIntBig>(nXSize) * nYSize * poSrcDS->GetRasterCount();
5686
5687
0
        bool bOK = NITFPatchImageLength(pszFilename, fp.get(), nIMIndex,
5688
0
                                        nImageOffset, nPixelCount, pszIC,
5689
0
                                        nICOffset, aosOptions.List());
5690
5691
        // This will call RPFFrameWriteCADRG_RPFDES()
5692
0
        bOK &= NITFWriteExtraSegments(
5693
0
            pszFilename, fp.get(), aosCgmMD.List(), aosTextMD.List(),
5694
0
            &offsetPatcher, aosOptions, copyContext->nReciprocalScale);
5695
5696
0
        if (!bOK || !offsetPatcher.Finalize(fp.get()))
5697
0
            return nullptr;
5698
5699
0
        fp.reset();
5700
5701
0
        GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
5702
0
        poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true, -1));
5703
0
        if (poDstDS == nullptr)
5704
0
        {
5705
0
            return nullptr;
5706
0
        }
5707
5708
0
        const GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
5709
0
        GDALColorTable oDstCT;
5710
0
        const int nMaxCTEntries = CADRG_MAX_COLOR_ENTRY_COUNT +
5711
0
                                  (CADRGInfo->HasTransparentPixels() ? 1 : 0);
5712
0
        for (int i = 0; i < nMaxCTEntries; ++i)
5713
0
        {
5714
0
            if (i < poCT->GetColorEntryCount())
5715
0
                oDstCT.SetColorEntry(i, poCT->GetColorEntry(i));
5716
0
            else
5717
0
            {
5718
0
                GDALColorEntry entry = {0, 0, 0, 0};
5719
0
                oDstCT.SetColorEntry(i, &entry);
5720
0
            }
5721
0
        }
5722
0
        poDstDS->GetRasterBand(1)->SetColorTable(&oDstCT);
5723
0
    }
5724
5725
    /* ==================================================================== */
5726
    /*      Loop copying bands to an uncompressed file.                     */
5727
    /* ==================================================================== */
5728
20
    else
5729
20
    {
5730
20
        if (nIMIndex + 1 == nImageCount)
5731
20
        {
5732
20
            bool bOK = NITFWriteExtraSegments(pszFilename, nullptr,
5733
20
                                              aosCgmMD.List(), aosTextMD.List(),
5734
20
                                              nullptr, aosOptions, 0);
5735
20
            if (!bOK)
5736
0
            {
5737
0
                return nullptr;
5738
0
            }
5739
20
        }
5740
5741
        // Save error state to restore it afterwards since some operations
5742
        // in Open() might reset it.
5743
20
        CPLErr eLastErr = CPLGetLastErrorType();
5744
20
        int nLastErrNo = CPLGetLastErrorNo();
5745
20
        CPLString osLastErrorMsg = CPLGetLastErrorMsg();
5746
5747
20
        GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
5748
20
        poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true,
5749
20
                                   nImageCount == 1 ? -1 : nIMIndex));
5750
5751
20
        if (CPLGetLastErrorType() == CE_None && eLastErr != CE_None)
5752
20
            CPLErrorSetState(eLastErr, nLastErrNo, osLastErrorMsg.c_str());
5753
5754
20
        if (poDstDS == nullptr)
5755
0
        {
5756
0
            return nullptr;
5757
0
        }
5758
5759
20
        std::unique_ptr<void, VSIFreeReleaser> pData(
5760
20
            VSIMalloc2(nXSize, GDALGetDataTypeSizeBytes(eType)));
5761
20
        if (pData == nullptr)
5762
0
        {
5763
0
            return nullptr;
5764
0
        }
5765
5766
20
        CPLErr eErr = CE_None;
5767
5768
2.68k
        for (int iBand = 0; nIMIndex >= 0 && eErr == CE_None &&
5769
2.68k
                            iBand < poSrcDS->GetRasterCount();
5770
2.66k
             iBand++)
5771
2.66k
        {
5772
2.66k
            GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
5773
2.66k
            GDALRasterBand *poDstBand = poDstDS->GetRasterBand(iBand + 1);
5774
5775
            /* --------------------------------------------------------------------
5776
             */
5777
            /*      Do we need to copy a colortable or other metadata? */
5778
            /* --------------------------------------------------------------------
5779
             */
5780
2.66k
            GDALColorTable *poCT = poSrcBand->GetColorTable();
5781
2.66k
            if (poCT != nullptr)
5782
1.10k
                poDstBand->SetColorTable(poCT);
5783
5784
            /* --------------------------------------------------------------------
5785
             */
5786
            /*      Copy image data. */
5787
            /* --------------------------------------------------------------------
5788
             */
5789
26.7k
            for (int iLine = 0; iLine < nYSize; iLine++)
5790
24.0k
            {
5791
24.0k
                eErr = poSrcBand->RasterIO(GF_Read, 0, iLine, nXSize, 1,
5792
24.0k
                                           pData.get(), nXSize, 1, eType, 0, 0,
5793
24.0k
                                           nullptr);
5794
24.0k
                if (eErr != CE_None)
5795
5
                    break;
5796
5797
24.0k
                eErr = poDstBand->RasterIO(GF_Write, 0, iLine, nXSize, 1,
5798
24.0k
                                           pData.get(), nXSize, 1, eType, 0, 0,
5799
24.0k
                                           nullptr);
5800
5801
24.0k
                if (eErr != CE_None)
5802
0
                    break;
5803
5804
24.0k
                if (!pfnProgress(
5805
24.0k
                        (iBand + (iLine + 1) / static_cast<double>(nYSize)) /
5806
24.0k
                            static_cast<double>(poSrcDS->GetRasterCount()),
5807
24.0k
                        nullptr, pProgressData))
5808
0
                {
5809
0
                    CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
5810
0
                    eErr = CE_Failure;
5811
0
                    break;
5812
0
                }
5813
24.0k
            }
5814
2.66k
        }
5815
5816
20
        if (eErr != CE_None)
5817
5
        {
5818
5
            return nullptr;
5819
5
        }
5820
20
    }
5821
5822
    /* -------------------------------------------------------------------- */
5823
    /*      Set the georeferencing.                                         */
5824
    /* -------------------------------------------------------------------- */
5825
15
    if (poDstDS->psImage == nullptr)
5826
0
    {
5827
        // do nothing
5828
0
    }
5829
15
    else if (bManualWriteOfIGEOLO)
5830
0
    {
5831
0
        if (!NITFWriteIGEOLO(poDstDS->psImage, poDstDS->psImage->chICORDS,
5832
0
                             poDstDS->psImage->nZone, dfIGEOLOULX, dfIGEOLOULY,
5833
0
                             dfIGEOLOURX, dfIGEOLOURY, dfIGEOLOLRX, dfIGEOLOLRY,
5834
0
                             dfIGEOLOLLX, dfIGEOLOLLY))
5835
0
        {
5836
0
            return nullptr;
5837
0
        }
5838
0
    }
5839
15
    else if (bWriteGeoTransform)
5840
7
    {
5841
7
        poDstDS->psImage->nZone = nZone;
5842
7
        poDstDS->SetGeoTransform(gt);
5843
7
    }
5844
8
    else if (bWriteGCPs)
5845
0
    {
5846
0
        poDstDS->psImage->nZone = nZone;
5847
0
        poDstDS->SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(),
5848
0
                         poSrcDS->GetGCPSpatialRef());
5849
0
    }
5850
5851
15
    poDstDS->CloneInfo(poSrcDS, nGCIFFlags);
5852
5853
15
    if ((nGCIFFlags & GCIF_METADATA) == 0)
5854
0
    {
5855
0
        const int nSavedMOFlags = poDstDS->GetMOFlags();
5856
0
        papszSrcMD = poSrcDS->GetMetadata();
5857
0
        if (papszSrcMD != nullptr)
5858
0
        {
5859
0
            if (!bUseSrcNITFMetadata)
5860
0
            {
5861
0
                CPLStringList aosNewMD(CSLDuplicate(poDstDS->GetMetadata()));
5862
0
                bool bAdded = false;
5863
0
                for (const char *pszKeyValue : cpl::Iterate(papszSrcMD))
5864
0
                {
5865
0
                    if (!STARTS_WITH(pszKeyValue, "NITF_"))
5866
0
                    {
5867
0
                        bAdded = true;
5868
0
                        aosNewMD.AddString(pszKeyValue);
5869
0
                    }
5870
0
                }
5871
0
                if (bAdded)
5872
0
                {
5873
0
                    poDstDS->SetMetadata(aosNewMD.List());
5874
0
                }
5875
0
            }
5876
0
            else if (CSLCount(poDstDS->GetMetadata()) != CSLCount(papszSrcMD))
5877
0
            {
5878
0
                poDstDS->SetMetadata(papszSrcMD);
5879
0
            }
5880
0
        }
5881
0
        poDstDS->SetMOFlags(nSavedMOFlags);
5882
0
    }
5883
5884
15
    if (bIsCADRG)
5885
0
    {
5886
0
        GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
5887
0
        poDstDS.reset();
5888
0
        poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true, -1));
5889
0
    }
5890
5891
15
    if (poDstDS && pfnProgress && !pfnProgress(1.0, "", pProgressData))
5892
0
    {
5893
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
5894
0
        poDstDS.reset();
5895
0
    }
5896
5897
15
    return poDstDS;
5898
15
}
5899
5900
/************************************************************************/
5901
/*                         NITFHasFSDWNGField()                         */
5902
/************************************************************************/
5903
5904
static bool NITFHasFSDWNGField(VSILFILE *fpVSIL)
5905
0
{
5906
0
    char szFHDR[9 + 1] = {0};
5907
0
    bool bOK = VSIFSeekL(fpVSIL, 0, SEEK_SET) == 0;
5908
0
    bOK &= VSIFReadL(szFHDR, 9, 1, fpVSIL) == 1;
5909
0
    if (EQUAL(szFHDR, "NITF02.00"))
5910
0
    {
5911
        // Read FSDWNG field
5912
0
        char szFSDWNG[6 + 1] = {0};
5913
0
        bOK &= VSIFSeekL(fpVSIL, 280, SEEK_SET) == 0;
5914
0
        bOK &= VSIFReadL(szFSDWNG, 1, 6, fpVSIL) == 6;
5915
0
        if (bOK && EQUAL(szFSDWNG, "999998"))
5916
0
        {
5917
0
            return true;
5918
0
        }
5919
0
    }
5920
0
    return false;
5921
0
}
5922
5923
/************************************************************************/
5924
/*                        NITFPatchImageLength()                        */
5925
/*                                                                      */
5926
/*      Fixup various stuff we don't know till we have written the      */
5927
/*      imagery.  In particular the file length, image data length      */
5928
/*      and the compression ratio achieved.                             */
5929
/************************************************************************/
5930
5931
static bool NITFPatchImageLength(const char *pszFilename, VSILFILE *fp,
5932
                                 int nIMIndex, GUIntBig nImageOffset,
5933
                                 GIntBig nPixelCount, const char *pszIC,
5934
                                 vsi_l_offset nICOffset,
5935
                                 CSLConstList papszCreationOptions)
5936
5937
0
{
5938
0
    VSILFILE *fpVSIL = fp ? fp : VSIFOpenL(pszFilename, "r+b");
5939
0
    if (fpVSIL == nullptr)
5940
0
        return false;
5941
5942
0
    CPL_IGNORE_RET_VAL(VSIFSeekL(fpVSIL, 0, SEEK_END));
5943
0
    GUIntBig nFileLen = VSIFTellL(fpVSIL);
5944
5945
    /* -------------------------------------------------------------------- */
5946
    /*      Update total file length.                                       */
5947
    /* -------------------------------------------------------------------- */
5948
0
    if (nFileLen >= NITF_MAX_FILE_SIZE)
5949
0
    {
5950
0
        CPLError(CE_Failure, CPLE_AppDefined,
5951
0
                 "Too big file : " CPL_FRMT_GUIB
5952
0
                 ". Truncating to " CPL_FRMT_GUIB,
5953
0
                 nFileLen, NITF_MAX_FILE_SIZE - 1);
5954
0
        nFileLen = NITF_MAX_FILE_SIZE - 1;
5955
0
    }
5956
0
    CPLString osLen =
5957
0
        CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u", nFileLen);
5958
0
    const int nExtraOffset = NITFHasFSDWNGField(fpVSIL) ? 40 : 0;
5959
0
    if (VSIFSeekL(fpVSIL, 342 + nExtraOffset, SEEK_SET) != 0 ||
5960
0
        VSIFWriteL(reinterpret_cast<const void *>(osLen.c_str()), 12, 1,
5961
0
                   fpVSIL) != 1)
5962
0
    {
5963
0
        CPLError(CE_Failure, CPLE_FileIO, "Write error");
5964
0
        if (!fp)
5965
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpVSIL));
5966
0
        return false;
5967
0
    }
5968
5969
    /* -------------------------------------------------------------------- */
5970
    /*      Update the image data length.                                   */
5971
    /* -------------------------------------------------------------------- */
5972
0
    GUIntBig nImageSize = nFileLen - nImageOffset;
5973
0
    if (nImageSize >= 9999999999ULL)
5974
0
    {
5975
0
        CPLError(CE_Failure, CPLE_AppDefined,
5976
0
                 "Too big image size : " CPL_FRMT_GUIB
5977
0
                 ". Truncating to 9999999998",
5978
0
                 nImageSize);
5979
0
        nImageSize = 9999999998ULL;
5980
0
    }
5981
0
    osLen =
5982
0
        CPLString().Printf("%010" CPL_FRMT_GB_WITHOUT_PREFIX "u", nImageSize);
5983
0
    if (VSIFSeekL(fpVSIL, 369 + nExtraOffset + 16 * nIMIndex, SEEK_SET) != 0 ||
5984
0
        VSIFWriteL(reinterpret_cast<const void *>(osLen.c_str()), 10, 1,
5985
0
                   fpVSIL) != 1)
5986
0
    {
5987
0
        CPLError(CE_Failure, CPLE_FileIO, "Write error");
5988
0
        if (!fp)
5989
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fpVSIL));
5990
0
        return false;
5991
0
    }
5992
5993
    /* -------------------------------------------------------------------- */
5994
    /*      Update COMRAT, the compression rate variable, and CLEVEL        */
5995
    /* -------------------------------------------------------------------- */
5996
5997
    /* Set to IC position */
5998
0
    bool bOK = VSIFSeekL(fpVSIL, nICOffset, SEEK_SET) == 0;
5999
6000
    /* Read IC */
6001
0
    char szICBuf[2];
6002
0
    bOK &= VSIFReadL(szICBuf, 2, 1, fpVSIL) == 1;
6003
6004
    /* The following line works around a "feature" of *BSD libc (at least
6005
     * PC-BSD 7.1) */
6006
    /* that makes the position of the file offset unreliable when executing a */
6007
    /* "seek, read and write" sequence. After the read(), the file offset seen
6008
     * by */
6009
    /* the write() is approximately the size of a block further... */
6010
0
    bOK &= VSIFSeekL(fpVSIL, VSIFTellL(fpVSIL), SEEK_SET) == 0;
6011
6012
0
    if (!EQUALN(szICBuf, pszIC, 2))
6013
0
    {
6014
0
        CPLError(CE_Warning, CPLE_AppDefined,
6015
0
                 "Unable to locate COMRAT to update in NITF header.");
6016
0
    }
6017
0
    else
6018
0
    {
6019
0
        char szCOMRAT[5] = "00.0";
6020
6021
0
        if (EQUAL(pszIC, "C8")) /* jpeg2000 */
6022
0
        {
6023
0
            double dfRate = static_cast<GIntBig>(nFileLen - nImageOffset) * 8 /
6024
0
                            static_cast<double>(nPixelCount);
6025
6026
0
            const char *pszProfile =
6027
0
                CSLFetchNameValueDef(papszCreationOptions, "PROFILE", "");
6028
0
            if (STARTS_WITH_CI(pszProfile, "NPJE"))
6029
0
            {
6030
0
                dfRate = std::max(0.1, std::min(99.9, dfRate));
6031
6032
                // We emit in Vxyz or Nxyz format with an implicit decimal place
6033
                // between yz and z as per spec.
6034
0
                snprintf(szCOMRAT, sizeof(szCOMRAT), "%c%03u",
6035
0
                         EQUAL(pszProfile, "NPJE_VISUALLY_LOSSLESS") ? 'V'
6036
0
                                                                     : 'N',
6037
                         // % 1000 to please -Wformat-truncation
6038
0
                         static_cast<unsigned>(dfRate * 10) % 1000);
6039
0
            }
6040
0
            else
6041
0
            {
6042
0
                dfRate = std::max(0.01, std::min(99.99, dfRate));
6043
6044
                // We emit in wxyz format with an implicit decimal place
6045
                // between wx and yz as per spec for lossy compression.
6046
                // We really should have a special case for lossless
6047
                // compression.
6048
0
                snprintf(szCOMRAT, sizeof(szCOMRAT), "%04u",
6049
                         // % 10000 to please -Wformat-truncation
6050
0
                         static_cast<unsigned>(dfRate * 100) % 10000);
6051
0
            }
6052
0
        }
6053
0
        else if (EQUAL(pszIC, "C4") || EQUAL(pszIC, "M4"))  // VQ
6054
0
        {
6055
0
            snprintf(szCOMRAT, sizeof(szCOMRAT), "0.75");
6056
0
        }
6057
6058
0
        bOK &= VSIFWriteL(szCOMRAT, 4, 1, fpVSIL) == 1;
6059
6060
        /* ---------------------------------------------------------------- */
6061
        /*      Update CLEVEL.                                              */
6062
        /* ---------------------------------------------------------------- */
6063
        // Get existing CLEVEL
6064
0
        constexpr vsi_l_offset OFFSET_CLEVEL = 9;
6065
0
        constexpr int SIZE_CLEVEL = 2;
6066
0
        bOK &= VSIFSeekL(fpVSIL, OFFSET_CLEVEL, SEEK_SET) == 0;
6067
0
        char szCLEVEL[SIZE_CLEVEL + 1] = {0};
6068
0
        bOK &= VSIFReadL(szCLEVEL, 1, SIZE_CLEVEL, fpVSIL) != 0;
6069
0
        unsigned int nCLevel = static_cast<unsigned>(atoi(szCLEVEL));
6070
0
        if (nCLevel >= 3 && nCLevel <= 7)
6071
0
        {
6072
0
            const unsigned int nCLevelOri = nCLevel;
6073
0
            if (nFileLen > 2147483647)
6074
0
            {
6075
0
                nCLevel = std::max(nCLevel, 7U);
6076
0
            }
6077
0
            else if (nFileLen > 1073741833)
6078
0
            {
6079
0
                nCLevel = std::max(nCLevel, 6U);
6080
0
            }
6081
0
            else if (nFileLen > 52428799)
6082
0
            {
6083
0
                nCLevel = std::max(nCLevel, 5U);
6084
0
            }
6085
0
            if (nCLevel != nCLevelOri)
6086
0
            {
6087
0
                CPLDebug("NITF", "Updating CLEVEL from %02u to %02u",
6088
0
                         nCLevelOri, nCLevel);
6089
                // %100 to please -Wformat-truncation
6090
0
                snprintf(szCLEVEL, sizeof(szCLEVEL), "%02u", nCLevel % 100);
6091
0
                bOK &= VSIFSeekL(fpVSIL, OFFSET_CLEVEL, SEEK_SET) == 0;
6092
0
                bOK &= VSIFWriteL(szCLEVEL, 1, SIZE_CLEVEL, fpVSIL) != 0;
6093
0
            }
6094
0
        }
6095
0
        else
6096
0
        {
6097
0
            CPLError(CE_Warning, CPLE_AppDefined,
6098
0
                     "Invalid CLEVEL=%s value found when updating NITF header.",
6099
0
                     szCLEVEL);
6100
0
        }
6101
0
    }
6102
6103
0
    if (!fp && VSIFCloseL(fpVSIL) != 0)
6104
0
        bOK = false;
6105
6106
0
    if (!bOK)
6107
0
    {
6108
0
        CPLError(CE_Failure, CPLE_FileIO, "I/O error");
6109
0
    }
6110
6111
0
    return bOK;
6112
0
}
6113
6114
/************************************************************************/
6115
/*                        NITFWriteCGMSegments()                        */
6116
/************************************************************************/
6117
static bool NITFWriteCGMSegments(const char *pszFilename, VSILFILE *&fpVSIL,
6118
                                 CSLConstList papszList)
6119
20
{
6120
20
    char errorMessage[255] = "";
6121
6122
    // size of each Cgm header entry (LS (4) + LSSH (6))
6123
20
    const int nCgmHdrEntrySz = 10;
6124
6125
20
    if (papszList == nullptr)
6126
20
        return true;
6127
6128
0
    int nNUMS = 0;
6129
0
    const char *pszNUMS = CSLFetchNameValue(papszList, "SEGMENT_COUNT");
6130
0
    if (pszNUMS != nullptr)
6131
0
    {
6132
0
        nNUMS = atoi(pszNUMS);
6133
0
    }
6134
6135
    /* -------------------------------------------------------------------- */
6136
    /*      Open the target file if not already done.                       */
6137
    /* -------------------------------------------------------------------- */
6138
0
    if (fpVSIL == nullptr)
6139
0
        fpVSIL = VSIFOpenL(pszFilename, "r+b");
6140
0
    if (fpVSIL == nullptr)
6141
0
        return false;
6142
6143
    // Calculates the offset for NUMS so we can update header data
6144
0
    char achNUMI[4];  // 3 digits plus null character
6145
0
    achNUMI[3] = '\0';
6146
6147
    // NUMI offset is at a fixed offset 360
6148
0
    const vsi_l_offset nNumIOffset =
6149
0
        360 + (NITFHasFSDWNGField(fpVSIL) ? 40 : 0);
6150
0
    bool bOK = VSIFSeekL(fpVSIL, nNumIOffset, SEEK_SET) == 0;
6151
0
    bOK &= VSIFReadL(achNUMI, 3, 1, fpVSIL) == 1;
6152
0
    const int nIM = atoi(achNUMI);
6153
6154
    // 6 for size of LISH and 10 for size of LI
6155
    // NUMS offset is NumI offset plus the size of NumI + size taken up each
6156
    // the header data multiply by the number of data
6157
6158
0
    const vsi_l_offset nNumSOffset = nNumIOffset + 3 + nIM * (6 + 10);
6159
6160
    /* -------------------------------------------------------------------- */
6161
    /*      Confirm that the NUMS in the file header already matches the    */
6162
    /*      number of graphic segments we want to write                     */
6163
    /* -------------------------------------------------------------------- */
6164
0
    char achNUMS[4];
6165
6166
0
    bOK &= VSIFSeekL(fpVSIL, nNumSOffset, SEEK_SET) == 0;
6167
0
    bOK &= VSIFReadL(achNUMS, 3, 1, fpVSIL) == 1;
6168
0
    achNUMS[3] = '\0';
6169
6170
0
    if (!bOK || atoi(achNUMS) != nNUMS)
6171
0
    {
6172
0
        CPLError(CE_Failure, CPLE_AppDefined,
6173
0
                 "It appears an attempt was made to add or update graphic\n"
6174
0
                 "segments on an NITF file with existing segments.  This\n"
6175
0
                 "is not currently supported by the GDAL NITF driver.");
6176
6177
0
        return false;
6178
0
    }
6179
6180
    // allocate space for graphic header.
6181
    // Size of LS = 4, size of LSSH = 6, and 1 for null character
6182
0
    char *pachLS =
6183
0
        static_cast<char *>(CPLCalloc(nNUMS * nCgmHdrEntrySz + 1, 1));
6184
6185
    /* -------------------------------------------------------------------- */
6186
    /*  Assume no extended data such as SXSHDL, SXSHD                       */
6187
    /* -------------------------------------------------------------------- */
6188
6189
    /* ==================================================================== */
6190
    /*      Write the Graphics segments at the end of the file.             */
6191
    /* ==================================================================== */
6192
6193
0
#define PLACE(location, name, text) memcpy(location, text, strlen(text))
6194
6195
0
    for (int i = 0; bOK && i < nNUMS; i++)
6196
0
    {
6197
6198
        // Get all the fields for current CGM segment
6199
0
        const char *pszSlocRow = CSLFetchNameValue(
6200
0
            papszList, CPLString().Printf("SEGMENT_%d_SLOC_ROW", i));
6201
0
        const char *pszSlocCol = CSLFetchNameValue(
6202
0
            papszList, CPLString().Printf("SEGMENT_%d_SLOC_COL", i));
6203
0
        const char *pszSdlvl = CSLFetchNameValue(
6204
0
            papszList, CPLString().Printf("SEGMENT_%d_SDLVL", i));
6205
0
        const char *pszSalvl = CSLFetchNameValue(
6206
0
            papszList, CPLString().Printf("SEGMENT_%d_SALVL", i));
6207
0
        const char *pszData = CSLFetchNameValue(
6208
0
            papszList, CPLString().Printf("SEGMENT_%d_DATA", i));
6209
6210
        // Error checking
6211
0
        if (pszSlocRow == nullptr)
6212
0
        {
6213
0
            snprintf(errorMessage, sizeof(errorMessage),
6214
0
                     "NITF graphic segment writing error: SLOC_ROW for segment "
6215
0
                     "%d is not defined",
6216
0
                     i);
6217
0
            break;
6218
0
        }
6219
0
        if (pszSlocCol == nullptr)
6220
0
        {
6221
0
            snprintf(errorMessage, sizeof(errorMessage),
6222
0
                     "NITF graphic segment writing error: SLOC_COL for segment "
6223
0
                     "%d is not defined",
6224
0
                     i);
6225
0
            break;
6226
0
        }
6227
0
        if (pszSdlvl == nullptr)
6228
0
        {
6229
0
            snprintf(errorMessage, sizeof(errorMessage),
6230
0
                     "NITF graphic segment writing error: SDLVL for segment %d "
6231
0
                     "is not defined",
6232
0
                     i);
6233
0
            break;
6234
0
        }
6235
0
        if (pszSalvl == nullptr)
6236
0
        {
6237
0
            snprintf(errorMessage, sizeof(errorMessage),
6238
0
                     "NITF graphic segment writing error: SALVLfor segment %d "
6239
0
                     "is not defined",
6240
0
                     i);
6241
0
            break;
6242
0
        }
6243
0
        if (pszData == nullptr)
6244
0
        {
6245
0
            snprintf(errorMessage, sizeof(errorMessage),
6246
0
                     "NITF graphic segment writing error: DATA for segment %d "
6247
0
                     "is not defined",
6248
0
                     i);
6249
0
            break;
6250
0
        }
6251
6252
0
        const int nSlocCol = atoi(pszSlocRow);
6253
0
        const int nSlocRow = atoi(pszSlocCol);
6254
0
        const int nSdlvl = atoi(pszSdlvl);
6255
0
        const int nSalvl = atoi(pszSalvl);
6256
6257
        // Create a buffer for graphics segment header, 258 is the size of
6258
        // the header that we will be writing.
6259
0
        char achGSH[258];
6260
6261
0
        memset(achGSH, ' ', sizeof(achGSH));
6262
6263
0
        PLACE(achGSH + 0, SY, "SY");
6264
0
        PLACE(achGSH + 2, SID, CPLSPrintf("%010d", i));
6265
0
        PLACE(achGSH + 12, SNAME, "DEFAULT NAME        ");
6266
0
        PLACE(achGSH + 32, SSCLAS, "U");
6267
0
        PLACE(achGSH + 33, SSCLASY, "0");
6268
0
        PLACE(achGSH + 199, ENCRYP, "0");
6269
0
        PLACE(achGSH + 200, SFMT, "C");
6270
0
        PLACE(achGSH + 201, SSTRUCT, "0000000000000");
6271
0
        PLACE(achGSH + 214, SDLVL, CPLSPrintf("%03d", nSdlvl));  // size3
6272
0
        PLACE(achGSH + 217, SALVL, CPLSPrintf("%03d", nSalvl));  // size3
6273
0
        PLACE(achGSH + 220, SLOC,
6274
0
              CPLSPrintf("%05d%05d", nSlocRow, nSlocCol));  // size 10
6275
0
        PLACE(achGSH + 230, SBAND1, "0000000000");
6276
0
        PLACE(achGSH + 240, SCOLOR, "C");
6277
0
        PLACE(achGSH + 241, SBAND2, "0000000000");
6278
0
        PLACE(achGSH + 251, SRES2, "00");
6279
0
        PLACE(achGSH + 253, SXSHDL, "00000");
6280
6281
        // Move to the end of the file
6282
0
        bOK &= VSIFSeekL(fpVSIL, 0, SEEK_END) == 0;
6283
0
        bOK &= VSIFWriteL(achGSH, sizeof(achGSH), 1, fpVSIL) == 1;
6284
6285
        /* ------------------------------------------------------------------ */
6286
        /*      Prepare and write CGM segment data.                           */
6287
        /* ------------------------------------------------------------------ */
6288
0
        int nCGMSize = 0;
6289
0
        char *pszCgmToWrite =
6290
0
            CPLUnescapeString(pszData, &nCGMSize, CPLES_BackslashQuotable);
6291
6292
0
        if (nCGMSize > 999998)
6293
0
        {
6294
0
            CPLError(CE_Warning, CPLE_NotSupported,
6295
0
                     "Length of SEGMENT_%d_DATA is %d, which is greater than "
6296
0
                     "999998. Truncating...",
6297
0
                     i + 1, nCGMSize);
6298
0
            nCGMSize = 999998;
6299
0
        }
6300
6301
0
        bOK &= static_cast<int>(
6302
0
                   VSIFWriteL(pszCgmToWrite, 1, nCGMSize, fpVSIL)) == nCGMSize;
6303
6304
        /* --------------------------------------------------------------------
6305
         */
6306
        /*      Update the subheader and data size info in the file header. */
6307
        /* --------------------------------------------------------------------
6308
         */
6309
0
        snprintf(pachLS + nCgmHdrEntrySz * i, nCgmHdrEntrySz + 1, "%04d%06d",
6310
0
                 static_cast<int>(sizeof(achGSH)), nCGMSize);
6311
6312
0
        CPLFree(pszCgmToWrite);
6313
0
    }  // End For
6314
6315
    /* -------------------------------------------------------------------- */
6316
    /*      Write out the graphic segment info.                             */
6317
    /* -------------------------------------------------------------------- */
6318
6319
0
    bOK &= VSIFSeekL(fpVSIL, nNumSOffset + 3, SEEK_SET) == 0;
6320
0
    bOK &= static_cast<int>(VSIFWriteL(pachLS, 1, nNUMS * nCgmHdrEntrySz,
6321
0
                                       fpVSIL)) == nNUMS * nCgmHdrEntrySz;
6322
6323
0
    CPLFree(pachLS);
6324
6325
0
    if (strlen(errorMessage) != 0)
6326
0
    {
6327
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", errorMessage);
6328
0
        bOK = false;
6329
0
    }
6330
6331
0
    return bOK;
6332
6333
0
#undef PLACE
6334
0
}
6335
6336
/************************************************************************/
6337
/*                       NITFWriteTextSegments()                        */
6338
/************************************************************************/
6339
6340
static bool NITFWriteTextSegments(const char *pszFilename, VSILFILE *&fpVSIL,
6341
                                  CSLConstList papszList)
6342
6343
20
{
6344
    /* -------------------------------------------------------------------- */
6345
    /*      Count the number of apparent text segments to write.  There     */
6346
    /*      is nothing at all to do if there are none to write.             */
6347
    /* -------------------------------------------------------------------- */
6348
20
    int nNUMT = 0;
6349
6350
20
    for (int iOpt = 0; papszList != nullptr && papszList[iOpt] != nullptr;
6351
20
         iOpt++)
6352
0
    {
6353
0
        if (STARTS_WITH_CI(papszList[iOpt], "DATA_"))
6354
0
            nNUMT++;
6355
0
    }
6356
6357
20
    if (nNUMT == 0)
6358
20
        return true;
6359
6360
    /* -------------------------------------------------------------------- */
6361
    /*      Open the target file if not already done.                       */
6362
    /* -------------------------------------------------------------------- */
6363
0
    if (fpVSIL == nullptr)
6364
0
        fpVSIL = VSIFOpenL(pszFilename, "r+b");
6365
0
    if (fpVSIL == nullptr)
6366
0
        return false;
6367
6368
    // Get number of text field.  Since there there could be multiple images
6369
    // or graphic segment, the  offset need to be calculated dynamically.
6370
6371
0
    char achNUMI[4];  // 3 digits plus null character
6372
0
    achNUMI[3] = '\0';
6373
    // NUMI offset is at a fixed offset 360
6374
0
    const vsi_l_offset nNumIOffset =
6375
0
        360 + (NITFHasFSDWNGField(fpVSIL) ? 40 : 0);
6376
0
    bool bOK = VSIFSeekL(fpVSIL, nNumIOffset, SEEK_SET) == 0;
6377
0
    bOK &= VSIFReadL(achNUMI, 3, 1, fpVSIL) == 1;
6378
0
    int nIM = atoi(achNUMI);
6379
6380
0
    char achNUMG[4];  // 3 digits plus null character
6381
0
    achNUMG[3] = '\0';
6382
6383
    // 3 for size of NUMI.  6 and 10 are the field size for LISH and LI
6384
0
    const vsi_l_offset nNumGOffset = nNumIOffset + 3 + nIM * (6 + 10);
6385
0
    bOK &= VSIFSeekL(fpVSIL, nNumGOffset, SEEK_SET) == 0;
6386
0
    bOK &= VSIFReadL(achNUMG, 3, 1, fpVSIL) == 1;
6387
0
    const int nGS = atoi(achNUMG);
6388
6389
    // NUMT offset
6390
    // 3 for size of NUMG.  4 and 6 are filed size of LSSH and LS.
6391
    // the last + 3 is for NUMX field, which is not used
6392
0
    const vsi_l_offset nNumTOffset = nNumGOffset + 3 + nGS * (4 + 6) + 3;
6393
6394
    /* -------------------------------------------------------------------- */
6395
    /*      Confirm that the NUMT in the file header already matches the    */
6396
    /*      number of text segments we want to write, and that the          */
6397
    /*      segment header/data size info is blank.                         */
6398
    /* -------------------------------------------------------------------- */
6399
0
    char achNUMT[4];
6400
0
    char *pachLT = static_cast<char *>(CPLCalloc(nNUMT * 9 + 1, 1));
6401
6402
0
    bOK &= VSIFSeekL(fpVSIL, nNumTOffset, SEEK_SET) == 0;
6403
0
    bOK &= VSIFReadL(achNUMT, 3, 1, fpVSIL) == 1;
6404
0
    achNUMT[3] = '\0';
6405
6406
0
    bOK &= VSIFReadL(pachLT, nNUMT * 9, 1, fpVSIL) == 1;
6407
6408
0
    if (!bOK || atoi(achNUMT) != nNUMT)
6409
0
    {
6410
0
        CPLError(CE_Failure, CPLE_AppDefined,
6411
0
                 "It appears an attempt was made to add or update text\n"
6412
0
                 "segments on an NITF file with existing segments.  This\n"
6413
0
                 "is not currently supported by the GDAL NITF driver.");
6414
6415
0
        CPLFree(pachLT);
6416
0
        return false;
6417
0
    }
6418
6419
0
    if (!STARTS_WITH_CI(pachLT, "         "))
6420
0
    {
6421
0
        CPLFree(pachLT);
6422
        // presumably the text segments are already written, do nothing.
6423
0
        return true;
6424
0
    }
6425
6426
/* -------------------------------------------------------------------- */
6427
/*      At this point we likely ought to confirm NUMDES, NUMRES,        */
6428
/*      UDHDL and XHDL are zero.  Consider adding later...              */
6429
/* -------------------------------------------------------------------- */
6430
6431
/* ==================================================================== */
6432
/*      Write the text segments at the end of the file.                 */
6433
/* ==================================================================== */
6434
0
#define PLACE(location, name, text) memcpy(location, text, strlen(text))
6435
0
    int iTextSeg = 0;
6436
6437
0
    for (int iOpt = 0;
6438
0
         bOK && papszList != nullptr && papszList[iOpt] != nullptr; iOpt++)
6439
0
    {
6440
0
        if (!STARTS_WITH_CI(papszList[iOpt], "DATA_"))
6441
0
            continue;
6442
6443
0
        const char *pszTextToWrite =
6444
0
            CPLParseNameValue(papszList[iOpt], nullptr);
6445
0
        if (pszTextToWrite == nullptr)
6446
0
            continue;
6447
6448
        /* --------------------------------------------------------------------
6449
         */
6450
        /*      Locate corresponding header data in the buffer */
6451
        /* --------------------------------------------------------------------
6452
         */
6453
6454
0
        const char *pszHeaderBuffer = nullptr;
6455
0
        for (int iOpt2 = 0; papszList[iOpt2] != nullptr; iOpt2++)
6456
0
        {
6457
0
            if (!STARTS_WITH_CI(papszList[iOpt2], "HEADER_"))
6458
0
                continue;
6459
6460
0
            char *pszHeaderKey = nullptr;
6461
0
            CPLParseNameValue(papszList[iOpt2], &pszHeaderKey);
6462
0
            char *pszDataKey = nullptr;
6463
0
            CPLParseNameValue(papszList[iOpt], &pszDataKey);
6464
0
            if (pszHeaderKey == nullptr || pszDataKey == nullptr)
6465
0
            {
6466
0
                CPLFree(pszHeaderKey);
6467
0
                CPLFree(pszDataKey);
6468
0
                continue;
6469
0
            }
6470
6471
            // Point to header and data number.
6472
0
            char *pszHeaderId = pszHeaderKey + 7;
6473
0
            char *pszDataId = pszDataKey + 5;
6474
6475
0
            const bool bIsSameId = strcmp(pszHeaderId, pszDataId) == 0;
6476
0
            CPLFree(pszHeaderKey);
6477
0
            CPLFree(pszDataKey);
6478
6479
            // if ID matches, read the header information and exit the loop
6480
0
            if (bIsSameId)
6481
0
            {
6482
0
                pszHeaderBuffer = CPLParseNameValue(papszList[iOpt2], nullptr);
6483
0
                break;
6484
0
            }
6485
0
        }
6486
6487
        /* --------------------------------------------------------------------
6488
         */
6489
        /*      Prepare and write text header. */
6490
        /* --------------------------------------------------------------------
6491
         */
6492
0
        char achTSH[282];
6493
0
        memset(achTSH, ' ', sizeof(achTSH));
6494
0
        bOK &= VSIFSeekL(fpVSIL, 0, SEEK_END) == 0;
6495
6496
0
        if (pszHeaderBuffer != nullptr)
6497
0
        {
6498
0
            memcpy(achTSH, pszHeaderBuffer,
6499
0
                   std::min(strlen(pszHeaderBuffer), sizeof(achTSH)));
6500
6501
            // Take care NITF2.0 date format changes
6502
0
            const char chTimeZone = achTSH[20];
6503
6504
            // Check for Zulu time zone character.  IpachLTf that exist, then
6505
            // it is NITF2.0 format.
6506
0
            if (chTimeZone == 'Z')
6507
0
            {
6508
0
                char *achOrigDate = achTSH + 12;  // original date string
6509
6510
                // The date value taken from default NITF file date
6511
0
                char achYear[3];
6512
6513
                // Offset to the year
6514
0
                strncpy(achYear, achOrigDate + 12, 2);
6515
0
                achYear[2] = '\0';
6516
0
                const int nYear = atoi(achYear);
6517
6518
                // Set century.
6519
                // Since NITF2.0 does not track the century, we are going to
6520
                // assume any year number greater then 94 (the year NITF2.0
6521
                // spec published), will be 1900s, otherwise, it is 2000s.
6522
0
                char achNewDate[] = "20021216151629";
6523
0
                if (nYear > 94)
6524
0
                    memcpy(achNewDate, "19", 2);
6525
0
                else
6526
0
                    memcpy(achNewDate, "20", 2);
6527
6528
0
                memcpy(achNewDate + 6, achOrigDate, 8);  // copy cover DDhhmmss
6529
0
                memcpy(achNewDate + 2, achOrigDate + 12, 2);  // copy over years
6530
6531
                // Perform month conversion
6532
0
                char *pszOrigMonth = achOrigDate + 9;
6533
0
                char *pszNewMonth = achNewDate + 4;
6534
6535
0
                if (STARTS_WITH(pszOrigMonth, "JAN"))
6536
0
                    memcpy(pszNewMonth, "01", 2);
6537
0
                else if (STARTS_WITH(pszOrigMonth, "FEB"))
6538
0
                    memcpy(pszNewMonth, "02", 2);
6539
0
                else if (STARTS_WITH(pszOrigMonth, "MAR"))
6540
0
                    memcpy(pszNewMonth, "03", 2);
6541
0
                else if (STARTS_WITH(pszOrigMonth, "APR"))
6542
0
                    memcpy(pszNewMonth, "04", 2);
6543
0
                else if (STARTS_WITH(pszOrigMonth, "MAY"))
6544
0
                    memcpy(pszNewMonth, "05", 2);
6545
0
                else if (STARTS_WITH(pszOrigMonth, "JUN"))
6546
0
                    memcpy(pszNewMonth, "07", 2);
6547
0
                else if (STARTS_WITH(pszOrigMonth, "AUG"))
6548
0
                    memcpy(pszNewMonth, "08", 2);
6549
0
                else if (STARTS_WITH(pszOrigMonth, "SEP"))
6550
0
                    memcpy(pszNewMonth, "09", 2);
6551
0
                else if (STARTS_WITH(pszOrigMonth, "OCT"))
6552
0
                    memcpy(pszNewMonth, "10", 2);
6553
0
                else if (STARTS_WITH(pszOrigMonth, "NOV"))
6554
0
                    memcpy(pszNewMonth, "11", 2);
6555
0
                else if (STARTS_WITH(pszOrigMonth, "DEC"))
6556
0
                    memcpy(pszNewMonth, "12", 2);
6557
6558
0
                PLACE(achTSH + 12, TXTDT, achNewDate);
6559
0
            }
6560
0
        }
6561
0
        else
6562
0
        {  // Use default value if header information is not found
6563
0
            PLACE(achTSH + 0, TE, "TE");
6564
0
            PLACE(achTSH + 9, TXTALVL, "000");
6565
0
            PLACE(achTSH + 12, TXTDT, "20021216151629");
6566
0
            PLACE(achTSH + 106, TSCLAS, "U");
6567
0
            PLACE(achTSH + 273, ENCRYP, "0");
6568
0
            PLACE(achTSH + 274, TXTFMT, "STA");
6569
0
            PLACE(achTSH + 277, TXSHDL, "00000");
6570
0
        }
6571
6572
0
        bOK &= VSIFWriteL(achTSH, sizeof(achTSH), 1, fpVSIL) == 1;
6573
6574
        /* --------------------------------------------------------------------
6575
         */
6576
        /*      Prepare and write text segment data. */
6577
        /* --------------------------------------------------------------------
6578
         */
6579
6580
0
        int nTextLength = static_cast<int>(strlen(pszTextToWrite));
6581
0
        if (nTextLength > 99998)
6582
0
        {
6583
0
            CPLError(CE_Warning, CPLE_NotSupported,
6584
0
                     "Length of DATA_%d is %d, which is greater than 99998. "
6585
0
                     "Truncating...",
6586
0
                     iTextSeg + 1, nTextLength);
6587
0
            nTextLength = 99998;
6588
0
        }
6589
6590
0
        bOK &= static_cast<int>(VSIFWriteL(pszTextToWrite, 1, nTextLength,
6591
0
                                           fpVSIL)) == nTextLength;
6592
6593
        /* --------------------------------------------------------------------
6594
         */
6595
        /*      Update the subheader and data size info in the file header. */
6596
        /* --------------------------------------------------------------------
6597
         */
6598
0
        CPLsnprintf(pachLT + 9 * iTextSeg + 0, 9 + 1, "%04d%05d",
6599
0
                    static_cast<int>(sizeof(achTSH)), nTextLength);
6600
6601
0
        iTextSeg++;
6602
0
    }
6603
6604
    /* -------------------------------------------------------------------- */
6605
    /*      Write out the text segment info.                                */
6606
    /* -------------------------------------------------------------------- */
6607
6608
0
    bOK &= VSIFSeekL(fpVSIL, nNumTOffset + 3, SEEK_SET) == 0;
6609
0
    bOK &=
6610
0
        static_cast<int>(VSIFWriteL(pachLT, 1, nNUMT * 9, fpVSIL)) == nNUMT * 9;
6611
6612
0
    CPLFree(pachLT);
6613
6614
0
    return bOK;
6615
0
#undef PLACE
6616
0
}
6617
6618
/************************************************************************/
6619
/*                            NITFWriteDES()                            */
6620
/************************************************************************/
6621
6622
static bool NITFWriteDES(VSILFILE *&fp, const char *pszFilename,
6623
                         vsi_l_offset nOffsetLDSH, int iDES,
6624
                         const char *pszDESName, const GByte *pabyDESData,
6625
                         int nArrayLen)
6626
0
{
6627
0
    constexpr int LEN_DE = 2;
6628
0
    constexpr int LEN_DESID = 25;
6629
0
    constexpr int LEN_DESOFLW = 6;
6630
0
    constexpr int LEN_DESITEM = 3;
6631
0
    const int nTotalLen = LEN_DE + LEN_DESID + nArrayLen;
6632
6633
0
    const bool bIsTRE_OVERFLOW = (strcmp(pszDESName, "TRE_OVERFLOW") == 0);
6634
0
    const int MIN_LEN_DES_SUBHEADER =
6635
0
        200 + (bIsTRE_OVERFLOW ? LEN_DESOFLW + LEN_DESITEM : 0);
6636
6637
0
    if (nTotalLen < MIN_LEN_DES_SUBHEADER)
6638
0
    {
6639
0
        CPLError(CE_Failure, CPLE_AppDefined,
6640
0
                 "DES does not contain enough data");
6641
0
        return false;
6642
0
    }
6643
6644
0
    int nDESITEM = 0;
6645
0
    GUIntBig nIXSOFLOffset = 0;
6646
0
    if (bIsTRE_OVERFLOW)
6647
0
    {
6648
0
        char szDESITEM[LEN_DESITEM + 1];
6649
0
        memcpy(szDESITEM, pabyDESData + 169 + LEN_DESOFLW, LEN_DESITEM);
6650
0
        szDESITEM[LEN_DESITEM] = '\0';
6651
0
        if (!isdigit(static_cast<unsigned char>(szDESITEM[0])) ||
6652
0
            !isdigit(static_cast<unsigned char>(szDESITEM[1])) ||
6653
0
            !isdigit(static_cast<unsigned char>(szDESITEM[2])))
6654
0
        {
6655
0
            CPLError(CE_Failure, CPLE_AppDefined,
6656
0
                     "Invalid value for DESITEM: '%s'", szDESITEM);
6657
0
            return false;
6658
0
        }
6659
0
        nDESITEM = atoi(szDESITEM);
6660
6661
0
        char szDESOFLW[LEN_DESOFLW + 1];
6662
0
        memcpy(szDESOFLW, pabyDESData + 169, LEN_DESOFLW);
6663
0
        szDESOFLW[LEN_DESOFLW] = '\0';
6664
0
        if (strcmp(szDESOFLW, "IXSHD ") == 0)
6665
0
        {
6666
0
            auto psFile = NITFOpenEx(fp, pszFilename);
6667
0
            if (psFile == nullptr)
6668
0
            {
6669
0
                fp = nullptr;
6670
0
                return false;
6671
0
            }
6672
6673
0
            int nImageIdx = 1;
6674
0
            for (int iSegment = 0; iSegment < psFile->nSegmentCount; ++iSegment)
6675
0
            {
6676
0
                const auto psSegInfo = psFile->pasSegmentInfo + iSegment;
6677
0
                if (!EQUAL(psSegInfo->szSegmentType, "IM"))
6678
0
                    continue;
6679
0
                if (nImageIdx == nDESITEM)
6680
0
                {
6681
0
                    auto psImage = NITFImageAccess(psFile, iSegment);
6682
0
                    if (psImage == nullptr)
6683
0
                    {
6684
0
                        nImageIdx = -1;
6685
0
                        break;
6686
0
                    }
6687
6688
0
                    if (psImage->nIXSOFL == -1)
6689
0
                    {
6690
0
                        CPLError(CE_Failure, CPLE_AppDefined,
6691
0
                                 "Missing IXSOFL field in image %d. "
6692
0
                                 "RESERVE_SPACE_FOR_TRE_OVERFLOW=YES creation "
6693
0
                                 "option likely missing.",
6694
0
                                 nImageIdx);
6695
0
                    }
6696
0
                    else if (psImage->nIXSOFL != 0)
6697
0
                    {
6698
0
                        CPLError(CE_Failure, CPLE_AppDefined,
6699
0
                                 "Expected IXSOFL of image %d to be 0. Got %d",
6700
0
                                 nImageIdx, psImage->nIXSOFL);
6701
0
                    }
6702
0
                    else
6703
0
                    {
6704
0
                        nIXSOFLOffset = psSegInfo->nSegmentHeaderStart +
6705
0
                                        psImage->nIXSOFLOffsetInSubfileHeader;
6706
0
                    }
6707
6708
0
                    NITFImageDeaccess(psImage);
6709
0
                    break;
6710
0
                }
6711
0
                ++nImageIdx;
6712
0
            }
6713
6714
0
            psFile->fp = nullptr;
6715
0
            NITFClose(psFile);
6716
6717
0
            if (nImageIdx != nDESITEM)
6718
0
            {
6719
0
                CPLError(CE_Failure, CPLE_AppDefined,
6720
0
                         "Cannot find image matching DESITEM = %d value",
6721
0
                         nDESITEM);
6722
0
                return false;
6723
0
            }
6724
0
            if (nIXSOFLOffset == 0)
6725
0
            {
6726
0
                return false;
6727
0
            }
6728
0
        }
6729
0
        else if (strcmp(szDESOFLW, "UDHD  ") == 0 ||
6730
0
                 strcmp(szDESOFLW, "UDID  ") == 0 ||
6731
0
                 strcmp(szDESOFLW, "XHD   ") == 0 ||
6732
0
                 strcmp(szDESOFLW, "SXSHD ") == 0 ||
6733
0
                 strcmp(szDESOFLW, "TXSHD ") == 0)
6734
0
        {
6735
0
            CPLError(CE_Warning, CPLE_AppDefined,
6736
0
                     "Unhandled value for DESOFLW: '%s'. "
6737
0
                     "Segment subheader fields will not be updated.",
6738
0
                     szDESOFLW);
6739
0
        }
6740
0
        else
6741
0
        {
6742
0
            CPLError(CE_Failure, CPLE_AppDefined,
6743
0
                     "Invalid value for DESOFLW: '%s'", szDESOFLW);
6744
0
            return false;
6745
0
        }
6746
0
    }
6747
6748
    // Extract DESSHL value
6749
0
    constexpr int LEN_DESSHL = 4;
6750
0
    char szDESSHL[LEN_DESSHL + 1];
6751
0
    const int OFFSET_DESSHL =
6752
0
        169 + (bIsTRE_OVERFLOW ? LEN_DESOFLW + LEN_DESITEM : 0);
6753
0
    memcpy(szDESSHL, pabyDESData + OFFSET_DESSHL, LEN_DESSHL);
6754
0
    szDESSHL[LEN_DESSHL] = '\0';
6755
0
    if (!isdigit(static_cast<unsigned char>(szDESSHL[0])) ||
6756
0
        !isdigit(static_cast<unsigned char>(szDESSHL[1])) ||
6757
0
        !isdigit(static_cast<unsigned char>(szDESSHL[2])) ||
6758
0
        !isdigit(static_cast<unsigned char>(szDESSHL[3])))
6759
0
    {
6760
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DESSHL: '%s'",
6761
0
                 szDESSHL);
6762
0
        return false;
6763
0
    }
6764
0
    const int nDESSHL = atoi(szDESSHL);
6765
0
    const int nSubHeadLen = nDESSHL + MIN_LEN_DES_SUBHEADER;
6766
0
    const int nDataLen =
6767
0
        nTotalLen - nSubHeadLen;  // Length of DESDATA field only
6768
0
    if (nDataLen < 0)
6769
0
    {
6770
0
        CPLError(
6771
0
            CE_Failure, CPLE_AppDefined,
6772
0
            "Value of DESSHL = '%s' is not consistent with provided DESData",
6773
0
            szDESSHL);
6774
0
        return false;
6775
0
    }
6776
6777
0
    if (nSubHeadLen > 9998 || nDataLen > 999999998)
6778
0
    {
6779
0
        CPLError(CE_Failure, CPLE_AppDefined, "DES is too big to be written");
6780
0
        return false;
6781
0
    }
6782
6783
0
    bool bOK = VSIFSeekL(fp, 0, SEEK_END) == 0;
6784
0
    bOK &= VSIFWriteL("DE", 1, 2, fp) == 2;
6785
0
    bOK &= VSIFWriteL(CPLSPrintf("%-25s", pszDESName), 1, 25, fp) == 25;
6786
0
    bOK &= VSIFWriteL(pabyDESData, 1, nArrayLen, fp) ==
6787
0
           static_cast<size_t>(nArrayLen);
6788
6789
    // Update LDSH and LD in the NITF Header
6790
0
    bOK &= VSIFSeekL(fp, nOffsetLDSH + iDES * 13, SEEK_SET) == 0;
6791
0
    bOK &= VSIFWriteL(CPLSPrintf("%04d", nSubHeadLen), 1, 4, fp) == 4;
6792
0
    bOK &= VSIFWriteL(CPLSPrintf("%09d", nDataLen), 1, 9, fp) == 9;
6793
6794
0
    if (nIXSOFLOffset > 0)
6795
0
    {
6796
0
        CPLDebug("NITF", "Patching IXSOFL of image %d to %d", iDES + 1,
6797
0
                 nDESITEM);
6798
0
        bOK &= VSIFSeekL(fp, nIXSOFLOffset, SEEK_SET) == 0;
6799
0
        bOK &= VSIFWriteL(CPLSPrintf("%03d", nDESITEM), 1, 3, fp) == 3;
6800
0
    }
6801
6802
0
    return bOK;
6803
0
}
6804
6805
/************************************************************************/
6806
/*                            NITFWriteDES()                            */
6807
/************************************************************************/
6808
6809
static bool NITFWriteDES(const char *pszFilename, VSILFILE *&fpVSIL,
6810
                         GDALOffsetPatcher::OffsetPatcher *offsetPatcher,
6811
                         const CPLStringList &aosOptions, int nReciprocalScale)
6812
20
{
6813
20
    int nDESFound = offsetPatcher ? 1 : 0;
6814
20
    for (const char *pszOpt : aosOptions)
6815
5.50k
    {
6816
5.50k
        if (cpl::starts_with(std::string_view(pszOpt),
6817
5.50k
                             std::string_view("DES=")))
6818
0
        {
6819
0
            nDESFound++;
6820
0
        }
6821
5.50k
    }
6822
20
    if (nDESFound == 0)
6823
20
    {
6824
20
        return true;
6825
20
    }
6826
6827
    /* -------------------------------------------------------------------- */
6828
    /*      Open the target file if not already done.                       */
6829
    /* -------------------------------------------------------------------- */
6830
0
    if (fpVSIL == nullptr)
6831
0
        fpVSIL = VSIFOpenL(pszFilename, "r+b");
6832
0
    if (fpVSIL == nullptr)
6833
0
        return false;
6834
6835
    // NUMI offset is at a fixed offset 360, unless there is a FSDWNG field
6836
0
    const vsi_l_offset nNumIOffset =
6837
0
        360 + (NITFHasFSDWNGField(fpVSIL) ? 40 : 0);
6838
6839
0
    char achNUMI[4];  // 3 digits plus null character
6840
0
    achNUMI[3] = '\0';
6841
0
    bool bOK = VSIFSeekL(fpVSIL, nNumIOffset, SEEK_SET) == 0;
6842
0
    bOK &= VSIFReadL(achNUMI, 3, 1, fpVSIL) == 1;
6843
0
    int nIM = atoi(achNUMI);
6844
6845
0
    char achNUMG[4];  // 3 digits plus null character
6846
0
    achNUMG[3] = '\0';
6847
6848
    // 3 for size of NUMI.  6 and 10 are the field size for LISH and LI
6849
0
    const vsi_l_offset nNumGOffset = nNumIOffset + 3 + nIM * (6 + 10);
6850
0
    bOK &= VSIFSeekL(fpVSIL, nNumGOffset, SEEK_SET) == 0;
6851
0
    bOK &= VSIFReadL(achNUMG, 3, 1, fpVSIL) == 1;
6852
0
    const int nGS = atoi(achNUMG);
6853
6854
    // NUMT offset
6855
    // 3 for size of NUMG.  4 and 6 are the field size of LSSH and LS.
6856
    // the last + 3 is for NUMX field, which is not used
6857
0
    const vsi_l_offset nNumTOffset = nNumGOffset + 3 + nGS * (4 + 6) + 3;
6858
0
    char achNUMT[4];
6859
0
    bOK &= VSIFSeekL(fpVSIL, nNumTOffset, SEEK_SET) == 0;
6860
0
    bOK &= VSIFReadL(achNUMT, 3, 1, fpVSIL) == 1;
6861
0
    achNUMT[3] = '\0';
6862
0
    const int nNUMT = atoi(achNUMT);
6863
6864
    // NUMDES offset
6865
    // 3 for size of NUMT. 4 and 5 are the field size of LTSH and LT.
6866
0
    const vsi_l_offset nNumDESOffset = nNumTOffset + 3 + (4 + 5) * nNUMT;
6867
0
    char achNUMDES[4];
6868
0
    bOK &= VSIFSeekL(fpVSIL, nNumDESOffset, SEEK_SET) == 0;
6869
0
    bOK &= VSIFReadL(achNUMDES, 3, 1, fpVSIL) == 1;
6870
0
    achNUMDES[3] = '\0';
6871
6872
0
    if (!bOK || atoi(achNUMDES) != nDESFound)
6873
0
    {
6874
0
        CPLError(CE_Failure, CPLE_AppDefined,
6875
0
                 "It appears an attempt was made to add or update DE\n"
6876
0
                 "segments on an NITF file with existing segments.  This\n"
6877
0
                 "is not currently supported by the GDAL NITF driver.");
6878
0
        return false;
6879
0
    }
6880
6881
0
    const auto nOffsetLDSH = nNumDESOffset + 3;
6882
6883
0
    int iDES = 0;
6884
0
    if (offsetPatcher)
6885
0
    {
6886
0
        bOK = RPFFrameWriteCADRG_RPFDES(offsetPatcher, fpVSIL, nOffsetLDSH,
6887
0
                                        aosOptions, nReciprocalScale);
6888
0
        iDES = 1;
6889
0
    }
6890
6891
0
    for (int iOption = 0; bOK && iOption < aosOptions.size(); iOption++)
6892
0
    {
6893
0
        if (!cpl::starts_with(std::string_view(aosOptions[iOption]),
6894
0
                              std::string_view("DES=")))
6895
0
        {
6896
0
            continue;
6897
0
        }
6898
6899
        /* We don't use CPLParseNameValue() as it removes leading spaces */
6900
        /* from the value (see #3088) */
6901
0
        const char *pszDESOpt = aosOptions[iOption] + 4;
6902
0
        const char *pszDelim = strchr(pszDESOpt, '=');
6903
0
        if (pszDelim == nullptr)
6904
0
        {
6905
0
            CPLError(CE_Failure, CPLE_AppDefined,
6906
0
                     "Could not parse creation options %s", pszDESOpt);
6907
0
            return false;
6908
0
        }
6909
6910
0
        const size_t nNameLength = strlen(pszDESOpt) - strlen(pszDelim);
6911
0
        if (nNameLength > 25)
6912
0
        {
6913
0
            CPLError(CE_Failure, CPLE_AppDefined,
6914
0
                     "Specified DESID is too long %s", pszDESOpt);
6915
0
            return false;
6916
0
        }
6917
6918
0
        char *pszDESName = static_cast<char *>(CPLMalloc(nNameLength + 1));
6919
0
        memcpy(pszDESName, pszDESOpt, nNameLength);
6920
0
        pszDESName[nNameLength] = '\0';
6921
6922
0
        const char *pszEscapedContents = pszDelim + 1;
6923
6924
0
        int nContentLength = 0;
6925
0
        GByte *pabyUnescapedContents =
6926
0
            reinterpret_cast<GByte *>(CPLUnescapeString(
6927
0
                pszEscapedContents, &nContentLength, CPLES_BackslashQuotable));
6928
6929
0
        if (!NITFWriteDES(fpVSIL, pszFilename, nOffsetLDSH, iDES, pszDESName,
6930
0
                          pabyUnescapedContents, nContentLength))
6931
0
        {
6932
0
            CPLFree(pszDESName);
6933
0
            CPLFree(pabyUnescapedContents);
6934
0
            CPLError(CE_Failure, CPLE_AppDefined, "Could not write DES %d",
6935
0
                     iDES);
6936
0
            return false;
6937
0
        }
6938
6939
0
        CPLFree(pszDESName);
6940
0
        CPLFree(pabyUnescapedContents);
6941
6942
0
        iDES++;
6943
0
    }
6944
6945
0
    return bOK;
6946
0
}
6947
6948
/************************************************************************/
6949
/*                          UpdateFileLength()                          */
6950
/************************************************************************/
6951
6952
static bool UpdateFileLength(VSILFILE *fp)
6953
0
{
6954
6955
    /* -------------------------------------------------------------------- */
6956
    /*      Update total file length.                                       */
6957
    /* -------------------------------------------------------------------- */
6958
0
    bool bOK = VSIFSeekL(fp, 0, SEEK_END) == 0;
6959
0
    GUIntBig nFileLen = VSIFTellL(fp);
6960
6961
    // Offset to file length entry
6962
0
    const vsi_l_offset nFLOffset = 342 + (NITFHasFSDWNGField(fp) ? 40 : 0);
6963
0
    bOK &= VSIFSeekL(fp, nFLOffset, SEEK_SET) == 0;
6964
0
    if (nFileLen >= NITF_MAX_FILE_SIZE)
6965
0
    {
6966
0
        CPLError(CE_Failure, CPLE_AppDefined,
6967
0
                 "Too big file : " CPL_FRMT_GUIB
6968
0
                 ". Truncating to " CPL_FRMT_GUIB,
6969
0
                 nFileLen, NITF_MAX_FILE_SIZE - 1);
6970
0
        nFileLen = NITF_MAX_FILE_SIZE - 1;
6971
0
    }
6972
0
    CPLString osLen =
6973
0
        CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u", nFileLen);
6974
0
    bOK &= VSIFWriteL(reinterpret_cast<const void *>(osLen.c_str()), 12, 1,
6975
0
                      fp) == 1;
6976
0
    return bOK;
6977
0
}
6978
6979
/************************************************************************/
6980
/*                       NITFWriteExtraSegments()                       */
6981
/************************************************************************/
6982
6983
static bool
6984
NITFWriteExtraSegments(const char *pszFilename, VSILFILE *fpIn,
6985
                       CSLConstList papszCgmMD, CSLConstList papszTextMD,
6986
                       GDALOffsetPatcher::OffsetPatcher *offsetPatcher,
6987
                       const CPLStringList &aosOptions, int nReciprocalScale)
6988
20
{
6989
20
    VSILFILE *fp = fpIn;
6990
20
    bool bOK = NITFWriteCGMSegments(pszFilename, fp, papszCgmMD);
6991
20
    bOK &= NITFWriteTextSegments(pszFilename, fp, papszTextMD);
6992
20
    bOK &= NITFWriteDES(pszFilename, fp, offsetPatcher, aosOptions,
6993
20
                        nReciprocalScale);
6994
20
    if (fp)
6995
0
    {
6996
0
        bOK &= UpdateFileLength(fp);
6997
6998
0
        if (fp != fpIn && VSIFCloseL(fp) != 0)
6999
0
            bOK = false;
7000
7001
0
        if (!bOK)
7002
0
        {
7003
0
            CPLError(CE_Failure, CPLE_FileIO, "I/O error");
7004
0
        }
7005
0
    }
7006
20
    return bOK;
7007
20
}
7008
7009
/************************************************************************/
7010
/*                         NITFWriteJPEGImage()                         */
7011
/************************************************************************/
7012
7013
#ifdef JPEG_SUPPORTED
7014
7015
int NITFWriteJPEGBlock(GDALDataset *poSrcDS, VSILFILE *fp, int nBlockXOff,
7016
                       int nBlockYOff, int nBlockXSize, int nBlockYSize,
7017
                       int bProgressive, int nQuality, const GByte *pabyAPP6,
7018
                       int nRestartInterval, GDALProgressFunc pfnProgress,
7019
                       void *pProgressData);
7020
7021
static bool NITFWriteJPEGImage(GDALDataset *poSrcDS, VSILFILE *fp,
7022
                               vsi_l_offset nStartOffset,
7023
                               CSLConstList papszOptions,
7024
                               GDALProgressFunc pfnProgress,
7025
                               void *pProgressData)
7026
0
{
7027
0
    if (!pfnProgress(0.0, nullptr, pProgressData))
7028
0
        return false;
7029
7030
    /* -------------------------------------------------------------------- */
7031
    /*      Some some rudimentary checks                                    */
7032
    /* -------------------------------------------------------------------- */
7033
0
    const int nBands = poSrcDS->GetRasterCount();
7034
0
    if (nBands != 1 && nBands != 3)
7035
0
    {
7036
0
        CPLError(CE_Failure, CPLE_NotSupported,
7037
0
                 "JPEG driver doesn't support %d bands.  Must be 1 (grey) "
7038
0
                 "or 3 (RGB) bands.\n",
7039
0
                 nBands);
7040
7041
0
        return false;
7042
0
    }
7043
7044
0
    GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
7045
7046
0
#if defined(JPEG_LIB_MK1) || defined(JPEG_DUAL_MODE_8_12)
7047
0
    if (eDT != GDT_UInt8 && eDT != GDT_UInt16)
7048
0
    {
7049
0
        CPLError(CE_Failure, CPLE_NotSupported,
7050
0
                 "JPEG driver doesn't support data type %s. "
7051
0
                 "Only eight and twelve bit bands supported (Mk1 libjpeg).\n",
7052
0
                 GDALGetDataTypeName(
7053
0
                     poSrcDS->GetRasterBand(1)->GetRasterDataType()));
7054
7055
0
        return false;
7056
0
    }
7057
7058
0
    if (eDT == GDT_UInt16 || eDT == GDT_Int16)
7059
0
        eDT = GDT_UInt16;
7060
0
    else
7061
0
        eDT = GDT_UInt8;
7062
7063
#else
7064
    if (eDT != GDT_UInt8)
7065
    {
7066
        CPLError(CE_Failure, CPLE_NotSupported,
7067
                 "JPEG driver doesn't support data type %s. "
7068
                 "Only eight bit byte bands supported.\n",
7069
                 GDALGetDataTypeName(
7070
                     poSrcDS->GetRasterBand(1)->GetRasterDataType()));
7071
7072
        return false;
7073
    }
7074
7075
    eDT = GDT_UInt8;  // force to 8bit.
7076
#endif
7077
7078
    /* -------------------------------------------------------------------- */
7079
    /*      What options has the user selected?                             */
7080
    /* -------------------------------------------------------------------- */
7081
0
    int nQuality = 75;
7082
0
    if (CSLFetchNameValue(papszOptions, "QUALITY") != nullptr)
7083
0
    {
7084
0
        nQuality = atoi(CSLFetchNameValue(papszOptions, "QUALITY"));
7085
0
        if (nQuality < 10 || nQuality > 100)
7086
0
        {
7087
0
            CPLError(CE_Failure, CPLE_IllegalArg,
7088
0
                     "QUALITY=%s is not a legal value in the range 10-100.",
7089
0
                     CSLFetchNameValue(papszOptions, "QUALITY"));
7090
0
            return false;
7091
0
        }
7092
0
    }
7093
7094
0
    int nRestartInterval = -1;
7095
0
    if (CSLFetchNameValue(papszOptions, "RESTART_INTERVAL") != nullptr)
7096
0
    {
7097
0
        nRestartInterval =
7098
0
            atoi(CSLFetchNameValue(papszOptions, "RESTART_INTERVAL"));
7099
0
    }
7100
7101
0
    const bool bProgressive = CPLFetchBool(papszOptions, "PROGRESSIVE", false);
7102
7103
    /* -------------------------------------------------------------------- */
7104
    /*      Compute blocking factors                                        */
7105
    /* -------------------------------------------------------------------- */
7106
0
    const int nXSize = poSrcDS->GetRasterXSize();
7107
0
    const int nYSize = poSrcDS->GetRasterYSize();
7108
0
    int nNPPBH = nXSize;
7109
0
    int nNPPBV = nYSize;
7110
7111
0
    if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") != nullptr)
7112
0
        nNPPBH = atoi(CSLFetchNameValue(papszOptions, "BLOCKXSIZE"));
7113
7114
0
    if (CSLFetchNameValue(papszOptions, "BLOCKYSIZE") != nullptr)
7115
0
        nNPPBV = atoi(CSLFetchNameValue(papszOptions, "BLOCKYSIZE"));
7116
7117
0
    if (CSLFetchNameValue(papszOptions, "NPPBH") != nullptr)
7118
0
        nNPPBH = atoi(CSLFetchNameValue(papszOptions, "NPPBH"));
7119
7120
0
    if (CSLFetchNameValue(papszOptions, "NPPBV") != nullptr)
7121
0
        nNPPBV = atoi(CSLFetchNameValue(papszOptions, "NPPBV"));
7122
7123
0
    if (nNPPBH <= 0 || nNPPBV <= 0 || nNPPBH > 9999 || nNPPBV > 9999)
7124
0
    {
7125
0
        nNPPBH = 256;
7126
0
        nNPPBV = 256;
7127
0
    }
7128
7129
0
    const int nNBPR = DIV_ROUND_UP(nXSize, nNPPBH);
7130
0
    const int nNBPC = DIV_ROUND_UP(nYSize, nNPPBV);
7131
7132
    /* -------------------------------------------------------------------- */
7133
    /*  Creates APP6 NITF application segment (required by MIL-STD-188-198) */
7134
    /*  see #3345                                                           */
7135
    /* -------------------------------------------------------------------- */
7136
0
    GByte abyAPP6[23];
7137
0
    memcpy(abyAPP6, "NITF", 4);
7138
0
    abyAPP6[4] = 0;
7139
0
    int nOffset = 5;
7140
7141
    /* Version : 2.0 */
7142
0
    GUInt16 nUInt16 = 0x0200;
7143
0
    CPL_MSBPTR16(&nUInt16);
7144
0
    memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16));
7145
0
    nOffset += sizeof(nUInt16);
7146
7147
    /* IMODE */
7148
0
    abyAPP6[nOffset] = (nBands == 1) ? 'B' : 'P';
7149
0
    nOffset++;
7150
7151
    /* Number of image blocks per row */
7152
0
    nUInt16 = static_cast<GUInt16>(nNBPR);
7153
0
    CPL_MSBPTR16(&nUInt16);
7154
0
    memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16));
7155
0
    nOffset += sizeof(nUInt16);
7156
7157
    /* Number of image blocks per column */
7158
0
    nUInt16 = static_cast<GUInt16>(nNBPC);
7159
0
    CPL_MSBPTR16(&nUInt16);
7160
0
    memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16));
7161
0
    nOffset += sizeof(nUInt16);
7162
7163
    /* Image color */
7164
0
    abyAPP6[nOffset] = (nBands == 1) ? 0 : 1;
7165
0
    nOffset++;
7166
7167
    /* Original sample precision */
7168
    /* coverity[dead_error_line] */
7169
0
    abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 12 : 8;
7170
0
    nOffset++;
7171
7172
    /* Image class */
7173
0
    abyAPP6[nOffset] = 0;
7174
0
    nOffset++;
7175
7176
    /* JPEG coding process */
7177
    /* coverity[dead_error_line] */
7178
0
    abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 4 : 1;
7179
0
    nOffset++;
7180
7181
    /* Quality */
7182
0
    abyAPP6[nOffset] = 0;
7183
0
    nOffset++;
7184
7185
    /* Stream color */
7186
0
    abyAPP6[nOffset] = (nBands == 1) ? 0 /* Monochrome */ : 2 /* YCbCr*/;
7187
0
    nOffset++;
7188
7189
    /* Stream bits */
7190
    /* coverity[dead_error_line] */
7191
0
    abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 12 : 8;
7192
0
    nOffset++;
7193
7194
    /* Horizontal filtering */
7195
0
    abyAPP6[nOffset] = 1;
7196
0
    nOffset++;
7197
7198
    /* Vertical filtering */
7199
0
    abyAPP6[nOffset] = 1;
7200
0
    nOffset++;
7201
7202
    /* Reserved */
7203
0
    abyAPP6[nOffset] = 0;
7204
0
    nOffset++;
7205
0
    abyAPP6[nOffset] = 0;
7206
0
    nOffset++;
7207
0
    (void)nOffset;
7208
7209
0
    CPLAssert(nOffset == sizeof(abyAPP6));
7210
7211
    /* -------------------------------------------------------------------- */
7212
    /*      Prepare block map if necessary                                  */
7213
    /* -------------------------------------------------------------------- */
7214
7215
0
    bool bOK = VSIFSeekL(fp, nStartOffset, SEEK_SET) == 0;
7216
7217
0
    const char *pszIC = CSLFetchNameValue(papszOptions, "IC");
7218
0
    GUInt32 nIMDATOFF = 0;
7219
0
    constexpr GUInt32 BLOCKMAP_HEADER_SIZE = 4 + 2 + 2 + 2;
7220
0
    if (EQUAL(pszIC, "M3"))
7221
0
    {
7222
        /* Prepare the block map */
7223
0
        GUInt32 nIMDATOFF_MSB = BLOCKMAP_HEADER_SIZE + nNBPC * nNBPR * 4;
7224
0
        nIMDATOFF = nIMDATOFF_MSB;
7225
0
        GUInt16 nBMRLNTH = 4;
7226
0
        GUInt16 nTMRLNTH = 0;
7227
0
        GUInt16 nTPXCDLNTH = 0;
7228
7229
0
        CPL_MSBPTR32(&nIMDATOFF_MSB);
7230
0
        CPL_MSBPTR16(&nBMRLNTH);
7231
0
        CPL_MSBPTR16(&nTMRLNTH);
7232
0
        CPL_MSBPTR16(&nTPXCDLNTH);
7233
7234
0
        bOK &= VSIFWriteL(&nIMDATOFF_MSB, 4, 1, fp) == 1;
7235
0
        bOK &= VSIFWriteL(&nBMRLNTH, 2, 1, fp) == 1;
7236
0
        bOK &= VSIFWriteL(&nTMRLNTH, 2, 1, fp) == 1;
7237
0
        bOK &= VSIFWriteL(&nTPXCDLNTH, 2, 1, fp) == 1;
7238
7239
        /* Reserve space for the table itself */
7240
0
        bOK &= VSIFSeekL(fp, static_cast<vsi_l_offset>(nNBPC) * nNBPR * 4,
7241
0
                         SEEK_CUR) == 0;
7242
0
    }
7243
7244
    /* -------------------------------------------------------------------- */
7245
    /*      Copy each block                                                 */
7246
    /* -------------------------------------------------------------------- */
7247
0
    for (int nBlockYOff = 0; bOK && nBlockYOff < nNBPC; nBlockYOff++)
7248
0
    {
7249
0
        for (int nBlockXOff = 0; bOK && nBlockXOff < nNBPR; nBlockXOff++)
7250
0
        {
7251
#ifdef DEBUG_VERBOSE
7252
            CPLDebug("NITF", "nBlockXOff=%d/%d, nBlockYOff=%d/%d", nBlockXOff,
7253
                     nNBPR, nBlockYOff, nNBPC);
7254
#endif
7255
0
            if (EQUAL(pszIC, "M3"))
7256
0
            {
7257
                /* Write block offset for current block */
7258
7259
0
                const GUIntBig nCurPos = VSIFTellL(fp);
7260
0
                bOK &= VSIFSeekL(fp,
7261
0
                                 nStartOffset + BLOCKMAP_HEADER_SIZE +
7262
0
                                     4 * (nBlockYOff * nNBPR + nBlockXOff),
7263
0
                                 SEEK_SET) == 0;
7264
0
                const GUIntBig nBlockOffset =
7265
0
                    nCurPos - nStartOffset - nIMDATOFF;
7266
0
                if (nBlockOffset <= UINT_MAX)
7267
0
                {
7268
0
                    GUInt32 nBlockOffset32 = static_cast<GUInt32>(nBlockOffset);
7269
0
                    CPL_MSBPTR32(&nBlockOffset32);
7270
0
                    bOK &= VSIFWriteL(&nBlockOffset32, 4, 1, fp) == 1;
7271
0
                }
7272
0
                else
7273
0
                {
7274
0
                    CPLError(CE_Failure, CPLE_AppDefined,
7275
0
                             "Offset for block (%d, %d) = " CPL_FRMT_GUIB
7276
0
                             ". Cannot fit into 32 bits...",
7277
0
                             nBlockXOff, nBlockYOff, nBlockOffset);
7278
7279
0
                    GUInt32 nBlockOffset32 = UINT_MAX;
7280
0
                    for (int i = nBlockYOff * nNBPR + nBlockXOff;
7281
0
                         bOK && i < nNBPC * nNBPR; i++)
7282
0
                    {
7283
0
                        bOK &= VSIFWriteL(&nBlockOffset32, 4, 1, fp) == 1;
7284
0
                    }
7285
0
                    if (!bOK)
7286
0
                    {
7287
0
                        CPLError(CE_Failure, CPLE_FileIO, "I/O error");
7288
0
                    }
7289
0
                    return bOK;
7290
0
                }
7291
0
                bOK &= VSIFSeekL(fp, nCurPos, SEEK_SET) == 0;
7292
0
            }
7293
7294
0
            if (bOK &&
7295
0
                !NITFWriteJPEGBlock(
7296
0
                    poSrcDS, fp, nBlockXOff, nBlockYOff, nNPPBH, nNPPBV,
7297
0
                    bProgressive, nQuality,
7298
0
                    (nBlockXOff == 0 && nBlockYOff == 0) ? abyAPP6 : nullptr,
7299
0
                    nRestartInterval, pfnProgress, pProgressData))
7300
0
            {
7301
0
                return false;
7302
0
            }
7303
0
        }
7304
0
    }
7305
0
    if (!bOK)
7306
0
    {
7307
0
        CPLError(CE_Failure, CPLE_FileIO, "I/O error");
7308
0
    }
7309
0
    return true;
7310
0
}
7311
7312
#endif /* def JPEG_SUPPORTED */
7313
7314
/************************************************************************/
7315
/*                         GDALRegister_NITF()                          */
7316
/************************************************************************/
7317
7318
typedef struct
7319
{
7320
    int nMaxLen;
7321
    const char *pszName;
7322
    const char *pszDescription;
7323
} NITFFieldDescription;
7324
7325
/* Keep in sync with NITFCreate */
7326
static const NITFFieldDescription asFieldDescription[] = {
7327
    {2, "CLEVEL", "Complexity level"},
7328
    {10, "OSTAID", "Originating Station ID"},
7329
    {14, "FDT", "File Date and Time"},
7330
    {80, "FTITLE", "File Title"},
7331
    {1, "FSCLAS", "File Security Classification"},
7332
    {2, "FSCLSY", "File Classification Security System (NITF02.10/NSIF only)"},
7333
    {11, "FSCODE", "File Codewords"},
7334
    {2, "FSCTLH", "File Control and Handling"},
7335
    {20, "FSREL", "File Releasing Instructions"},
7336
    {2, "FSDCTP", "File Declassification Type (NITF02.10/NSIF only)"},
7337
    {8, "FSDCDT", "File Declassification Date (NITF02.10/NSIF only)"},
7338
    {4, "FSDCXM", "File Declassification Exemption (NITF02.10/NSIF only)"},
7339
    {1, "FSDG", "File Downgrade (NITF02.10/NSIF only)"},
7340
    {8, "FSDGDT", "File Downgrade Date (NITF02.10/NSIF only)"},
7341
    {6, "FSDWNG", "File Security Downgrade (NITF02.00 only)"},
7342
    {40, "FSDEVT", "File Downgrading event (NITF02.00 only)"},
7343
    {43, "FSCLTX", "File Classification Text (NITF02.10/NSIF only)"},
7344
    {1, "FSCATP", "File Classification Authority Type (NITF02.10/NSIF only)"},
7345
    {40, "FSCAUT", "File Classification Authority"},
7346
    {1, "FSCRSN", "File Classification Reason (NITF02.10/NSIF only)"},
7347
    {8, "FSSRDT", "File Security Source Date (NITF02.10/NSIF only)"},
7348
    {15, "FSCTLN", "File Security Control Number"},
7349
    {5, "FSCOP", "File Copy Number"},
7350
    {5, "FSCPYS", "File Number of Copies"},
7351
    {24, "ONAME", "Originator Name"},
7352
    {18, "OPHONE", "Originator Phone Number"},
7353
    {10, "IID1", "Image Identifier 1"},
7354
    {14, "IDATIM", "Image Date and Time"},
7355
    {17, "TGTID", "Target Identifier"},
7356
    {80, "IID2", "Image Identifier 2 (NITF02.10/NSIF only)"},
7357
    {80, "ITITLE", "Image Title (NITF02.00 only)"},
7358
    {1, "ISCLAS", "Image Security Classification"},
7359
    {2, "ISCLSY", "Image Classification Security System (NITF02.10/NSIF only)"},
7360
    {11, "ISCODE", "Image Codewords"},
7361
    {2, "ISCTLH", "Image Control and Handling"},
7362
    {20, "ISREL", "Image Releasing Instructions (NITF02.10/NSIF only)"},
7363
    {2, "ISDCTP", "Image Declassification Type (NITF02.10/NSIF only)"},
7364
    {8, "ISDCDT", "Image Declassification Date (NITF02.10/NSIF only)"},
7365
    {4, "ISDCXM", "Image Declassification Exemption (NITF02.10/NSIF only)"},
7366
    {1, "ISDG", "Image Downgrade (NITF02.10/NSIF only)"},
7367
    {8, "ISDGDT", "Image Downgrade Date (NITF02.10/NSIF only)"},
7368
    {6, "ISDWNG", "Image Security Downgrade (NITF02.00 only)"},
7369
    {40, "ISDEVT", "Image Downgrading event (NITF02.00 only)"},
7370
    {43, "ISCLTX", "Image Classification Text (NITF02.10/NSIF only)"},
7371
    {1, "ISCATP", "Image Classification Authority Type (NITF02.10/NSIF only)"},
7372
    {40, "ISCAUT", "Image Classification Authority"},
7373
    {1, "ISCRSN", "Image Classification Reason (NITF02.10/NSIF only)"},
7374
    {8, "ISSRDT", "Image Security Source Date (NITF02.10/NSIF only)"},
7375
    {15, "ISCTLN", "Image Security Control Number (NITF02.10/NSIF only)"},
7376
    {42, "ISORCE", "Image Source"},
7377
    {8, "ICAT", "Image Category"},
7378
    {2, "ABPP", "Actual Bits-Per-Pixel Per Band"},
7379
    {1, "PJUST", "Pixel Justification"},
7380
    {720, "ICOM", "Image Comments (up to 9x80 characters)"},
7381
    {3, "IDLVL", "Image Display Level"},
7382
    {3, "IALVL", "Image Attachment Level"},
7383
    {5, "ILOCROW", "Image Location Row"},
7384
    {5, "ILOCCOL", "Image Location Column"},
7385
};
7386
7387
/* Keep in sync with NITFWriteBLOCKA */
7388
static const char *const apszFieldsBLOCKA[] = {
7389
    "BLOCK_INSTANCE", "0",     "2",    "N_GRAY",        "2",  "5",
7390
    "L_LINES",        "7",     "5",    "LAYOVER_ANGLE", "12", "3",
7391
    "SHADOW_ANGLE",   "15",    "3",    "BLANKS",        "18", "16",
7392
    "FRLC_LOC",       "34",    "21",   "LRLC_LOC",      "55", "21",
7393
    "LRFC_LOC",       "76",    "21",   "FRFC_LOC",      "97", "21",
7394
    nullptr,          nullptr, nullptr};
7395
7396
/************************************************************************/
7397
/*                              NITFDriver                              */
7398
/************************************************************************/
7399
7400
class NITFDriver final : public GDALDriver
7401
{
7402
    std::recursive_mutex m_oMutex{};
7403
    bool m_bCreationOptionListInitialized = false;
7404
    void InitCreationOptionList();
7405
7406
  public:
7407
    const char *GetMetadataItem(const char *pszName,
7408
                                const char *pszDomain) override;
7409
7410
    CSLConstList GetMetadata(const char *pszDomain) override
7411
21
    {
7412
21
        std::lock_guard oLock(m_oMutex);
7413
21
        InitCreationOptionList();
7414
21
        return GDALDriver::GetMetadata(pszDomain);
7415
21
    }
7416
};
7417
7418
/************************************************************************/
7419
/*                    NITFDriver::GetMetadataItem()                     */
7420
/************************************************************************/
7421
7422
const char *NITFDriver::GetMetadataItem(const char *pszName,
7423
                                        const char *pszDomain)
7424
1.37M
{
7425
1.37M
    std::lock_guard oLock(m_oMutex);
7426
1.37M
    if (EQUAL(pszName, GDAL_DMD_CREATIONOPTIONLIST))
7427
27
    {
7428
27
        InitCreationOptionList();
7429
27
    }
7430
1.37M
    return GDALDriver::GetMetadataItem(pszName, pszDomain);
7431
1.37M
}
7432
7433
/************************************************************************/
7434
/*                       InitCreationOptionList()                       */
7435
/************************************************************************/
7436
7437
void NITFDriver::InitCreationOptionList()
7438
48
{
7439
48
    if (m_bCreationOptionListInitialized)
7440
47
        return;
7441
1
    m_bCreationOptionListInitialized = true;
7442
7443
1
    const bool bHasJP2ECW = GDALGetDriverByName("JP2ECW") != nullptr;
7444
1
    const bool bHasJP2KAK = GDALGetDriverByName("JP2KAK") != nullptr;
7445
1
    const bool bHasJP2OPENJPEG = GDALGetDriverByName("JP2OPENJPEG") != nullptr;
7446
1
    const bool bHasJPEG2000Drivers =
7447
1
        bHasJP2ECW || bHasJP2KAK || bHasJP2OPENJPEG;
7448
7449
1
    CPLString osCreationOptions =
7450
1
        "<CreationOptionList>"
7451
1
        "   <Option name='IC' type='string-select' default='NC' "
7452
1
        "description='Compression mode. NC=no compression. "
7453
1
#ifdef JPEG_SUPPORTED
7454
1
        "C3/M3=JPEG compression. "
7455
1
#endif
7456
1
        "C4=VQ compression (only for PRODUCT_TYPE=CADRG). ";
7457
7458
1
    if (bHasJPEG2000Drivers)
7459
0
        osCreationOptions +=
7460
0
            "C8=JP2 compression through the JPEG2000 write capable drivers";
7461
7462
1
    osCreationOptions += "'>"
7463
1
                         "       <Value>NC</Value>"
7464
1
#ifdef JPEG_SUPPORTED
7465
1
                         "       <Value>C3</Value>"
7466
1
                         "       <Value>M3</Value>"
7467
1
#endif
7468
1
                         "       <Value>C4</Value>";
7469
7470
1
    if (bHasJPEG2000Drivers)
7471
0
        osCreationOptions += "       <Value>C8</Value>";
7472
7473
1
    osCreationOptions += "   </Option>";
7474
7475
#if !defined(JPEG_SUPPORTED)
7476
    if (bHasJPEG2000Drivers)
7477
#endif
7478
1
    {
7479
1
        osCreationOptions +=
7480
1
            "   <Option name='QUALITY' type='string' "
7481
1
            "description='JPEG (10-100) or JPEG2000 quality, possibly as a"
7482
1
            "separated list of values for JPEG2000_DRIVER=JP2OPENJPEG' "
7483
1
            "default='75'/>";
7484
1
    }
7485
7486
1
#ifdef JPEG_SUPPORTED
7487
1
    osCreationOptions +=
7488
1
        "   <Option name='PROGRESSIVE' type='boolean' description='JPEG "
7489
1
        "progressive mode'/>"
7490
1
        "   <Option name='RESTART_INTERVAL' type='int' description='Restart "
7491
1
        "interval (in MCUs). -1 for auto, 0 for none, > 0 for user specified' "
7492
1
        "default='-1'/>";
7493
1
#endif
7494
1
    osCreationOptions +=
7495
1
        "   <Option name='NUMI' type='int' default='1' description='Number of "
7496
1
        "images to create (1-999). Only works with IC=NC if "
7497
1
        "WRITE_ONLY_FIRST_IMAGE=NO'/>"
7498
1
        "   <Option name='WRITE_ONLY_FIRST_IMAGE' type='boolean' default='NO' "
7499
1
        "description='To be used with NUMI. If YES, only write first image. "
7500
1
        "Subsequent one must be written with APPEND_SUBDATASET=YES'/>";
7501
7502
1
    if (bHasJPEG2000Drivers)
7503
0
    {
7504
0
        osCreationOptions +=
7505
0
            "   <Option name='TARGET' type='float' description='For JP2 only. "
7506
0
            "Compression Percentage'/>"
7507
0
            "   <Option name='PROFILE' type='string-select' description='For "
7508
0
            "JP2 only.'>";
7509
7510
0
        if (bHasJP2ECW)
7511
0
        {
7512
0
            osCreationOptions += "       <Value>BASELINE_0</Value>";
7513
0
        }
7514
0
        if (bHasJP2ECW || bHasJP2OPENJPEG)
7515
0
        {
7516
0
            osCreationOptions +=
7517
0
                "       <Value>BASELINE_1</Value>"
7518
0
                "       <Value>BASELINE_2</Value>"
7519
0
                "       <Value>NPJE</Value>"
7520
0
                "       <Value>NPJE_VISUALLY_LOSSLESS</Value>"
7521
0
                "       <Value>NPJE_NUMERICALLY_LOSSLESS</Value>";
7522
0
        }
7523
0
        if (bHasJP2ECW)
7524
0
        {
7525
0
            osCreationOptions += "       <Value>EPJE</Value>";
7526
0
        }
7527
0
        osCreationOptions +=
7528
0
            "   </Option>"
7529
0
            "   <Option name='JPEG2000_DRIVER' type='string-select' "
7530
0
            "description='Short name of the JPEG2000 driver'>";
7531
0
        if (bHasJP2OPENJPEG)
7532
0
            osCreationOptions += "       <Value>JP2OPENJPEG</Value>";
7533
0
        if (bHasJP2ECW)
7534
0
            osCreationOptions += "       <Value>JP2ECW</Value>";
7535
0
        if (bHasJP2KAK)
7536
0
            osCreationOptions += "       <Value>JP2KAK</Value>";
7537
0
        osCreationOptions += "   </Option>"
7538
0
                             "   <Option name='J2KLRA' type='boolean' "
7539
0
                             "description='Write J2KLRA TRE'/>";
7540
0
    }
7541
7542
1
    osCreationOptions +=
7543
1
        "   <Option name='ICORDS' type='string-select' description='To ensure "
7544
1
        "that space will be reserved for geographic corner coordinates in DMS "
7545
1
        "(G), in decimal degrees (D), UTM North (N) or UTM South (S)'>"
7546
1
        "       <Value>G</Value>"
7547
1
        "       <Value>D</Value>"
7548
1
        "       <Value>N</Value>"
7549
1
        "       <Value>S</Value>"
7550
1
        "   </Option>"
7551
1
        "   <Option name='IGEOLO' type='string' description='Image corner "
7552
1
        "coordinates. "
7553
1
        "Normally automatically set. If specified, ICORDS must also be "
7554
1
        "specified'/>"
7555
1
        "   <Option name='FHDR' type='string-select' description='File "
7556
1
        "version' default='NITF02.10'>"
7557
1
        "       <Value>NITF02.10</Value>"
7558
1
        "       <Value>NSIF01.00</Value>"
7559
1
        "       <Value>NITF02.00</Value>"
7560
1
        "   </Option>"
7561
1
        "   <Option name='IREP' type='string' description='Set to RGB/LUT to "
7562
1
        "reserve space for a color table for each output band. (Only needed "
7563
1
        "for Create() method, not CreateCopy())'/>"
7564
1
        "   <Option name='IREPBAND' type='string' description='Comma separated "
7565
1
        "list of band IREPBANDs in band order'/>"
7566
1
        "   <Option name='ISUBCAT' type='string' description='Comma separated "
7567
1
        "list of band ISUBCATs in band order'/>"
7568
1
        "   <Option name='LUT_SIZE' type='integer' description='Set to control "
7569
1
        "the size of pseudocolor tables for RGB/LUT bands' default='256'/>"
7570
1
        "   <Option name='BLOCKXSIZE' type='int' description='Set the block "
7571
1
        "width'/>"
7572
1
        "   <Option name='BLOCKYSIZE' type='int' description='Set the block "
7573
1
        "height'/>"
7574
1
        "   <Option name='BLOCKSIZE' type='int' description='Set the block "
7575
1
        "with and height. Overridden by BLOCKXSIZE and BLOCKYSIZE'/>"
7576
1
        "   <Option name='TEXT' type='string' description='TEXT options as "
7577
1
        "text-option-name=text-option-content'/>"
7578
1
        "   <Option name='CGM' type='string' description='CGM options in "
7579
1
        "cgm-option-name=cgm-option-content'/>";
7580
7581
1
    for (unsigned int i = 0;
7582
59
         i < sizeof(asFieldDescription) / sizeof(asFieldDescription[0]); i++)
7583
58
    {
7584
58
        if (EQUAL(asFieldDescription[i].pszName, "ABPP"))
7585
1
        {
7586
1
            osCreationOptions +=
7587
1
                CPLString().Printf("   <Option name='%s' alias='NBITS' "
7588
1
                                   "type='string' description='%s' "
7589
1
                                   "maxsize='%d'/>",
7590
1
                                   asFieldDescription[i].pszName,
7591
1
                                   asFieldDescription[i].pszDescription,
7592
1
                                   asFieldDescription[i].nMaxLen);
7593
1
        }
7594
57
        else
7595
57
        {
7596
57
            osCreationOptions += CPLString().Printf(
7597
57
                "   <Option name='%s' type='string' description='%s' "
7598
57
                "maxsize='%d'/>",
7599
57
                asFieldDescription[i].pszName,
7600
57
                asFieldDescription[i].pszDescription,
7601
57
                asFieldDescription[i].nMaxLen);
7602
57
        }
7603
58
    }
7604
7605
1
    osCreationOptions +=
7606
1
        "   <Option name='TRE' type='string' description='Under the format "
7607
1
        "TRE=tre-name,tre-contents'/>"
7608
1
        "   <Option name='FILE_TRE' type='string' description='Under the "
7609
1
        "format FILE_TRE=tre-name,tre-contents'/>"
7610
1
        "   <Option name='RESERVE_SPACE_FOR_TRE_OVERFLOW' type='boolean' "
7611
1
        "description='Set to true to reserve space for IXSOFL when writing a "
7612
1
        "TRE_OVERFLOW DES'/>"
7613
1
        "   <Option name='BLOCKA_BLOCK_COUNT' type='int'/>"
7614
1
        "   <Option name='DES' type='string' description='Under the format "
7615
1
        "DES=des-name=des-contents'/>"
7616
1
        "   <Option name='NUMDES' type='int' default='0' description='Number "
7617
1
        "of DES segments. Only to be used on first image segment'/>";
7618
11
    for (unsigned int i = 0; apszFieldsBLOCKA[i] != nullptr; i += 3)
7619
10
    {
7620
10
        char szFieldDescription[128];
7621
10
        snprintf(szFieldDescription, sizeof(szFieldDescription),
7622
10
                 "   <Option name='BLOCKA_%s_*' type='string' maxsize='%d'/>",
7623
10
                 apszFieldsBLOCKA[i], atoi(apszFieldsBLOCKA[i + 2]));
7624
10
        osCreationOptions += szFieldDescription;
7625
10
    }
7626
1
    osCreationOptions +=
7627
1
        "   <Option name='SDE_TRE' type='boolean' description='Write GEOLOB "
7628
1
        "and GEOPSB TREs (only geographic SRS for now)' default='NO'/>"
7629
1
        "   <Option name='RPC00B' type='boolean' description='Write RPC00B TRE "
7630
1
        "(either from source TRE, or from RPC metadata)' default='YES'/>"
7631
1
        "   <Option name='RPCTXT' type='boolean' description='Write out "
7632
1
        "_RPC.TXT file' default='NO'/>"
7633
1
        "   <Option name='USE_SRC_NITF_METADATA' type='boolean' "
7634
1
        "description='Whether to use NITF source metadata in NITF-to-NITF "
7635
1
        "conversions' default='YES'/>"
7636
1
        "   <Option name='PRODUCT_TYPE' type='string-select' description='"
7637
1
        "Sub-specification the output dataset should respect' "
7638
1
        "default='REGULAR'>"
7639
1
        "       <Value>REGULAR</Value>"
7640
1
        "       <Value>CADRG</Value>"
7641
1
        "   </Option>"
7642
1
        "   <Option name='SCALE' type='string' min='1000' max='20000000' "
7643
1
        "description='Reciprocal scale to use when generating output frames. "
7644
1
        "Special value GUESS can be used. Only used when PRODUCT_TYPE=CADRG'/>"
7645
1
        "   <Option name='COLOR_QUANTIZATION_BITS' type='int' min='5' max='8' "
7646
1
        "default='5' description='"
7647
1
        "Number of bits per R,G,B color component used during color palette "
7648
1
        "computation. The higher the better quality and slower computation "
7649
1
        "time. Only used when PRODUCT_TYPE=CADRG'/>"
7650
1
        "   <Option name='COLOR_TABLE_PER_FRAME' type='boolean' default='NO' "
7651
1
        "description='Whether the color table should be optimized on the whole "
7652
1
        "input dataset, or per output frame. "
7653
1
        "Only used when PRODUCT_TYPE=CADRG'/>"
7654
1
        "   <Option name='DPI' type='float' description='"
7655
1
        "Dot-Per-Inch value that may need to be specified together with SCALE. "
7656
1
        "Only used when PRODUCT_TYPE=CADRG' min='1' max='7200'/>"
7657
1
        "   <Option name='ZONE' type='string' description='"
7658
1
        "ARC Zone to which restrict generation of CADRG frames (1 to 9, A to "
7659
1
        "H, J)."
7660
1
        "Only used when PRODUCT_TYPE=CADRG'/>"
7661
1
        "   <Option name='SERIES_CODE' type='string-select' description='"
7662
1
        "Two-letter code specifying the map/chart type. Only used when "
7663
1
        "PRODUCT_TYPE=CADRG' default='MM'>"
7664
1
        "       <Value>GN</Value>"
7665
1
        "       <Value>JN</Value>"
7666
1
        "       <Value>ON</Value>"
7667
1
        "       <Value>TP</Value>"
7668
1
        "       <Value>LF</Value>"
7669
1
        "       <Value>JG</Value>"
7670
1
        "       <Value>JA</Value>"
7671
1
        "       <Value>JR</Value>"
7672
1
        "       <Value>TF</Value>"
7673
1
        "       <Value>AT</Value>"
7674
1
        "       <Value>TC</Value>"
7675
1
        "       <Value>TL</Value>"
7676
1
        "       <Value>HA</Value>"
7677
1
        "       <Value>CO</Value>"
7678
1
        "       <Value>OA</Value>"
7679
1
        "       <Value>CG</Value>"
7680
1
        "       <Value>CM</Value>"
7681
1
        "       <Value>MM</Value>"
7682
1
        "       <OtherValues/>"
7683
1
        "   </Option>"
7684
1
        "   <Option name='RESAMPLING' type='string-select' description='"
7685
1
        "Resampling method used during CADRG frame creation' "
7686
1
        "default='CUBIC'>"
7687
1
        "       <Value>CUBIC</Value>"
7688
1
        "       <Value>BILINEAR</Value>"
7689
1
        "       <Value>LANCZOS</Value>"
7690
1
        "       <Value>NEAREST</Value>"
7691
1
        "   </Option>"
7692
1
        "   <Option name='VERSION_NUMBER' type='string' description='"
7693
1
        "Two letter version number (using letters among 0-9, A-H and J). "
7694
1
        "Only used when PRODUCT_TYPE=CADRG' default='01'/>"
7695
1
        "   <Option name='PRODUCER_CODE_ID' type='string' description='"
7696
1
        "One letter code indicating the data producer. Only used when "
7697
1
        "PRODUCT_TYPE=CADRG' default='0'/>"
7698
1
        "   <Option name='SECURITY_COUNTRY_CODE' type='string' description='"
7699
1
        "Two letter country ISO code of the security classification'/>"
7700
1
        "   <Option name='CURRENCY_DATE' type='string' description='"
7701
1
        "Date of the most recent revision to the RPF product, as YYYYMMDD. "
7702
1
        "Can also be set to empty string or special value NOW. "
7703
1
        "Only used when PRODUCT_TYPE=CADRG'/>"
7704
1
        "   <Option name='PRODUCTION_DATE' type='string' description='"
7705
1
        "Date that the source data was transferred to RPF format, as YYYYMMDD. "
7706
1
        "Can also be set to empty string or special value NOW. "
7707
1
        "Only used when PRODUCT_TYPE=CADRG'/>"
7708
1
        "   <Option name='SIGNIFICANT_DATE' type='string' description='"
7709
1
        "Date describing the basic date of the source produc, as YYYYMMDD. "
7710
1
        "Can also be set to empty string or special value NOW. "
7711
1
        "Only used when PRODUCT_TYPE=CADRG'/>"
7712
1
        "   <Option name='DATA_SERIES_DESIGNATION' type='string' "
7713
1
        "description='Short title for the identification of a group of products"
7714
1
        " usually having the same scale and/or cartographic specification "
7715
1
        "(e.g. JOG 1501A). Up to 10 characters. Only used when "
7716
1
        "PRODUCT_TYPE=CADRG'/>"
7717
1
        "   <Option name='MAP_DESIGNATION' type='string' "
7718
1
        "description='Designation, within the data series, of the hardcopy "
7719
1
        "source (e.g. G18 if the hardcopy source is ONC G18). Up to 8 "
7720
1
        "characters. Only used when PRODUCT_TYPE=CADRG'/>"
7721
1
        "</CreationOptionList>";
7722
7723
1
    SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, osCreationOptions);
7724
1
}
7725
7726
void GDALRegister_NITF()
7727
7728
22
{
7729
22
    if (GDALGetDriverByName(NITF_DRIVER_NAME) != nullptr)
7730
0
        return;
7731
7732
22
    GDALDriver *poDriver = new NITFDriver();
7733
22
    NITFDriverSetCommonMetadata(poDriver);
7734
7735
22
    poDriver->pfnOpen = NITFDataset::Open;
7736
22
    poDriver->pfnCreate = NITFDataset::NITFDatasetCreate;
7737
22
    poDriver->pfnCreateCopy = NITFDataset::NITFCreateCopy;
7738
7739
22
    GetGDALDriverManager()->RegisterDriver(poDriver);
7740
7741
#ifdef NITF_PLUGIN
7742
    GDALRegister_RPFTOC();
7743
    GDALRegister_ECRGTOC();
7744
#endif
7745
22
}