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