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