Coverage Report

Created: 2026-02-14 09:00

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