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