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