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