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