1 : /******************************************************************************
2 : * $Id: nitfdataset.cpp 25666 2013-02-22 17:59:01Z rouault $
3 : *
4 : * Project: NITF Read/Write Translator
5 : * Purpose: NITFDataset and driver related implementations.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2002, Frank Warmerdam
10 : *
11 : * Portions Copyright (c) Her majesty the Queen in right of Canada as
12 : * represented by the Minister of National Defence, 2006.
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : ****************************************************************************/
32 :
33 : #include "nitfdataset.h"
34 : #include "cpl_string.h"
35 : #include "cpl_csv.h"
36 :
37 : CPL_CVSID("$Id: nitfdataset.cpp 25666 2013-02-22 17:59:01Z rouault $");
38 :
39 : static void NITFPatchImageLength( const char *pszFilename,
40 : GUIntBig nImageOffset,
41 : GIntBig nPixelCount, const char *pszIC );
42 : static int NITFWriteCGMSegments( const char *pszFilename, char **papszList );
43 : static void NITFWriteTextSegments( const char *pszFilename, char **papszList );
44 :
45 : #ifdef JPEG_SUPPORTED
46 : static int NITFWriteJPEGImage( GDALDataset *, VSILFILE *, vsi_l_offset, char **,
47 : GDALProgressFunc pfnProgress,
48 : void * pProgressData );
49 : #endif
50 :
51 : #ifdef ESRI_BUILD
52 : static void SetBandMetadata( NITFImage *psImage, GDALRasterBand *poBand, int nBand );
53 : #endif
54 :
55 : /************************************************************************/
56 : /* ==================================================================== */
57 : /* NITFDataset */
58 : /* ==================================================================== */
59 : /************************************************************************/
60 :
61 : /************************************************************************/
62 : /* NITFDataset() */
63 : /************************************************************************/
64 :
65 559 : NITFDataset::NITFDataset()
66 :
67 : {
68 559 : psFile = NULL;
69 559 : psImage = NULL;
70 559 : bGotGeoTransform = FALSE;
71 559 : pszProjection = CPLStrdup("");
72 559 : poJ2KDataset = NULL;
73 559 : bJP2Writing = FALSE;
74 559 : poJPEGDataset = NULL;
75 :
76 559 : panJPEGBlockOffset = NULL;
77 559 : pabyJPEGBlock = NULL;
78 559 : nQLevel = 0;
79 :
80 559 : nGCPCount = 0;
81 559 : pasGCPList = NULL;
82 559 : pszGCPProjection = NULL;
83 :
84 559 : adfGeoTransform[0] = 0.0;
85 559 : adfGeoTransform[1] = 1.0;
86 559 : adfGeoTransform[2] = 0.0;
87 559 : adfGeoTransform[3] = 0.0;
88 559 : adfGeoTransform[4] = 0.0;
89 559 : adfGeoTransform[5] = 1.0;
90 :
91 559 : poDriver = (GDALDriver*) GDALGetDriverByName("NITF");
92 :
93 559 : papszTextMDToWrite = NULL;
94 559 : papszCgmMDToWrite = NULL;
95 :
96 559 : bInLoadXML = FALSE;
97 559 : bExposeUnderlyingJPEGDatasetOverviews = FALSE;
98 559 : }
99 :
100 : /************************************************************************/
101 : /* ~NITFDataset() */
102 : /************************************************************************/
103 :
104 559 : NITFDataset::~NITFDataset()
105 :
106 : {
107 559 : CloseDependentDatasets();
108 :
109 : /* -------------------------------------------------------------------- */
110 : /* Free datastructures. */
111 : /* -------------------------------------------------------------------- */
112 559 : CPLFree( pszProjection );
113 :
114 559 : GDALDeinitGCPs( nGCPCount, pasGCPList );
115 559 : CPLFree( pasGCPList );
116 559 : CPLFree( pszGCPProjection );
117 :
118 559 : CPLFree( panJPEGBlockOffset );
119 559 : CPLFree( pabyJPEGBlock );
120 559 : }
121 :
122 : /************************************************************************/
123 : /* CloseDependentDatasets() */
124 : /************************************************************************/
125 :
126 559 : int NITFDataset::CloseDependentDatasets()
127 : {
128 559 : FlushCache();
129 :
130 559 : 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 559 : if( poJ2KDataset != NULL && bJP2Writing )
138 : {
139 : int i;
140 :
141 4 : for( i = 0; i < nBands && papoBands != NULL; i++ )
142 : {
143 3 : if( papoBands[i]->GetColorInterpretation() != GCI_Undefined )
144 : NITFSetColorInterpretation( psImage, i+1,
145 3 : papoBands[i]->GetColorInterpretation() );
146 : }
147 : }
148 :
149 : /* -------------------------------------------------------------------- */
150 : /* Close the underlying NITF file. */
151 : /* -------------------------------------------------------------------- */
152 559 : GUIntBig nImageStart = 0;
153 559 : if( psFile != NULL )
154 : {
155 559 : if (psFile->nSegmentCount > 0)
156 559 : nImageStart = psFile->pasSegmentInfo[0].nSegmentStart;
157 :
158 559 : NITFClose( psFile );
159 559 : psFile = NULL;
160 : }
161 :
162 : /* -------------------------------------------------------------------- */
163 : /* If we have a jpeg2000 output file, make sure it gets closed */
164 : /* and flushed out. */
165 : /* -------------------------------------------------------------------- */
166 559 : if( poJ2KDataset != NULL )
167 : {
168 23 : GDALClose( (GDALDatasetH) poJ2KDataset );
169 23 : poJ2KDataset = NULL;
170 23 : bHasDroppedRef = TRUE;
171 : }
172 :
173 : /* -------------------------------------------------------------------- */
174 : /* Update file length, and COMRAT for JPEG2000 files we are */
175 : /* writing to. */
176 : /* -------------------------------------------------------------------- */
177 559 : if( bJP2Writing )
178 : {
179 : GIntBig nPixelCount = nRasterXSize * ((GIntBig) nRasterYSize) *
180 1 : nBands;
181 :
182 1 : NITFPatchImageLength( GetDescription(), nImageStart, nPixelCount,
183 2 : "C8" );
184 : }
185 :
186 559 : bJP2Writing = FALSE;
187 :
188 : /* -------------------------------------------------------------------- */
189 : /* If we have a jpeg output file, make sure it gets closed */
190 : /* and flushed out. */
191 : /* -------------------------------------------------------------------- */
192 559 : if( poJPEGDataset != NULL )
193 : {
194 17 : GDALClose( (GDALDatasetH) poJPEGDataset );
195 17 : poJPEGDataset = NULL;
196 17 : bHasDroppedRef = TRUE;
197 : }
198 :
199 : /* -------------------------------------------------------------------- */
200 : /* If the dataset was opened by Create(), we may need to write */
201 : /* the CGM and TEXT segments */
202 : /* -------------------------------------------------------------------- */
203 559 : NITFWriteCGMSegments( GetDescription(), papszCgmMDToWrite );
204 559 : NITFWriteTextSegments( GetDescription(), papszTextMDToWrite );
205 :
206 559 : CSLDestroy(papszTextMDToWrite);
207 559 : papszTextMDToWrite = NULL;
208 559 : CSLDestroy(papszCgmMDToWrite);
209 559 : papszCgmMDToWrite = NULL;
210 :
211 : /* -------------------------------------------------------------------- */
212 : /* Destroy the raster bands if they exist. */
213 : /* We must do it now since the rasterbands can be NITFWrapperRasterBand */
214 : /* that derive from the GDALProxyRasterBand object, which keeps */
215 : /* a reference on the JPEG/JP2K dataset, so any later call to */
216 : /* FlushCache() would result in FlushCache() being called on a */
217 : /* already destroyed object */
218 : /* -------------------------------------------------------------------- */
219 211351 : for( int iBand = 0; iBand < nBands; iBand++ )
220 : {
221 210792 : delete papoBands[iBand];
222 : }
223 559 : nBands = 0;
224 :
225 559 : return bHasDroppedRef;
226 : }
227 :
228 : /************************************************************************/
229 : /* FlushCache() */
230 : /************************************************************************/
231 :
232 559 : void NITFDataset::FlushCache()
233 :
234 : {
235 : // If the JPEG/JP2K dataset has dirty pam info, then we should consider
236 : // ourselves to as well.
237 559 : if( poJPEGDataset != NULL
238 : && (poJPEGDataset->GetPamFlags() & GPF_DIRTY) )
239 3 : MarkPamDirty();
240 559 : if( poJ2KDataset != NULL
241 : && (poJ2KDataset->GetPamFlags() & GPF_DIRTY) )
242 1 : MarkPamDirty();
243 :
244 559 : if( poJ2KDataset != NULL && bJP2Writing)
245 1 : poJ2KDataset->FlushCache();
246 :
247 559 : GDALPamDataset::FlushCache();
248 559 : }
249 :
250 : #ifdef ESRI_BUILD
251 :
252 : /************************************************************************/
253 : /* ExtractEsriMD() */
254 : /* */
255 : /* Extracts ESRI-specific required meta data from metadata */
256 : /* string list papszStrList. */
257 : /************************************************************************/
258 :
259 : static char **ExtractEsriMD( char **papszMD )
260 : {
261 : char **papszEsriMD = NULL;
262 :
263 : if( papszMD )
264 : {
265 : // These are the current generic ESRI metadata.
266 : const char *const pEsriMDAcquisitionDate = "ESRI_MD_ACQUISITION_DATE";
267 : const char *const pEsriMDAngleToNorth = "ESRI_MD_ANGLE_TO_NORTH";
268 : const char *const pEsriMDCircularError = "ESRI_MD_CE";
269 : const char *const pEsriMDDataType = "ESRI_MD_DATA_TYPE";
270 : const char *const pEsriMDIsCloudCover = "ESRI_MD_ISCLOUDCOVER";
271 : const char *const pEsriMDLinearError = "ESRI_MD_LE";
272 : const char *const pEsriMDOffNaDir = "ESRI_MD_OFF_NADIR";
273 : const char *const pEsriMDPercentCloudCover = "ESRI_MD_PERCENT_CLOUD_COVER";
274 : const char *const pEsriMDProductName = "ESRI_MD_PRODUCT_NAME";
275 : const char *const pEsriMDSensorAzimuth = "ESRI_MD_SENSOR_AZIMUTH";
276 : const char *const pEsriMDSensorElevation = "ESRI_MD_SENSOR_ELEVATION";
277 : const char *const pEsriMDSensorName = "ESRI_MD_SENSOR_NAME";
278 : const char *const pEsriMDSunAzimuth = "ESRI_MD_SUN_AZIMUTH";
279 : const char *const pEsriMDSunElevation = "ESRI_MD_SUN_ELEVATION";
280 :
281 : char szField[11];
282 : const char *pCCImageSegment = CSLFetchNameValue( papszMD, "NITF_IID1" );
283 : std::string ccSegment("false");
284 :
285 : if( ( pCCImageSegment != NULL ) && ( strlen(pCCImageSegment) <= 10 ) )
286 : {
287 : szField[0] = '\0';
288 : strncpy( szField, pCCImageSegment, strlen(pCCImageSegment) );
289 : szField[strlen(pCCImageSegment)] = '\0';
290 :
291 : // Trim white off tag.
292 : while( ( strlen(szField) > 0 ) && ( szField[strlen(szField)-1] == ' ' ) )
293 : szField[strlen(szField)-1] = '\0';
294 :
295 : if ((strlen(szField) == 2) && (EQUALN(szField, "CC", 2))) ccSegment.assign("true");
296 : }
297 :
298 : const char *pAcquisitionDate = CSLFetchNameValue( papszMD, "NITF_FDT" );
299 : const char *pAngleToNorth = CSLFetchNameValue( papszMD, "NITF_CSEXRA_ANGLE_TO_NORTH" );
300 : const char *pCircularError = CSLFetchNameValue( papszMD, "NITF_CSEXRA_CIRCL_ERR" ); // Unit in feet.
301 : const char *pLinearError = CSLFetchNameValue( papszMD, "NITF_CSEXRA_LINEAR_ERR" ); // Unit in feet.
302 : const char *pPercentCloudCover = CSLFetchNameValue( papszMD, "NITF_PIAIMC_CLOUDCVR" );
303 : const char *pProductName = CSLFetchNameValue( papszMD, "NITF_CSDIDA_PRODUCT_ID" );
304 : const char *pSensorName = CSLFetchNameValue( papszMD, "NITF_PIAIMC_SENSNAME" );
305 : const char *pSunAzimuth = CSLFetchNameValue( papszMD, "NITF_CSEXRA_SUN_AZIMUTH" );
306 : const char *pSunElevation = CSLFetchNameValue( papszMD, "NITF_CSEXRA_SUN_ELEVATION" );
307 :
308 : // Get ESRI_MD_DATA_TYPE.
309 : const char *pDataType = NULL;
310 : const char *pImgSegFieldICAT = CSLFetchNameValue( papszMD, "NITF_ICAT" );
311 :
312 : if( ( pImgSegFieldICAT != NULL ) && ( EQUALN(pImgSegFieldICAT, "DTEM", 4) ) )
313 : pDataType = "Elevation";
314 : else
315 : pDataType = "Generic";
316 :
317 : if( pAngleToNorth == NULL )
318 : pAngleToNorth = CSLFetchNameValue( papszMD, "NITF_USE00A_ANGLE_TO_NORTH" );
319 :
320 : // Percent cloud cover == 999 means that the information is not available.
321 : if( (pPercentCloudCover != NULL) && (EQUALN(pPercentCloudCover, "999", 3)) )
322 : pPercentCloudCover = NULL;
323 :
324 : pAngleToNorth = CSLFetchNameValue( papszMD, "NITF_USE00A_ANGLE_TO_NORTH" );
325 :
326 : if( pSunAzimuth == NULL )
327 : pSunAzimuth = CSLFetchNameValue( papszMD, "NITF_USE00A_SUN_AZ" );
328 :
329 : if( pSunElevation == NULL )
330 : pSunElevation = CSLFetchNameValue( papszMD, "NITF_USE00A_SUN_EL" );
331 :
332 : // CSLAddNameValue will not add the key/value pair if the value is NULL.
333 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDAcquisitionDate, pAcquisitionDate );
334 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDAngleToNorth, pAngleToNorth );
335 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDCircularError, pCircularError );
336 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDDataType, pDataType );
337 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDIsCloudCover, ccSegment.c_str() );
338 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDLinearError, pLinearError );
339 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDProductName, pProductName );
340 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDPercentCloudCover, pPercentCloudCover );
341 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDSensorName, pSensorName );
342 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDSunAzimuth, pSunAzimuth );
343 : papszEsriMD = CSLAddNameValue( papszEsriMD, pEsriMDSunElevation, pSunElevation );
344 : }
345 :
346 : return (papszEsriMD);
347 : }
348 :
349 : /************************************************************************/
350 : /* SetBandMetadata() */
351 : /************************************************************************/
352 :
353 : static void SetBandMetadata( NITFImage *psImage, GDALRasterBand *poBand, int nBand )
354 : {
355 : if( (psImage != NULL) && (poBand != NULL) && (nBand > 0) )
356 : {
357 : NITFBandInfo *psBandInfo = psImage->pasBandInfo + nBand - 1;
358 :
359 : if( psBandInfo != NULL )
360 : {
361 : // Set metadata BandName, WavelengthMax and WavelengthMin.
362 :
363 : if ( psBandInfo->szIREPBAND != NULL )
364 : {
365 : if( EQUAL(psBandInfo->szIREPBAND,"B") )
366 : {
367 : poBand->SetMetadataItem( "BandName", "Blue" );
368 : poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT );
369 : poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT );
370 : }
371 : else if( EQUAL(psBandInfo->szIREPBAND,"G") )
372 : {
373 : poBand->SetMetadataItem( "BandName", "Green" );
374 : poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT );
375 : poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT );
376 : }
377 : else if( EQUAL(psBandInfo->szIREPBAND,"R") )
378 : {
379 : poBand->SetMetadataItem( "BandName", "Red" );
380 : poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT );
381 : poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT );
382 : }
383 : else if( EQUAL(psBandInfo->szIREPBAND,"N") )
384 : {
385 : poBand->SetMetadataItem( "BandName", "NearInfrared" );
386 : poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT );
387 : poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT );
388 : }
389 : else if( ( EQUAL(psBandInfo->szIREPBAND,"M") ) || ( ( psImage->szIREP != NULL ) && ( EQUAL(psImage->szIREP,"MONO") ) ) )
390 : {
391 : poBand->SetMetadataItem( "BandName", "Panchromatic" );
392 : }
393 : else
394 : {
395 : if( ( psImage->szICAT != NULL ) && ( EQUAL(psImage->szICAT,"IR") ) )
396 : {
397 : poBand->SetMetadataItem( "BandName", "Infrared" );
398 : poBand->SetMetadataItem( "WavelengthMax", psBandInfo->szISUBCAT );
399 : poBand->SetMetadataItem( "WavelengthMin", psBandInfo->szISUBCAT );
400 : }
401 : }
402 : }
403 : }
404 : }
405 : }
406 :
407 : #endif /* def ESRI_BUILD */
408 :
409 : /************************************************************************/
410 : /* Identify() */
411 : /************************************************************************/
412 :
413 15435 : int NITFDataset::Identify( GDALOpenInfo * poOpenInfo )
414 :
415 : {
416 15435 : const char *pszFilename = poOpenInfo->pszFilename;
417 :
418 : /* -------------------------------------------------------------------- */
419 : /* Is this a dataset selector? If so, it is obviously NITF. */
420 : /* -------------------------------------------------------------------- */
421 15435 : if( EQUALN(pszFilename, "NITF_IM:",8) )
422 13 : return TRUE;
423 :
424 : /* -------------------------------------------------------------------- */
425 : /* Avoid that on Windows, JPEG_SUBFILE:x,y,z,data/../tmp/foo.ntf */
426 : /* to be recognized by the NITF driver, because */
427 : /* 'JPEG_SUBFILE:x,y,z,data' is considered as a (valid) directory */
428 : /* and thus the whole filename is evaluated as tmp/foo.ntf */
429 : /* -------------------------------------------------------------------- */
430 15422 : if( EQUALN(pszFilename,"JPEG_SUBFILE:",13) )
431 68 : return FALSE;
432 :
433 : /* -------------------------------------------------------------------- */
434 : /* First we check to see if the file has the expected header */
435 : /* bytes. */
436 : /* -------------------------------------------------------------------- */
437 15354 : if( poOpenInfo->nHeaderBytes < 4 )
438 11966 : return FALSE;
439 :
440 3388 : if( !EQUALN((char *) poOpenInfo->pabyHeader,"NITF",4)
441 : && !EQUALN((char *) poOpenInfo->pabyHeader,"NSIF",4)
442 : && !EQUALN((char *) poOpenInfo->pabyHeader,"NITF",4) )
443 2745 : return FALSE;
444 :
445 : int i;
446 : /* Check that it's not in fact a NITF A.TOC file, which is handled by the RPFTOC driver */
447 591608 : for(i=0;i<(int)poOpenInfo->nHeaderBytes-(int)strlen("A.TOC");i++)
448 : {
449 590965 : if (EQUALN((const char*)poOpenInfo->pabyHeader + i, "A.TOC", strlen("A.TOC")))
450 0 : return FALSE;
451 : }
452 :
453 643 : return TRUE;
454 : }
455 :
456 : /************************************************************************/
457 : /* Open() */
458 : /************************************************************************/
459 :
460 4954 : GDALDataset *NITFDataset::Open( GDALOpenInfo * poOpenInfo )
461 : {
462 4954 : return OpenInternal(poOpenInfo, NULL, FALSE);
463 : }
464 :
465 5113 : GDALDataset *NITFDataset::OpenInternal( GDALOpenInfo * poOpenInfo,
466 : GDALDataset *poWritableJ2KDataset,
467 : int bOpenForCreate)
468 :
469 : {
470 5113 : int nIMIndex = -1;
471 5113 : const char *pszFilename = poOpenInfo->pszFilename;
472 :
473 5113 : if( !Identify( poOpenInfo ) )
474 4522 : return NULL;
475 :
476 : /* -------------------------------------------------------------------- */
477 : /* Select a specific subdataset. */
478 : /* -------------------------------------------------------------------- */
479 591 : if( EQUALN(pszFilename, "NITF_IM:",8) )
480 : {
481 13 : pszFilename += 8;
482 13 : nIMIndex = atoi(pszFilename);
483 :
484 43 : while( *pszFilename != '\0' && *pszFilename != ':' )
485 17 : pszFilename++;
486 :
487 13 : if( *pszFilename == ':' )
488 13 : pszFilename++;
489 : }
490 :
491 : /* -------------------------------------------------------------------- */
492 : /* Open the file with library. */
493 : /* -------------------------------------------------------------------- */
494 : NITFFile *psFile;
495 :
496 591 : psFile = NITFOpen( pszFilename, poOpenInfo->eAccess == GA_Update );
497 591 : if( psFile == NULL )
498 : {
499 13 : return NULL;
500 : }
501 :
502 578 : if (!bOpenForCreate)
503 : {
504 419 : NITFCollectAttachments( psFile );
505 419 : NITFReconcileAttachments( psFile );
506 : }
507 :
508 : /* -------------------------------------------------------------------- */
509 : /* Is there an image to operate on? */
510 : /* -------------------------------------------------------------------- */
511 578 : int iSegment, nThisIM = 0;
512 578 : NITFImage *psImage = NULL;
513 :
514 2603 : for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
515 : {
516 2600 : if( EQUAL(psFile->pasSegmentInfo[iSegment].szSegmentType,"IM")
517 : && (nThisIM++ == nIMIndex || nIMIndex == -1) )
518 : {
519 575 : psImage = NITFImageAccess( psFile, iSegment );
520 575 : if( psImage == NULL )
521 : {
522 19 : NITFClose( psFile );
523 19 : return NULL;
524 : }
525 556 : break;
526 : }
527 : }
528 :
529 : /* -------------------------------------------------------------------- */
530 : /* If no image segments found report this to the user. */
531 : /* -------------------------------------------------------------------- */
532 559 : if( psImage == NULL )
533 : {
534 : CPLError( CE_Warning, CPLE_AppDefined,
535 : "The file %s appears to be an NITF file, but no image\n"
536 : "blocks were found on it.",
537 3 : poOpenInfo->pszFilename );
538 : }
539 :
540 : /* -------------------------------------------------------------------- */
541 : /* Create a corresponding GDALDataset. */
542 : /* -------------------------------------------------------------------- */
543 : NITFDataset *poDS;
544 :
545 559 : poDS = new NITFDataset();
546 :
547 559 : poDS->psFile = psFile;
548 559 : poDS->psImage = psImage;
549 559 : poDS->eAccess = poOpenInfo->eAccess;
550 1118 : poDS->osNITFFilename = pszFilename;
551 559 : poDS->nIMIndex = nIMIndex;
552 :
553 559 : if( psImage )
554 : {
555 556 : if (psImage->nCols <= 0 || psImage->nRows <= 0 ||
556 : psImage->nBlockWidth <= 0 || psImage->nBlockHeight <= 0)
557 : {
558 : CPLError( CE_Failure, CPLE_AppDefined,
559 : "Bad values in NITF image : nCols=%d, nRows=%d, nBlockWidth=%d, nBlockHeight=%d",
560 0 : psImage->nCols, psImage->nRows, psImage->nBlockWidth, psImage->nBlockHeight);
561 0 : delete poDS;
562 0 : return NULL;
563 : }
564 :
565 556 : poDS->nRasterXSize = psImage->nCols;
566 556 : poDS->nRasterYSize = psImage->nRows;
567 : }
568 : else
569 : {
570 3 : poDS->nRasterXSize = 1;
571 3 : poDS->nRasterYSize = 1;
572 : }
573 :
574 : /* Can be set to NO to avoid opening the underlying JPEG2000/JPEG */
575 : /* stream. Might speed up operations when just metadata is needed */
576 : int bOpenUnderlyingDS = CSLTestBoolean(
577 559 : CPLGetConfigOption("NITF_OPEN_UNDERLYING_DS", "YES"));
578 :
579 : /* -------------------------------------------------------------------- */
580 : /* If the image is JPEG2000 (C8) compressed, we will need to */
581 : /* open the image data as a JPEG2000 dataset. */
582 : /* -------------------------------------------------------------------- */
583 559 : int nUsableBands = 0;
584 : int iBand;
585 559 : int bSetColorInterpretation = TRUE;
586 559 : int bSetColorTable = FALSE;
587 :
588 559 : if( psImage )
589 556 : nUsableBands = psImage->nBands;
590 :
591 582 : if( bOpenUnderlyingDS && psImage != NULL && EQUAL(psImage->szIC,"C8") )
592 : {
593 23 : CPLString osDSName;
594 :
595 : osDSName.Printf( "/vsisubfile/" CPL_FRMT_GUIB "_" CPL_FRMT_GUIB ",%s",
596 23 : psFile->pasSegmentInfo[iSegment].nSegmentStart,
597 23 : psFile->pasSegmentInfo[iSegment].nSegmentSize,
598 46 : pszFilename );
599 :
600 23 : if( poWritableJ2KDataset != NULL )
601 : {
602 1 : poDS->poJ2KDataset = (GDALPamDataset *) poWritableJ2KDataset;
603 1 : poDS->bJP2Writing = TRUE;
604 1 : poWritableJ2KDataset = NULL;
605 : }
606 : else
607 : {
608 : /* We explicitely list the allowed drivers to avoid hostile content */
609 : /* to be opened by a random driver, and also to make sure that */
610 : /* a future new JPEG2000 compatible driver derives from GDALPamDataset */
611 : static const char * const apszDrivers[] = { "JP2KAK", "JP2ECW", "JP2MRSID",
612 : "JPEG2000", "JP2OPENJPEG", NULL };
613 : poDS->poJ2KDataset = (GDALPamDataset *)
614 22 : GDALOpenInternal( osDSName, GA_ReadOnly, apszDrivers);
615 :
616 22 : if( poDS->poJ2KDataset == NULL )
617 : {
618 0 : int bFoundJPEG2000Driver = FALSE;
619 0 : for(int iDriver=0;apszDrivers[iDriver]!=NULL;iDriver++)
620 : {
621 0 : if (GDALGetDriverByName(apszDrivers[iDriver]) != NULL)
622 0 : bFoundJPEG2000Driver = TRUE;
623 : }
624 :
625 : CPLError( CE_Failure, CPLE_AppDefined,
626 : "Unable to open JPEG2000 image within NITF file.\n%s\n%s",
627 : (!bFoundJPEG2000Driver) ?
628 : "No JPEG2000 capable driver (JP2KAK, JP2ECW, JP2MRSID, JP2OPENJPEG, etc...) is available." :
629 : "One or several JPEG2000 capable drivers are available but the datastream could not be opened successfully.",
630 0 : "You can define the NITF_OPEN_UNDERLYING_DS configuration option to NO, in order to just get the metadata.");
631 0 : delete poDS;
632 0 : return NULL;
633 : }
634 :
635 : poDS->poJ2KDataset->SetPamFlags(
636 22 : poDS->poJ2KDataset->GetPamFlags() | GPF_NOSAVE );
637 : }
638 :
639 23 : if( poDS->GetRasterXSize() != poDS->poJ2KDataset->GetRasterXSize()
640 : || poDS->GetRasterYSize() != poDS->poJ2KDataset->GetRasterYSize())
641 : {
642 : CPLError( CE_Failure, CPLE_AppDefined,
643 0 : "JPEG2000 data stream has not the same dimensions as the NITF file.");
644 0 : delete poDS;
645 0 : return NULL;
646 : }
647 :
648 23 : if ( nUsableBands == 1)
649 : {
650 18 : const char* pszIREP = CSLFetchNameValue(psImage->papszMetadata, "NITF_IREP");
651 18 : if (pszIREP != NULL && EQUAL(pszIREP, "RGB/LUT"))
652 : {
653 6 : if (poDS->poJ2KDataset->GetRasterCount() == 3)
654 : {
655 : /* Test case : http://www.gwg.nga.mil/ntb/baseline/software/testfile/Jpeg2000/jp2_09/file9_jp2_2places.ntf */
656 : /* 256-entry palette/LUT in both JP2 Header and image Subheader */
657 : /* In this case, the JPEG2000 driver will probably do the RGB expension */
658 2 : nUsableBands = 3;
659 2 : bSetColorInterpretation = FALSE;
660 : }
661 12 : else if (poDS->poJ2KDataset->GetRasterCount() == 1 &&
662 4 : psImage->pasBandInfo[0].nSignificantLUTEntries > 0 &&
663 4 : poDS->poJ2KDataset->GetRasterBand(1)->GetColorTable() == NULL)
664 : {
665 : /* Test case : http://www.gwg.nga.mil/ntb/baseline/software/testfile/Jpeg2000/jp2_09/file9_j2c.ntf */
666 : /* 256-entry/LUT in Image Subheader, JP2 header completely removed */
667 : /* The JPEG2000 driver will decode it as a grey band */
668 : /* So we must set the color table on the wrapper band */
669 4 : bSetColorTable = TRUE;
670 : }
671 : }
672 : }
673 :
674 23 : if( poDS->poJ2KDataset->GetRasterCount() < nUsableBands )
675 : {
676 : CPLError( CE_Warning, CPLE_AppDefined,
677 : "JPEG2000 data stream has less useful bands than expected, likely\n"
678 0 : "because some channels have differing resolutions." );
679 :
680 0 : nUsableBands = poDS->poJ2KDataset->GetRasterCount();
681 0 : }
682 : }
683 :
684 : /* -------------------------------------------------------------------- */
685 : /* If the image is JPEG (C3) compressed, we will need to open */
686 : /* the image data as a JPEG dataset. */
687 : /* -------------------------------------------------------------------- */
688 536 : else if( bOpenUnderlyingDS && psImage != NULL
689 : && EQUAL(psImage->szIC,"C3")
690 : && psImage->nBlocksPerRow == 1
691 : && psImage->nBlocksPerColumn == 1 )
692 : {
693 17 : GUIntBig nJPEGStart = psFile->pasSegmentInfo[iSegment].nSegmentStart;
694 :
695 17 : poDS->nQLevel = poDS->ScanJPEGQLevel( &nJPEGStart );
696 :
697 17 : CPLString osDSName;
698 :
699 : osDSName.Printf( "JPEG_SUBFILE:Q%d," CPL_FRMT_GUIB "," CPL_FRMT_GUIB ",%s",
700 : poDS->nQLevel, nJPEGStart,
701 17 : psFile->pasSegmentInfo[iSegment].nSegmentSize
702 17 : - (nJPEGStart - psFile->pasSegmentInfo[iSegment].nSegmentStart),
703 34 : pszFilename );
704 :
705 : CPLDebug( "GDAL",
706 17 : "NITFDataset::Open() as IC=C3 (JPEG compressed)\n");
707 :
708 17 : poDS->poJPEGDataset = (GDALPamDataset*) GDALOpen(osDSName,GA_ReadOnly);
709 17 : if( poDS->poJPEGDataset == NULL )
710 : {
711 0 : int bFoundJPEGDriver = GDALGetDriverByName("JPEG") != NULL;
712 : CPLError( CE_Failure, CPLE_AppDefined,
713 : "Unable to open JPEG image within NITF file.\n%s\n%s",
714 : (!bFoundJPEGDriver) ?
715 : "The JPEG driver is not available." :
716 : "The JPEG driver is available but the datastream could not be opened successfully.",
717 0 : "You can define the NITF_OPEN_UNDERLYING_DS configuration option to NO, in order to just get the metadata.");
718 0 : delete poDS;
719 0 : return NULL;
720 : }
721 :
722 : /* In some circumstances, the JPEG image can be larger than the NITF */
723 : /* (NCOLS, NROWS) dimensions (#5001), so accept it as a valid case */
724 : /* But reject when it is smaller than the NITF dimensions. */
725 17 : if( poDS->GetRasterXSize() > poDS->poJPEGDataset->GetRasterXSize()
726 : || poDS->GetRasterYSize() > poDS->poJPEGDataset->GetRasterYSize())
727 : {
728 : CPLError( CE_Failure, CPLE_AppDefined,
729 0 : "JPEG data stream has smaller dimensions than the NITF file.");
730 0 : delete poDS;
731 0 : return NULL;
732 : }
733 :
734 : poDS->poJPEGDataset->SetPamFlags(
735 17 : poDS->poJPEGDataset->GetPamFlags() | GPF_NOSAVE );
736 :
737 17 : if( poDS->poJPEGDataset->GetRasterCount() < nUsableBands )
738 : {
739 : CPLError( CE_Warning, CPLE_AppDefined,
740 : "JPEG data stream has less useful bands than expected, likely\n"
741 0 : "because some channels have differing resolutions." );
742 :
743 0 : nUsableBands = poDS->poJPEGDataset->GetRasterCount();
744 0 : }
745 : }
746 :
747 : /* -------------------------------------------------------------------- */
748 : /* Create band information objects. */
749 : /* -------------------------------------------------------------------- */
750 :
751 559 : GDALDataset* poBaseDS = NULL;
752 559 : if (poDS->poJ2KDataset != NULL)
753 23 : poBaseDS = poDS->poJ2KDataset;
754 536 : else if (poDS->poJPEGDataset != NULL)
755 17 : poBaseDS = poDS->poJPEGDataset;
756 :
757 211351 : for( iBand = 0; iBand < nUsableBands; iBand++ )
758 : {
759 210792 : if( poBaseDS != NULL)
760 : {
761 : GDALRasterBand* poBaseBand =
762 68 : poBaseDS->GetRasterBand(iBand+1);
763 :
764 : #ifdef ESRI_BUILD
765 : SetBandMetadata( psImage, poBaseBand, iBand+1 );
766 : #endif
767 :
768 : NITFWrapperRasterBand* poBand =
769 68 : new NITFWrapperRasterBand(poDS, poBaseBand, iBand+1 );
770 :
771 68 : NITFBandInfo *psBandInfo = psImage->pasBandInfo + iBand;
772 68 : if (bSetColorInterpretation)
773 : {
774 : /* FIXME? Does it make sense if the JPEG/JPEG2000 driver decodes */
775 : /* YCbCr data as RGB. We probably don't want to set */
776 : /* the color interpretation as Y, Cb, Cr */
777 62 : if( EQUAL(psBandInfo->szIREPBAND,"R") )
778 2 : poBand->SetColorInterpretation( GCI_RedBand );
779 62 : if( EQUAL(psBandInfo->szIREPBAND,"G") )
780 2 : poBand->SetColorInterpretation( GCI_GreenBand );
781 62 : if( EQUAL(psBandInfo->szIREPBAND,"B") )
782 2 : poBand->SetColorInterpretation( GCI_BlueBand );
783 62 : if( EQUAL(psBandInfo->szIREPBAND,"M") )
784 25 : poBand->SetColorInterpretation( GCI_GrayIndex );
785 62 : if( EQUAL(psBandInfo->szIREPBAND,"Y") )
786 8 : poBand->SetColorInterpretation( GCI_YCbCr_YBand );
787 62 : if( EQUAL(psBandInfo->szIREPBAND,"Cb") )
788 8 : poBand->SetColorInterpretation( GCI_YCbCr_CbBand );
789 62 : if( EQUAL(psBandInfo->szIREPBAND,"Cr") )
790 8 : poBand->SetColorInterpretation( GCI_YCbCr_CrBand );
791 : }
792 68 : if (bSetColorTable)
793 : {
794 4 : poBand->SetColorTableFromNITFBandInfo();
795 4 : poBand->SetColorInterpretation( GCI_PaletteIndex );
796 : }
797 :
798 68 : poDS->SetBand( iBand+1, poBand );
799 : }
800 : else
801 : {
802 210724 : GDALRasterBand* poBand = new NITFRasterBand( poDS, iBand+1 );
803 210724 : if (poBand->GetRasterDataType() == GDT_Unknown)
804 : {
805 0 : delete poBand;
806 0 : delete poDS;
807 0 : return NULL;
808 : }
809 :
810 : #ifdef ESRI_BUILD
811 : SetBandMetadata( psImage, poBand, iBand+1 );
812 : #endif
813 :
814 210724 : poDS->SetBand( iBand+1, poBand );
815 : }
816 : }
817 :
818 : /* -------------------------------------------------------------------- */
819 : /* Report problems with odd bit sizes. */
820 : /* -------------------------------------------------------------------- */
821 559 : if( poOpenInfo->eAccess == GA_Update &&
822 : psImage != NULL
823 : && (psImage->nBitsPerSample % 8 != 0)
824 : && poDS->poJPEGDataset == NULL
825 : && poDS->poJ2KDataset == NULL )
826 : {
827 : CPLError( CE_Warning, CPLE_AppDefined,
828 : "Image with %d bits per sample cannot be opened in update mode.",
829 0 : psImage->nBitsPerSample );
830 0 : delete poDS;
831 0 : return NULL;
832 : }
833 :
834 : /* -------------------------------------------------------------------- */
835 : /* Process the projection from the ICORDS. */
836 : /* -------------------------------------------------------------------- */
837 559 : OGRSpatialReference oSRSWork;
838 :
839 559 : if( psImage == NULL )
840 : {
841 : /* nothing */
842 : }
843 681 : else if( psImage->chICORDS == 'G' || psImage->chICORDS == 'D' )
844 : {
845 125 : CPLFree( poDS->pszProjection );
846 125 : poDS->pszProjection = NULL;
847 :
848 125 : oSRSWork.SetWellKnownGeogCS( "WGS84" );
849 125 : oSRSWork.exportToWkt( &(poDS->pszProjection) );
850 : }
851 431 : else if( psImage->chICORDS == 'C' )
852 : {
853 0 : CPLFree( poDS->pszProjection );
854 0 : poDS->pszProjection = NULL;
855 :
856 0 : oSRSWork.SetWellKnownGeogCS( "WGS84" );
857 0 : oSRSWork.exportToWkt( &(poDS->pszProjection) );
858 :
859 : /* convert latitudes from geocentric to geodetic form. */
860 :
861 : psImage->dfULY =
862 : NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(
863 0 : psImage->dfULY );
864 : psImage->dfLLY =
865 : NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(
866 0 : psImage->dfLLY );
867 : psImage->dfURY =
868 : NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(
869 0 : psImage->dfURY );
870 : psImage->dfLRY =
871 : NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(
872 0 : psImage->dfLRY );
873 : }
874 474 : else if( psImage->chICORDS == 'S' || psImage->chICORDS == 'N' )
875 : {
876 43 : CPLFree( poDS->pszProjection );
877 43 : poDS->pszProjection = NULL;
878 :
879 43 : oSRSWork.SetUTM( psImage->nZone, psImage->chICORDS == 'N' );
880 43 : oSRSWork.SetWellKnownGeogCS( "WGS84" );
881 43 : oSRSWork.exportToWkt( &(poDS->pszProjection) );
882 : }
883 388 : else if( psImage->chICORDS == 'U' && psImage->nZone != 0 )
884 : {
885 2 : CPLFree( poDS->pszProjection );
886 2 : poDS->pszProjection = NULL;
887 :
888 2 : oSRSWork.SetUTM( ABS(psImage->nZone), psImage->nZone > 0 );
889 2 : oSRSWork.SetWellKnownGeogCS( "WGS84" );
890 2 : oSRSWork.exportToWkt( &(poDS->pszProjection) );
891 : }
892 :
893 :
894 : /* -------------------------------------------------------------------- */
895 : /* Try looking for a .nfw file. */
896 : /* -------------------------------------------------------------------- */
897 559 : if( psImage
898 : && GDALReadWorldFile( pszFilename, "nfw",
899 : poDS->adfGeoTransform ) )
900 : {
901 : const char *pszHDR;
902 : VSILFILE *fpHDR;
903 : char **papszLines;
904 : int isNorth;
905 : int zone;
906 :
907 3 : poDS->bGotGeoTransform = TRUE;
908 :
909 : /* If nfw found, try looking for a header with projection info */
910 : /* in space imaging style format */
911 3 : pszHDR = CPLResetExtension( pszFilename, "hdr" );
912 :
913 3 : fpHDR = VSIFOpenL( pszHDR, "rt" );
914 :
915 3 : if( fpHDR == NULL && VSIIsCaseSensitiveFS(pszHDR) )
916 : {
917 1 : pszHDR = CPLResetExtension( pszFilename, "HDR" );
918 1 : fpHDR = VSIFOpenL( pszHDR, "rt" );
919 : }
920 :
921 3 : if( fpHDR != NULL )
922 : {
923 2 : VSIFCloseL( fpHDR );
924 2 : papszLines=CSLLoad2(pszHDR, 16, 200, NULL);
925 2 : if (CSLCount(papszLines) == 16)
926 : {
927 :
928 2 : if (psImage->chICORDS == 'N')
929 2 : isNorth=1;
930 0 : else if (psImage->chICORDS =='S')
931 0 : isNorth=0;
932 0 : else if (psImage->chICORDS == 'G' || psImage->chICORDS == 'D' || psImage->chICORDS == 'C')
933 : {
934 0 : if (psImage->dfLLY+psImage->dfLRY+psImage->dfULY+psImage->dfURY < 0)
935 0 : isNorth=0;
936 : else
937 0 : isNorth=1;
938 : }
939 0 : else if (psImage->chICORDS == 'U')
940 : {
941 0 : isNorth = psImage->nZone >= 0;
942 : }
943 : else
944 : {
945 0 : isNorth = 1; /* arbitrarly suppose we are in northern hemisphere */
946 :
947 : /* unless we have other information to determine the hemisphere */
948 0 : char** papszUSE00A_MD = NITFReadSTDIDC( psImage );
949 0 : if( papszUSE00A_MD != NULL )
950 : {
951 0 : const char* pszLocation = CSLFetchNameValue(papszUSE00A_MD, "NITF_STDIDC_LOCATION");
952 0 : if (pszLocation && strlen(pszLocation) == 11)
953 : {
954 0 : isNorth = (pszLocation[4] == 'N');
955 : }
956 0 : CSLDestroy( papszUSE00A_MD );
957 : }
958 : else
959 : {
960 : NITFRPC00BInfo sRPCInfo;
961 0 : if( NITFReadRPC00B( psImage, &sRPCInfo ) && sRPCInfo.SUCCESS )
962 : {
963 0 : isNorth = (sRPCInfo.LAT_OFF >= 0);
964 : }
965 : }
966 : }
967 :
968 8 : if( (EQUALN(papszLines[7],
969 : "Selected Projection: Universal Transverse Mercator",50)) &&
970 2 : (EQUALN(papszLines[8],"Zone: ",6)) &&
971 2 : (strlen(papszLines[8]) >= 7))
972 : {
973 2 : CPLFree( poDS->pszProjection );
974 2 : poDS->pszProjection = NULL;
975 2 : zone=atoi(&(papszLines[8][6]));
976 2 : oSRSWork.Clear();
977 2 : oSRSWork.SetUTM( zone, isNorth );
978 2 : oSRSWork.SetWellKnownGeogCS( "WGS84" );
979 2 : oSRSWork.exportToWkt( &(poDS->pszProjection) );
980 : }
981 : else
982 : {
983 : /* Couldn't find associated projection info.
984 : Go back to original file for geotransform.
985 : */
986 0 : poDS->bGotGeoTransform = FALSE;
987 : }
988 : }
989 : else
990 0 : poDS->bGotGeoTransform = FALSE;
991 2 : CSLDestroy(papszLines);
992 : }
993 : else
994 1 : poDS->bGotGeoTransform = FALSE;
995 : }
996 :
997 : /* -------------------------------------------------------------------- */
998 : /* Does this look like a CADRG polar tile ? (#2940) */
999 : /* -------------------------------------------------------------------- */
1000 559 : const char* pszIID1 = (psImage) ? CSLFetchNameValue(psImage->papszMetadata, "NITF_IID1") : NULL;
1001 559 : const char* pszITITLE = (psImage) ? CSLFetchNameValue(psImage->papszMetadata, "NITF_ITITLE") : NULL;
1002 605 : if( psImage != NULL && !poDS->bGotGeoTransform &&
1003 : (psImage->chICORDS == 'G' || psImage->chICORDS == 'D') &&
1004 : pszIID1 != NULL && EQUAL(pszIID1, "CADRG") &&
1005 : pszITITLE != NULL && strlen(pszITITLE) >= 12
1006 24 : && (pszITITLE[strlen(pszITITLE) - 1] == '9'
1007 22 : || pszITITLE[strlen(pszITITLE) - 1] == 'J') )
1008 : {
1009 : /* To get a perfect rectangle in Azimuthal Equidistant projection, we must use */
1010 : /* the sphere and not WGS84 ellipsoid. That's a bit strange... */
1011 2 : const char* pszNorthPolarProjection = "+proj=aeqd +lat_0=90 +lon_0=0 +x_0=0 +y_0=0 +a=6378137 +b=6378137 +units=m +no_defs";
1012 2 : const char* pszSouthPolarProjection = "+proj=aeqd +lat_0=-90 +lon_0=0 +x_0=0 +y_0=0 +a=6378137 +b=6378137 +units=m +no_defs";
1013 :
1014 2 : OGRSpatialReference oSRS_AEQD, oSRS_WGS84;
1015 :
1016 2 : const char *pszPolarProjection = (psImage->dfULY > 0) ? pszNorthPolarProjection : pszSouthPolarProjection;
1017 2 : oSRS_AEQD.importFromProj4(pszPolarProjection);
1018 :
1019 2 : oSRS_WGS84.SetWellKnownGeogCS( "WGS84" );
1020 :
1021 2 : CPLPushErrorHandler( CPLQuietErrorHandler );
1022 : OGRCoordinateTransformationH hCT =
1023 2 : (OGRCoordinateTransformationH)OGRCreateCoordinateTransformation(&oSRS_WGS84, &oSRS_AEQD);
1024 2 : CPLPopErrorHandler();
1025 2 : if (hCT)
1026 : {
1027 2 : double dfULX_AEQD = psImage->dfULX;
1028 2 : double dfULY_AEQD = psImage->dfULY;
1029 2 : double dfURX_AEQD = psImage->dfURX;
1030 2 : double dfURY_AEQD = psImage->dfURY;
1031 2 : double dfLLX_AEQD = psImage->dfLLX;
1032 2 : double dfLLY_AEQD = psImage->dfLLY;
1033 2 : double dfLRX_AEQD = psImage->dfLRX;
1034 2 : double dfLRY_AEQD = psImage->dfLRY;
1035 2 : double z = 0;
1036 2 : int bSuccess = TRUE;
1037 2 : bSuccess &= OCTTransform(hCT, 1, &dfULX_AEQD, &dfULY_AEQD, &z);
1038 2 : bSuccess &= OCTTransform(hCT, 1, &dfURX_AEQD, &dfURY_AEQD, &z);
1039 2 : bSuccess &= OCTTransform(hCT, 1, &dfLLX_AEQD, &dfLLY_AEQD, &z);
1040 2 : bSuccess &= OCTTransform(hCT, 1, &dfLRX_AEQD, &dfLRY_AEQD, &z);
1041 2 : if (bSuccess)
1042 : {
1043 : /* Check that the coordinates of the 4 corners in Azimuthal Equidistant projection */
1044 : /* are a rectangle */
1045 2 : if (fabs((dfULX_AEQD - dfLLX_AEQD) / dfLLX_AEQD) < 1e-6 &&
1046 : fabs((dfURX_AEQD - dfLRX_AEQD) / dfLRX_AEQD) < 1e-6 &&
1047 : fabs((dfULY_AEQD - dfURY_AEQD) / dfURY_AEQD) < 1e-6 &&
1048 : fabs((dfLLY_AEQD - dfLRY_AEQD) / dfLRY_AEQD) < 1e-6)
1049 : {
1050 2 : CPLFree(poDS->pszProjection);
1051 2 : oSRS_AEQD.exportToWkt( &(poDS->pszProjection) );
1052 :
1053 2 : poDS->bGotGeoTransform = TRUE;
1054 2 : poDS->adfGeoTransform[0] = dfULX_AEQD;
1055 2 : poDS->adfGeoTransform[1] = (dfURX_AEQD - dfULX_AEQD) / poDS->nRasterXSize;
1056 2 : poDS->adfGeoTransform[2] = 0;
1057 2 : poDS->adfGeoTransform[3] = dfULY_AEQD;
1058 2 : poDS->adfGeoTransform[4] = 0;
1059 2 : poDS->adfGeoTransform[5] = (dfLLY_AEQD - dfULY_AEQD) / poDS->nRasterYSize;
1060 : }
1061 : }
1062 2 : OCTDestroyCoordinateTransformation(hCT);
1063 : }
1064 : else
1065 : {
1066 : // if we cannot instantiate the transformer, then we
1067 : // will at least attempt to record what we believe the
1068 : // natural coordinate system of the image is. This is
1069 : // primarily used by ArcGIS (#3337)
1070 :
1071 0 : CPLErrorReset();
1072 :
1073 : CPLError( CE_Warning, CPLE_AppDefined,
1074 0 : "Failed to instantiate coordinate system transformer, likely PROJ.DLL/libproj.so is not available. Returning image corners as lat/long GCPs as a fallback." );
1075 :
1076 0 : char *pszAEQD = NULL;
1077 0 : oSRS_AEQD.exportToWkt( &(pszAEQD) );
1078 0 : poDS->SetMetadataItem( "GCPPROJECTIONX", pszAEQD, "IMAGE_STRUCTURE" );
1079 0 : CPLFree( pszAEQD );
1080 2 : }
1081 : }
1082 :
1083 : /* -------------------------------------------------------------------- */
1084 : /* Do we have RPCs? */
1085 : /* -------------------------------------------------------------------- */
1086 559 : int bHasRPC00 = FALSE;
1087 : NITFRPC00BInfo sRPCInfo;
1088 559 : memset(&sRPCInfo, 0, sizeof(sRPCInfo)); /* To avoid warnings from not clever compilers */
1089 :
1090 559 : if( psImage && NITFReadRPC00B( psImage, &sRPCInfo ) && sRPCInfo.SUCCESS )
1091 3 : bHasRPC00 = TRUE;
1092 :
1093 : /* -------------------------------------------------------------------- */
1094 : /* Do we have IGEOLO data that can be treated as a */
1095 : /* geotransform? Our approach should support images in an */
1096 : /* affine rotated frame of reference. */
1097 : /* -------------------------------------------------------------------- */
1098 559 : int nGCPCount = 0;
1099 559 : GDAL_GCP *psGCPs = NULL;
1100 :
1101 559 : if( psImage && !poDS->bGotGeoTransform && psImage->chICORDS != ' ' )
1102 : {
1103 167 : nGCPCount = 4;
1104 :
1105 167 : psGCPs = (GDAL_GCP *) CPLMalloc(sizeof(GDAL_GCP) * nGCPCount);
1106 167 : GDALInitGCPs( nGCPCount, psGCPs );
1107 :
1108 167 : if( psImage->bIsBoxCenterOfPixel )
1109 : {
1110 133 : psGCPs[0].dfGCPPixel = 0.5;
1111 133 : psGCPs[0].dfGCPLine = 0.5;
1112 133 : psGCPs[1].dfGCPPixel = poDS->nRasterXSize-0.5;
1113 133 : psGCPs[1].dfGCPLine = 0.5;
1114 133 : psGCPs[2].dfGCPPixel = poDS->nRasterXSize-0.5;
1115 133 : psGCPs[2].dfGCPLine = poDS->nRasterYSize-0.5;
1116 133 : psGCPs[3].dfGCPPixel = 0.5;
1117 133 : psGCPs[3].dfGCPLine = poDS->nRasterYSize-0.5;
1118 : }
1119 : else
1120 : {
1121 34 : psGCPs[0].dfGCPPixel = 0.0;
1122 34 : psGCPs[0].dfGCPLine = 0.0;
1123 34 : psGCPs[1].dfGCPPixel = poDS->nRasterXSize;
1124 34 : psGCPs[1].dfGCPLine = 0.0;
1125 34 : psGCPs[2].dfGCPPixel = poDS->nRasterXSize;
1126 34 : psGCPs[2].dfGCPLine = poDS->nRasterYSize;
1127 34 : psGCPs[3].dfGCPPixel = 0.0;
1128 34 : psGCPs[3].dfGCPLine = poDS->nRasterYSize;
1129 : }
1130 :
1131 167 : psGCPs[0].dfGCPX = psImage->dfULX;
1132 167 : psGCPs[0].dfGCPY = psImage->dfULY;
1133 :
1134 167 : psGCPs[1].dfGCPX = psImage->dfURX;
1135 167 : psGCPs[1].dfGCPY = psImage->dfURY;
1136 :
1137 167 : psGCPs[2].dfGCPX = psImage->dfLRX;
1138 167 : psGCPs[2].dfGCPY = psImage->dfLRY;
1139 :
1140 167 : psGCPs[3].dfGCPX = psImage->dfLLX;
1141 167 : psGCPs[3].dfGCPY = psImage->dfLLY;
1142 :
1143 : /* -------------------------------------------------------------------- */
1144 : /* ESRI desires to use the RPCs to produce a denser and more */
1145 : /* accurate set of GCPs in this case. Details are unclear at */
1146 : /* this time. */
1147 : /* -------------------------------------------------------------------- */
1148 : #ifdef ESRI_BUILD
1149 : if( bHasRPC00
1150 : && ( (psImage->chICORDS == 'G') || (psImage->chICORDS == 'C') ) )
1151 : {
1152 : if( nGCPCount == 4 )
1153 : NITFDensifyGCPs( &psGCPs, &nGCPCount );
1154 :
1155 : NITFUpdateGCPsWithRPC( &sRPCInfo, psGCPs, &nGCPCount );
1156 : }
1157 : #endif /* def ESRI_BUILD */
1158 : }
1159 :
1160 : /* -------------------------------------------------------------------- */
1161 : /* Convert the GCPs into a geotransform definition, if possible. */
1162 : /* -------------------------------------------------------------------- */
1163 559 : if( !psImage )
1164 : {
1165 : /* nothing */
1166 : }
1167 556 : else if( poDS->bGotGeoTransform == FALSE
1168 : && nGCPCount > 0
1169 : && GDALGCPsToGeoTransform( nGCPCount, psGCPs,
1170 : poDS->adfGeoTransform, FALSE ) )
1171 : {
1172 163 : poDS->bGotGeoTransform = TRUE;
1173 : }
1174 :
1175 : /* -------------------------------------------------------------------- */
1176 : /* If we have IGEOLO that isn't north up, return it as GCPs. */
1177 : /* -------------------------------------------------------------------- */
1178 393 : else if( (psImage->dfULX != 0 || psImage->dfURX != 0
1179 : || psImage->dfLRX != 0 || psImage->dfLLX != 0)
1180 : && psImage->chICORDS != ' ' &&
1181 : ( poDS->bGotGeoTransform == FALSE ) &&
1182 : nGCPCount >= 4 )
1183 : {
1184 : CPLDebug( "GDAL",
1185 : "NITFDataset::Open() wasn't able to derive a first order\n"
1186 2 : "geotransform. It will be returned as GCPs.");
1187 :
1188 2 : poDS->nGCPCount = nGCPCount;
1189 2 : poDS->pasGCPList = psGCPs;
1190 :
1191 2 : psGCPs = NULL;
1192 2 : nGCPCount = 0;
1193 :
1194 2 : CPLFree( poDS->pasGCPList[0].pszId );
1195 2 : poDS->pasGCPList[0].pszId = CPLStrdup( "UpperLeft" );
1196 :
1197 2 : CPLFree( poDS->pasGCPList[1].pszId );
1198 2 : poDS->pasGCPList[1].pszId = CPLStrdup( "UpperRight" );
1199 :
1200 2 : CPLFree( poDS->pasGCPList[2].pszId );
1201 2 : poDS->pasGCPList[2].pszId = CPLStrdup( "LowerRight" );
1202 :
1203 2 : CPLFree( poDS->pasGCPList[3].pszId );
1204 2 : poDS->pasGCPList[3].pszId = CPLStrdup( "LowerLeft" );
1205 :
1206 2 : poDS->pszGCPProjection = CPLStrdup( poDS->pszProjection );
1207 : }
1208 :
1209 : // This cleans up the original copy of the GCPs used to test if
1210 : // this IGEOLO could be used for a geotransform if we did not
1211 : // steal the to use as primary gcps.
1212 559 : if( nGCPCount > 0 )
1213 : {
1214 165 : GDALDeinitGCPs( nGCPCount, psGCPs );
1215 165 : CPLFree( psGCPs );
1216 : }
1217 :
1218 : /* -------------------------------------------------------------------- */
1219 : /* Do we have PRJPSB and MAPLOB TREs to get better */
1220 : /* georeferencing from? */
1221 : /* -------------------------------------------------------------------- */
1222 559 : if (psImage)
1223 556 : poDS->CheckGeoSDEInfo();
1224 :
1225 : /* -------------------------------------------------------------------- */
1226 : /* Do we have metadata. */
1227 : /* -------------------------------------------------------------------- */
1228 : char **papszMergedMD;
1229 : char **papszTRE_MD;
1230 :
1231 : // File and Image level metadata.
1232 559 : papszMergedMD = CSLDuplicate( poDS->psFile->papszMetadata );
1233 :
1234 559 : if( psImage )
1235 : {
1236 : papszMergedMD = CSLInsertStrings( papszMergedMD,
1237 : CSLCount( papszMergedMD ),
1238 556 : psImage->papszMetadata );
1239 :
1240 : // Comments.
1241 556 : if( psImage->pszComments != NULL && strlen(psImage->pszComments) != 0 )
1242 : papszMergedMD = CSLSetNameValue(
1243 19 : papszMergedMD, "NITF_IMAGE_COMMENTS", psImage->pszComments );
1244 :
1245 : // Compression code.
1246 : papszMergedMD = CSLSetNameValue( papszMergedMD, "NITF_IC",
1247 556 : psImage->szIC );
1248 :
1249 : // IMODE
1250 : char szIMODE[2];
1251 556 : szIMODE[0] = psImage->chIMODE;
1252 556 : szIMODE[1] = '\0';
1253 556 : papszMergedMD = CSLSetNameValue( papszMergedMD, "NITF_IMODE", szIMODE );
1254 :
1255 : // ILOC/Attachment info
1256 556 : if( psImage->nIDLVL != 0 )
1257 : {
1258 : NITFSegmentInfo *psSegInfo
1259 554 : = psFile->pasSegmentInfo + psImage->iSegment;
1260 :
1261 : papszMergedMD =
1262 : CSLSetNameValue( papszMergedMD, "NITF_IDLVL",
1263 554 : CPLString().Printf("%d",psImage->nIDLVL) );
1264 : papszMergedMD =
1265 : CSLSetNameValue( papszMergedMD, "NITF_IALVL",
1266 554 : CPLString().Printf("%d",psImage->nIALVL) );
1267 : papszMergedMD =
1268 : CSLSetNameValue( papszMergedMD, "NITF_ILOC_ROW",
1269 554 : CPLString().Printf("%d",psImage->nILOCRow) );
1270 : papszMergedMD =
1271 : CSLSetNameValue( papszMergedMD, "NITF_ILOC_COLUMN",
1272 554 : CPLString().Printf("%d",psImage->nILOCColumn));
1273 : papszMergedMD =
1274 : CSLSetNameValue( papszMergedMD, "NITF_CCS_ROW",
1275 554 : CPLString().Printf("%d",psSegInfo->nCCS_R) );
1276 : papszMergedMD =
1277 : CSLSetNameValue( papszMergedMD, "NITF_CCS_COLUMN",
1278 554 : CPLString().Printf("%d", psSegInfo->nCCS_C));
1279 : papszMergedMD =
1280 : CSLSetNameValue( papszMergedMD, "NITF_IMAG",
1281 554 : psImage->szIMAG );
1282 : }
1283 :
1284 556 : papszMergedMD = NITFGenericMetadataRead(papszMergedMD, psFile, psImage, NULL);
1285 :
1286 : // BLOCKA
1287 556 : papszTRE_MD = NITFReadBLOCKA( psImage );
1288 556 : if( papszTRE_MD != NULL )
1289 : {
1290 : papszMergedMD = CSLInsertStrings( papszMergedMD,
1291 : CSLCount( papszTRE_MD ),
1292 14 : papszTRE_MD );
1293 14 : CSLDestroy( papszTRE_MD );
1294 : }
1295 : }
1296 :
1297 : #ifdef ESRI_BUILD
1298 : // Extract ESRI generic metadata.
1299 : char **papszESRI_MD = ExtractEsriMD( papszMergedMD );
1300 : if( papszESRI_MD != NULL )
1301 : {
1302 : papszMergedMD = CSLInsertStrings( papszMergedMD,
1303 : CSLCount( papszESRI_MD ),
1304 : papszESRI_MD );
1305 : CSLDestroy( papszESRI_MD );
1306 : }
1307 : #endif
1308 :
1309 559 : poDS->SetMetadata( papszMergedMD );
1310 559 : CSLDestroy( papszMergedMD );
1311 :
1312 : /* -------------------------------------------------------------------- */
1313 : /* Image structure metadata. */
1314 : /* -------------------------------------------------------------------- */
1315 559 : if( psImage == NULL )
1316 : /* do nothing */;
1317 556 : else if( psImage->szIC[1] == '1' )
1318 : poDS->SetMetadataItem( "COMPRESSION", "BILEVEL",
1319 1 : "IMAGE_STRUCTURE" );
1320 555 : else if( psImage->szIC[1] == '2' )
1321 : poDS->SetMetadataItem( "COMPRESSION", "ARIDPCM",
1322 2 : "IMAGE_STRUCTURE" );
1323 553 : else if( psImage->szIC[1] == '3' )
1324 : poDS->SetMetadataItem( "COMPRESSION", "JPEG",
1325 27 : "IMAGE_STRUCTURE" );
1326 526 : else if( psImage->szIC[1] == '4' )
1327 : poDS->SetMetadataItem( "COMPRESSION", "VECTOR QUANTIZATION",
1328 26 : "IMAGE_STRUCTURE" );
1329 500 : else if( psImage->szIC[1] == '5' )
1330 : poDS->SetMetadataItem( "COMPRESSION", "LOSSLESS JPEG",
1331 0 : "IMAGE_STRUCTURE" );
1332 500 : else if( psImage->szIC[1] == '8' )
1333 : poDS->SetMetadataItem( "COMPRESSION", "JPEG2000",
1334 24 : "IMAGE_STRUCTURE" );
1335 :
1336 : /* -------------------------------------------------------------------- */
1337 : /* Do we have RPC info. */
1338 : /* -------------------------------------------------------------------- */
1339 559 : if( psImage && bHasRPC00 )
1340 : {
1341 : char szValue[1280];
1342 : int i;
1343 :
1344 3 : sprintf( szValue, "%.16g", sRPCInfo.LINE_OFF );
1345 3 : poDS->SetMetadataItem( "LINE_OFF", szValue, "RPC" );
1346 :
1347 3 : sprintf( szValue, "%.16g", sRPCInfo.LINE_SCALE );
1348 3 : poDS->SetMetadataItem( "LINE_SCALE", szValue, "RPC" );
1349 :
1350 3 : sprintf( szValue, "%.16g", sRPCInfo.SAMP_OFF );
1351 3 : poDS->SetMetadataItem( "SAMP_OFF", szValue, "RPC" );
1352 :
1353 3 : sprintf( szValue, "%.16g", sRPCInfo.SAMP_SCALE );
1354 3 : poDS->SetMetadataItem( "SAMP_SCALE", szValue, "RPC" );
1355 :
1356 3 : sprintf( szValue, "%.16g", sRPCInfo.LONG_OFF );
1357 3 : poDS->SetMetadataItem( "LONG_OFF", szValue, "RPC" );
1358 :
1359 3 : sprintf( szValue, "%.16g", sRPCInfo.LONG_SCALE );
1360 3 : poDS->SetMetadataItem( "LONG_SCALE", szValue, "RPC" );
1361 :
1362 3 : sprintf( szValue, "%.16g", sRPCInfo.LAT_OFF );
1363 3 : poDS->SetMetadataItem( "LAT_OFF", szValue, "RPC" );
1364 :
1365 3 : sprintf( szValue, "%.16g", sRPCInfo.LAT_SCALE );
1366 3 : poDS->SetMetadataItem( "LAT_SCALE", szValue, "RPC" );
1367 :
1368 3 : sprintf( szValue, "%.16g", sRPCInfo.HEIGHT_OFF );
1369 3 : poDS->SetMetadataItem( "HEIGHT_OFF", szValue, "RPC" );
1370 :
1371 3 : sprintf( szValue, "%.16g", sRPCInfo.HEIGHT_SCALE );
1372 3 : poDS->SetMetadataItem( "HEIGHT_SCALE", szValue, "RPC" );
1373 :
1374 3 : szValue[0] = '\0';
1375 63 : for( i = 0; i < 20; i++ )
1376 : sprintf( szValue+strlen(szValue), "%.16g ",
1377 60 : sRPCInfo.LINE_NUM_COEFF[i] );
1378 3 : poDS->SetMetadataItem( "LINE_NUM_COEFF", szValue, "RPC" );
1379 :
1380 3 : szValue[0] = '\0';
1381 63 : for( i = 0; i < 20; i++ )
1382 : sprintf( szValue+strlen(szValue), "%.16g ",
1383 60 : sRPCInfo.LINE_DEN_COEFF[i] );
1384 3 : poDS->SetMetadataItem( "LINE_DEN_COEFF", szValue, "RPC" );
1385 :
1386 3 : szValue[0] = '\0';
1387 63 : for( i = 0; i < 20; i++ )
1388 : sprintf( szValue+strlen(szValue), "%.16g ",
1389 60 : sRPCInfo.SAMP_NUM_COEFF[i] );
1390 3 : poDS->SetMetadataItem( "SAMP_NUM_COEFF", szValue, "RPC" );
1391 :
1392 3 : szValue[0] = '\0';
1393 63 : for( i = 0; i < 20; i++ )
1394 : sprintf( szValue+strlen(szValue), "%.16g ",
1395 60 : sRPCInfo.SAMP_DEN_COEFF[i] );
1396 3 : poDS->SetMetadataItem( "SAMP_DEN_COEFF", szValue, "RPC" );
1397 :
1398 : sprintf( szValue, "%.16g",
1399 3 : sRPCInfo.LONG_OFF - ( sRPCInfo.LONG_SCALE / 2.0 ) );
1400 3 : poDS->SetMetadataItem( "MIN_LONG", szValue, "RPC" );
1401 :
1402 : sprintf( szValue, "%.16g",
1403 3 : sRPCInfo.LONG_OFF + ( sRPCInfo.LONG_SCALE / 2.0 ) );
1404 3 : poDS->SetMetadataItem( "MAX_LONG", szValue, "RPC" );
1405 :
1406 : sprintf( szValue, "%.16g",
1407 3 : sRPCInfo.LAT_OFF - ( sRPCInfo.LAT_SCALE / 2.0 ) );
1408 3 : poDS->SetMetadataItem( "MIN_LAT", szValue, "RPC" );
1409 :
1410 : sprintf( szValue, "%.16g",
1411 3 : sRPCInfo.LAT_OFF + ( sRPCInfo.LAT_SCALE / 2.0 ) );
1412 3 : poDS->SetMetadataItem( "MAX_LAT", szValue, "RPC" );
1413 : }
1414 :
1415 : /* -------------------------------------------------------------------- */
1416 : /* Do we have Chip info? */
1417 : /* -------------------------------------------------------------------- */
1418 : NITFICHIPBInfo sChipInfo;
1419 :
1420 559 : if( psImage
1421 : && NITFReadICHIPB( psImage, &sChipInfo ) && sChipInfo.XFRM_FLAG == 0 )
1422 : {
1423 : char szValue[1280];
1424 :
1425 3 : sprintf( szValue, "%.16g", sChipInfo.SCALE_FACTOR );
1426 3 : poDS->SetMetadataItem( "ICHIP_SCALE_FACTOR", szValue );
1427 :
1428 3 : sprintf( szValue, "%d", sChipInfo.ANAMORPH_CORR );
1429 3 : poDS->SetMetadataItem( "ICHIP_ANAMORPH_CORR", szValue );
1430 :
1431 3 : sprintf( szValue, "%d", sChipInfo.SCANBLK_NUM );
1432 3 : poDS->SetMetadataItem( "ICHIP_SCANBLK_NUM", szValue );
1433 :
1434 3 : sprintf( szValue, "%.16g", sChipInfo.OP_ROW_11 );
1435 3 : poDS->SetMetadataItem( "ICHIP_OP_ROW_11", szValue );
1436 :
1437 3 : sprintf( szValue, "%.16g", sChipInfo.OP_COL_11 );
1438 3 : poDS->SetMetadataItem( "ICHIP_OP_COL_11", szValue );
1439 :
1440 3 : sprintf( szValue, "%.16g", sChipInfo.OP_ROW_12 );
1441 3 : poDS->SetMetadataItem( "ICHIP_OP_ROW_12", szValue );
1442 :
1443 3 : sprintf( szValue, "%.16g", sChipInfo.OP_COL_12 );
1444 3 : poDS->SetMetadataItem( "ICHIP_OP_COL_12", szValue );
1445 :
1446 3 : sprintf( szValue, "%.16g", sChipInfo.OP_ROW_21 );
1447 3 : poDS->SetMetadataItem( "ICHIP_OP_ROW_21", szValue );
1448 :
1449 3 : sprintf( szValue, "%.16g", sChipInfo.OP_COL_21 );
1450 3 : poDS->SetMetadataItem( "ICHIP_OP_COL_21", szValue );
1451 :
1452 3 : sprintf( szValue, "%.16g", sChipInfo.OP_ROW_22 );
1453 3 : poDS->SetMetadataItem( "ICHIP_OP_ROW_22", szValue );
1454 :
1455 3 : sprintf( szValue, "%.16g", sChipInfo.OP_COL_22 );
1456 3 : poDS->SetMetadataItem( "ICHIP_OP_COL_22", szValue );
1457 :
1458 3 : sprintf( szValue, "%.16g", sChipInfo.FI_ROW_11 );
1459 3 : poDS->SetMetadataItem( "ICHIP_FI_ROW_11", szValue );
1460 :
1461 3 : sprintf( szValue, "%.16g", sChipInfo.FI_COL_11 );
1462 3 : poDS->SetMetadataItem( "ICHIP_FI_COL_11", szValue );
1463 :
1464 3 : sprintf( szValue, "%.16g", sChipInfo.FI_ROW_12 );
1465 3 : poDS->SetMetadataItem( "ICHIP_FI_ROW_12", szValue );
1466 :
1467 3 : sprintf( szValue, "%.16g", sChipInfo.FI_COL_12 );
1468 3 : poDS->SetMetadataItem( "ICHIP_FI_COL_12", szValue );
1469 :
1470 3 : sprintf( szValue, "%.16g", sChipInfo.FI_ROW_21 );
1471 3 : poDS->SetMetadataItem( "ICHIP_FI_ROW_21", szValue );
1472 :
1473 3 : sprintf( szValue, "%.16g", sChipInfo.FI_COL_21 );
1474 3 : poDS->SetMetadataItem( "ICHIP_FI_COL_21", szValue );
1475 :
1476 3 : sprintf( szValue, "%.16g", sChipInfo.FI_ROW_22 );
1477 3 : poDS->SetMetadataItem( "ICHIP_FI_ROW_22", szValue );
1478 :
1479 3 : sprintf( szValue, "%.16g", sChipInfo.FI_COL_22 );
1480 3 : poDS->SetMetadataItem( "ICHIP_FI_COL_22", szValue );
1481 :
1482 3 : sprintf( szValue, "%d", sChipInfo.FI_ROW );
1483 3 : poDS->SetMetadataItem( "ICHIP_FI_ROW", szValue );
1484 :
1485 3 : sprintf( szValue, "%d", sChipInfo.FI_COL );
1486 3 : poDS->SetMetadataItem( "ICHIP_FI_COL", szValue );
1487 :
1488 : }
1489 :
1490 559 : const NITFSeries* series = NITFGetSeriesInfo(pszFilename);
1491 559 : if (series)
1492 : {
1493 : poDS->SetMetadataItem("NITF_SERIES_ABBREVIATION",
1494 29 : (series->abbreviation) ? series->abbreviation : "Unknown");
1495 : poDS->SetMetadataItem("NITF_SERIES_NAME",
1496 29 : (series->name) ? series->name : "Unknown");
1497 : }
1498 :
1499 : /* -------------------------------------------------------------------- */
1500 : /* If there are multiple image segments, and we are the zeroth, */
1501 : /* then setup the subdataset metadata. */
1502 : /* -------------------------------------------------------------------- */
1503 559 : int nSubDSCount = 0;
1504 :
1505 559 : if( nIMIndex == -1 )
1506 : {
1507 546 : char **papszSubdatasets = NULL;
1508 546 : int nIMCounter = 0;
1509 :
1510 3579 : for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
1511 : {
1512 3033 : if( EQUAL(psFile->pasSegmentInfo[iSegment].szSegmentType,"IM") )
1513 : {
1514 2551 : CPLString oName;
1515 2551 : CPLString oValue;
1516 :
1517 2551 : oName.Printf( "SUBDATASET_%d_NAME", nIMCounter+1 );
1518 2551 : oValue.Printf( "NITF_IM:%d:%s", nIMCounter, pszFilename );
1519 : papszSubdatasets = CSLSetNameValue( papszSubdatasets,
1520 2551 : oName, oValue );
1521 :
1522 2551 : oName.Printf( "SUBDATASET_%d_DESC", nIMCounter+1 );
1523 2551 : oValue.Printf( "Image %d of %s", nIMCounter+1, pszFilename );
1524 : papszSubdatasets = CSLSetNameValue( papszSubdatasets,
1525 2551 : oName, oValue );
1526 :
1527 2551 : nIMCounter++;
1528 : }
1529 : }
1530 :
1531 546 : nSubDSCount = CSLCount(papszSubdatasets) / 2;
1532 546 : if( nSubDSCount > 1 )
1533 : poDS->GDALMajorObject::SetMetadata( papszSubdatasets,
1534 8 : "SUBDATASETS" );
1535 :
1536 546 : CSLDestroy( papszSubdatasets );
1537 : }
1538 :
1539 : /* -------------------------------------------------------------------- */
1540 : /* Initialize any PAM information. */
1541 : /* -------------------------------------------------------------------- */
1542 559 : poDS->SetDescription( poOpenInfo->pszFilename );
1543 :
1544 559 : if( nSubDSCount > 1 || nIMIndex != -1 )
1545 : {
1546 21 : if( nIMIndex == -1 )
1547 8 : nIMIndex = 0;
1548 :
1549 21 : poDS->SetSubdatasetName( CPLString().Printf("%d",nIMIndex) );
1550 21 : poDS->SetPhysicalFilename( pszFilename );
1551 : }
1552 :
1553 559 : poDS->bInLoadXML = TRUE;
1554 559 : poDS->TryLoadXML();
1555 559 : poDS->bInLoadXML = FALSE;
1556 :
1557 : /* -------------------------------------------------------------------- */
1558 : /* Do we have a special overview file? If not, do we have */
1559 : /* RSets that should be treated as an overview file? */
1560 : /* -------------------------------------------------------------------- */
1561 : const char *pszOverviewFile =
1562 559 : poDS->GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS" );
1563 :
1564 559 : if( pszOverviewFile == NULL )
1565 : {
1566 556 : if( poDS->CheckForRSets(pszFilename) )
1567 3 : pszOverviewFile = poDS->osRSetVRT;
1568 : }
1569 :
1570 : /* -------------------------------------------------------------------- */
1571 : /* If we have jpeg or jpeg2000 bands we may need to set the */
1572 : /* overview file on their dataset. (#3276) */
1573 : /* -------------------------------------------------------------------- */
1574 559 : GDALDataset *poSubDS = poDS->poJ2KDataset;
1575 559 : if( poDS->poJPEGDataset )
1576 17 : poSubDS = poDS->poJPEGDataset;
1577 :
1578 559 : if( poSubDS && pszOverviewFile != NULL )
1579 : {
1580 : poSubDS->SetMetadataItem( "OVERVIEW_FILE",
1581 : pszOverviewFile,
1582 2 : "OVERVIEWS" );
1583 : }
1584 :
1585 : /* -------------------------------------------------------------------- */
1586 : /* If we have jpeg, or jpeg2000 bands we may need to clear */
1587 : /* their PAM dirty flag too. */
1588 : /* -------------------------------------------------------------------- */
1589 559 : if( poDS->poJ2KDataset != NULL )
1590 : poDS->poJ2KDataset->SetPamFlags(
1591 23 : poDS->poJ2KDataset->GetPamFlags() & ~GPF_DIRTY );
1592 559 : if( poDS->poJPEGDataset != NULL )
1593 : poDS->poJPEGDataset->SetPamFlags(
1594 17 : poDS->poJPEGDataset->GetPamFlags() & ~GPF_DIRTY );
1595 :
1596 : /* -------------------------------------------------------------------- */
1597 : /* Check for overviews. */
1598 : /* -------------------------------------------------------------------- */
1599 559 : if( !EQUAL(poOpenInfo->pszFilename,pszFilename) )
1600 13 : poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" );
1601 : else
1602 546 : poDS->oOvManager.Initialize( poDS, pszFilename );
1603 :
1604 : /* If there are PAM overviews, don't expose the underlying JPEG dataset */
1605 : /* overviews (in case of monoblock C3) */
1606 559 : if( poDS->GetRasterCount() > 0 && poDS->GetRasterBand(1) != NULL )
1607 : poDS->bExposeUnderlyingJPEGDatasetOverviews =
1608 : ((GDALPamRasterBand*)poDS->GetRasterBand(1))->
1609 556 : GDALPamRasterBand::GetOverviewCount() == 0;
1610 :
1611 559 : return( poDS );
1612 : }
1613 :
1614 : /************************************************************************/
1615 : /* LoadDODDatum() */
1616 : /* */
1617 : /* Try to turn a US military datum name into a datum definition. */
1618 : /************************************************************************/
1619 :
1620 3 : static OGRErr LoadDODDatum( OGRSpatialReference *poSRS,
1621 : const char *pszDatumName )
1622 :
1623 : {
1624 : /* -------------------------------------------------------------------- */
1625 : /* The most common case... */
1626 : /* -------------------------------------------------------------------- */
1627 3 : if( EQUALN(pszDatumName,"WGE ",4) )
1628 : {
1629 0 : poSRS->SetWellKnownGeogCS( "WGS84" );
1630 0 : return OGRERR_NONE;
1631 : }
1632 :
1633 : /* -------------------------------------------------------------------- */
1634 : /* All the rest we will try and load from gt_datum.csv */
1635 : /* (Geotrans datum file). */
1636 : /* -------------------------------------------------------------------- */
1637 : char szExpanded[6];
1638 3 : const char *pszGTDatum = CSVFilename( "gt_datum.csv" );
1639 :
1640 3 : strncpy( szExpanded, pszDatumName, 3 );
1641 3 : szExpanded[3] = '\0';
1642 3 : if( pszDatumName[3] != ' ' )
1643 : {
1644 : int nLen;
1645 3 : strcat( szExpanded, "-" );
1646 3 : nLen = strlen(szExpanded);
1647 3 : szExpanded[nLen] = pszDatumName[3];
1648 3 : szExpanded[nLen + 1] = '\0';
1649 : }
1650 :
1651 : CPLString osDName = CSVGetField( pszGTDatum, "CODE", szExpanded,
1652 3 : CC_ApproxString, "NAME" );
1653 3 : if( strlen(osDName) == 0 )
1654 : {
1655 : CPLError( CE_Failure, CPLE_AppDefined,
1656 : "Failed to find datum %s/%s in gt_datum.csv.",
1657 0 : pszDatumName, szExpanded );
1658 0 : return OGRERR_FAILURE;
1659 : }
1660 :
1661 : CPLString osEllipseCode = CSVGetField( pszGTDatum, "CODE", szExpanded,
1662 3 : CC_ApproxString, "ELLIPSOID" );
1663 : double dfDeltaX = CPLAtof(CSVGetField( pszGTDatum, "CODE", szExpanded,
1664 3 : CC_ApproxString, "DELTAX" ) );
1665 : double dfDeltaY = CPLAtof(CSVGetField( pszGTDatum, "CODE", szExpanded,
1666 3 : CC_ApproxString, "DELTAY" ) );
1667 : double dfDeltaZ = CPLAtof(CSVGetField( pszGTDatum, "CODE", szExpanded,
1668 3 : CC_ApproxString, "DELTAZ" ) );
1669 :
1670 : /* -------------------------------------------------------------------- */
1671 : /* Lookup the ellipse code. */
1672 : /* -------------------------------------------------------------------- */
1673 3 : const char *pszGTEllipse = CSVFilename( "gt_ellips.csv" );
1674 :
1675 : CPLString osEName = CSVGetField( pszGTEllipse, "CODE", osEllipseCode,
1676 3 : CC_ApproxString, "NAME" );
1677 3 : if( strlen(osEName) == 0 )
1678 : {
1679 : CPLError( CE_Failure, CPLE_AppDefined,
1680 : "Failed to find datum %s in gt_ellips.csv.",
1681 0 : osEllipseCode.c_str() );
1682 0 : return OGRERR_FAILURE;
1683 : }
1684 :
1685 : double dfA = CPLAtof(CSVGetField( pszGTEllipse, "CODE", osEllipseCode,
1686 3 : CC_ApproxString, "A" ));
1687 : double dfInvF = CPLAtof(CSVGetField( pszGTEllipse, "CODE", osEllipseCode,
1688 3 : CC_ApproxString, "RF" ));
1689 :
1690 : /* -------------------------------------------------------------------- */
1691 : /* Create geographic coordinate system. */
1692 : /* -------------------------------------------------------------------- */
1693 3 : poSRS->SetGeogCS( osDName, osDName, osEName, dfA, dfInvF );
1694 :
1695 3 : poSRS->SetTOWGS84( dfDeltaX, dfDeltaY, dfDeltaZ );
1696 :
1697 3 : return OGRERR_NONE;
1698 : }
1699 :
1700 : /************************************************************************/
1701 : /* CheckGeoSDEInfo() */
1702 : /* */
1703 : /* Check for GeoSDE TREs (GEOPSB/PRJPSB and MAPLOB). If we */
1704 : /* have them, use them to override our coordinate system and */
1705 : /* geotransform info. */
1706 : /************************************************************************/
1707 :
1708 556 : void NITFDataset::CheckGeoSDEInfo()
1709 :
1710 : {
1711 556 : if( !psImage )
1712 0 : return;
1713 :
1714 : /* -------------------------------------------------------------------- */
1715 : /* Do we have the required TREs? */
1716 : /* -------------------------------------------------------------------- */
1717 : const char *pszGEOPSB , *pszPRJPSB, *pszMAPLOB;
1718 556 : OGRSpatialReference oSRS;
1719 : char szName[81];
1720 : int nGEOPSBSize, nPRJPSBSize, nMAPLOBSize;
1721 :
1722 556 : pszGEOPSB = NITFFindTRE( psFile->pachTRE, psFile->nTREBytes,"GEOPSB",&nGEOPSBSize);
1723 556 : pszPRJPSB = NITFFindTRE( psFile->pachTRE, psFile->nTREBytes,"PRJPSB",&nPRJPSBSize);
1724 556 : pszMAPLOB = NITFFindTRE(psImage->pachTRE,psImage->nTREBytes,"MAPLOB",&nMAPLOBSize);
1725 :
1726 556 : if( pszGEOPSB == NULL || pszPRJPSB == NULL || pszMAPLOB == NULL )
1727 : return;
1728 :
1729 : /* -------------------------------------------------------------------- */
1730 : /* Collect projection parameters. */
1731 : /* -------------------------------------------------------------------- */
1732 :
1733 : char szParm[16];
1734 3 : if (nPRJPSBSize < 82 + 1)
1735 : {
1736 : CPLError(CE_Failure, CPLE_AppDefined,
1737 0 : "Cannot read PRJPSB TRE. Not enough bytes");
1738 : return;
1739 : }
1740 3 : int nParmCount = atoi(NITFGetField(szParm,pszPRJPSB,82,1));
1741 : int i;
1742 3 : double adfParm[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
1743 : double dfFN;
1744 : double dfFE;
1745 3 : if (nPRJPSBSize < 83+15*nParmCount+15+15)
1746 : {
1747 : CPLError(CE_Failure, CPLE_AppDefined,
1748 0 : "Cannot read PRJPSB TRE. Not enough bytes");
1749 : return;
1750 : }
1751 3 : for( i = 0; i < nParmCount; i++ )
1752 0 : adfParm[i] = atof(NITFGetField(szParm,pszPRJPSB,83+15*i,15));
1753 :
1754 3 : dfFE = atof(NITFGetField(szParm,pszPRJPSB,83+15*nParmCount,15));
1755 3 : dfFN = atof(NITFGetField(szParm,pszPRJPSB,83+15*nParmCount+15,15));
1756 :
1757 : /* -------------------------------------------------------------------- */
1758 : /* Try to handle the projection. */
1759 : /* -------------------------------------------------------------------- */
1760 3 : if( EQUALN(pszPRJPSB+80,"AC",2) )
1761 : oSRS.SetACEA( adfParm[1], adfParm[2], adfParm[3], adfParm[0],
1762 3 : dfFE, dfFN );
1763 :
1764 0 : else if( EQUALN(pszPRJPSB+80,"AK",2) )
1765 0 : oSRS.SetLAEA( adfParm[1], adfParm[0], dfFE, dfFN );
1766 :
1767 0 : else if( EQUALN(pszPRJPSB+80,"AL",2) )
1768 0 : oSRS.SetAE( adfParm[1], adfParm[0], dfFE, dfFN );
1769 :
1770 0 : else if( EQUALN(pszPRJPSB+80,"BF",2) )
1771 0 : oSRS.SetBonne( adfParm[1], adfParm[0], dfFE, dfFN );
1772 :
1773 0 : else if( EQUALN(pszPRJPSB+80,"CP",2) )
1774 0 : oSRS.SetEquirectangular( adfParm[1], adfParm[0], dfFE, dfFN );
1775 :
1776 0 : else if( EQUALN(pszPRJPSB+80,"CS",2) )
1777 0 : oSRS.SetCS( adfParm[1], adfParm[0], dfFE, dfFN );
1778 :
1779 0 : else if( EQUALN(pszPRJPSB+80,"EF",2) )
1780 0 : oSRS.SetEckertIV( adfParm[0], dfFE, dfFN );
1781 :
1782 0 : else if( EQUALN(pszPRJPSB+80,"ED",2) )
1783 0 : oSRS.SetEckertVI( adfParm[0], dfFE, dfFN );
1784 :
1785 0 : else if( EQUALN(pszPRJPSB+80,"GN",2) )
1786 0 : oSRS.SetGnomonic( adfParm[1], adfParm[0], dfFE, dfFN );
1787 :
1788 0 : else if( EQUALN(pszPRJPSB+80,"HX",2) )
1789 : oSRS.SetHOM2PNO( adfParm[1],
1790 : adfParm[3], adfParm[2],
1791 : adfParm[5], adfParm[4],
1792 0 : adfParm[0], dfFE, dfFN );
1793 :
1794 0 : else if( EQUALN(pszPRJPSB+80,"KA",2) )
1795 : oSRS.SetEC( adfParm[1], adfParm[2], adfParm[3], adfParm[0],
1796 0 : dfFE, dfFN );
1797 :
1798 0 : else if( EQUALN(pszPRJPSB+80,"LE",2) )
1799 : oSRS.SetLCC( adfParm[1], adfParm[2], adfParm[3], adfParm[0],
1800 0 : dfFE, dfFN );
1801 :
1802 0 : else if( EQUALN(pszPRJPSB+80,"LI",2) )
1803 0 : oSRS.SetCEA( adfParm[1], adfParm[0], dfFE, dfFN );
1804 :
1805 0 : else if( EQUALN(pszPRJPSB+80,"MC",2) )
1806 0 : oSRS.SetMercator( adfParm[2], adfParm[1], 1.0, dfFE, dfFN );
1807 :
1808 0 : else if( EQUALN(pszPRJPSB+80,"MH",2) )
1809 0 : oSRS.SetMC( 0.0, adfParm[1], dfFE, dfFN );
1810 :
1811 0 : else if( EQUALN(pszPRJPSB+80,"MP",2) )
1812 0 : oSRS.SetMollweide( adfParm[0], dfFE, dfFN );
1813 :
1814 0 : else if( EQUALN(pszPRJPSB+80,"NT",2) )
1815 0 : oSRS.SetNZMG( adfParm[1], adfParm[0], dfFE, dfFN );
1816 :
1817 0 : else if( EQUALN(pszPRJPSB+80,"OD",2) )
1818 0 : oSRS.SetOrthographic( adfParm[1], adfParm[0], dfFE, dfFN );
1819 :
1820 0 : else if( EQUALN(pszPRJPSB+80,"PC",2) )
1821 0 : oSRS.SetPolyconic( adfParm[1], adfParm[0], dfFE, dfFN );
1822 :
1823 0 : else if( EQUALN(pszPRJPSB+80,"PG",2) )
1824 0 : oSRS.SetPS( adfParm[1], adfParm[0], 1.0, dfFE, dfFN );
1825 :
1826 0 : else if( EQUALN(pszPRJPSB+80,"RX",2) )
1827 0 : oSRS.SetRobinson( adfParm[0], dfFE, dfFN );
1828 :
1829 0 : else if( EQUALN(pszPRJPSB+80,"SA",2) )
1830 0 : oSRS.SetSinusoidal( adfParm[0], dfFE, dfFN );
1831 :
1832 0 : else if( EQUALN(pszPRJPSB+80,"TC",2) )
1833 0 : oSRS.SetTM( adfParm[2], adfParm[0], adfParm[1], dfFE, dfFN );
1834 :
1835 0 : else if( EQUALN(pszPRJPSB+80,"VA",2) )
1836 0 : oSRS.SetVDG( adfParm[0], dfFE, dfFN );
1837 :
1838 : else
1839 0 : oSRS.SetLocalCS( NITFGetField(szName,pszPRJPSB,0,80) );
1840 :
1841 : /* -------------------------------------------------------------------- */
1842 : /* Try to apply the datum. */
1843 : /* -------------------------------------------------------------------- */
1844 3 : if (nGEOPSBSize < 86 + 4)
1845 : {
1846 : CPLError(CE_Failure, CPLE_AppDefined,
1847 0 : "Cannot read GEOPSB TRE. Not enough bytes");
1848 : return;
1849 : }
1850 3 : LoadDODDatum( &oSRS, NITFGetField(szParm,pszGEOPSB,86,4) );
1851 :
1852 : /* -------------------------------------------------------------------- */
1853 : /* Get the geotransform */
1854 : /* -------------------------------------------------------------------- */
1855 : double adfGT[6];
1856 3 : double dfMeterPerUnit = 1.0;
1857 :
1858 3 : if (nMAPLOBSize < 28 + 15)
1859 : {
1860 : CPLError(CE_Failure, CPLE_AppDefined,
1861 0 : "Cannot read MAPLOB TRE. Not enough bytes");
1862 : return;
1863 : }
1864 :
1865 3 : if( EQUALN(pszMAPLOB+0,"DM ",3) )
1866 0 : dfMeterPerUnit = 0.1;
1867 3 : else if( EQUALN(pszMAPLOB+0,"CM ",3) )
1868 0 : dfMeterPerUnit = 0.01;
1869 3 : else if( EQUALN(pszMAPLOB+0,"MM ",3) )
1870 0 : dfMeterPerUnit = 0.001;
1871 3 : else if( EQUALN(pszMAPLOB+0,"UM ",3) )
1872 0 : dfMeterPerUnit = 0.000001;
1873 3 : else if( EQUALN(pszMAPLOB+0,"KM ",3) )
1874 0 : dfMeterPerUnit = 1000.0;
1875 3 : else if( EQUALN(pszMAPLOB+0,"M ",3) )
1876 3 : dfMeterPerUnit = 1.0;
1877 : else
1878 : {
1879 : CPLError( CE_Warning, CPLE_AppDefined,
1880 : "MAPLOB Unit=%3.3s not regonised, geolocation may be wrong.",
1881 0 : pszMAPLOB+0 );
1882 : }
1883 :
1884 3 : adfGT[0] = atof(NITFGetField(szParm,pszMAPLOB,13,15));
1885 3 : adfGT[1] = atof(NITFGetField(szParm,pszMAPLOB,3,5)) * dfMeterPerUnit;
1886 3 : adfGT[2] = 0.0;
1887 3 : adfGT[3] = atof(NITFGetField(szParm,pszMAPLOB,28,15));
1888 3 : adfGT[4] = 0.0;
1889 3 : adfGT[5] = -atof(NITFGetField(szParm,pszMAPLOB,8,5)) * dfMeterPerUnit;
1890 :
1891 : /* -------------------------------------------------------------------- */
1892 : /* Apply back to dataset. */
1893 : /* -------------------------------------------------------------------- */
1894 3 : CPLFree( pszProjection );
1895 3 : pszProjection = NULL;
1896 :
1897 3 : oSRS.exportToWkt( &pszProjection );
1898 :
1899 3 : memcpy( adfGeoTransform, adfGT, sizeof(double)*6 );
1900 3 : bGotGeoTransform = TRUE;
1901 : }
1902 :
1903 : /************************************************************************/
1904 : /* AdviseRead() */
1905 : /************************************************************************/
1906 :
1907 0 : CPLErr NITFDataset::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
1908 : int nBufXSize, int nBufYSize,
1909 : GDALDataType eDT,
1910 : int nBandCount, int *panBandList,
1911 : char **papszOptions )
1912 :
1913 : {
1914 0 : if( poJ2KDataset == NULL )
1915 : return GDALDataset::AdviseRead( nXOff, nYOff, nXSize, nYSize,
1916 : nBufXSize, nBufYSize, eDT,
1917 : nBandCount, panBandList,
1918 0 : papszOptions);
1919 0 : else if( poJPEGDataset != NULL )
1920 : return poJPEGDataset->AdviseRead( nXOff, nYOff, nXSize, nYSize,
1921 : nBufXSize, nBufYSize, eDT,
1922 : nBandCount, panBandList,
1923 0 : papszOptions);
1924 : else
1925 : return poJ2KDataset->AdviseRead( nXOff, nYOff, nXSize, nYSize,
1926 : nBufXSize, nBufYSize, eDT,
1927 : nBandCount, panBandList,
1928 0 : papszOptions);
1929 : }
1930 :
1931 : /************************************************************************/
1932 : /* IRasterIO() */
1933 : /************************************************************************/
1934 :
1935 865 : CPLErr NITFDataset::IRasterIO( GDALRWFlag eRWFlag,
1936 : int nXOff, int nYOff, int nXSize, int nYSize,
1937 : void * pData, int nBufXSize, int nBufYSize,
1938 : GDALDataType eBufType,
1939 : int nBandCount, int *panBandMap,
1940 : int nPixelSpace, int nLineSpace, int nBandSpace)
1941 :
1942 : {
1943 865 : if( poJ2KDataset != NULL )
1944 : return poJ2KDataset->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
1945 : pData, nBufXSize, nBufYSize, eBufType,
1946 : nBandCount, panBandMap,
1947 100 : nPixelSpace, nLineSpace, nBandSpace );
1948 765 : else if( poJPEGDataset != NULL )
1949 : return poJPEGDataset->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
1950 : pData, nBufXSize, nBufYSize, eBufType,
1951 : nBandCount, panBandMap,
1952 64 : nPixelSpace, nLineSpace, nBandSpace );
1953 : else
1954 : return GDALDataset::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
1955 : pData, nBufXSize, nBufYSize, eBufType,
1956 : nBandCount, panBandMap,
1957 701 : nPixelSpace, nLineSpace, nBandSpace );
1958 : }
1959 :
1960 :
1961 : /************************************************************************/
1962 : /* GetGeoTransform() */
1963 : /************************************************************************/
1964 :
1965 128 : CPLErr NITFDataset::GetGeoTransform( double *padfGeoTransform )
1966 :
1967 : {
1968 128 : memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 );
1969 :
1970 128 : if( bGotGeoTransform )
1971 125 : return CE_None;
1972 : else
1973 3 : return GDALPamDataset::GetGeoTransform( padfGeoTransform );
1974 : }
1975 :
1976 : /************************************************************************/
1977 : /* SetGeoTransform() */
1978 : /************************************************************************/
1979 :
1980 71 : CPLErr NITFDataset::SetGeoTransform( double *padfGeoTransform )
1981 :
1982 : {
1983 : double dfIGEOLOULX, dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY,
1984 : dfIGEOLOLRX, dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY;
1985 :
1986 71 : bGotGeoTransform = TRUE;
1987 : /* Valgrind would complain because SetGeoTransform() is called */
1988 : /* from SetProjection() with adfGeoTransform as argument */
1989 71 : if (adfGeoTransform != padfGeoTransform)
1990 65 : memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 );
1991 :
1992 71 : dfIGEOLOULX = padfGeoTransform[0] + 0.5 * padfGeoTransform[1]
1993 71 : + 0.5 * padfGeoTransform[2];
1994 71 : dfIGEOLOULY = padfGeoTransform[3] + 0.5 * padfGeoTransform[4]
1995 71 : + 0.5 * padfGeoTransform[5];
1996 71 : dfIGEOLOURX = dfIGEOLOULX + padfGeoTransform[1] * (nRasterXSize - 1);
1997 71 : dfIGEOLOURY = dfIGEOLOULY + padfGeoTransform[4] * (nRasterXSize - 1);
1998 71 : dfIGEOLOLRX = dfIGEOLOULX + padfGeoTransform[1] * (nRasterXSize - 1)
1999 71 : + padfGeoTransform[2] * (nRasterYSize - 1);
2000 71 : dfIGEOLOLRY = dfIGEOLOULY + padfGeoTransform[4] * (nRasterXSize - 1)
2001 71 : + padfGeoTransform[5] * (nRasterYSize - 1);
2002 71 : dfIGEOLOLLX = dfIGEOLOULX + padfGeoTransform[2] * (nRasterYSize - 1);
2003 71 : dfIGEOLOLLY = dfIGEOLOULY + padfGeoTransform[5] * (nRasterYSize - 1);
2004 :
2005 71 : if( NITFWriteIGEOLO( psImage, psImage->chICORDS,
2006 : psImage->nZone,
2007 : dfIGEOLOULX, dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY,
2008 : dfIGEOLOLRX, dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY ) )
2009 52 : return CE_None;
2010 : else
2011 19 : return GDALPamDataset::SetGeoTransform( padfGeoTransform );
2012 : }
2013 :
2014 : /************************************************************************/
2015 : /* SetGCPs() */
2016 : /************************************************************************/
2017 :
2018 3 : CPLErr NITFDataset::SetGCPs( int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
2019 : const char *pszGCPProjectionIn )
2020 : {
2021 3 : if( nGCPCountIn != 4 )
2022 : {
2023 : CPLError(CE_Failure, CPLE_NotSupported,
2024 0 : "NITF only supports writing 4 GCPs.");
2025 0 : return CE_Failure;
2026 : }
2027 :
2028 : /* Free previous GCPs */
2029 3 : GDALDeinitGCPs( nGCPCount, pasGCPList );
2030 3 : CPLFree( pasGCPList );
2031 :
2032 : /* Duplicate in GCPs */
2033 3 : nGCPCount = nGCPCountIn;
2034 3 : pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
2035 :
2036 3 : CPLFree(pszGCPProjection);
2037 3 : pszGCPProjection = CPLStrdup(pszGCPProjectionIn);
2038 :
2039 3 : int iUL = -1, iUR = -1, iLR = -1, iLL = -1;
2040 :
2041 : #define EPS_GCP 1e-5
2042 15 : for(int i = 0; i < 4; i++ )
2043 : {
2044 21 : if (fabs(pasGCPList[i].dfGCPPixel - 0.5) < EPS_GCP &&
2045 6 : fabs(pasGCPList[i].dfGCPLine - 0.5) < EPS_GCP)
2046 3 : iUL = i;
2047 :
2048 18 : else if (fabs(pasGCPList[i].dfGCPPixel - (nRasterXSize - 0.5)) < EPS_GCP &&
2049 6 : fabs(pasGCPList[i].dfGCPLine - 0.5) < EPS_GCP)
2050 3 : iUR = i;
2051 :
2052 12 : else if (fabs(pasGCPList[i].dfGCPPixel - (nRasterXSize - 0.5)) < EPS_GCP &&
2053 3 : fabs(pasGCPList[i].dfGCPLine - (nRasterYSize - 0.5)) < EPS_GCP )
2054 3 : iLR = i;
2055 :
2056 6 : else if (fabs(pasGCPList[i].dfGCPPixel - 0.5) < EPS_GCP &&
2057 3 : fabs(pasGCPList[i].dfGCPLine - (nRasterYSize - 0.5)) < EPS_GCP)
2058 3 : iLL = i;
2059 : }
2060 :
2061 3 : if (iUL < 0 || iUR < 0 || iLR < 0 || iLL < 0)
2062 : {
2063 : CPLError(CE_Failure, CPLE_NotSupported,
2064 : "The 4 GCPs image coordinates must be exactly "
2065 : "at the *center* of the 4 corners of the image "
2066 : "( (%.1f, %.1f), (%.1f %.1f), (%.1f %.1f), (%.1f %.1f) ).",
2067 : 0.5, 0.5,
2068 : nRasterYSize - 0.5, 0.5,
2069 : nRasterXSize - 0.5, nRasterYSize - 0.5,
2070 0 : nRasterXSize - 0.5, 0.5);
2071 0 : return CE_Failure;
2072 : }
2073 :
2074 3 : double dfIGEOLOULX = pasGCPList[iUL].dfGCPX;
2075 3 : double dfIGEOLOULY = pasGCPList[iUL].dfGCPY;
2076 3 : double dfIGEOLOURX = pasGCPList[iUR].dfGCPX;
2077 3 : double dfIGEOLOURY = pasGCPList[iUR].dfGCPY;
2078 3 : double dfIGEOLOLRX = pasGCPList[iLR].dfGCPX;
2079 3 : double dfIGEOLOLRY = pasGCPList[iLR].dfGCPY;
2080 3 : double dfIGEOLOLLX = pasGCPList[iLL].dfGCPX;
2081 3 : double dfIGEOLOLLY = pasGCPList[iLL].dfGCPY;
2082 :
2083 : /* To recompute the zone */
2084 3 : char* pszProjectionBack = pszProjection ? CPLStrdup(pszProjection) : NULL;
2085 3 : CPLErr eErr = SetProjection(pszGCPProjection);
2086 3 : CPLFree(pszProjection);
2087 3 : pszProjection = pszProjectionBack;
2088 :
2089 3 : if (eErr != CE_None)
2090 0 : return eErr;
2091 :
2092 3 : if( NITFWriteIGEOLO( psImage, psImage->chICORDS,
2093 : psImage->nZone,
2094 : dfIGEOLOULX, dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY,
2095 : dfIGEOLOLRX, dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY ) )
2096 3 : return CE_None;
2097 : else
2098 0 : return CE_Failure;
2099 : }
2100 :
2101 : /************************************************************************/
2102 : /* GetProjectionRef() */
2103 : /************************************************************************/
2104 :
2105 121 : const char *NITFDataset::GetProjectionRef()
2106 :
2107 : {
2108 121 : if( bGotGeoTransform )
2109 115 : return pszProjection;
2110 : else
2111 6 : return GDALPamDataset::GetProjectionRef();
2112 : }
2113 :
2114 : /************************************************************************/
2115 : /* SetProjection() */
2116 : /************************************************************************/
2117 :
2118 25 : CPLErr NITFDataset::SetProjection(const char* _pszProjection)
2119 :
2120 : {
2121 : int bNorth;
2122 25 : OGRSpatialReference oSRS, oSRS_WGS84;
2123 25 : char *pszWKT = (char *) _pszProjection;
2124 :
2125 25 : if( pszWKT != NULL )
2126 25 : oSRS.importFromWkt( &pszWKT );
2127 : else
2128 0 : return CE_Failure;
2129 :
2130 25 : oSRS_WGS84.SetWellKnownGeogCS( "WGS84" );
2131 25 : if ( oSRS.IsSameGeogCS(&oSRS_WGS84) == FALSE)
2132 : {
2133 : CPLError(CE_Failure, CPLE_NotSupported,
2134 0 : "NITF only supports WGS84 geographic and UTM projections.\n");
2135 0 : return CE_Failure;
2136 : }
2137 :
2138 25 : if( oSRS.IsGeographic() && oSRS.GetPrimeMeridian() == 0.0)
2139 : {
2140 24 : if (psImage->chICORDS != 'G' && psImage->chICORDS != 'D')
2141 : {
2142 : CPLError(CE_Failure, CPLE_NotSupported,
2143 19 : "NITF file should have been created with creation option 'ICORDS=G' (or 'ICORDS=D').\n");
2144 19 : return CE_Failure;
2145 : }
2146 : }
2147 1 : else if( oSRS.GetUTMZone( &bNorth ) > 0)
2148 : {
2149 1 : if (bNorth && psImage->chICORDS != 'N')
2150 : {
2151 : CPLError(CE_Failure, CPLE_NotSupported,
2152 0 : "NITF file should have been created with creation option 'ICORDS=N'.\n");
2153 0 : return CE_Failure;
2154 : }
2155 1 : else if (!bNorth && psImage->chICORDS != 'S')
2156 : {
2157 : CPLError(CE_Failure, CPLE_NotSupported,
2158 0 : "NITF file should have been created with creation option 'ICORDS=S'.\n");
2159 0 : return CE_Failure;
2160 : }
2161 :
2162 1 : psImage->nZone = oSRS.GetUTMZone( NULL );
2163 : }
2164 : else
2165 : {
2166 : CPLError(CE_Failure, CPLE_NotSupported,
2167 0 : "NITF only supports WGS84 geographic and UTM projections.\n");
2168 0 : return CE_Failure;
2169 : }
2170 :
2171 6 : CPLFree(pszProjection);
2172 6 : pszProjection = CPLStrdup(_pszProjection);
2173 :
2174 6 : if (bGotGeoTransform)
2175 6 : SetGeoTransform(adfGeoTransform);
2176 :
2177 6 : return CE_None;
2178 : }
2179 :
2180 : #ifdef ESRI_BUILD
2181 : /************************************************************************/
2182 : /* InitializeNITFDESMetadata() */
2183 : /************************************************************************/
2184 :
2185 : void NITFDataset::InitializeNITFDESMetadata()
2186 : {
2187 : static const char *pszDESMetadataDomain = "NITF_DES_METADATA";
2188 : static const char *pszDESsDomain = "NITF_DES";
2189 : static const char *pszMDXmlDataContentDESDATA = "NITF_DES_XML_DATA_CONTENT_DESDATA";
2190 : static const char *pszXmlDataContent = "XML_DATA_CONTENT";
2191 : static const int idxXmlDataContentDESDATA = 973;
2192 : static const int sizeXmlDataContent = (int)strlen(pszXmlDataContent);
2193 :
2194 : char **ppszDESMetadataList = oSpecialMD.GetMetadata( pszDESMetadataDomain );
2195 :
2196 : if( ppszDESMetadataList != NULL ) return;
2197 :
2198 : char **ppszDESsList = this->GetMetadata( pszDESsDomain );
2199 :
2200 : if( ppszDESsList == NULL ) return;
2201 :
2202 : bool foundXmlDataContent = false;
2203 : char *pachNITFDES = NULL;
2204 :
2205 : // Set metadata "NITF_DES_XML_DATA_CONTENT_DESDATA".
2206 : // NOTE: There should only be one instance of XML_DATA_CONTENT DES.
2207 :
2208 : while( ((pachNITFDES = *ppszDESsList) != NULL) && (!foundXmlDataContent) )
2209 : {
2210 : // The data stream has been Base64 encoded, need to decode it.
2211 : // NOTE: The actual length of the DES data stream is appended at the beginning of the encoded
2212 : // data and is separated by a space.
2213 :
2214 : const char* pszSpace = strchr(pachNITFDES, ' ');
2215 :
2216 : char* pszData = NULL;
2217 : int nDataLen = 0;
2218 : if( pszSpace )
2219 : {
2220 : pszData = CPLStrdup( pszSpace+1 );
2221 : nDataLen = CPLBase64DecodeInPlace((GByte*)pszData);
2222 : pszData[nDataLen] = 0;
2223 : }
2224 :
2225 : if ( nDataLen > 2 + sizeXmlDataContent && EQUALN(pszData, "DE", 2) )
2226 : {
2227 : // Check to see if this is a XML_DATA_CONTENT DES.
2228 : if ( EQUALN(pszData + 2, pszXmlDataContent, sizeXmlDataContent) &&
2229 : nDataLen > idxXmlDataContentDESDATA )
2230 : {
2231 : foundXmlDataContent = true;
2232 :
2233 : // Get the value of the DESDATA field and set metadata "NITF_DES_XML_DATA_CONTENT_DESDATA".
2234 : const char* pszXML = pszData + idxXmlDataContentDESDATA;
2235 :
2236 : // Set the metadata.
2237 : oSpecialMD.SetMetadataItem( pszMDXmlDataContentDESDATA, pszXML, pszDESMetadataDomain );
2238 : }
2239 : }
2240 :
2241 : CPLFree(pszData);
2242 :
2243 : pachNITFDES = NULL;
2244 : ppszDESsList += 1;
2245 : }
2246 : }
2247 :
2248 :
2249 : /************************************************************************/
2250 : /* InitializeNITFDESs() */
2251 : /************************************************************************/
2252 :
2253 : void NITFDataset::InitializeNITFDESs()
2254 : {
2255 : static const char *pszDESsDomain = "NITF_DES";
2256 :
2257 : char **ppszDESsList = oSpecialMD.GetMetadata( pszDESsDomain );
2258 :
2259 : if( ppszDESsList != NULL ) return;
2260 :
2261 : /* -------------------------------------------------------------------- */
2262 : /* Go through all the segments and process all DES segments. */
2263 : /* -------------------------------------------------------------------- */
2264 :
2265 : char *pachDESData = NULL;
2266 : int nDESDataSize = 0;
2267 : std::string encodedDESData("");
2268 : CPLStringList aosList;
2269 :
2270 : for( int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
2271 : {
2272 : NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
2273 :
2274 : if( EQUAL(psSegInfo->szSegmentType,"DE") )
2275 : {
2276 : nDESDataSize = psSegInfo->nSegmentHeaderSize + psSegInfo->nSegmentSize;
2277 : pachDESData = (char*) VSIMalloc( nDESDataSize + 1 );
2278 :
2279 : if (pachDESData == NULL)
2280 : {
2281 : CPLError( CE_Failure, CPLE_OutOfMemory, "Cannot allocate memory for DES segment" );
2282 : return;
2283 : }
2284 :
2285 : if( VSIFSeekL( psFile->fp, psSegInfo->nSegmentHeaderStart,
2286 : SEEK_SET ) != 0
2287 : || (int)VSIFReadL( pachDESData, 1, nDESDataSize,
2288 : psFile->fp ) != nDESDataSize )
2289 : {
2290 : CPLError( CE_Failure, CPLE_FileIO,
2291 : "Failed to read %d byte DES subheader from " CPL_FRMT_GUIB ".",
2292 : nDESDataSize,
2293 : psSegInfo->nSegmentHeaderStart );
2294 : CPLFree( pachDESData );
2295 : return;
2296 : }
2297 :
2298 : pachDESData[nDESDataSize] = '\0';
2299 :
2300 : /* -------------------------------------------------------------------- */
2301 : /* Accumulate all the DES segments. */
2302 : /* -------------------------------------------------------------------- */
2303 :
2304 : char* pszBase64 = CPLBase64Encode( nDESDataSize, (const GByte *)pachDESData );
2305 : encodedDESData = pszBase64;
2306 : CPLFree(pszBase64);
2307 :
2308 : CPLFree( pachDESData );
2309 : pachDESData = NULL;
2310 :
2311 : if( encodedDESData.empty() )
2312 : {
2313 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to encode DES subheader data!");
2314 : return;
2315 : }
2316 :
2317 : // The length of the DES subheader data plus a space is append to the beginning of the encoded
2318 : // string so that we can recover the actual length of the image subheader when we decode it.
2319 :
2320 : char buffer[20];
2321 :
2322 : sprintf(buffer, "%d", nDESDataSize);
2323 :
2324 : std::string desSubheaderStr(buffer);
2325 : desSubheaderStr.append(" ");
2326 : desSubheaderStr.append(encodedDESData);
2327 :
2328 : aosList.AddString(desSubheaderStr.c_str() );
2329 : }
2330 : }
2331 :
2332 : if (aosList.size() > 0)
2333 : oSpecialMD.SetMetadata( aosList.List(), pszDESsDomain );
2334 : }
2335 :
2336 : /************************************************************************/
2337 : /* InitializeNITFTREs() */
2338 : /************************************************************************/
2339 :
2340 : void NITFDataset::InitializeNITFTREs()
2341 : {
2342 : static const char *pszFileHeaderTREsDomain = "NITF_FILE_HEADER_TRES";
2343 : static const char *pszImageSegmentTREsDomain = "NITF_IMAGE_SEGMENT_TRES";
2344 :
2345 : char **ppszFileHeaderTREsList = oSpecialMD.GetMetadata( pszFileHeaderTREsDomain );
2346 : char **ppszImageSegmentTREsList = oSpecialMD.GetMetadata( pszImageSegmentTREsDomain );
2347 :
2348 : if( (ppszFileHeaderTREsList != NULL) && (ppszImageSegmentTREsList != NULL ) ) return;
2349 :
2350 : /* -------------------------------------------------------------------- */
2351 : /* Loop over TRE sources (file and image). */
2352 : /* -------------------------------------------------------------------- */
2353 :
2354 : for( int nTRESrc = 0; nTRESrc < 2; nTRESrc++ )
2355 : {
2356 : int nTREBytes = 0;
2357 : char *pszTREData = NULL;
2358 : const char *pszTREsDomain = NULL;
2359 : CPLStringList aosList;
2360 :
2361 : /* -------------------------------------------------------------------- */
2362 : /* Extract file header or image segment TREs. */
2363 : /* -------------------------------------------------------------------- */
2364 :
2365 : if( nTRESrc == 0 )
2366 : {
2367 : if( ppszFileHeaderTREsList != NULL ) continue;
2368 :
2369 : nTREBytes = psFile->nTREBytes;
2370 : pszTREData = psFile->pachTRE;
2371 : pszTREsDomain = pszFileHeaderTREsDomain;
2372 : }
2373 : else
2374 : {
2375 : if( ppszImageSegmentTREsList != NULL ) continue;
2376 :
2377 : if( psImage )
2378 : {
2379 : nTREBytes = psImage->nTREBytes;
2380 : pszTREData = psImage->pachTRE;
2381 : pszTREsDomain = pszImageSegmentTREsDomain;
2382 : }
2383 : else
2384 : {
2385 : nTREBytes = 0;
2386 : pszTREData = NULL;
2387 : }
2388 : }
2389 :
2390 : /* -------------------------------------------------------------------- */
2391 : /* Loop over TREs. */
2392 : /* -------------------------------------------------------------------- */
2393 :
2394 : while( nTREBytes >= 11 )
2395 : {
2396 : char szTemp[100];
2397 : char szTag[7];
2398 : char *pszEscapedData = NULL;
2399 : int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 ));
2400 :
2401 : if (nThisTRESize < 0)
2402 : {
2403 : NITFGetField(szTemp, pszTREData, 0, 6 );
2404 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid size (%d) for TRE %s",
2405 : nThisTRESize, szTemp);
2406 : return;
2407 : }
2408 :
2409 : if (nThisTRESize > nTREBytes - 11)
2410 : {
2411 : CPLError(CE_Failure, CPLE_AppDefined, "Not enough bytes in TRE");
2412 : return;
2413 : }
2414 :
2415 : strncpy( szTag, pszTREData, 6 );
2416 : szTag[6] = '\0';
2417 :
2418 : // trim white off tag.
2419 : while( strlen(szTag) > 0 && szTag[strlen(szTag)-1] == ' ' )
2420 : szTag[strlen(szTag)-1] = '\0';
2421 :
2422 : // escape data.
2423 : pszEscapedData = CPLEscapeString( pszTREData + 6,
2424 : nThisTRESize + 5,
2425 : CPLES_BackslashQuotable );
2426 :
2427 : char * pszLine = (char *) CPLMalloc( strlen(szTag)+strlen(pszEscapedData)+2 );
2428 : sprintf( pszLine, "%s=%s", szTag, pszEscapedData );
2429 : aosList.AddString(pszLine);
2430 : CPLFree(pszLine);
2431 : pszLine = NULL;
2432 :
2433 : CPLFree( pszEscapedData );
2434 : pszEscapedData = NULL;
2435 :
2436 : nTREBytes -= (nThisTRESize + 11);
2437 : pszTREData += (nThisTRESize + 11);
2438 : }
2439 :
2440 : if (aosList.size() > 0)
2441 : oSpecialMD.SetMetadata( aosList.List(), pszTREsDomain );
2442 : }
2443 : }
2444 : #endif
2445 :
2446 : /************************************************************************/
2447 : /* InitializeNITFMetadata() */
2448 : /************************************************************************/
2449 :
2450 2 : void NITFDataset::InitializeNITFMetadata()
2451 :
2452 : {
2453 : static const char *pszDomainName = "NITF_METADATA";
2454 : static const char *pszTagNITFFileHeader = "NITFFileHeader";
2455 : static const char *pszTagNITFImageSubheader = "NITFImageSubheader";
2456 :
2457 2 : if( oSpecialMD.GetMetadata( pszDomainName ) != NULL )
2458 0 : return;
2459 :
2460 : // nHeaderLenOffset is the number of bytes to skip from the beginning of the NITF file header
2461 : // in order to get to the field HL (NITF file header length).
2462 :
2463 2 : int nHeaderLen = 0;
2464 2 : int nHeaderLenOffset = 0;
2465 :
2466 : // Get the NITF file header length.
2467 :
2468 2 : if( psFile->pachHeader != NULL )
2469 : {
2470 4 : if ( (strncmp(psFile->pachHeader, "NITF02.10", 9) == 0) || (strncmp(psFile->pachHeader, "NSIF01.00", 9) == 0) )
2471 2 : nHeaderLenOffset = 354;
2472 0 : else if ( (strncmp(psFile->pachHeader, "NITF01.10", 9) == 0) || (strncmp(psFile->pachHeader, "NITF02.00", 9) == 0) )
2473 0 : nHeaderLenOffset = ( strncmp((psFile->pachHeader+280), "999998", 6 ) == 0 ) ? 394 : 354;
2474 : }
2475 :
2476 : char fieldHL[7];
2477 :
2478 2 : if( nHeaderLenOffset > 0 )
2479 : {
2480 2 : char *pszFieldHL = psFile->pachHeader + nHeaderLenOffset;
2481 :
2482 2 : memcpy(fieldHL, pszFieldHL, 6);
2483 2 : fieldHL[6] = '\0';
2484 2 : nHeaderLen = atoi(fieldHL);
2485 : }
2486 :
2487 2 : if( nHeaderLen <= 0 )
2488 : {
2489 0 : CPLError(CE_Failure, CPLE_AppDefined, "Zero length NITF file header!");
2490 0 : return;
2491 : }
2492 :
2493 : char *encodedHeader = CPLBase64Encode(nHeaderLen,
2494 2 : (GByte*)psFile->pachHeader);
2495 :
2496 2 : if (encodedHeader == NULL || strlen(encodedHeader) == 0 )
2497 : {
2498 : CPLError(CE_Failure, CPLE_AppDefined,
2499 0 : "Failed to encode NITF file header!");
2500 0 : return;
2501 : }
2502 :
2503 : // The length of the NITF file header plus a space is append to the beginning of the encoded string so
2504 : // that we can recover the length of the NITF file header when we decode it without having to pull it
2505 : // out the HL field again.
2506 :
2507 2 : std::string nitfFileheaderStr(fieldHL);
2508 2 : nitfFileheaderStr.append(" ");
2509 2 : nitfFileheaderStr.append(encodedHeader);
2510 :
2511 2 : CPLFree( encodedHeader );
2512 :
2513 2 : oSpecialMD.SetMetadataItem( pszTagNITFFileHeader, nitfFileheaderStr.c_str(), pszDomainName );
2514 :
2515 : // Get the image subheader length.
2516 :
2517 2 : int nImageSubheaderLen = 0;
2518 :
2519 2 : for( int i = 0; i < psFile->nSegmentCount; ++i )
2520 : {
2521 2 : if (strncmp(psFile->pasSegmentInfo[i].szSegmentType, "IM", 2) == 0)
2522 : {
2523 2 : nImageSubheaderLen = psFile->pasSegmentInfo[i].nSegmentHeaderSize;
2524 2 : break;
2525 : }
2526 : }
2527 :
2528 2 : if( nImageSubheaderLen < 0 )
2529 : {
2530 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid length NITF image subheader!");
2531 : return;
2532 : }
2533 :
2534 2 : if( nImageSubheaderLen > 0 )
2535 : {
2536 2 : char *encodedImageSubheader = CPLBase64Encode(nImageSubheaderLen,(GByte*) psImage->pachHeader);
2537 :
2538 2 : if( encodedImageSubheader == NULL || strlen(encodedImageSubheader) ==0 )
2539 : {
2540 : CPLError(CE_Failure, CPLE_AppDefined,
2541 0 : "Failed to encode image subheader!");
2542 : return;
2543 : }
2544 :
2545 : // The length of the image subheader plus a space is append to the beginning of the encoded string so
2546 : // that we can recover the actual length of the image subheader when we decode it.
2547 :
2548 : char buffer[20];
2549 :
2550 2 : sprintf(buffer, "%d", nImageSubheaderLen);
2551 :
2552 2 : std::string imageSubheaderStr(buffer);
2553 2 : imageSubheaderStr.append(" ");
2554 2 : imageSubheaderStr.append(encodedImageSubheader);
2555 :
2556 2 : CPLFree( encodedImageSubheader );
2557 :
2558 2 : oSpecialMD.SetMetadataItem( pszTagNITFImageSubheader, imageSubheaderStr.c_str(), pszDomainName );
2559 0 : }
2560 : }
2561 :
2562 : /************************************************************************/
2563 : /* InitializeCGMMetadata() */
2564 : /************************************************************************/
2565 :
2566 11 : void NITFDataset::InitializeCGMMetadata()
2567 :
2568 : {
2569 11 : if( oSpecialMD.GetMetadataItem( "SEGMENT_COUNT", "CGM" ) != NULL )
2570 1 : return;
2571 :
2572 : int iSegment;
2573 10 : int iCGM = 0;
2574 10 : char **papszCGMMetadata = NULL;
2575 :
2576 : papszCGMMetadata =
2577 10 : CSLSetNameValue( papszCGMMetadata, "SEGMENT_COUNT", "0" );
2578 :
2579 : /* ==================================================================== */
2580 : /* Process all graphics segments. */
2581 : /* ==================================================================== */
2582 38 : for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
2583 : {
2584 28 : NITFSegmentInfo *psSegment = psFile->pasSegmentInfo + iSegment;
2585 :
2586 28 : if( !EQUAL(psSegment->szSegmentType,"GR")
2587 : && !EQUAL(psSegment->szSegmentType,"SY") )
2588 14 : continue;
2589 :
2590 : papszCGMMetadata =
2591 : CSLSetNameValue( papszCGMMetadata,
2592 : CPLString().Printf("SEGMENT_%d_SLOC_ROW", iCGM),
2593 14 : CPLString().Printf("%d",psSegment->nLOC_R) );
2594 : papszCGMMetadata =
2595 : CSLSetNameValue( papszCGMMetadata,
2596 : CPLString().Printf("SEGMENT_%d_SLOC_COL", iCGM),
2597 28 : CPLString().Printf("%d",psSegment->nLOC_C) );
2598 :
2599 : papszCGMMetadata =
2600 : CSLSetNameValue( papszCGMMetadata,
2601 : CPLString().Printf("SEGMENT_%d_CCS_ROW", iCGM),
2602 28 : CPLString().Printf("%d",psSegment->nCCS_R) );
2603 : papszCGMMetadata =
2604 : CSLSetNameValue( papszCGMMetadata,
2605 : CPLString().Printf("SEGMENT_%d_CCS_COL", iCGM),
2606 28 : CPLString().Printf("%d",psSegment->nCCS_C) );
2607 :
2608 : papszCGMMetadata =
2609 : CSLSetNameValue( papszCGMMetadata,
2610 : CPLString().Printf("SEGMENT_%d_SDLVL", iCGM),
2611 28 : CPLString().Printf("%d",psSegment->nDLVL) );
2612 : papszCGMMetadata =
2613 : CSLSetNameValue( papszCGMMetadata,
2614 : CPLString().Printf("SEGMENT_%d_SALVL", iCGM),
2615 28 : CPLString().Printf("%d",psSegment->nALVL) );
2616 :
2617 : /* -------------------------------------------------------------------- */
2618 : /* Load the raw CGM data itself. */
2619 : /* -------------------------------------------------------------------- */
2620 : char *pabyCGMData, *pszEscapedCGMData;
2621 :
2622 14 : pabyCGMData = (char *) VSICalloc(1,(size_t)psSegment->nSegmentSize);
2623 14 : if (pabyCGMData == NULL)
2624 : {
2625 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
2626 0 : CSLDestroy( papszCGMMetadata );
2627 0 : return;
2628 : }
2629 14 : if( VSIFSeekL( psFile->fp, psSegment->nSegmentStart,
2630 : SEEK_SET ) != 0
2631 : || VSIFReadL( pabyCGMData, 1, (size_t)psSegment->nSegmentSize,
2632 : psFile->fp ) != psSegment->nSegmentSize )
2633 : {
2634 : CPLError( CE_Warning, CPLE_FileIO,
2635 : "Failed to read " CPL_FRMT_GUIB " bytes of graphic data at " CPL_FRMT_GUIB ".",
2636 : psSegment->nSegmentSize,
2637 0 : psSegment->nSegmentStart );
2638 0 : CPLFree(pabyCGMData);
2639 0 : CSLDestroy( papszCGMMetadata );
2640 0 : return;
2641 : }
2642 :
2643 : pszEscapedCGMData = CPLEscapeString( pabyCGMData,
2644 : (int)psSegment->nSegmentSize,
2645 14 : CPLES_BackslashQuotable );
2646 14 : if (pszEscapedCGMData == NULL)
2647 : {
2648 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
2649 0 : CPLFree(pabyCGMData);
2650 0 : CSLDestroy( papszCGMMetadata );
2651 0 : return;
2652 : }
2653 :
2654 : papszCGMMetadata =
2655 : CSLSetNameValue( papszCGMMetadata,
2656 : CPLString().Printf("SEGMENT_%d_DATA", iCGM),
2657 14 : pszEscapedCGMData );
2658 14 : CPLFree( pszEscapedCGMData );
2659 14 : CPLFree( pabyCGMData );
2660 :
2661 14 : iCGM++;
2662 : }
2663 :
2664 : /* -------------------------------------------------------------------- */
2665 : /* Record the CGM segment count. */
2666 : /* -------------------------------------------------------------------- */
2667 : papszCGMMetadata =
2668 : CSLSetNameValue( papszCGMMetadata,
2669 : "SEGMENT_COUNT",
2670 10 : CPLString().Printf( "%d", iCGM ) );
2671 :
2672 10 : oSpecialMD.SetMetadata( papszCGMMetadata, "CGM" );
2673 :
2674 10 : CSLDestroy( papszCGMMetadata );
2675 : }
2676 :
2677 : /************************************************************************/
2678 : /* InitializeTextMetadata() */
2679 : /************************************************************************/
2680 :
2681 12 : void NITFDataset::InitializeTextMetadata()
2682 :
2683 : {
2684 12 : if( oSpecialMD.GetMetadata( "TEXT" ) != NULL )
2685 2 : return;
2686 :
2687 : int iSegment;
2688 10 : int iText = 0;
2689 :
2690 : /* ==================================================================== */
2691 : /* Process all text segments. */
2692 : /* ==================================================================== */
2693 237 : for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
2694 : {
2695 227 : NITFSegmentInfo *psSegment = psFile->pasSegmentInfo + iSegment;
2696 :
2697 227 : if( !EQUAL(psSegment->szSegmentType,"TX") )
2698 216 : continue;
2699 :
2700 : /* -------------------------------------------------------------------- */
2701 : /* Load the text header */
2702 : /* -------------------------------------------------------------------- */
2703 :
2704 : /* Allocate one extra byte for the NULL terminating character */
2705 : char *pabyHeaderData = (char *) CPLCalloc(1,
2706 11 : (size_t) psSegment->nSegmentHeaderSize + 1);
2707 11 : if (VSIFSeekL(psFile->fp, psSegment->nSegmentHeaderStart,
2708 : SEEK_SET) != 0 ||
2709 : VSIFReadL(pabyHeaderData, 1, (size_t) psSegment->nSegmentHeaderSize,
2710 : psFile->fp) != psSegment->nSegmentHeaderSize)
2711 : {
2712 : CPLError( CE_Warning, CPLE_FileIO,
2713 : "Failed to read %d bytes of text header data at " CPL_FRMT_GUIB ".",
2714 : psSegment->nSegmentHeaderSize,
2715 0 : psSegment->nSegmentHeaderStart);
2716 0 : CPLFree(pabyHeaderData);
2717 0 : return;
2718 : }
2719 :
2720 : oSpecialMD.SetMetadataItem( CPLString().Printf("HEADER_%d", iText),
2721 11 : pabyHeaderData, "TEXT");
2722 11 : CPLFree(pabyHeaderData);
2723 :
2724 : /* -------------------------------------------------------------------- */
2725 : /* Load the raw TEXT data itself. */
2726 : /* -------------------------------------------------------------------- */
2727 : char *pabyTextData;
2728 :
2729 : /* Allocate one extra byte for the NULL terminating character */
2730 11 : pabyTextData = (char *) VSICalloc(1,(size_t)psSegment->nSegmentSize+1);
2731 11 : if (pabyTextData == NULL)
2732 : {
2733 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
2734 0 : return;
2735 : }
2736 11 : if( VSIFSeekL( psFile->fp, psSegment->nSegmentStart,
2737 : SEEK_SET ) != 0
2738 : || VSIFReadL( pabyTextData, 1, (size_t)psSegment->nSegmentSize,
2739 : psFile->fp ) != psSegment->nSegmentSize )
2740 : {
2741 : CPLError( CE_Warning, CPLE_FileIO,
2742 : "Failed to read " CPL_FRMT_GUIB " bytes of text data at " CPL_FRMT_GUIB ".",
2743 : psSegment->nSegmentSize,
2744 0 : psSegment->nSegmentStart );
2745 0 : CPLFree( pabyTextData );
2746 0 : return;
2747 : }
2748 :
2749 : oSpecialMD.SetMetadataItem( CPLString().Printf( "DATA_%d", iText),
2750 11 : pabyTextData, "TEXT" );
2751 11 : CPLFree( pabyTextData );
2752 :
2753 11 : iText++;
2754 : }
2755 : }
2756 :
2757 : /************************************************************************/
2758 : /* InitializeTREMetadata() */
2759 : /************************************************************************/
2760 :
2761 15 : void NITFDataset::InitializeTREMetadata()
2762 :
2763 : {
2764 15 : if( oSpecialMD.GetMetadata( "TRE" ) != NULL )
2765 3 : return;
2766 :
2767 12 : CPLXMLNode* psTresNode = CPLCreateXMLNode(NULL, CXT_Element, "tres");
2768 :
2769 : /* -------------------------------------------------------------------- */
2770 : /* Loop over TRE sources (file and image). */
2771 : /* -------------------------------------------------------------------- */
2772 : int nTRESrc;
2773 :
2774 36 : for( nTRESrc = 0; nTRESrc < 2; nTRESrc++ )
2775 : {
2776 : int nTREBytes;
2777 : char *pszTREData;
2778 :
2779 24 : if( nTRESrc == 0 )
2780 : {
2781 12 : nTREBytes = psFile->nTREBytes;
2782 12 : pszTREData = psFile->pachTRE;
2783 : }
2784 : else
2785 : {
2786 12 : if( psImage )
2787 : {
2788 12 : nTREBytes = psImage->nTREBytes;
2789 12 : pszTREData = psImage->pachTRE;
2790 : }
2791 : else
2792 : {
2793 0 : nTREBytes = 0;
2794 0 : pszTREData = NULL;
2795 : }
2796 : }
2797 :
2798 : /* -------------------------------------------------------------------- */
2799 : /* Loop over TREs. */
2800 : /* -------------------------------------------------------------------- */
2801 :
2802 61 : while( nTREBytes >= 11 )
2803 : {
2804 : char szTemp[100];
2805 : char szTag[7];
2806 : char *pszEscapedData;
2807 13 : int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 ));
2808 :
2809 13 : if (nThisTRESize < 0)
2810 : {
2811 0 : NITFGetField(szTemp, pszTREData, 0, 6 );
2812 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid size (%d) for TRE %s",
2813 0 : nThisTRESize, szTemp);
2814 0 : return;
2815 : }
2816 13 : if (nThisTRESize > nTREBytes - 11)
2817 : {
2818 0 : CPLError(CE_Failure, CPLE_AppDefined, "Not enough bytes in TRE");
2819 0 : return;
2820 : }
2821 :
2822 13 : strncpy( szTag, pszTREData, 6 );
2823 13 : szTag[6] = '\0';
2824 :
2825 : // trim white off tag.
2826 26 : while( strlen(szTag) > 0 && szTag[strlen(szTag)-1] == ' ' )
2827 0 : szTag[strlen(szTag)-1] = '\0';
2828 :
2829 13 : CPLXMLNode* psTreNode = NITFCreateXMLTre(psFile, szTag, pszTREData + 11,nThisTRESize);
2830 13 : if (psTreNode)
2831 : {
2832 : CPLCreateXMLNode(CPLCreateXMLNode(psTreNode, CXT_Attribute, "location"),
2833 11 : CXT_Text, nTRESrc == 0 ? "file" : "image");
2834 11 : CPLAddXMLChild(psTresNode, psTreNode);
2835 : }
2836 :
2837 : // escape data.
2838 : pszEscapedData = CPLEscapeString( pszTREData + 11,
2839 : nThisTRESize,
2840 13 : CPLES_BackslashQuotable );
2841 13 : if (pszEscapedData == NULL)
2842 : {
2843 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
2844 0 : return;
2845 : }
2846 :
2847 : char szUniqueTag[32];
2848 13 : strcpy(szUniqueTag, szTag);
2849 13 : int nCountUnique = 2;
2850 26 : while(oSpecialMD.GetMetadataItem( szUniqueTag, "TRE") != NULL)
2851 : {
2852 0 : sprintf(szUniqueTag, "%s_%d", szTag, nCountUnique);
2853 0 : nCountUnique ++;
2854 : }
2855 13 : oSpecialMD.SetMetadataItem( szUniqueTag, pszEscapedData, "TRE" );
2856 13 : CPLFree( pszEscapedData );
2857 :
2858 13 : nTREBytes -= (nThisTRESize + 11);
2859 13 : pszTREData += (nThisTRESize + 11);
2860 : }
2861 : }
2862 :
2863 : /* -------------------------------------------------------------------- */
2864 : /* Loop over TRE in DES */
2865 : /* -------------------------------------------------------------------- */
2866 : int iSegment;
2867 31 : for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
2868 : {
2869 19 : NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
2870 : NITFDES *psDES;
2871 19 : int nOffset = 0;
2872 : char szTREName[7];
2873 : int nThisTRESize;
2874 :
2875 19 : if( !EQUAL(psSegInfo->szSegmentType,"DE") )
2876 17 : continue;
2877 :
2878 2 : psDES = NITFDESAccess( psFile, iSegment );
2879 2 : if( psDES == NULL )
2880 0 : continue;
2881 :
2882 2 : char* pabyTREData = NULL;
2883 2 : nOffset = 0;
2884 8 : while (NITFDESGetTRE( psDES, nOffset, szTREName, &pabyTREData, &nThisTRESize))
2885 : {
2886 : char* pszEscapedData = CPLEscapeString( pabyTREData, nThisTRESize,
2887 4 : CPLES_BackslashQuotable );
2888 4 : if (pszEscapedData == NULL)
2889 : {
2890 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
2891 0 : NITFDESFreeTREData(pabyTREData);
2892 0 : NITFDESDeaccess(psDES);
2893 0 : return;
2894 : }
2895 :
2896 : // trim white off tag.
2897 8 : while( strlen(szTREName) > 0 && szTREName[strlen(szTREName)-1] == ' ' )
2898 0 : szTREName[strlen(szTREName)-1] = '\0';
2899 :
2900 4 : CPLXMLNode* psTreNode = NITFCreateXMLTre(psFile, szTREName, pabyTREData,nThisTRESize);
2901 4 : if (psTreNode)
2902 : {
2903 4 : const char* pszDESID = CSLFetchNameValue(psDES->papszMetadata, "NITF_DESID");
2904 : CPLCreateXMLNode(CPLCreateXMLNode(psTreNode, CXT_Attribute, "location"),
2905 4 : CXT_Text, pszDESID ? CPLSPrintf("des %s", pszDESID) : "des");
2906 4 : CPLAddXMLChild(psTresNode, psTreNode);
2907 : }
2908 :
2909 : char szUniqueTag[32];
2910 4 : strcpy(szUniqueTag, szTREName);
2911 4 : int nCountUnique = 2;
2912 8 : while(oSpecialMD.GetMetadataItem( szUniqueTag, "TRE") != NULL)
2913 : {
2914 0 : sprintf(szUniqueTag, "%s_%d", szTREName, nCountUnique);
2915 0 : nCountUnique ++;
2916 : }
2917 4 : oSpecialMD.SetMetadataItem( szUniqueTag, pszEscapedData, "TRE" );
2918 :
2919 4 : CPLFree(pszEscapedData);
2920 :
2921 4 : nOffset += 11 + nThisTRESize;
2922 :
2923 4 : NITFDESFreeTREData(pabyTREData);
2924 : }
2925 :
2926 2 : NITFDESDeaccess(psDES);
2927 : }
2928 :
2929 12 : if (psTresNode->psChild != NULL)
2930 : {
2931 7 : char* pszXML = CPLSerializeXMLTree(psTresNode);
2932 : char* apszMD[2];
2933 7 : apszMD[0] = pszXML;
2934 7 : apszMD[1] = NULL;
2935 7 : oSpecialMD.SetMetadata( apszMD, "xml:TRE" );
2936 7 : CPLFree(pszXML);
2937 : }
2938 12 : CPLDestroyXMLNode(psTresNode);
2939 : }
2940 :
2941 : /************************************************************************/
2942 : /* GetMetadata() */
2943 : /************************************************************************/
2944 :
2945 136 : char **NITFDataset::GetMetadata( const char * pszDomain )
2946 :
2947 : {
2948 136 : if( pszDomain != NULL && EQUAL(pszDomain,"NITF_METADATA") )
2949 : {
2950 : // InitializeNITFMetadata retrieves the NITF file header and all image segment file headers. (NOTE: The returned strings are base64-encoded).
2951 :
2952 1 : InitializeNITFMetadata();
2953 1 : return oSpecialMD.GetMetadata( pszDomain );
2954 : }
2955 :
2956 : #ifdef ESRI_BUILD
2957 : if( pszDomain != NULL && EQUAL(pszDomain,"NITF_DES") )
2958 : {
2959 : // InitializeNITFDESs retrieves all the DES file headers (NOTE: The returned strings are base64-encoded).
2960 :
2961 : InitializeNITFDESs();
2962 : return oSpecialMD.GetMetadata( pszDomain );
2963 : }
2964 :
2965 : if( pszDomain != NULL && EQUAL(pszDomain,"NITF_DES_METADATA") )
2966 : {
2967 : // InitializeNITFDESs retrieves all the DES file headers (NOTE: The returned strings are base64-encoded).
2968 :
2969 : InitializeNITFDESMetadata();
2970 : return oSpecialMD.GetMetadata( pszDomain );
2971 : }
2972 :
2973 : if( pszDomain != NULL && EQUAL(pszDomain,"NITF_FILE_HEADER_TRES") )
2974 : {
2975 : // InitializeNITFTREs retrieves all the TREs that are resides in the NITF file header and all the
2976 : // TREs that are resides in the current image segment.
2977 : // NOTE: the returned strings are backslash-escaped
2978 :
2979 : InitializeNITFTREs();
2980 : return oSpecialMD.GetMetadata( pszDomain );
2981 : }
2982 :
2983 : if( pszDomain != NULL && EQUAL(pszDomain,"NITF_IMAGE_SEGMENT_TRES") )
2984 : {
2985 : // InitializeNITFTREs retrieves all the TREs that are resides in the NITF file header and all the
2986 : // TREs that are resides in the current image segment.
2987 : // NOTE: the returned strings are backslash-escaped
2988 :
2989 : InitializeNITFTREs();
2990 : return oSpecialMD.GetMetadata( pszDomain );
2991 : }
2992 : #endif
2993 :
2994 135 : if( pszDomain != NULL && EQUAL(pszDomain,"CGM") )
2995 : {
2996 10 : InitializeCGMMetadata();
2997 10 : return oSpecialMD.GetMetadata( pszDomain );
2998 : }
2999 :
3000 125 : if( pszDomain != NULL && EQUAL(pszDomain,"TEXT") )
3001 : {
3002 11 : InitializeTextMetadata();
3003 11 : return oSpecialMD.GetMetadata( pszDomain );
3004 : }
3005 :
3006 114 : if( pszDomain != NULL && EQUAL(pszDomain,"TRE") )
3007 : {
3008 10 : InitializeTREMetadata();
3009 10 : return oSpecialMD.GetMetadata( pszDomain );
3010 : }
3011 :
3012 104 : if( pszDomain != NULL && EQUAL(pszDomain,"xml:TRE") )
3013 : {
3014 3 : InitializeTREMetadata();
3015 3 : return oSpecialMD.GetMetadata( pszDomain );
3016 : }
3017 :
3018 101 : return GDALPamDataset::GetMetadata( pszDomain );
3019 : }
3020 :
3021 : /************************************************************************/
3022 : /* GetMetadataItem() */
3023 : /************************************************************************/
3024 :
3025 1842 : const char *NITFDataset::GetMetadataItem(const char * pszName,
3026 : const char * pszDomain )
3027 :
3028 : {
3029 1842 : if( pszDomain != NULL && EQUAL(pszDomain,"NITF_METADATA") )
3030 : {
3031 : // InitializeNITFMetadata retrieves the NITF file header and all image segment file headers. (NOTE: The returned strings are base64-encoded).
3032 :
3033 1 : InitializeNITFMetadata();
3034 1 : return oSpecialMD.GetMetadataItem( pszName, pszDomain );
3035 : }
3036 :
3037 : #ifdef ESRI_BUILD
3038 : if( pszDomain != NULL && EQUAL(pszDomain,"NITF_DES_METADATA") )
3039 : {
3040 : // InitializeNITFDESs retrieves all the DES file headers (NOTE: The returned strings are base64-encoded).
3041 :
3042 : InitializeNITFDESMetadata();
3043 : return oSpecialMD.GetMetadataItem( pszName, pszDomain );
3044 : }
3045 :
3046 : if( pszDomain != NULL && EQUAL(pszDomain,"NITF_FILE_HEADER_TRES") )
3047 : {
3048 : // InitializeNITFTREs retrieves all the TREs that are resides in the NITF file header and all the
3049 : // TREs that are resides in the current image segment.
3050 : // NOTE: the returned strings are backslash-escaped
3051 :
3052 : InitializeNITFTREs();
3053 : return oSpecialMD.GetMetadataItem( pszName, pszDomain );
3054 : }
3055 :
3056 : if( pszDomain != NULL && EQUAL(pszDomain,"NITF_IMAGE_SEGMENT_TRES") )
3057 : {
3058 : // InitializeNITFTREs retrieves all the TREs that are resides in the NITF file header and all the
3059 : // TREs that are resides in the current image segment.
3060 : // NOTE: the returned strings are backslash-escaped
3061 :
3062 : InitializeNITFTREs();
3063 : return oSpecialMD.GetMetadataItem( pszName, pszDomain );
3064 : }
3065 : #endif
3066 :
3067 1841 : if( pszDomain != NULL && EQUAL(pszDomain,"CGM") )
3068 : {
3069 1 : InitializeCGMMetadata();
3070 1 : return oSpecialMD.GetMetadataItem( pszName, pszDomain );
3071 : }
3072 :
3073 1840 : if( pszDomain != NULL && EQUAL(pszDomain,"TEXT") )
3074 : {
3075 1 : InitializeTextMetadata();
3076 1 : return oSpecialMD.GetMetadataItem( pszName, pszDomain );
3077 : }
3078 :
3079 1839 : if( pszDomain != NULL && EQUAL(pszDomain,"TRE") )
3080 : {
3081 2 : InitializeTREMetadata();
3082 2 : return oSpecialMD.GetMetadataItem( pszName, pszDomain );
3083 : }
3084 :
3085 1837 : if( pszDomain != NULL && EQUAL(pszDomain,"OVERVIEWS")
3086 : && osRSetVRT.size() > 0 )
3087 2 : return osRSetVRT;
3088 :
3089 1835 : return GDALPamDataset::GetMetadataItem( pszName, pszDomain );
3090 : }
3091 :
3092 :
3093 : /************************************************************************/
3094 : /* GetGCPCount() */
3095 : /************************************************************************/
3096 :
3097 14 : int NITFDataset::GetGCPCount()
3098 :
3099 : {
3100 14 : return nGCPCount;
3101 : }
3102 :
3103 : /************************************************************************/
3104 : /* GetGCPProjection() */
3105 : /************************************************************************/
3106 :
3107 5 : const char *NITFDataset::GetGCPProjection()
3108 :
3109 : {
3110 5 : if( nGCPCount > 0 && pszGCPProjection != NULL )
3111 2 : return pszGCPProjection;
3112 : else
3113 3 : return "";
3114 : }
3115 :
3116 : /************************************************************************/
3117 : /* GetGCP() */
3118 : /************************************************************************/
3119 :
3120 3 : const GDAL_GCP *NITFDataset::GetGCPs()
3121 :
3122 : {
3123 3 : return pasGCPList;
3124 : }
3125 :
3126 : /************************************************************************/
3127 : /* CheckForRSets() */
3128 : /* */
3129 : /* Check for reduced resolution images in .r<n> files and if */
3130 : /* found return filename for a virtual file wrapping them as an */
3131 : /* overview file. (#3457) */
3132 : /************************************************************************/
3133 :
3134 556 : int NITFDataset::CheckForRSets( const char *pszNITFFilename )
3135 :
3136 : {
3137 556 : bool isR0File = EQUAL(CPLGetExtension(pszNITFFilename),"r0");
3138 :
3139 : /* -------------------------------------------------------------------- */
3140 : /* Check to see if we have RSets. */
3141 : /* -------------------------------------------------------------------- */
3142 556 : std::vector<CPLString> aosRSetFilenames;
3143 : int i;
3144 :
3145 556 : for( i = 1; i <= 5; i++ )
3146 : {
3147 562 : CPLString osTarget;
3148 : VSIStatBufL sStat;
3149 :
3150 562 : if ( isR0File )
3151 : {
3152 9 : osTarget = pszNITFFilename;
3153 9 : osTarget[osTarget.size()-1] = (char) ('0' + i );
3154 : }
3155 : else
3156 553 : osTarget.Printf( "%s.r%d", pszNITFFilename, i );
3157 :
3158 562 : if( VSIStatL( osTarget, &sStat ) != 0 )
3159 : break;
3160 :
3161 6 : aosRSetFilenames.push_back( osTarget );
3162 : }
3163 :
3164 556 : if( aosRSetFilenames.size() == 0 )
3165 553 : return FALSE;
3166 :
3167 : /* -------------------------------------------------------------------- */
3168 : /* We do, so try to create a wrapping VRT file. */
3169 : /* -------------------------------------------------------------------- */
3170 3 : CPLString osFragment;
3171 : int iBand;
3172 :
3173 : osRSetVRT.Printf( "<VRTDataset rasterXSize=\"%d\" rasterYSize=\"%d\">\n",
3174 3 : GetRasterXSize()/2, GetRasterYSize()/2 );
3175 :
3176 12 : for( iBand = 0; iBand < GetRasterCount(); iBand++ )
3177 : {
3178 9 : GDALRasterBand *poBand = GetRasterBand(iBand+1);
3179 :
3180 : osRSetVRT += osFragment.
3181 : Printf( " <VRTRasterBand dataType=\"%s\" band=\"%d\">\n",
3182 : GDALGetDataTypeName( poBand->GetRasterDataType() ),
3183 9 : iBand+1 );
3184 :
3185 27 : for( i = 0; i < (int) aosRSetFilenames.size(); i++ )
3186 : {
3187 18 : char* pszEscaped = CPLEscapeString(aosRSetFilenames[i].c_str(), -1, CPLES_XML);
3188 18 : if( i == 0 )
3189 : osRSetVRT += osFragment.Printf(
3190 : " <SimpleSource><SourceFilename>%s</SourceFilename><SourceBand>%d</SourceBand></SimpleSource>\n",
3191 9 : pszEscaped, iBand+1 );
3192 : else
3193 : osRSetVRT += osFragment.Printf(
3194 : " <Overview><SourceFilename>%s</SourceFilename><SourceBand>%d</SourceBand></Overview>\n",
3195 9 : pszEscaped, iBand+1 );
3196 18 : CPLFree(pszEscaped);
3197 : }
3198 : osRSetVRT += osFragment.
3199 9 : Printf( " </VRTRasterBand>\n" );
3200 : }
3201 :
3202 3 : osRSetVRT += "</VRTDataset>\n";
3203 :
3204 3 : return TRUE;
3205 : }
3206 :
3207 : /************************************************************************/
3208 : /* IBuildOverviews() */
3209 : /************************************************************************/
3210 :
3211 4 : CPLErr NITFDataset::IBuildOverviews( const char *pszResampling,
3212 : int nOverviews, int *panOverviewList,
3213 : int nListBands, int *panBandList,
3214 : GDALProgressFunc pfnProgress,
3215 : void * pProgressData )
3216 :
3217 : {
3218 : /* -------------------------------------------------------------------- */
3219 : /* If we have been using RSets we will need to clear them first. */
3220 : /* -------------------------------------------------------------------- */
3221 4 : if( osRSetVRT.size() > 0 )
3222 : {
3223 1 : oOvManager.CleanOverviews();
3224 1 : osRSetVRT = "";
3225 : }
3226 :
3227 4 : bExposeUnderlyingJPEGDatasetOverviews = FALSE;
3228 :
3229 : /* -------------------------------------------------------------------- */
3230 : /* If we have an underlying JPEG2000 dataset (hopefully via */
3231 : /* JP2KAK) we will try and build zero overviews as a way of */
3232 : /* tricking it into clearing existing overviews-from-jpeg2000. */
3233 : /* -------------------------------------------------------------------- */
3234 5 : if( poJ2KDataset != NULL
3235 1 : && !poJ2KDataset->GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS" ) )
3236 : poJ2KDataset->IBuildOverviews( pszResampling, 0, NULL,
3237 : nListBands, panBandList,
3238 1 : GDALDummyProgress, NULL );
3239 :
3240 : /* -------------------------------------------------------------------- */
3241 : /* Use the overview manager to build requested overviews. */
3242 : /* -------------------------------------------------------------------- */
3243 : CPLErr eErr = GDALPamDataset::IBuildOverviews( pszResampling,
3244 : nOverviews, panOverviewList,
3245 : nListBands, panBandList,
3246 4 : pfnProgress, pProgressData );
3247 :
3248 : /* -------------------------------------------------------------------- */
3249 : /* If we are working with jpeg or jpeg2000, let the underlying */
3250 : /* dataset know about the overview file. */
3251 : /* -------------------------------------------------------------------- */
3252 4 : GDALDataset *poSubDS = poJ2KDataset;
3253 4 : if( poJPEGDataset )
3254 1 : poSubDS = poJPEGDataset;
3255 :
3256 : const char *pszOverviewFile =
3257 4 : GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS" );
3258 :
3259 6 : if( poSubDS && pszOverviewFile != NULL && eErr == CE_None
3260 2 : && poSubDS->GetMetadataItem( "OVERVIEW_FILE", "OVERVIEWS") == NULL )
3261 : {
3262 : poSubDS->SetMetadataItem( "OVERVIEW_FILE",
3263 : pszOverviewFile,
3264 2 : "OVERVIEWS" );
3265 : }
3266 :
3267 4 : return eErr;
3268 : }
3269 :
3270 : /************************************************************************/
3271 : /* ScanJPEGQLevel() */
3272 : /* */
3273 : /* Search the NITF APP header in the jpeg data stream to find */
3274 : /* out what predefined Q level tables should be used (or -1 if */
3275 : /* they are inline). */
3276 : /************************************************************************/
3277 :
3278 45 : int NITFDataset::ScanJPEGQLevel( GUIntBig *pnDataStart )
3279 :
3280 : {
3281 : GByte abyHeader[100];
3282 :
3283 45 : if( VSIFSeekL( psFile->fp, *pnDataStart,
3284 : SEEK_SET ) != 0 )
3285 : {
3286 : CPLError( CE_Failure, CPLE_FileIO,
3287 0 : "Seek error to jpeg data stream." );
3288 0 : return 0;
3289 : }
3290 :
3291 45 : if( VSIFReadL( abyHeader, 1, sizeof(abyHeader), psFile->fp )
3292 : < sizeof(abyHeader) )
3293 : {
3294 : CPLError( CE_Failure, CPLE_FileIO,
3295 0 : "Read error to jpeg data stream." );
3296 0 : return 0;
3297 : }
3298 :
3299 : /* -------------------------------------------------------------------- */
3300 : /* Scan ahead for jpeg magic code. In some files (eg. NSIF) */
3301 : /* there seems to be some extra junk before the image data stream. */
3302 : /* -------------------------------------------------------------------- */
3303 45 : GUInt32 nOffset = 0;
3304 225 : while( nOffset < sizeof(abyHeader) - 23
3305 45 : && (abyHeader[nOffset+0] != 0xff
3306 45 : || abyHeader[nOffset+1] != 0xd8
3307 45 : || abyHeader[nOffset+2] != 0xff) )
3308 0 : nOffset++;
3309 :
3310 45 : if( nOffset >= sizeof(abyHeader) - 23 )
3311 0 : return 0;
3312 :
3313 45 : *pnDataStart += nOffset;
3314 :
3315 45 : if( nOffset > 0 )
3316 : CPLDebug( "NITF",
3317 : "JPEG data stream at offset %d from start of data segement, NSIF?",
3318 0 : nOffset );
3319 :
3320 : /* -------------------------------------------------------------------- */
3321 : /* Do we have an NITF app tag? If so, pull out the Q level. */
3322 : /* -------------------------------------------------------------------- */
3323 45 : if( !EQUAL((char *)abyHeader+nOffset+6,"NITF") )
3324 25 : return 0;
3325 :
3326 20 : return abyHeader[22+nOffset];
3327 : }
3328 :
3329 : /************************************************************************/
3330 : /* ScanJPEGBlocks() */
3331 : /************************************************************************/
3332 :
3333 3 : CPLErr NITFDataset::ScanJPEGBlocks()
3334 :
3335 : {
3336 : int iBlock;
3337 : GUIntBig nJPEGStart =
3338 3 : psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart;
3339 :
3340 3 : nQLevel = ScanJPEGQLevel( &nJPEGStart );
3341 :
3342 : /* -------------------------------------------------------------------- */
3343 : /* Allocate offset array */
3344 : /* -------------------------------------------------------------------- */
3345 : panJPEGBlockOffset = (GIntBig *)
3346 : VSICalloc(sizeof(GIntBig),
3347 3 : psImage->nBlocksPerRow*psImage->nBlocksPerColumn);
3348 3 : if (panJPEGBlockOffset == NULL)
3349 : {
3350 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
3351 0 : return CE_Failure;
3352 : }
3353 3 : panJPEGBlockOffset[0] = nJPEGStart;
3354 :
3355 3 : if ( psImage->nBlocksPerRow * psImage->nBlocksPerColumn == 1)
3356 0 : return CE_None;
3357 :
3358 26 : for( iBlock = psImage->nBlocksPerRow * psImage->nBlocksPerColumn - 1;
3359 : iBlock > 0; iBlock-- )
3360 23 : panJPEGBlockOffset[iBlock] = -1;
3361 :
3362 : /* -------------------------------------------------------------------- */
3363 : /* Scan through the whole image data stream identifying all */
3364 : /* block boundaries. Each block starts with 0xFFD8 (SOI). */
3365 : /* They also end with 0xFFD9, but we don't currently look for */
3366 : /* that. */
3367 : /* -------------------------------------------------------------------- */
3368 3 : int iNextBlock = 1;
3369 3 : GIntBig iSegOffset = 2;
3370 3 : GIntBig iSegSize = psFile->pasSegmentInfo[psImage->iSegment].nSegmentSize
3371 3 : - (nJPEGStart - psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart);
3372 : GByte abyBlock[512];
3373 3 : int ignoreBytes = 0;
3374 :
3375 3587 : while( iSegOffset < iSegSize-1 )
3376 : {
3377 3584 : size_t nReadSize = MIN((size_t)sizeof(abyBlock),(size_t)(iSegSize - iSegOffset));
3378 : size_t i;
3379 :
3380 3584 : if( VSIFSeekL( psFile->fp, panJPEGBlockOffset[0] + iSegOffset,
3381 : SEEK_SET ) != 0 )
3382 : {
3383 : CPLError( CE_Failure, CPLE_FileIO,
3384 0 : "Seek error to jpeg data stream." );
3385 0 : return CE_Failure;
3386 : }
3387 :
3388 3584 : if( VSIFReadL( abyBlock, 1, nReadSize, psFile->fp ) < (size_t)nReadSize)
3389 : {
3390 : CPLError( CE_Failure, CPLE_FileIO,
3391 0 : "Read error to jpeg data stream." );
3392 0 : return CE_Failure;
3393 : }
3394 :
3395 1834360 : for( i = 0; i < nReadSize-1; i++ )
3396 : {
3397 1830779 : if (ignoreBytes == 0)
3398 : {
3399 1830582 : if( abyBlock[i] == 0xff )
3400 : {
3401 : /* start-of-image marker */
3402 9984 : if ( abyBlock[i+1] == 0xd8 )
3403 : {
3404 46 : panJPEGBlockOffset[iNextBlock++]
3405 23 : = panJPEGBlockOffset[0] + iSegOffset + i;
3406 :
3407 23 : if( iNextBlock == psImage->nBlocksPerRow*psImage->nBlocksPerColumn)
3408 : {
3409 3 : return CE_None;
3410 : }
3411 : }
3412 : /* Skip application-specific data to avoid false positive while detecting */
3413 : /* start-of-image markers (#2927). The size of the application data is */
3414 : /* found in the two following bytes */
3415 : /* We need this complex mechanism of ignoreBytes for dealing with */
3416 : /* application data crossing several abyBlock ... */
3417 9961 : else if ( abyBlock[i+1] >= 0xe0 && abyBlock[i+1] < 0xf0 )
3418 : {
3419 7 : ignoreBytes = -2;
3420 : }
3421 : }
3422 : }
3423 197 : else if (ignoreBytes < 0)
3424 : {
3425 14 : if (ignoreBytes == -1)
3426 : {
3427 : /* Size of the application data */
3428 7 : ignoreBytes = abyBlock[i]*256 + abyBlock[i+1];
3429 : }
3430 : else
3431 7 : ignoreBytes++;
3432 : }
3433 : else
3434 : {
3435 183 : ignoreBytes--;
3436 : }
3437 : }
3438 :
3439 3581 : iSegOffset += nReadSize - 1;
3440 : }
3441 :
3442 0 : return CE_None;
3443 : }
3444 :
3445 : /************************************************************************/
3446 : /* ReadJPEGBlock() */
3447 : /************************************************************************/
3448 :
3449 55 : CPLErr NITFDataset::ReadJPEGBlock( int iBlockX, int iBlockY )
3450 :
3451 : {
3452 : CPLErr eErr;
3453 :
3454 : /* -------------------------------------------------------------------- */
3455 : /* If this is our first request, do a scan for block boundaries. */
3456 : /* -------------------------------------------------------------------- */
3457 55 : if( panJPEGBlockOffset == NULL )
3458 : {
3459 5 : if (EQUAL(psImage->szIC,"M3"))
3460 : {
3461 : /* -------------------------------------------------------------------- */
3462 : /* When a data mask subheader is present, we don't need to scan */
3463 : /* the whole file. We just use the psImage->panBlockStart table */
3464 : /* -------------------------------------------------------------------- */
3465 : panJPEGBlockOffset = (GIntBig *)
3466 : VSICalloc(sizeof(GIntBig),
3467 2 : psImage->nBlocksPerRow*psImage->nBlocksPerColumn);
3468 2 : if (panJPEGBlockOffset == NULL)
3469 : {
3470 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
3471 0 : return CE_Failure;
3472 : }
3473 : int i;
3474 31 : for (i=0;i< psImage->nBlocksPerRow*psImage->nBlocksPerColumn;i++)
3475 : {
3476 29 : panJPEGBlockOffset[i] = psImage->panBlockStart[i];
3477 29 : if (panJPEGBlockOffset[i] != -1 && panJPEGBlockOffset[i] != 0xffffffff)
3478 : {
3479 25 : GUIntBig nOffset = panJPEGBlockOffset[i];
3480 25 : nQLevel = ScanJPEGQLevel(&nOffset);
3481 : /* The beginning of the JPEG stream should be the offset */
3482 : /* from the panBlockStart table */
3483 25 : if (nOffset != (GUIntBig)panJPEGBlockOffset[i])
3484 : {
3485 : CPLError(CE_Failure, CPLE_AppDefined,
3486 0 : "JPEG block doesn't start at expected offset");
3487 0 : return CE_Failure;
3488 : }
3489 : }
3490 : }
3491 : }
3492 : else /* 'C3' case */
3493 : {
3494 : /* -------------------------------------------------------------------- */
3495 : /* Scan through the whole image data stream identifying all */
3496 : /* block boundaries. */
3497 : /* -------------------------------------------------------------------- */
3498 3 : eErr = ScanJPEGBlocks();
3499 3 : if( eErr != CE_None )
3500 0 : return eErr;
3501 : }
3502 : }
3503 :
3504 : /* -------------------------------------------------------------------- */
3505 : /* Allocate image data block (where the uncompressed image will go) */
3506 : /* -------------------------------------------------------------------- */
3507 55 : if( pabyJPEGBlock == NULL )
3508 : {
3509 : /* Allocate enough memory to hold 12bit JPEG data */
3510 : pabyJPEGBlock = (GByte *)
3511 : VSICalloc(psImage->nBands,
3512 5 : psImage->nBlockWidth * psImage->nBlockHeight * 2);
3513 5 : if (pabyJPEGBlock == NULL)
3514 : {
3515 0 : CPLError( CE_Failure, CPLE_OutOfMemory, "Out of memory");
3516 0 : return CE_Failure;
3517 : }
3518 : }
3519 :
3520 :
3521 : /* -------------------------------------------------------------------- */
3522 : /* Read JPEG Chunk. */
3523 : /* -------------------------------------------------------------------- */
3524 55 : CPLString osFilename;
3525 55 : int iBlock = iBlockX + iBlockY * psImage->nBlocksPerRow;
3526 : GDALDataset *poDS;
3527 55 : int anBands[3] = { 1, 2, 3 };
3528 :
3529 55 : if (panJPEGBlockOffset[iBlock] == -1 || panJPEGBlockOffset[iBlock] == 0xffffffff)
3530 : {
3531 4 : memset(pabyJPEGBlock, 0, psImage->nBands*psImage->nBlockWidth*psImage->nBlockHeight*2);
3532 4 : return CE_None;
3533 : }
3534 :
3535 : osFilename.Printf( "JPEG_SUBFILE:Q%d," CPL_FRMT_GIB ",%d,%s",
3536 : nQLevel,
3537 : panJPEGBlockOffset[iBlock], 0,
3538 51 : osNITFFilename.c_str() );
3539 :
3540 51 : poDS = (GDALDataset *) GDALOpen( osFilename, GA_ReadOnly );
3541 51 : if( poDS == NULL )
3542 0 : return CE_Failure;
3543 :
3544 51 : if( poDS->GetRasterXSize() != psImage->nBlockWidth
3545 : || poDS->GetRasterYSize() != psImage->nBlockHeight )
3546 : {
3547 : CPLError( CE_Failure, CPLE_AppDefined,
3548 : "JPEG block %d not same size as NITF blocksize.",
3549 0 : iBlock );
3550 0 : delete poDS;
3551 0 : return CE_Failure;
3552 : }
3553 :
3554 51 : if( poDS->GetRasterCount() < psImage->nBands )
3555 : {
3556 : CPLError( CE_Failure, CPLE_AppDefined,
3557 : "JPEG block %d has not enough bands.",
3558 0 : iBlock );
3559 0 : delete poDS;
3560 0 : return CE_Failure;
3561 : }
3562 :
3563 51 : if( poDS->GetRasterBand(1)->GetRasterDataType() != GetRasterBand(1)->GetRasterDataType())
3564 : {
3565 : CPLError( CE_Failure, CPLE_AppDefined,
3566 : "JPEG block %d data type (%s) not consistant with band data type (%s).",
3567 : iBlock, GDALGetDataTypeName(poDS->GetRasterBand(1)->GetRasterDataType()),
3568 0 : GDALGetDataTypeName(GetRasterBand(1)->GetRasterDataType()) );
3569 0 : delete poDS;
3570 0 : return CE_Failure;
3571 : }
3572 :
3573 : eErr = poDS->RasterIO( GF_Read,
3574 : 0, 0,
3575 : psImage->nBlockWidth, psImage->nBlockHeight,
3576 : pabyJPEGBlock,
3577 : psImage->nBlockWidth, psImage->nBlockHeight,
3578 51 : GetRasterBand(1)->GetRasterDataType(), psImage->nBands, anBands, 0, 0, 0 );
3579 :
3580 51 : delete poDS;
3581 :
3582 51 : return eErr;
3583 : }
3584 :
3585 : /************************************************************************/
3586 : /* GetFileList() */
3587 : /************************************************************************/
3588 :
3589 114 : char **NITFDataset::GetFileList()
3590 :
3591 : {
3592 114 : char **papszFileList = GDALPamDataset::GetFileList();
3593 :
3594 : /* -------------------------------------------------------------------- */
3595 : /* Check for .imd file. */
3596 : /* -------------------------------------------------------------------- */
3597 114 : papszFileList = AddFile( papszFileList, "IMD", "imd" );
3598 :
3599 : /* -------------------------------------------------------------------- */
3600 : /* Check for .rpb file. */
3601 : /* -------------------------------------------------------------------- */
3602 114 : papszFileList = AddFile( papszFileList, "RPB", "rpb" );
3603 :
3604 : /* -------------------------------------------------------------------- */
3605 : /* Check for other files. */
3606 : /* -------------------------------------------------------------------- */
3607 114 : papszFileList = AddFile( papszFileList, "ATT", "att" );
3608 114 : papszFileList = AddFile( papszFileList, "EPH", "eph" );
3609 114 : papszFileList = AddFile( papszFileList, "GEO", "geo" );
3610 114 : papszFileList = AddFile( papszFileList, "XML", "xml" );
3611 :
3612 114 : return papszFileList;
3613 : }
3614 :
3615 : /************************************************************************/
3616 : /* AddFile() */
3617 : /* */
3618 : /* Helper method for GetFileList() */
3619 : /************************************************************************/
3620 684 : char **NITFDataset::AddFile(char **papszFileList, const char* EXTENSION, const char* extension)
3621 : {
3622 : VSIStatBufL sStatBuf;
3623 684 : CPLString osTarget = CPLResetExtension( osNITFFilename, EXTENSION );
3624 684 : if( VSIStatL( osTarget, &sStatBuf ) == 0 )
3625 0 : papszFileList = CSLAddString( papszFileList, osTarget );
3626 : else
3627 : {
3628 684 : osTarget = CPLResetExtension( osNITFFilename, extension );
3629 684 : if( VSIStatL( osTarget, &sStatBuf ) == 0 )
3630 0 : papszFileList = CSLAddString( papszFileList, osTarget );
3631 : }
3632 :
3633 684 : return papszFileList;
3634 : }
3635 :
3636 : /************************************************************************/
3637 : /* GDALToNITFDataType() */
3638 : /************************************************************************/
3639 :
3640 234 : static const char *GDALToNITFDataType( GDALDataType eType )
3641 :
3642 : {
3643 : const char *pszPVType;
3644 :
3645 234 : switch( eType )
3646 : {
3647 : case GDT_Byte:
3648 : case GDT_UInt16:
3649 : case GDT_UInt32:
3650 199 : pszPVType = "INT";
3651 199 : break;
3652 :
3653 : case GDT_Int16:
3654 : case GDT_Int32:
3655 12 : pszPVType = "SI";
3656 12 : break;
3657 :
3658 : case GDT_Float32:
3659 : case GDT_Float64:
3660 10 : pszPVType = "R";
3661 10 : break;
3662 :
3663 : case GDT_CInt16:
3664 : case GDT_CInt32:
3665 : CPLError( CE_Failure, CPLE_AppDefined,
3666 6 : "NITF format does not support complex integer data." );
3667 6 : return NULL;
3668 :
3669 : case GDT_CFloat32:
3670 4 : pszPVType = "C";
3671 4 : break;
3672 :
3673 : default:
3674 : CPLError( CE_Failure, CPLE_AppDefined,
3675 : "Unsupported raster pixel type (%s).",
3676 3 : GDALGetDataTypeName(eType) );
3677 3 : return NULL;
3678 : }
3679 :
3680 225 : return pszPVType;
3681 : }
3682 :
3683 : /************************************************************************/
3684 : /* NITFJP2Options() */
3685 : /* */
3686 : /* Prepare JP2-in-NITF creation options based in part of the */
3687 : /* NITF creation options. */
3688 : /************************************************************************/
3689 :
3690 3 : static char **NITFJP2Options( char **papszOptions )
3691 :
3692 : {
3693 : int i;
3694 3 : char** papszJP2Options = NULL;
3695 :
3696 3 : papszJP2Options = CSLAddString(papszJP2Options, "PROFILE=NPJE");
3697 3 : papszJP2Options = CSLAddString(papszJP2Options, "CODESTREAM_ONLY=TRUE");
3698 :
3699 10 : for( i = 0; papszOptions != NULL && papszOptions[i] != NULL; i++ )
3700 : {
3701 7 : if( EQUALN(papszOptions[i],"PROFILE=",8) )
3702 : {
3703 0 : CPLFree(papszJP2Options[0]);
3704 0 : papszJP2Options[0] = CPLStrdup(papszOptions[i]);
3705 : }
3706 7 : else if( EQUALN(papszOptions[i],"TARGET=",7) )
3707 3 : papszJP2Options = CSLAddString(papszJP2Options, papszOptions[i]);
3708 : }
3709 :
3710 3 : return papszJP2Options;
3711 : }
3712 :
3713 :
3714 :
3715 : /************************************************************************/
3716 : /* NITFExtractTEXTAndCGMCreationOption() */
3717 : /************************************************************************/
3718 :
3719 228 : static char** NITFExtractTEXTAndCGMCreationOption( GDALDataset* poSrcDS,
3720 : char **papszOptions,
3721 : char ***ppapszTextMD,
3722 : char ***ppapszCgmMD )
3723 : {
3724 228 : char** papszFullOptions = CSLDuplicate(papszOptions);
3725 :
3726 : /* -------------------------------------------------------------------- */
3727 : /* Prepare for text segments. */
3728 : /* -------------------------------------------------------------------- */
3729 228 : int iOpt, nNUMT = 0;
3730 228 : char **papszTextMD = CSLFetchNameValueMultiple (papszOptions, "TEXT");
3731 : // Notice: CSLFetchNameValueMultiple remove the leading "TEXT=" when
3732 : // returning the list, which is what we want.
3733 :
3734 : // Use TEXT information from original image if no creation option is passed in.
3735 228 : if (poSrcDS != NULL && papszTextMD == NULL)
3736 : {
3737 : // Read CGM adata from original image, duplicate the list becuase
3738 : // we frees papszCgmMD at end of the function.
3739 59 : papszTextMD = CSLDuplicate( poSrcDS->GetMetadata( "TEXT" ));
3740 : }
3741 :
3742 248 : for( iOpt = 0;
3743 12 : papszTextMD != NULL && papszTextMD[iOpt] != NULL;
3744 : iOpt++ )
3745 : {
3746 8 : if( !EQUALN(papszTextMD[iOpt],"DATA_",5) )
3747 3 : continue;
3748 :
3749 5 : nNUMT++;
3750 : }
3751 :
3752 228 : if( nNUMT > 0 )
3753 : {
3754 : papszFullOptions = CSLAddString( papszFullOptions,
3755 : CPLString().Printf( "NUMT=%d",
3756 4 : nNUMT ) );
3757 : }
3758 :
3759 : /* -------------------------------------------------------------------- */
3760 : /* Prepare for CGM segments. */
3761 : /* -------------------------------------------------------------------- */
3762 : const char *pszNUMS; // graphic segment option string
3763 228 : int nNUMS = 0;
3764 :
3765 228 : char **papszCgmMD = CSLFetchNameValueMultiple (papszOptions, "CGM");
3766 : // Notice: CSLFetchNameValueMultiple remove the leading "CGM=" when
3767 : // returning the list, which is what we want.
3768 :
3769 : // Use CGM information from original image if no creation option is passed in.
3770 228 : if (poSrcDS != NULL && papszCgmMD == NULL)
3771 : {
3772 : // Read CGM adata from original image, duplicate the list becuase
3773 : // we frees papszCgmMD at end of the function.
3774 59 : papszCgmMD = CSLDuplicate( poSrcDS->GetMetadata( "CGM" ));
3775 : }
3776 :
3777 : // Set NUMS based on the number of segments
3778 228 : if (papszCgmMD != NULL)
3779 : {
3780 8 : pszNUMS = CSLFetchNameValue(papszCgmMD, "SEGMENT_COUNT");
3781 :
3782 8 : if (pszNUMS != NULL) {
3783 8 : nNUMS = atoi(pszNUMS);
3784 : }
3785 : papszFullOptions = CSLAddString(papszFullOptions,
3786 8 : CPLString().Printf("NUMS=%d", nNUMS));
3787 : }
3788 :
3789 228 : *ppapszTextMD = papszTextMD;
3790 228 : *ppapszCgmMD = papszCgmMD;
3791 :
3792 228 : return papszFullOptions;
3793 : }
3794 :
3795 : /************************************************************************/
3796 : /* NITFDatasetCreate() */
3797 : /************************************************************************/
3798 :
3799 : GDALDataset *
3800 174 : NITFDataset::NITFDatasetCreate( const char *pszFilename, int nXSize, int nYSize, int nBands,
3801 : GDALDataType eType, char **papszOptions )
3802 :
3803 : {
3804 174 : const char *pszPVType = GDALToNITFDataType( eType );
3805 174 : const char *pszIC = CSLFetchNameValue( papszOptions, "IC" );
3806 :
3807 174 : if( pszPVType == NULL )
3808 6 : return NULL;
3809 :
3810 : /* -------------------------------------------------------------------- */
3811 : /* We disallow any IC value except NC when creating this way. */
3812 : /* -------------------------------------------------------------------- */
3813 168 : GDALDriver *poJ2KDriver = NULL;
3814 :
3815 169 : if( pszIC != NULL && EQUAL(pszIC,"C8") )
3816 : {
3817 1 : int bHasCreate = FALSE;
3818 :
3819 1 : poJ2KDriver = GetGDALDriverManager()->GetDriverByName( "JP2ECW" );
3820 1 : if( poJ2KDriver != NULL )
3821 : bHasCreate = poJ2KDriver->GetMetadataItem( GDAL_DCAP_CREATE,
3822 1 : NULL ) != NULL;
3823 1 : if( !bHasCreate )
3824 : {
3825 : CPLError( CE_Failure, CPLE_AppDefined,
3826 : "Unable to create JPEG2000 encoded NITF files. The\n"
3827 0 : "JP2ECW driver is unavailable, or missing Create support." );
3828 0 : return NULL;
3829 : }
3830 : }
3831 :
3832 167 : else if( pszIC != NULL && !EQUAL(pszIC,"NC") )
3833 : {
3834 : CPLError( CE_Failure, CPLE_AppDefined,
3835 : "Unsupported compression (IC=%s) used in direct\n"
3836 : "NITF File creation",
3837 0 : pszIC );
3838 0 : return NULL;
3839 : }
3840 :
3841 168 : const char* pszSDE_TRE = CSLFetchNameValue(papszOptions, "SDE_TRE");
3842 168 : if (pszSDE_TRE != NULL)
3843 : {
3844 : CPLError( CE_Warning, CPLE_AppDefined,
3845 0 : "SDE_TRE creation option ignored by Create() method (only valid in CreateCopy())" );
3846 : }
3847 :
3848 :
3849 : /* -------------------------------------------------------------------- */
3850 : /* Prepare for text and CGM segments. */
3851 : /* -------------------------------------------------------------------- */
3852 168 : char **papszTextMD = NULL;
3853 168 : char **papszCgmMD = NULL;
3854 : char **papszFullOptions = NITFExtractTEXTAndCGMCreationOption( NULL,
3855 : papszOptions,
3856 : &papszTextMD,
3857 168 : &papszCgmMD );
3858 :
3859 : /* -------------------------------------------------------------------- */
3860 : /* Create the file. */
3861 : /* -------------------------------------------------------------------- */
3862 :
3863 168 : if( !NITFCreate( pszFilename, nXSize, nYSize, nBands,
3864 : GDALGetDataTypeSize( eType ), pszPVType,
3865 : papszFullOptions ) )
3866 : {
3867 9 : CSLDestroy(papszTextMD);
3868 9 : CSLDestroy(papszCgmMD);
3869 9 : CSLDestroy(papszFullOptions);
3870 9 : return NULL;
3871 : }
3872 :
3873 159 : CSLDestroy(papszFullOptions);
3874 159 : papszFullOptions = NULL;
3875 :
3876 : /* -------------------------------------------------------------------- */
3877 : /* Various special hacks related to JPEG2000 encoded files. */
3878 : /* -------------------------------------------------------------------- */
3879 159 : GDALDataset* poWritableJ2KDataset = NULL;
3880 159 : if( poJ2KDriver )
3881 : {
3882 1 : NITFFile *psFile = NITFOpen( pszFilename, TRUE );
3883 1 : if (psFile == NULL)
3884 : {
3885 0 : CSLDestroy(papszTextMD);
3886 0 : CSLDestroy(papszCgmMD);
3887 0 : return NULL;
3888 : }
3889 1 : GUIntBig nImageOffset = psFile->pasSegmentInfo[0].nSegmentStart;
3890 :
3891 1 : CPLString osDSName;
3892 :
3893 1 : osDSName.Printf("/vsisubfile/" CPL_FRMT_GUIB "_%d,%s", nImageOffset, -1, pszFilename);
3894 :
3895 1 : NITFClose( psFile );
3896 :
3897 1 : char** papszJP2Options = NITFJP2Options(papszOptions);
3898 : poWritableJ2KDataset =
3899 : poJ2KDriver->Create( osDSName, nXSize, nYSize, nBands, eType,
3900 1 : papszJP2Options );
3901 1 : CSLDestroy(papszJP2Options);
3902 :
3903 1 : if( poWritableJ2KDataset == NULL )
3904 : {
3905 0 : CSLDestroy(papszTextMD);
3906 0 : CSLDestroy(papszCgmMD);
3907 0 : return NULL;
3908 0 : }
3909 : }
3910 :
3911 : /* -------------------------------------------------------------------- */
3912 : /* Open the dataset in update mode. */
3913 : /* -------------------------------------------------------------------- */
3914 159 : GDALOpenInfo oOpenInfo( pszFilename, GA_Update );
3915 : NITFDataset* poDS = (NITFDataset*)
3916 159 : NITFDataset::OpenInternal(&oOpenInfo, poWritableJ2KDataset, TRUE);
3917 159 : if (poDS)
3918 : {
3919 159 : poDS->papszTextMDToWrite = papszTextMD;
3920 159 : poDS->papszCgmMDToWrite = papszCgmMD;
3921 : }
3922 : else
3923 : {
3924 0 : CSLDestroy(papszTextMD);
3925 0 : CSLDestroy(papszCgmMD);
3926 : }
3927 159 : return poDS;
3928 : }
3929 :
3930 : /************************************************************************/
3931 : /* NITFCreateCopy() */
3932 : /************************************************************************/
3933 :
3934 : GDALDataset *
3935 61 : NITFDataset::NITFCreateCopy(
3936 : const char *pszFilename, GDALDataset *poSrcDS,
3937 : int bStrict, char **papszOptions,
3938 : GDALProgressFunc pfnProgress, void * pProgressData )
3939 :
3940 : {
3941 : GDALDataType eType;
3942 : GDALRasterBand *poBand1;
3943 61 : int bJPEG2000 = FALSE;
3944 61 : int bJPEG = FALSE;
3945 61 : NITFDataset *poDstDS = NULL;
3946 61 : GDALDriver *poJ2KDriver = NULL;
3947 :
3948 61 : int nBands = poSrcDS->GetRasterCount();
3949 61 : if( nBands == 0 )
3950 : {
3951 : CPLError( CE_Failure, CPLE_NotSupported,
3952 1 : "Unable to export files with zero bands." );
3953 1 : return NULL;
3954 : }
3955 :
3956 60 : poBand1 = poSrcDS->GetRasterBand(1);
3957 60 : if( poBand1 == NULL )
3958 : {
3959 0 : return NULL;
3960 : }
3961 :
3962 : /* -------------------------------------------------------------------- */
3963 : /* Only allow supported compression values. */
3964 : /* -------------------------------------------------------------------- */
3965 60 : const char* pszIC = CSLFetchNameValue( papszOptions, "IC" );
3966 60 : if( pszIC != NULL )
3967 : {
3968 9 : if( EQUAL(pszIC,"NC") )
3969 : /* ok */;
3970 9 : else if( EQUAL(pszIC,"C8") )
3971 : {
3972 : poJ2KDriver =
3973 3 : GetGDALDriverManager()->GetDriverByName( "JP2ECW" );
3974 5 : if( poJ2KDriver == NULL ||
3975 2 : poJ2KDriver->GetMetadataItem( GDAL_DCAP_CREATECOPY, NULL ) == NULL )
3976 : {
3977 : /* Try with Jasper as an alternate driver */
3978 : poJ2KDriver =
3979 1 : GetGDALDriverManager()->GetDriverByName( "JPEG2000" );
3980 : }
3981 3 : if( poJ2KDriver == NULL )
3982 : {
3983 : /* Try with JP2KAK as an alternate driver */
3984 : poJ2KDriver =
3985 0 : GetGDALDriverManager()->GetDriverByName( "JP2KAK" );
3986 : }
3987 3 : if( poJ2KDriver == NULL )
3988 : {
3989 : CPLError(
3990 : CE_Failure, CPLE_AppDefined,
3991 : "Unable to write JPEG2000 compressed NITF file.\n"
3992 : "No 'subfile' JPEG2000 write supporting drivers are\n"
3993 0 : "configured." );
3994 0 : return NULL;
3995 : }
3996 3 : bJPEG2000 = TRUE;
3997 : }
3998 12 : else if( EQUAL(pszIC,"C3") || EQUAL(pszIC,"M3") )
3999 : {
4000 6 : bJPEG = TRUE;
4001 : #ifndef JPEG_SUPPORTED
4002 : CPLError(
4003 : CE_Failure, CPLE_AppDefined,
4004 : "Unable to write JPEG compressed NITF file.\n"
4005 : "Libjpeg is not configured into build." );
4006 : return NULL;
4007 : #endif
4008 : }
4009 : else
4010 : {
4011 : CPLError( CE_Failure, CPLE_AppDefined,
4012 : "Only IC=NC (uncompressed), IC=C3/M3 (JPEG) and IC=C8 (JPEG2000)\n"
4013 0 : "allowed with NITF CreateCopy method." );
4014 0 : return NULL;
4015 : }
4016 : }
4017 :
4018 : /* -------------------------------------------------------------------- */
4019 : /* Get the data type. Complex integers isn't supported by */
4020 : /* NITF, so map that to complex float if we aren't in strict */
4021 : /* mode. */
4022 : /* -------------------------------------------------------------------- */
4023 60 : eType = poBand1->GetRasterDataType();
4024 60 : if( !bStrict && (eType == GDT_CInt16 || eType == GDT_CInt32) )
4025 0 : eType = GDT_CFloat32;
4026 :
4027 : /* -------------------------------------------------------------------- */
4028 : /* Prepare for text and CGM segments. */
4029 : /* -------------------------------------------------------------------- */
4030 60 : char **papszTextMD = NULL;
4031 60 : char **papszCgmMD = NULL;
4032 : char **papszFullOptions = NITFExtractTEXTAndCGMCreationOption( poSrcDS,
4033 : papszOptions,
4034 : &papszTextMD,
4035 60 : &papszCgmMD );
4036 :
4037 : /* -------------------------------------------------------------------- */
4038 : /* Copy over other source metadata items as creation options */
4039 : /* that seem useful. */
4040 : /* -------------------------------------------------------------------- */
4041 60 : char **papszSrcMD = poSrcDS->GetMetadata();
4042 : int iMD;
4043 :
4044 444 : for( iMD = 0; papszSrcMD && papszSrcMD[iMD]; iMD++ )
4045 : {
4046 758 : if( EQUALN(papszSrcMD[iMD],"NITF_BLOCKA",11)
4047 374 : || EQUALN(papszSrcMD[iMD],"NITF_FHDR",9) )
4048 : {
4049 15 : char *pszName = NULL;
4050 15 : const char *pszValue = CPLParseNameValue( papszSrcMD[iMD],
4051 30 : &pszName );
4052 15 : if( pszName != NULL &&
4053 : CSLFetchNameValue( papszFullOptions, pszName+5 ) == NULL )
4054 : papszFullOptions =
4055 14 : CSLSetNameValue( papszFullOptions, pszName+5, pszValue );
4056 15 : CPLFree(pszName);
4057 : }
4058 : }
4059 :
4060 : /* -------------------------------------------------------------------- */
4061 : /* Copy TRE definitions as creation options. */
4062 : /* -------------------------------------------------------------------- */
4063 60 : papszSrcMD = poSrcDS->GetMetadata( "TRE" );
4064 :
4065 60 : for( iMD = 0; papszSrcMD && papszSrcMD[iMD]; iMD++ )
4066 : {
4067 1 : CPLString osTRE;
4068 :
4069 3 : if (EQUALN(papszSrcMD[iMD], "RPFHDR", 6) ||
4070 1 : EQUALN(papszSrcMD[iMD], "RPFIMG", 6) ||
4071 1 : EQUALN(papszSrcMD[iMD], "RPFDES", 6))
4072 : {
4073 : /* Do not copy RPF TRE. They contain absolute offsets */
4074 : /* No chance that they make sense in the new NITF file */
4075 0 : continue;
4076 : }
4077 :
4078 1 : osTRE = "TRE=";
4079 1 : osTRE += papszSrcMD[iMD];
4080 :
4081 1 : papszFullOptions = CSLAddString( papszFullOptions, osTRE );
4082 : }
4083 :
4084 : /* -------------------------------------------------------------------- */
4085 : /* Set if we can set IREP. */
4086 : /* -------------------------------------------------------------------- */
4087 60 : if( CSLFetchNameValue(papszFullOptions,"IREP") == NULL )
4088 : {
4089 93 : if ( ((poSrcDS->GetRasterCount() == 3 && bJPEG) ||
4090 : (poSrcDS->GetRasterCount() >= 3 && !bJPEG)) && eType == GDT_Byte &&
4091 13 : poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_RedBand &&
4092 10 : poSrcDS->GetRasterBand(2)->GetColorInterpretation() == GCI_GreenBand &&
4093 10 : poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
4094 : {
4095 10 : if( bJPEG )
4096 : papszFullOptions =
4097 4 : CSLSetNameValue( papszFullOptions, "IREP", "YCbCr601" );
4098 : else
4099 : papszFullOptions =
4100 6 : CSLSetNameValue( papszFullOptions, "IREP", "RGB" );
4101 : }
4102 69 : else if( poSrcDS->GetRasterCount() == 1 && eType == GDT_Byte
4103 19 : && poBand1->GetColorTable() != NULL )
4104 : {
4105 : papszFullOptions =
4106 1 : CSLSetNameValue( papszFullOptions, "IREP", "RGB/LUT" );
4107 : papszFullOptions =
4108 : CSLSetNameValue( papszFullOptions, "LUT_SIZE",
4109 : CPLString().Printf(
4110 1 : "%d", poBand1->GetColorTable()->GetColorEntryCount()) );
4111 : }
4112 49 : else if( GDALDataTypeIsComplex(eType) )
4113 : papszFullOptions =
4114 5 : CSLSetNameValue( papszFullOptions, "IREP", "NODISPLY" );
4115 :
4116 : else
4117 : papszFullOptions =
4118 44 : CSLSetNameValue( papszFullOptions, "IREP", "MONO" );
4119 : }
4120 :
4121 : /* -------------------------------------------------------------------- */
4122 : /* Do we have lat/long georeferencing information? */
4123 : /* -------------------------------------------------------------------- */
4124 : double adfGeoTransform[6];
4125 60 : int bWriteGeoTransform = FALSE;
4126 60 : int bWriteGCPs = FALSE;
4127 60 : int bNorth, nZone = 0;
4128 60 : OGRSpatialReference oSRS, oSRS_WGS84;
4129 60 : char *pszWKT = (char *) poSrcDS->GetProjectionRef();
4130 60 : if( pszWKT == NULL || pszWKT[0] == '\0' )
4131 7 : pszWKT = (char *) poSrcDS->GetGCPProjection();
4132 :
4133 60 : if( pszWKT != NULL && pszWKT[0] != '\0' )
4134 : {
4135 54 : oSRS.importFromWkt( &pszWKT );
4136 :
4137 : /* NITF is only WGS84 */
4138 54 : oSRS_WGS84.SetWellKnownGeogCS( "WGS84" );
4139 54 : if ( oSRS.IsSameGeogCS(&oSRS_WGS84) == FALSE)
4140 : {
4141 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
4142 12 : "NITF only supports WGS84 geographic and UTM projections.\n");
4143 12 : if (bStrict)
4144 : {
4145 0 : CSLDestroy(papszFullOptions);
4146 0 : CSLDestroy(papszCgmMD);
4147 0 : CSLDestroy(papszTextMD);
4148 0 : return NULL;
4149 : }
4150 : }
4151 :
4152 54 : const char* pszICORDS = CSLFetchNameValue(papszFullOptions, "ICORDS");
4153 :
4154 : /* -------------------------------------------------------------------- */
4155 : /* Should we write DIGEST Spatial Data Extension TRE ? */
4156 : /* -------------------------------------------------------------------- */
4157 54 : const char* pszSDE_TRE = CSLFetchNameValue(papszFullOptions, "SDE_TRE");
4158 54 : int bSDE_TRE = pszSDE_TRE && CSLTestBoolean(pszSDE_TRE);
4159 54 : if (bSDE_TRE)
4160 : {
4161 5 : if( oSRS.IsGeographic() && oSRS.GetPrimeMeridian() == 0.0
4162 1 : && poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None &&
4163 2 : adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 &&
4164 1 : adfGeoTransform[5] < 0.0)
4165 : {
4166 : /* Override ICORDS to G if necessary */
4167 1 : if (pszICORDS != NULL && EQUAL(pszICORDS, "D"))
4168 : {
4169 : papszFullOptions =
4170 0 : CSLSetNameValue( papszFullOptions, "ICORDS", "G" );
4171 : CPLError(CE_Warning, CPLE_AppDefined,
4172 0 : "Forcing ICORDS=G when writing GEOLOB");
4173 : }
4174 : else
4175 : {
4176 : /* Code a bit below will complain with other ICORDS value */
4177 : }
4178 :
4179 1 : if (CSLPartialFindString(papszFullOptions, "TRE=GEOLOB=") != - 1)
4180 : {
4181 : CPLDebug("NITF", "GEOLOB TRE was explicitely defined before. "
4182 0 : "Overriding it with current georefencing info.");
4183 : }
4184 :
4185 : /* Structure of SDE TRE documented here */
4186 : /*http://www.gwg.nga.mil/ntb/baseline/docs/digest/part2_annex_d.pdf */
4187 :
4188 : /* -------------------------------------------------------------------- */
4189 : /* Write GEOLOB TRE */
4190 : /* -------------------------------------------------------------------- */
4191 : char szGEOLOB[48+1];
4192 1 : char* pszGEOLOB = szGEOLOB;
4193 1 : double dfARV = 360.0 / adfGeoTransform[1];
4194 1 : double dfBRV = 360.0 / -adfGeoTransform[5];
4195 1 : double dfLSO = adfGeoTransform[0];
4196 1 : double dfPSO = adfGeoTransform[3];
4197 1 : sprintf(pszGEOLOB, "%09d", (int)(dfARV + 0.5)); pszGEOLOB += 9;
4198 1 : sprintf(pszGEOLOB, "%09d", (int)(dfBRV + 0.5)); pszGEOLOB += 9;
4199 1 : sprintf(pszGEOLOB, "%#+015.10f", dfLSO); pszGEOLOB += 15;
4200 1 : sprintf(pszGEOLOB, "%#+015.10f", dfPSO); pszGEOLOB += 15;
4201 1 : CPLAssert(pszGEOLOB == szGEOLOB + 48);
4202 :
4203 1 : CPLString osGEOLOB("TRE=GEOLOB=");
4204 1 : osGEOLOB += szGEOLOB;
4205 1 : papszFullOptions = CSLAddString( papszFullOptions, osGEOLOB ) ;
4206 :
4207 : /* -------------------------------------------------------------------- */
4208 : /* Write GEOPSB TRE if not already explicitely provided */
4209 : /* -------------------------------------------------------------------- */
4210 1 : if (CSLPartialFindString(papszFullOptions, "FILE_TRE=GEOPSB=") == -1 &&
4211 : CSLPartialFindString(papszFullOptions, "TRE=GEOPSB=") == -1)
4212 : {
4213 : char szGEOPSB[443+1];
4214 1 : memset(szGEOPSB, ' ', 443);
4215 1 : szGEOPSB[443] = 0;
4216 : #define WRITE_STR_NOSZ(dst, src) memcpy(dst, src, strlen(src))
4217 1 : char* pszGEOPSB = szGEOPSB;
4218 1 : WRITE_STR_NOSZ(pszGEOPSB, "GEO"); pszGEOPSB += 3;
4219 1 : WRITE_STR_NOSZ(pszGEOPSB, "DEG"); pszGEOPSB += 3;
4220 1 : WRITE_STR_NOSZ(pszGEOPSB, "World Geodetic System 1984"); pszGEOPSB += 80;
4221 1 : WRITE_STR_NOSZ(pszGEOPSB, "WGE"); pszGEOPSB += 4;
4222 1 : WRITE_STR_NOSZ(pszGEOPSB, "World Geodetic System 1984"); pszGEOPSB += 80;
4223 1 : WRITE_STR_NOSZ(pszGEOPSB, "WE"); pszGEOPSB += 3;
4224 1 : WRITE_STR_NOSZ(pszGEOPSB, "Geodetic"); pszGEOPSB += 80; /* DVR */
4225 1 : WRITE_STR_NOSZ(pszGEOPSB, "GEOD"); pszGEOPSB += 4; /* VDCDVR */
4226 1 : WRITE_STR_NOSZ(pszGEOPSB, "Mean Sea"); pszGEOPSB += 80; /* SDA */
4227 1 : WRITE_STR_NOSZ(pszGEOPSB, "MSL"); pszGEOPSB += 4; /* VDCSDA */
4228 1 : WRITE_STR_NOSZ(pszGEOPSB, "000000000000000"); pszGEOPSB += 15; /* ZOR */
4229 1 : pszGEOPSB += 3; /* GRD */
4230 1 : pszGEOPSB += 80; /* GRN */
4231 1 : WRITE_STR_NOSZ(pszGEOPSB, "0000"); pszGEOPSB += 4; /* ZNA */
4232 1 : CPLAssert(pszGEOPSB == szGEOPSB + 443);
4233 :
4234 1 : CPLString osGEOPSB("FILE_TRE=GEOPSB=");
4235 1 : osGEOPSB += szGEOPSB;
4236 1 : papszFullOptions = CSLAddString( papszFullOptions, osGEOPSB ) ;
4237 : }
4238 : else
4239 : {
4240 0 : CPLDebug("NITF", "GEOPSB TRE was explicitely defined before. Keeping it.");
4241 1 : }
4242 :
4243 : }
4244 : else
4245 : {
4246 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
4247 0 : "Georeferencing info isn't compatible with writing a GEOLOB TRE (only geographic SRS handled for now)");
4248 0 : if (bStrict)
4249 : {
4250 0 : CSLDestroy(papszFullOptions);
4251 0 : return NULL;
4252 : }
4253 : }
4254 : }
4255 :
4256 54 : bWriteGeoTransform = ( poSrcDS->GetGeoTransform( adfGeoTransform ) == CE_None );
4257 54 : bWriteGCPs = ( !bWriteGeoTransform && poSrcDS->GetGCPCount() == 4 );
4258 :
4259 54 : if( oSRS.IsGeographic() && oSRS.GetPrimeMeridian() == 0.0 )
4260 : {
4261 42 : if (pszICORDS == NULL)
4262 : {
4263 : papszFullOptions =
4264 40 : CSLSetNameValue( papszFullOptions, "ICORDS", "G" );
4265 : }
4266 2 : else if (EQUAL(pszICORDS, "G") || EQUAL(pszICORDS, "D"))
4267 : {
4268 : /* Do nothing */
4269 : }
4270 : else
4271 : {
4272 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
4273 : "Inconsistant ICORDS value with SRS : %s%s.\n", pszICORDS,
4274 0 : (!bStrict) ? ". Setting it to G instead" : "");
4275 0 : if (bStrict)
4276 : {
4277 0 : CSLDestroy(papszFullOptions);
4278 0 : return NULL;
4279 : }
4280 : papszFullOptions =
4281 0 : CSLSetNameValue( papszFullOptions, "ICORDS", "G" );
4282 : }
4283 : }
4284 :
4285 12 : else if( oSRS.GetUTMZone( &bNorth ) > 0 )
4286 : {
4287 12 : if( bNorth )
4288 : papszFullOptions =
4289 12 : CSLSetNameValue( papszFullOptions, "ICORDS", "N" );
4290 : else
4291 : papszFullOptions =
4292 0 : CSLSetNameValue( papszFullOptions, "ICORDS", "S" );
4293 :
4294 12 : nZone = oSRS.GetUTMZone( NULL );
4295 : }
4296 : else
4297 : {
4298 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
4299 0 : "NITF only supports WGS84 geographic and UTM projections.\n");
4300 0 : if (bStrict)
4301 : {
4302 0 : CSLDestroy(papszFullOptions);
4303 0 : CSLDestroy(papszCgmMD);
4304 0 : CSLDestroy(papszTextMD);
4305 0 : return NULL;
4306 : }
4307 : }
4308 : }
4309 :
4310 : /* -------------------------------------------------------------------- */
4311 : /* Create the output file. */
4312 : /* -------------------------------------------------------------------- */
4313 60 : int nXSize = poSrcDS->GetRasterXSize();
4314 60 : int nYSize = poSrcDS->GetRasterYSize();
4315 60 : const char *pszPVType = GDALToNITFDataType( eType );
4316 :
4317 60 : if( pszPVType == NULL )
4318 : {
4319 3 : CSLDestroy(papszFullOptions);
4320 3 : CSLDestroy(papszCgmMD);
4321 3 : CSLDestroy(papszTextMD);
4322 3 : return NULL;
4323 : }
4324 :
4325 57 : if (!NITFCreate( pszFilename, nXSize, nYSize, poSrcDS->GetRasterCount(),
4326 : GDALGetDataTypeSize( eType ), pszPVType,
4327 : papszFullOptions ))
4328 : {
4329 15 : CSLDestroy( papszFullOptions );
4330 15 : CSLDestroy(papszCgmMD);
4331 15 : CSLDestroy(papszTextMD);
4332 15 : return NULL;
4333 : }
4334 :
4335 42 : CSLDestroy( papszFullOptions );
4336 42 : papszFullOptions = NULL;
4337 :
4338 : /* ==================================================================== */
4339 : /* JPEG2000 case. We need to write the data through a J2K */
4340 : /* driver in pixel interleaved form. */
4341 : /* ==================================================================== */
4342 42 : if( bJPEG2000 )
4343 : {
4344 3 : NITFFile *psFile = NITFOpen( pszFilename, TRUE );
4345 3 : if (psFile == NULL)
4346 : {
4347 0 : CSLDestroy(papszCgmMD);
4348 0 : CSLDestroy(papszTextMD);
4349 0 : return NULL;
4350 : }
4351 :
4352 3 : GDALDataset *poJ2KDataset = NULL;
4353 3 : GUIntBig nImageOffset = psFile->pasSegmentInfo[0].nSegmentStart;
4354 3 : CPLString osDSName;
4355 :
4356 3 : NITFClose( psFile );
4357 :
4358 : osDSName.Printf( "/vsisubfile/" CPL_FRMT_GUIB "_%d,%s",
4359 : nImageOffset, -1,
4360 3 : pszFilename );
4361 :
4362 3 : if (EQUAL(poJ2KDriver->GetDescription(), "JP2ECW"))
4363 : {
4364 2 : char** papszJP2Options = NITFJP2Options(papszOptions);
4365 : poJ2KDataset =
4366 : poJ2KDriver->CreateCopy( osDSName, poSrcDS, FALSE,
4367 : papszJP2Options,
4368 2 : pfnProgress, pProgressData );
4369 2 : CSLDestroy(papszJP2Options);
4370 : }
4371 : else
4372 : {
4373 : /* Jasper case */
4374 1 : const char* apszOptions[] = { "FORMAT=JPC", NULL };
4375 : poJ2KDataset =
4376 : poJ2KDriver->CreateCopy( osDSName, poSrcDS, FALSE,
4377 : (char **)apszOptions,
4378 1 : pfnProgress, pProgressData );
4379 : }
4380 3 : if( poJ2KDataset == NULL )
4381 : {
4382 0 : CSLDestroy(papszCgmMD);
4383 0 : CSLDestroy(papszTextMD);
4384 0 : return NULL;
4385 : }
4386 :
4387 3 : delete poJ2KDataset;
4388 :
4389 : // Now we need to figure out the actual length of the file
4390 : // and correct the image segment size information.
4391 : GIntBig nPixelCount = nXSize * ((GIntBig) nYSize) *
4392 3 : poSrcDS->GetRasterCount();
4393 :
4394 3 : NITFPatchImageLength( pszFilename, nImageOffset, nPixelCount, "C8" );
4395 3 : NITFWriteCGMSegments( pszFilename, papszCgmMD );
4396 3 : NITFWriteTextSegments( pszFilename, papszTextMD );
4397 :
4398 3 : GDALOpenInfo oOpenInfo( pszFilename, GA_Update );
4399 3 : poDstDS = (NITFDataset *) Open( &oOpenInfo );
4400 :
4401 3 : if( poDstDS == NULL )
4402 : {
4403 0 : CSLDestroy(papszCgmMD);
4404 0 : CSLDestroy(papszTextMD);
4405 0 : return NULL;
4406 0 : }
4407 : }
4408 :
4409 : /* ==================================================================== */
4410 : /* Loop copying bands to an uncompressed file. */
4411 : /* ==================================================================== */
4412 39 : else if( bJPEG )
4413 : {
4414 : #ifdef JPEG_SUPPORTED
4415 5 : NITFFile *psFile = NITFOpen( pszFilename, TRUE );
4416 5 : if (psFile == NULL)
4417 : {
4418 0 : CSLDestroy(papszCgmMD);
4419 0 : CSLDestroy(papszTextMD);
4420 0 : return NULL;
4421 : }
4422 5 : GUIntBig nImageOffset = psFile->pasSegmentInfo[0].nSegmentStart;
4423 : int bSuccess;
4424 :
4425 : bSuccess =
4426 : NITFWriteJPEGImage( poSrcDS, psFile->fp, nImageOffset,
4427 : papszOptions,
4428 5 : pfnProgress, pProgressData );
4429 :
4430 5 : if( !bSuccess )
4431 : {
4432 0 : NITFClose( psFile );
4433 0 : CSLDestroy(papszCgmMD);
4434 0 : CSLDestroy(papszTextMD);
4435 0 : return NULL;
4436 : }
4437 :
4438 : // Now we need to figure out the actual length of the file
4439 : // and correct the image segment size information.
4440 : GIntBig nPixelCount = nXSize * ((GIntBig) nYSize) *
4441 5 : poSrcDS->GetRasterCount();
4442 :
4443 5 : NITFClose( psFile );
4444 :
4445 : NITFPatchImageLength( pszFilename, nImageOffset,
4446 5 : nPixelCount, pszIC );
4447 :
4448 5 : NITFWriteCGMSegments( pszFilename, papszCgmMD );
4449 5 : NITFWriteTextSegments( pszFilename, papszTextMD );
4450 :
4451 5 : GDALOpenInfo oOpenInfo( pszFilename, GA_Update );
4452 5 : poDstDS = (NITFDataset *) Open( &oOpenInfo );
4453 :
4454 5 : if( poDstDS == NULL )
4455 : {
4456 0 : CSLDestroy(papszCgmMD);
4457 0 : CSLDestroy(papszTextMD);
4458 0 : return NULL;
4459 0 : }
4460 : #endif /* def JPEG_SUPPORTED */
4461 : }
4462 :
4463 : /* ==================================================================== */
4464 : /* Loop copying bands to an uncompressed file. */
4465 : /* ==================================================================== */
4466 : else
4467 : {
4468 34 : NITFWriteCGMSegments( pszFilename, papszCgmMD );
4469 34 : NITFWriteTextSegments( pszFilename, papszTextMD );
4470 :
4471 34 : GDALOpenInfo oOpenInfo( pszFilename, GA_Update );
4472 34 : poDstDS = (NITFDataset *) Open( &oOpenInfo );
4473 34 : if( poDstDS == NULL )
4474 : {
4475 0 : CSLDestroy(papszCgmMD);
4476 0 : CSLDestroy(papszTextMD);
4477 0 : return NULL;
4478 : }
4479 :
4480 34 : void *pData = VSIMalloc2(nXSize, (GDALGetDataTypeSize(eType) / 8));
4481 34 : if (pData == NULL)
4482 : {
4483 0 : delete poDstDS;
4484 0 : CSLDestroy(papszCgmMD);
4485 0 : CSLDestroy(papszTextMD);
4486 0 : return NULL;
4487 : }
4488 :
4489 34 : CPLErr eErr = CE_None;
4490 :
4491 84 : for( int iBand = 0; eErr == CE_None && iBand < poSrcDS->GetRasterCount(); iBand++ )
4492 : {
4493 50 : GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( iBand+1 );
4494 50 : GDALRasterBand *poDstBand = poDstDS->GetRasterBand( iBand+1 );
4495 :
4496 : /* -------------------------------------------------------------------- */
4497 : /* Do we need to copy a colortable or other metadata? */
4498 : /* -------------------------------------------------------------------- */
4499 : GDALColorTable *poCT;
4500 :
4501 50 : poCT = poSrcBand->GetColorTable();
4502 50 : if( poCT != NULL )
4503 1 : poDstBand->SetColorTable( poCT );
4504 :
4505 : /* -------------------------------------------------------------------- */
4506 : /* Copy image data. */
4507 : /* -------------------------------------------------------------------- */
4508 1900 : for( int iLine = 0; iLine < nYSize; iLine++ )
4509 : {
4510 : eErr = poSrcBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
4511 1850 : pData, nXSize, 1, eType, 0, 0 );
4512 1850 : if( eErr != CE_None )
4513 0 : break;
4514 :
4515 : eErr = poDstBand->RasterIO( GF_Write, 0, iLine, nXSize, 1,
4516 1850 : pData, nXSize, 1, eType, 0, 0 );
4517 :
4518 1850 : if( eErr != CE_None )
4519 0 : break;
4520 :
4521 1850 : if( !pfnProgress( (iBand + (iLine+1) / (double) nYSize)
4522 : / (double) poSrcDS->GetRasterCount(),
4523 : NULL, pProgressData ) )
4524 : {
4525 0 : CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
4526 0 : eErr = CE_Failure;
4527 0 : break;
4528 : }
4529 : }
4530 : }
4531 :
4532 34 : CPLFree( pData );
4533 :
4534 34 : if ( eErr != CE_None )
4535 : {
4536 0 : delete poDstDS;
4537 0 : CSLDestroy(papszCgmMD);
4538 0 : CSLDestroy(papszTextMD);
4539 0 : return NULL;
4540 0 : }
4541 : }
4542 :
4543 : /* -------------------------------------------------------------------- */
4544 : /* Set the georeferencing. */
4545 : /* -------------------------------------------------------------------- */
4546 42 : if( bWriteGeoTransform )
4547 : {
4548 36 : poDstDS->psImage->nZone = nZone;
4549 36 : poDstDS->SetGeoTransform( adfGeoTransform );
4550 : }
4551 6 : else if( bWriteGCPs )
4552 : {
4553 1 : poDstDS->psImage->nZone = nZone;
4554 1 : poDstDS->SetGCPs( poSrcDS->GetGCPCount(),
4555 1 : poSrcDS->GetGCPs(),
4556 3 : poSrcDS->GetGCPProjection() );
4557 : }
4558 :
4559 42 : poDstDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
4560 :
4561 42 : CSLDestroy(papszCgmMD);
4562 42 : CSLDestroy(papszTextMD);
4563 :
4564 42 : return poDstDS;
4565 : }
4566 :
4567 : /************************************************************************/
4568 : /* NITFPatchImageLength() */
4569 : /* */
4570 : /* Fixup various stuff we don't know till we have written the */
4571 : /* imagery. In particular the file length, image data length */
4572 : /* and the compression ratio achieved. */
4573 : /************************************************************************/
4574 :
4575 9 : static void NITFPatchImageLength( const char *pszFilename,
4576 : GUIntBig nImageOffset,
4577 : GIntBig nPixelCount,
4578 : const char *pszIC )
4579 :
4580 : {
4581 9 : VSILFILE *fpVSIL = VSIFOpenL( pszFilename, "r+b" );
4582 9 : if( fpVSIL == NULL )
4583 0 : return;
4584 :
4585 9 : VSIFSeekL( fpVSIL, 0, SEEK_END );
4586 9 : GUIntBig nFileLen = VSIFTellL( fpVSIL );
4587 :
4588 : /* -------------------------------------------------------------------- */
4589 : /* Update total file length. */
4590 : /* -------------------------------------------------------------------- */
4591 9 : if (nFileLen >= (GUIntBig)(1e12 - 1))
4592 : {
4593 : CPLError(CE_Failure, CPLE_AppDefined,
4594 : "Too big file : " CPL_FRMT_GUIB ". Truncating to 999999999998",
4595 0 : nFileLen);
4596 0 : nFileLen = (GUIntBig)(1e12 - 2);
4597 : }
4598 9 : VSIFSeekL( fpVSIL, 342, SEEK_SET );
4599 9 : CPLString osLen = CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u",nFileLen);
4600 9 : VSIFWriteL( (void *) osLen.c_str(), 1, 12, fpVSIL );
4601 :
4602 : /* -------------------------------------------------------------------- */
4603 : /* Update the image data length. */
4604 : /* -------------------------------------------------------------------- */
4605 9 : GUIntBig nImageSize = nFileLen-nImageOffset;
4606 9 : if (GUINTBIG_TO_DOUBLE(nImageSize) >= 1e10 - 1)
4607 : {
4608 : CPLError(CE_Failure, CPLE_AppDefined,
4609 : "Too big image size : " CPL_FRMT_GUIB". Truncating to 9999999998",
4610 0 : nImageSize);
4611 0 : nImageSize = (GUIntBig)(1e10 - 2);
4612 : }
4613 9 : VSIFSeekL( fpVSIL, 369, SEEK_SET );
4614 9 : osLen = CPLString().Printf("%010" CPL_FRMT_GB_WITHOUT_PREFIX "u",nImageSize);
4615 9 : VSIFWriteL( (void *) osLen.c_str(), 1, 10, fpVSIL );
4616 :
4617 : /* -------------------------------------------------------------------- */
4618 : /* Update COMRAT, the compression rate variable. We have to */
4619 : /* take into account the presence of graphic and text segments, */
4620 : /* the optional presence of IGEOLO and ICOM to find its position. */
4621 : /* -------------------------------------------------------------------- */
4622 : char szICBuf[2];
4623 : char achNUM[4]; // buffer for segment size. 3 digits plus null character
4624 9 : achNUM[3] = '\0';
4625 :
4626 : // get number of graphic and text segment so we can calculate offset for
4627 : // image IC.
4628 9 : int nNumIOffset = 360;
4629 9 : VSIFSeekL( fpVSIL, nNumIOffset, SEEK_SET );
4630 9 : VSIFReadL( achNUM, 1, 3, fpVSIL );
4631 9 : int nIM = atoi(achNUM); // number of image segment
4632 :
4633 9 : int nNumSOffset = nNumIOffset + 3 + nIM * 16;
4634 9 : VSIFSeekL( fpVSIL, nNumSOffset, SEEK_SET );
4635 9 : VSIFReadL( achNUM, 1, 3, fpVSIL );
4636 9 : int nGS = atoi(achNUM); // number of graphic segment
4637 :
4638 9 : int nNumTOffset = nNumSOffset + 3 + 10 * nGS + 3;
4639 9 : VSIFSeekL( fpVSIL, nNumTOffset, SEEK_SET );
4640 9 : VSIFReadL( achNUM, 1, 3, fpVSIL );
4641 9 : int nTS = atoi(achNUM); // number of text segment
4642 :
4643 9 : int nAdditionalOffset = nGS * 10 + nTS * 9;
4644 :
4645 : /* Read ICORDS */
4646 9 : VSIFSeekL( fpVSIL, 775 + nAdditionalOffset , SEEK_SET );
4647 : char chICORDS;
4648 9 : VSIFReadL( &chICORDS, 1, 1, fpVSIL );
4649 9 : if (chICORDS != ' ')
4650 8 : VSIFSeekL( fpVSIL, 60, SEEK_CUR); /* skip IGEOLO */
4651 :
4652 : /* Read NICOM */
4653 : char achNICOM[2];
4654 9 : VSIFReadL( achNICOM, 1, 1, fpVSIL );
4655 9 : achNICOM[1] = 0;
4656 9 : int nNICOM = atoi(achNICOM);
4657 9 : VSIFSeekL( fpVSIL, nNICOM * 80, SEEK_CUR); /* skip comments */
4658 :
4659 : /* Read IC */
4660 9 : VSIFReadL( szICBuf, 2, 1, fpVSIL );
4661 :
4662 : /* The following line works around a "feature" of *BSD libc (at least PC-BSD 7.1) */
4663 : /* that makes the position of the file offset unreliable when executing a */
4664 : /* "seek, read and write" sequence. After the read(), the file offset seen by */
4665 : /* the write() is approximatively the size of a block further... */
4666 9 : VSIFSeekL( fpVSIL, VSIFTellL( fpVSIL ), SEEK_SET );
4667 :
4668 9 : if( !EQUALN(szICBuf,pszIC,2) )
4669 : {
4670 : CPLError( CE_Warning, CPLE_AppDefined,
4671 0 : "Unable to locate COMRAT to update in NITF header." );
4672 : }
4673 : else
4674 : {
4675 : char szCOMRAT[5];
4676 :
4677 9 : if( EQUAL(pszIC,"C8") ) /* jpeg2000 */
4678 : {
4679 4 : double dfRate = (GIntBig)(nFileLen-nImageOffset) * 8 / (double) nPixelCount;
4680 4 : dfRate = MAX(0.01,MIN(99.99,dfRate));
4681 :
4682 : // We emit in wxyz format with an implicit decimal place
4683 : // between wx and yz as per spec for lossy compression.
4684 : // We really should have a special case for lossless compression.
4685 4 : sprintf( szCOMRAT, "%04d", (int) (dfRate * 100));
4686 : }
4687 5 : else if( EQUAL(pszIC, "C3") || EQUAL(pszIC, "M3") ) /* jpeg */
4688 : {
4689 5 : strcpy( szCOMRAT, "00.0" );
4690 : }
4691 :
4692 9 : VSIFWriteL( szCOMRAT, 4, 1, fpVSIL );
4693 : }
4694 :
4695 9 : VSIFCloseL( fpVSIL );
4696 : }
4697 :
4698 : /************************************************************************/
4699 : /* NITFWriteCGMSegments() */
4700 : /************************************************************************/
4701 601 : static int NITFWriteCGMSegments( const char *pszFilename, char **papszList)
4702 : {
4703 601 : char errorMessage[255] = "";
4704 :
4705 : // size of each Cgm header entry (LS (4) + LSSH (6))
4706 601 : const int nCgmHdrEntrySz = 10;
4707 :
4708 601 : if (papszList == NULL)
4709 593 : return TRUE;
4710 :
4711 8 : int nNUMS = 0;
4712 : const char *pszNUMS;
4713 8 : pszNUMS = CSLFetchNameValue(papszList, "SEGMENT_COUNT");
4714 8 : if (pszNUMS != NULL)
4715 : {
4716 8 : nNUMS = atoi(pszNUMS);
4717 : }
4718 :
4719 : /* -------------------------------------------------------------------- */
4720 : /* Open the target file. */
4721 : /* -------------------------------------------------------------------- */
4722 8 : VSILFILE *fpVSIL = VSIFOpenL(pszFilename, "r+b");
4723 :
4724 8 : if (fpVSIL == NULL)
4725 0 : return FALSE;
4726 :
4727 : // Calculates the offset for NUMS so we can update header data
4728 : char achNUMI[4]; // 3 digits plus null character
4729 8 : achNUMI[3] = '\0';
4730 :
4731 : // NUMI offset is at a fixed offset 363
4732 8 : int nNumIOffset = 360;
4733 8 : VSIFSeekL(fpVSIL, nNumIOffset, SEEK_SET );
4734 8 : VSIFReadL(achNUMI, 1, 3, fpVSIL);
4735 8 : int nIM = atoi(achNUMI);
4736 :
4737 : // 6 for size of LISH and 10 for size of LI
4738 : // NUMS offset is NumI offset plus the size of NumI + size taken up each
4739 : // the header data multiply by the number of data
4740 :
4741 8 : int nNumSOffset = nNumIOffset + 3+ nIM * (6 + 10);
4742 :
4743 : /* -------------------------------------------------------------------- */
4744 : /* Confirm that the NUMS in the file header already matches the */
4745 : /* number of graphic segments we want to write */
4746 : /* -------------------------------------------------------------------- */
4747 : char achNUMS[4];
4748 :
4749 8 : VSIFSeekL( fpVSIL, nNumSOffset, SEEK_SET );
4750 8 : VSIFReadL( achNUMS, 1, 3, fpVSIL );
4751 8 : achNUMS[3] = '\0';
4752 :
4753 8 : if( atoi(achNUMS) != nNUMS )
4754 : {
4755 : CPLError( CE_Failure, CPLE_AppDefined,
4756 : "It appears an attempt was made to add or update graphic\n"
4757 : "segments on an NITF file with existing segments. This\n"
4758 0 : "is not currently supported by the GDAL NITF driver." );
4759 :
4760 0 : VSIFCloseL( fpVSIL );
4761 0 : return FALSE;
4762 : }
4763 :
4764 :
4765 : // allocate space for graphic header.
4766 : // Size of LS = 4, size of LSSH = 6, and 1 for null character
4767 8 : char *pachLS = (char *) CPLCalloc(nNUMS * nCgmHdrEntrySz + 1, 1);
4768 :
4769 : /* -------------------------------------------------------------------- */
4770 : /* Assume no extended data such as SXSHDL, SXSHD */
4771 : /* -------------------------------------------------------------------- */
4772 :
4773 : /* ==================================================================== */
4774 : /* Write the Graphics segments at the end of the file. */
4775 : /* ==================================================================== */
4776 :
4777 : #define PLACE(location,name,text) strncpy(location,text,strlen(text))
4778 :
4779 12 : for (int i = 0; i < nNUMS; i++)
4780 : {
4781 :
4782 : // Get all the fields for current CGM segment
4783 : const char *pszSlocRow = CSLFetchNameValue(papszList,
4784 4 : CPLString().Printf("SEGMENT_%d_SLOC_ROW", i));
4785 : const char *pszSlocCol = CSLFetchNameValue(papszList,
4786 8 : CPLString().Printf("SEGMENT_%d_SLOC_COL", i));
4787 : const char *pszSdlvl = CSLFetchNameValue(papszList,
4788 8 : CPLString().Printf("SEGMENT_%d_SDLVL", i));
4789 : const char *pszSalvl = CSLFetchNameValue(papszList,
4790 8 : CPLString().Printf("SEGMENT_%d_SALVL", i));
4791 : const char *pszData = CSLFetchNameValue(papszList,
4792 8 : CPLString().Printf("SEGMENT_%d_DATA", i));
4793 :
4794 : // Error checking
4795 4 : if (pszSlocRow == NULL)
4796 : {
4797 0 : sprintf(errorMessage, "NITF graphic segment writing error: SLOC_ROW for segment %d is not defined",i);
4798 0 : break;
4799 : }
4800 4 : if (pszSlocCol == NULL)
4801 : {
4802 0 : sprintf(errorMessage, "NITF graphic segment writing error: SLOC_COL for segment %d is not defined",i);
4803 0 : break;
4804 : }
4805 4 : if (pszSdlvl == NULL)
4806 : {
4807 0 : sprintf(errorMessage, "NITF graphic segment writing error: SDLVL for segment %d is not defined", i);
4808 0 : break;
4809 : }
4810 4 : if (pszSalvl == NULL)
4811 : {
4812 0 : sprintf(errorMessage, "NITF graphic segment writing error: SALVLfor segment %d is not defined", i);
4813 0 : break;
4814 : }
4815 4 : if (pszData == NULL)
4816 : {
4817 0 : sprintf(errorMessage, "NITF graphic segment writing error: DATA for segment %d is not defined", i);
4818 0 : break;
4819 : }
4820 :
4821 4 : int nSlocCol = atoi(pszSlocRow);
4822 4 : int nSlocRow = atoi(pszSlocCol);
4823 4 : int nSdlvl = atoi(pszSdlvl);
4824 4 : int nSalvl = atoi(pszSalvl);
4825 :
4826 : // Create a buffer for graphics segment header, 258 is the size of
4827 : // the header that we will be writing.
4828 : char achGSH[258];
4829 :
4830 4 : memset(achGSH, ' ', sizeof(achGSH));
4831 :
4832 :
4833 4 : PLACE( achGSH+ 0, SY , "SY" );
4834 4 : PLACE( achGSH+ 2, SID ,CPLSPrintf("%010d", i) );
4835 4 : PLACE( achGSH+ 12, SNAME , "DEFAULT NAME " );
4836 4 : PLACE( achGSH+32, SSCLAS , "U" );
4837 4 : PLACE( achGSH+33, SSCLASY , "0" );
4838 4 : PLACE( achGSH+199, ENCRYP , "0" );
4839 4 : PLACE( achGSH+200, SFMT , "C" );
4840 4 : PLACE( achGSH+201, SSTRUCT , "0000000000000" );
4841 4 : PLACE( achGSH+214, SDLVL , CPLSPrintf("%03d",nSdlvl)); // size3
4842 4 : PLACE( achGSH+217, SALVL , CPLSPrintf("%03d",nSalvl)); // size3
4843 4 : PLACE( achGSH+220, SLOC , CPLSPrintf("%05d%05d",nSlocRow,nSlocCol) ); // size 10
4844 4 : PLACE( achGSH+230, SBAND1 , "0000000000" );
4845 4 : PLACE( achGSH+240, SCOLOR, "C" );
4846 4 : PLACE( achGSH+241, SBAND2, "0000000000" );
4847 4 : PLACE( achGSH+251, SRES2, "00" );
4848 4 : PLACE( achGSH+253, SXSHDL, "00000" );
4849 :
4850 : // Move to the end of the file
4851 4 : VSIFSeekL(fpVSIL, 0, SEEK_END );
4852 4 : VSIFWriteL(achGSH, 1, sizeof(achGSH), fpVSIL);
4853 :
4854 : /* -------------------------------------- ------------------------------ */
4855 : /* Prepare and write CGM segment data. */
4856 : /* -------------------------------------------------------------------- */
4857 4 : int nCGMSize = 0;
4858 : char *pszCgmToWrite = CPLUnescapeString(pszData, &nCGMSize,
4859 4 : CPLES_BackslashQuotable);
4860 :
4861 4 : if (nCGMSize > 999998)
4862 : {
4863 : CPLError(CE_Warning, CPLE_NotSupported,
4864 : "Length of SEGMENT_%d_DATA is %d, which is greater than 999998. Truncating...",
4865 0 : i + 1, nCGMSize);
4866 0 : nCGMSize = 999998;
4867 : }
4868 :
4869 4 : VSIFWriteL(pszCgmToWrite, 1, nCGMSize, fpVSIL);
4870 :
4871 : /* -------------------------------------------------------------------- */
4872 : /* Update the subheader and data size info in the file header. */
4873 : /* -------------------------------------------------------------------- */
4874 4 : sprintf( pachLS + nCgmHdrEntrySz * i, "%04d%06d",(int) sizeof(achGSH), nCGMSize );
4875 :
4876 4 : CPLFree(pszCgmToWrite);
4877 :
4878 : } // End For
4879 :
4880 :
4881 : /* -------------------------------------------------------------------- */
4882 : /* Write out the graphic segment info. */
4883 : /* -------------------------------------------------------------------- */
4884 :
4885 8 : VSIFSeekL(fpVSIL, nNumSOffset + 3, SEEK_SET );
4886 8 : VSIFWriteL(pachLS, 1, nNUMS * nCgmHdrEntrySz, fpVSIL);
4887 :
4888 : /* -------------------------------------------------------------------- */
4889 : /* Update total file length. */
4890 : /* -------------------------------------------------------------------- */
4891 8 : VSIFSeekL(fpVSIL, 0, SEEK_END );
4892 8 : GUIntBig nFileLen = VSIFTellL(fpVSIL);
4893 : // Offset to file length entry
4894 8 : VSIFSeekL(fpVSIL, 342, SEEK_SET );
4895 8 : if (GUINTBIG_TO_DOUBLE(nFileLen) >= 1e12 - 1)
4896 : {
4897 : CPLError(CE_Failure, CPLE_AppDefined,
4898 : "Too big file : " CPL_FRMT_GUIB ". Truncating to 999999999998",
4899 0 : nFileLen);
4900 0 : nFileLen = (GUIntBig) (1e12 - 2);
4901 : }
4902 : CPLString osLen = CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u",
4903 8 : nFileLen);
4904 8 : VSIFWriteL((void *) osLen.c_str(), 1, 12, fpVSIL);
4905 :
4906 8 : VSIFCloseL(fpVSIL);
4907 :
4908 8 : CPLFree(pachLS);
4909 :
4910 8 : if (strlen(errorMessage) != 0)
4911 : {
4912 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", errorMessage);
4913 0 : return FALSE;
4914 : }
4915 :
4916 8 : return TRUE;
4917 : }
4918 :
4919 : /************************************************************************/
4920 : /* NITFWriteTextSegments() */
4921 : /************************************************************************/
4922 :
4923 601 : static void NITFWriteTextSegments( const char *pszFilename,
4924 : char **papszList )
4925 :
4926 : {
4927 : /* -------------------------------------------------------------------- */
4928 : /* Count the number of apparent text segments to write. There */
4929 : /* is nothing at all to do if there are none to write. */
4930 : /* -------------------------------------------------------------------- */
4931 601 : int iOpt, nNUMT = 0;
4932 :
4933 609 : for( iOpt = 0; papszList != NULL && papszList[iOpt] != NULL; iOpt++ )
4934 : {
4935 8 : if( EQUALN(papszList[iOpt],"DATA_",5) )
4936 5 : nNUMT++;
4937 : }
4938 :
4939 601 : if( nNUMT == 0 )
4940 597 : return;
4941 :
4942 : /* -------------------------------------------------------------------- */
4943 : /* Open the target file. */
4944 : /* -------------------------------------------------------------------- */
4945 4 : VSILFILE *fpVSIL = VSIFOpenL( pszFilename, "r+b" );
4946 :
4947 4 : if( fpVSIL == NULL )
4948 0 : return;
4949 :
4950 : // Get number of text field. Since there there could be multiple images
4951 : // or graphic segment, the offset need to be calculated dynamically.
4952 :
4953 : char achNUMI[4]; // 3 digits plus null character
4954 4 : achNUMI[3] = '\0';
4955 : // NUMI offset is at a fixed offset 363
4956 4 : int nNumIOffset = 360;
4957 4 : VSIFSeekL( fpVSIL, nNumIOffset, SEEK_SET );
4958 4 : VSIFReadL( achNUMI, 1, 3, fpVSIL );
4959 4 : int nIM = atoi(achNUMI);
4960 :
4961 : char achNUMG[4]; // 3 digits plus null character
4962 4 : achNUMG[3] = '\0';
4963 :
4964 : // 3 for size of NUMI. 6 and 10 are the field size for LISH and LI
4965 4 : int nNumGOffset = nNumIOffset + 3 + nIM * (6 + 10);
4966 4 : VSIFSeekL( fpVSIL, nNumGOffset, SEEK_SET );
4967 4 : VSIFReadL( achNUMG, 1, 3, fpVSIL );
4968 4 : int nGS = atoi(achNUMG);
4969 :
4970 : // NUMT offset
4971 : // 3 for size of NUMG. 4 and 6 are filed size of LSSH and LS.
4972 : // the last + 3 is for NUMX field, which is not used
4973 4 : int nNumTOffset = nNumGOffset + 3 + nGS * (4 + 6) + 3;
4974 :
4975 : /* -------------------------------------------------------------------- */
4976 : /* Confirm that the NUMT in the file header already matches the */
4977 : /* number of text segements we want to write, and that the */
4978 : /* segment header/data size info is blank. */
4979 : /* -------------------------------------------------------------------- */
4980 : char achNUMT[4];
4981 4 : char *pachLT = (char *) CPLCalloc(nNUMT * 9 + 1, 1);
4982 :
4983 4 : VSIFSeekL( fpVSIL, nNumTOffset, SEEK_SET );
4984 4 : VSIFReadL( achNUMT, 1, 3, fpVSIL );
4985 4 : achNUMT[3] = '\0';
4986 :
4987 4 : VSIFReadL( pachLT, 1, nNUMT * 9, fpVSIL );
4988 :
4989 4 : if( atoi(achNUMT) != nNUMT )
4990 : {
4991 : CPLError( CE_Failure, CPLE_AppDefined,
4992 : "It appears an attempt was made to add or update text\n"
4993 : "segments on an NITF file with existing segments. This\n"
4994 0 : "is not currently supported by the GDAL NITF driver." );
4995 :
4996 0 : VSIFCloseL( fpVSIL );
4997 0 : CPLFree( pachLT );
4998 0 : return;
4999 : }
5000 :
5001 4 : if( !EQUALN(pachLT," ",9) )
5002 : {
5003 0 : CPLFree( pachLT );
5004 : // presumably the text segments are already written, do nothing.
5005 0 : VSIFCloseL( fpVSIL );
5006 0 : return;
5007 : }
5008 :
5009 : /* -------------------------------------------------------------------- */
5010 : /* At this point we likely ought to confirm NUMDES, NUMRES, */
5011 : /* UDHDL and XHDL are zero. Consider adding later... */
5012 : /* -------------------------------------------------------------------- */
5013 :
5014 : /* ==================================================================== */
5015 : /* Write the text segments at the end of the file. */
5016 : /* ==================================================================== */
5017 : #define PLACE(location,name,text) strncpy(location,text,strlen(text))
5018 4 : int iTextSeg = 0;
5019 :
5020 12 : for( iOpt = 0; papszList != NULL && papszList[iOpt] != NULL; iOpt++ )
5021 : {
5022 : const char *pszTextToWrite;
5023 :
5024 8 : if( !EQUALN(papszList[iOpt],"DATA_",5) )
5025 3 : continue;
5026 :
5027 5 : const char *pszHeaderBuffer = NULL;
5028 :
5029 5 : pszTextToWrite = CPLParseNameValue( papszList[iOpt], NULL );
5030 5 : if( pszTextToWrite == NULL )
5031 0 : continue;
5032 :
5033 : /* -------------------------------------------------------------------- */
5034 : /* Locate corresponding header data in the buffer */
5035 : /* -------------------------------------------------------------------- */
5036 :
5037 12 : for( int iOpt2 = 0; papszList != NULL && papszList[iOpt2] != NULL; iOpt2++ ) {
5038 10 : if( !EQUALN(papszList[iOpt2],"HEADER_",7) )
5039 7 : continue;
5040 :
5041 3 : char *pszHeaderKey = NULL, *pszDataKey = NULL;
5042 3 : CPLParseNameValue( papszList[iOpt2], &pszHeaderKey );
5043 3 : CPLParseNameValue( papszList[iOpt], &pszDataKey );
5044 3 : if( pszHeaderKey == NULL || pszDataKey == NULL )
5045 : {
5046 0 : CPLFree(pszHeaderKey);
5047 0 : CPLFree(pszDataKey);
5048 0 : continue;
5049 : }
5050 :
5051 : char *pszHeaderId, *pszDataId; //point to header and data number
5052 3 : pszHeaderId = pszHeaderKey + 7;
5053 3 : pszDataId = pszDataKey + 5;
5054 :
5055 3 : bool bIsSameId = strcmp(pszHeaderId, pszDataId) == 0;
5056 3 : CPLFree(pszHeaderKey);
5057 3 : CPLFree(pszDataKey);
5058 :
5059 : // if ID matches, read the header information and exit the loop
5060 3 : if (bIsSameId) {
5061 3 : pszHeaderBuffer = CPLParseNameValue( papszList[iOpt2], NULL);
5062 3 : break;
5063 : }
5064 : }
5065 :
5066 : /* -------------------------------------------------------------------- */
5067 : /* Prepare and write text header. */
5068 : /* -------------------------------------------------------------------- */
5069 : char achTSH[282];
5070 5 : memset( achTSH, ' ', sizeof(achTSH) );
5071 5 : VSIFSeekL( fpVSIL, 0, SEEK_END );
5072 :
5073 5 : if (pszHeaderBuffer!= NULL) {
5074 3 : memcpy( achTSH, pszHeaderBuffer, MIN(strlen(pszHeaderBuffer), sizeof(achTSH)) );
5075 :
5076 : // Take care NITF2.0 date format changes
5077 3 : char chTimeZone = achTSH[20];
5078 :
5079 : // Check for Zulu time zone character. IpachLTf that exist, then
5080 : // it's NITF2.0 format.
5081 3 : if (chTimeZone == 'Z') {
5082 0 : char *achOrigDate=achTSH+12; // original date string
5083 :
5084 : // The date value taken from default NITF file date
5085 0 : char achNewDate[]="20021216151629";
5086 : char achYear[3];
5087 : int nYear;
5088 :
5089 : // Offset to the year
5090 0 : strncpy(achYear,achOrigDate+12, 2);
5091 0 : achYear[2] = '\0';
5092 0 : nYear = atoi(achYear);
5093 :
5094 : // Set century.
5095 : // Since NITF2.0 does not track the century, we are going to
5096 : // assume any year number greater then 94 (the year NITF2.0
5097 : // spec published), will be 1900s, otherwise, it's 2000s.
5098 0 : if (nYear > 94) strncpy(achNewDate,"19",2);
5099 0 : else strncpy(achNewDate,"20",2);
5100 :
5101 0 : strncpy(achNewDate+6, achOrigDate,8); // copy cover DDhhmmss
5102 0 : strncpy(achNewDate+2, achOrigDate+12,2); // copy over years
5103 :
5104 : // Perform month conversion
5105 0 : char *pszOrigMonth = achOrigDate+9;
5106 0 : char *pszNewMonth = achNewDate+4;
5107 :
5108 0 : if (strncmp(pszOrigMonth,"JAN",3) == 0) strncpy(pszNewMonth,"01",2);
5109 0 : else if (strncmp(pszOrigMonth,"FEB",3) == 0) strncpy(pszNewMonth,"02",2);
5110 0 : else if (strncmp(pszOrigMonth,"MAR",3) == 0) strncpy(pszNewMonth,"03",2);
5111 0 : else if (strncmp(pszOrigMonth,"APR",3) == 0) strncpy(pszNewMonth,"04",2);
5112 0 : else if (strncmp(pszOrigMonth,"MAY",3) == 0) strncpy(pszNewMonth,"05",2);
5113 0 : else if (strncmp(pszOrigMonth,"JUN",3) == 0) strncpy(pszNewMonth,"07",2);
5114 0 : else if (strncmp(pszOrigMonth,"AUG",3) == 0) strncpy(pszNewMonth,"08",2);
5115 0 : else if (strncmp(pszOrigMonth,"SEP",3) == 0) strncpy(pszNewMonth,"09",2);
5116 0 : else if (strncmp(pszOrigMonth,"OCT",3) == 0) strncpy(pszNewMonth,"10",2);
5117 0 : else if (strncmp(pszOrigMonth,"NOV",3) == 0) strncpy(pszNewMonth,"11",2);
5118 0 : else if (strncmp(pszOrigMonth,"DEC",3) == 0) strncpy(pszNewMonth,"12",2);
5119 :
5120 0 : PLACE( achTSH+ 12, TXTDT , achNewDate );
5121 :
5122 : }
5123 : } else { // Use default value if header information is not found
5124 2 : PLACE( achTSH+ 0, TE , "TE" );
5125 2 : PLACE( achTSH+ 9, TXTALVL , "000" );
5126 2 : PLACE( achTSH+ 12, TXTDT , "20021216151629" );
5127 2 : PLACE( achTSH+106, TSCLAS , "U" );
5128 2 : PLACE( achTSH+273, ENCRYP , "0" );
5129 2 : PLACE( achTSH+274, TXTFMT , "STA" );
5130 2 : PLACE( achTSH+277, TXSHDL , "00000" );
5131 : }
5132 :
5133 :
5134 5 : VSIFWriteL( achTSH, 1, sizeof(achTSH), fpVSIL );
5135 :
5136 : /* -------------------------------------------------------------------- */
5137 : /* Prepare and write text segment data. */
5138 : /* -------------------------------------------------------------------- */
5139 :
5140 5 : int nTextLength = (int) strlen(pszTextToWrite);
5141 5 : if (nTextLength > 99998)
5142 : {
5143 : CPLError(CE_Warning, CPLE_NotSupported,
5144 : "Length of DATA_%d is %d, which is greater than 99998. Truncating...",
5145 0 : iTextSeg + 1, nTextLength);
5146 0 : nTextLength = 99998;
5147 : }
5148 :
5149 5 : VSIFWriteL( pszTextToWrite, 1, nTextLength, fpVSIL );
5150 :
5151 : /* -------------------------------------------------------------------- */
5152 : /* Update the subheader and data size info in the file header. */
5153 : /* -------------------------------------------------------------------- */
5154 : sprintf( pachLT + 9*iTextSeg+0, "%04d%05d",
5155 5 : (int) sizeof(achTSH), nTextLength );
5156 :
5157 5 : iTextSeg++;
5158 : }
5159 :
5160 : /* -------------------------------------------------------------------- */
5161 : /* Write out the text segment info. */
5162 : /* -------------------------------------------------------------------- */
5163 :
5164 4 : VSIFSeekL( fpVSIL, nNumTOffset + 3, SEEK_SET );
5165 4 : VSIFWriteL( pachLT, 1, nNUMT * 9, fpVSIL );
5166 :
5167 : /* -------------------------------------------------------------------- */
5168 : /* Update total file length. */
5169 : /* -------------------------------------------------------------------- */
5170 4 : VSIFSeekL( fpVSIL, 0, SEEK_END );
5171 4 : GUIntBig nFileLen = VSIFTellL( fpVSIL );
5172 :
5173 4 : VSIFSeekL( fpVSIL, 342, SEEK_SET );
5174 4 : if (GUINTBIG_TO_DOUBLE(nFileLen) >= 1e12 - 1)
5175 : {
5176 : CPLError(CE_Failure, CPLE_AppDefined,
5177 : "Too big file : " CPL_FRMT_GUIB ". Truncating to 999999999998",
5178 0 : nFileLen);
5179 0 : nFileLen = (GUIntBig)(1e12 - 2);
5180 : }
5181 4 : CPLString osLen = CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u",nFileLen);
5182 4 : VSIFWriteL( (void *) osLen.c_str(), 1, 12, fpVSIL );
5183 :
5184 4 : VSIFCloseL( fpVSIL );
5185 4 : CPLFree( pachLT );
5186 : }
5187 :
5188 : /************************************************************************/
5189 : /* NITFWriteJPEGImage() */
5190 : /************************************************************************/
5191 :
5192 : #ifdef JPEG_SUPPORTED
5193 :
5194 : int
5195 : NITFWriteJPEGBlock( GDALDataset *poSrcDS, VSILFILE *fp,
5196 : int nBlockXOff, int nBlockYOff,
5197 : int nBlockXSize, int nBlockYSize,
5198 : int bProgressive, int nQuality,
5199 : const GByte* pabyAPP6, int nRestartInterval,
5200 : GDALProgressFunc pfnProgress, void * pProgressData );
5201 :
5202 : static int
5203 5 : NITFWriteJPEGImage( GDALDataset *poSrcDS, VSILFILE *fp, vsi_l_offset nStartOffset,
5204 : char **papszOptions,
5205 : GDALProgressFunc pfnProgress, void * pProgressData )
5206 : {
5207 5 : int nBands = poSrcDS->GetRasterCount();
5208 5 : int nXSize = poSrcDS->GetRasterXSize();
5209 5 : int nYSize = poSrcDS->GetRasterYSize();
5210 5 : int nQuality = 75;
5211 5 : int bProgressive = FALSE;
5212 5 : int nRestartInterval = -1;
5213 :
5214 5 : if( !pfnProgress( 0.0, NULL, pProgressData ) )
5215 0 : return FALSE;
5216 :
5217 : /* -------------------------------------------------------------------- */
5218 : /* Some some rudimentary checks */
5219 : /* -------------------------------------------------------------------- */
5220 5 : if( nBands != 1 && nBands != 3 )
5221 : {
5222 : CPLError( CE_Failure, CPLE_NotSupported,
5223 : "JPEG driver doesn't support %d bands. Must be 1 (grey) "
5224 0 : "or 3 (RGB) bands.\n", nBands );
5225 :
5226 0 : return FALSE;
5227 : }
5228 :
5229 5 : GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
5230 :
5231 : #if defined(JPEG_LIB_MK1) || defined(JPEG_DUAL_MODE_8_12)
5232 5 : if( eDT != GDT_Byte && eDT != GDT_UInt16 )
5233 : {
5234 : CPLError( CE_Failure, CPLE_NotSupported,
5235 : "JPEG driver doesn't support data type %s. "
5236 : "Only eight and twelve bit bands supported (Mk1 libjpeg).\n",
5237 : GDALGetDataTypeName(
5238 0 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
5239 :
5240 0 : return FALSE;
5241 : }
5242 :
5243 6 : if( eDT == GDT_UInt16 || eDT == GDT_Int16 )
5244 1 : eDT = GDT_UInt16;
5245 : else
5246 4 : eDT = GDT_Byte;
5247 :
5248 : #else
5249 : if( eDT != GDT_Byte )
5250 : {
5251 : CPLError( CE_Failure, CPLE_NotSupported,
5252 : "JPEG driver doesn't support data type %s. "
5253 : "Only eight bit byte bands supported.\n",
5254 : GDALGetDataTypeName(
5255 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
5256 :
5257 : return FALSE;
5258 : }
5259 :
5260 : eDT = GDT_Byte; // force to 8bit.
5261 : #endif
5262 :
5263 : /* -------------------------------------------------------------------- */
5264 : /* What options has the user selected? */
5265 : /* -------------------------------------------------------------------- */
5266 5 : if( CSLFetchNameValue(papszOptions,"QUALITY") != NULL )
5267 : {
5268 2 : nQuality = atoi(CSLFetchNameValue(papszOptions,"QUALITY"));
5269 2 : if( nQuality < 10 || nQuality > 100 )
5270 : {
5271 : CPLError( CE_Failure, CPLE_IllegalArg,
5272 : "QUALITY=%s is not a legal value in the range 10-100.",
5273 0 : CSLFetchNameValue(papszOptions,"QUALITY") );
5274 0 : return FALSE;
5275 : }
5276 : }
5277 :
5278 5 : if( CSLFetchNameValue(papszOptions,"RESTART_INTERVAL") != NULL )
5279 : {
5280 0 : nRestartInterval = atoi(CSLFetchNameValue(papszOptions,"RESTART_INTERVAL"));
5281 : }
5282 :
5283 5 : bProgressive = CSLFetchBoolean( papszOptions, "PROGRESSIVE", FALSE );
5284 :
5285 : /* -------------------------------------------------------------------- */
5286 : /* Compute blocking factors */
5287 : /* -------------------------------------------------------------------- */
5288 5 : int nNPPBH = nXSize;
5289 5 : int nNPPBV = nYSize;
5290 :
5291 5 : if( CSLFetchNameValue( papszOptions, "BLOCKSIZE" ) != NULL )
5292 2 : nNPPBH = nNPPBV = atoi(CSLFetchNameValue( papszOptions, "BLOCKSIZE" ));
5293 :
5294 5 : if( CSLFetchNameValue( papszOptions, "BLOCKXSIZE" ) != NULL )
5295 1 : nNPPBH = atoi(CSLFetchNameValue( papszOptions, "BLOCKXSIZE" ));
5296 :
5297 5 : if( CSLFetchNameValue( papszOptions, "BLOCKYSIZE" ) != NULL )
5298 1 : nNPPBV = atoi(CSLFetchNameValue( papszOptions, "BLOCKYSIZE" ));
5299 :
5300 5 : if( CSLFetchNameValue( papszOptions, "NPPBH" ) != NULL )
5301 0 : nNPPBH = atoi(CSLFetchNameValue( papszOptions, "NPPBH" ));
5302 :
5303 5 : if( CSLFetchNameValue( papszOptions, "NPPBV" ) != NULL )
5304 0 : nNPPBV = atoi(CSLFetchNameValue( papszOptions, "NPPBV" ));
5305 :
5306 5 : if( nNPPBH <= 0 || nNPPBV <= 0 ||
5307 : nNPPBH > 9999 || nNPPBV > 9999 )
5308 0 : nNPPBH = nNPPBV = 256;
5309 :
5310 5 : int nNBPR = (nXSize + nNPPBH - 1) / nNPPBH;
5311 5 : int nNBPC = (nYSize + nNPPBV - 1) / nNPPBV;
5312 :
5313 : /* -------------------------------------------------------------------- */
5314 : /* Creates APP6 NITF application segment (required by MIL-STD-188-198) */
5315 : /* see #3345 */
5316 : /* -------------------------------------------------------------------- */
5317 : GByte abyAPP6[23];
5318 : GUInt16 nUInt16;
5319 5 : int nOffset = 0;
5320 :
5321 5 : memcpy(abyAPP6, "NITF", 4);
5322 5 : abyAPP6[4] = 0;
5323 5 : nOffset += 5;
5324 :
5325 : /* Version : 2.0 */
5326 5 : nUInt16 = 0x0200;
5327 5 : CPL_MSBPTR16(&nUInt16);
5328 5 : memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16));
5329 5 : nOffset += sizeof(nUInt16);
5330 :
5331 : /* IMODE */
5332 5 : abyAPP6[nOffset] = (nBands == 1) ? 'B' : 'P';
5333 5 : nOffset ++;
5334 :
5335 : /* Number of image blocks per row */
5336 5 : nUInt16 = (GUInt16) nNBPR;
5337 5 : CPL_MSBPTR16(&nUInt16);
5338 5 : memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16));
5339 5 : nOffset += sizeof(nUInt16);
5340 :
5341 : /* Number of image blocks per column */
5342 5 : nUInt16 = (GUInt16) nNBPC;
5343 5 : CPL_MSBPTR16(&nUInt16);
5344 5 : memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16));
5345 5 : nOffset += sizeof(nUInt16);
5346 :
5347 : /* Image color */
5348 5 : abyAPP6[nOffset] = (nBands == 1) ? 0 : 1;
5349 5 : nOffset ++;
5350 :
5351 : /* Original sample precision */
5352 5 : abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 12 : 8;
5353 5 : nOffset ++;
5354 :
5355 : /* Image class */
5356 5 : abyAPP6[nOffset] = 0;
5357 5 : nOffset ++;
5358 :
5359 : /* JPEG coding process */
5360 5 : abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 4 : 1;
5361 5 : nOffset ++;
5362 :
5363 : /* Quality */
5364 5 : abyAPP6[nOffset] = 0;
5365 5 : nOffset ++;
5366 :
5367 : /* Stream color */
5368 5 : abyAPP6[nOffset] = (nBands == 1) ? 0 /* Monochrome */ : 2 /* YCbCr*/ ;
5369 5 : nOffset ++;
5370 :
5371 : /* Stream bits */
5372 5 : abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 12 : 8;
5373 5 : nOffset ++;
5374 :
5375 : /* Horizontal filtering */
5376 5 : abyAPP6[nOffset] = 1;
5377 5 : nOffset ++;
5378 :
5379 : /* Vertical filtering */
5380 5 : abyAPP6[nOffset] = 1;
5381 5 : nOffset ++;
5382 :
5383 : /* Reserved */
5384 5 : abyAPP6[nOffset] = 0;
5385 5 : nOffset ++;
5386 5 : abyAPP6[nOffset] = 0;
5387 5 : nOffset ++;
5388 :
5389 5 : CPLAssert(nOffset == sizeof(abyAPP6));
5390 :
5391 : /* -------------------------------------------------------------------- */
5392 : /* Prepare block map if necessary */
5393 : /* -------------------------------------------------------------------- */
5394 :
5395 5 : VSIFSeekL( fp, nStartOffset, SEEK_SET );
5396 :
5397 5 : const char* pszIC = CSLFetchNameValue( papszOptions, "IC" );
5398 5 : GUInt32 nIMDATOFF = 0;
5399 5 : if (EQUAL(pszIC, "M3"))
5400 : {
5401 : GUInt32 nIMDATOFF_MSB;
5402 : GUInt16 nBMRLNTH, nTMRLNTH, nTPXCDLNTH;
5403 :
5404 : /* Prepare the block map */
5405 : #define BLOCKMAP_HEADER_SIZE (4 + 2 + 2 + 2)
5406 1 : nIMDATOFF_MSB = nIMDATOFF = BLOCKMAP_HEADER_SIZE + nNBPC * nNBPR * 4;
5407 1 : nBMRLNTH = 4;
5408 1 : nTMRLNTH = 0;
5409 1 : nTPXCDLNTH = 0;
5410 :
5411 1 : CPL_MSBPTR32( &nIMDATOFF_MSB );
5412 1 : CPL_MSBPTR16( &nBMRLNTH );
5413 1 : CPL_MSBPTR16( &nTMRLNTH );
5414 1 : CPL_MSBPTR16( &nTPXCDLNTH );
5415 :
5416 1 : VSIFWriteL( &nIMDATOFF_MSB, 1, 4, fp );
5417 1 : VSIFWriteL( &nBMRLNTH, 1, 2, fp );
5418 1 : VSIFWriteL( &nTMRLNTH, 1, 2, fp );
5419 1 : VSIFWriteL( &nTPXCDLNTH, 1, 2, fp );
5420 :
5421 : /* Reserve space for the table itself */
5422 1 : VSIFSeekL( fp, nNBPC * nNBPR * 4, SEEK_CUR );
5423 : }
5424 :
5425 : /* -------------------------------------------------------------------- */
5426 : /* Copy each block */
5427 : /* -------------------------------------------------------------------- */
5428 : int nBlockXOff, nBlockYOff;
5429 12 : for(nBlockYOff=0;nBlockYOff<nNBPC;nBlockYOff++)
5430 : {
5431 18 : for(nBlockXOff=0;nBlockXOff<nNBPR;nBlockXOff++)
5432 : {
5433 : /*CPLDebug("NITF", "nBlockXOff=%d/%d, nBlockYOff=%d/%d",
5434 : nBlockXOff, nNBPR, nBlockYOff, nNBPC);*/
5435 11 : if (EQUAL(pszIC, "M3"))
5436 : {
5437 : /* Write block offset for current block */
5438 :
5439 4 : GUIntBig nCurPos = VSIFTellL(fp);
5440 4 : VSIFSeekL( fp, nStartOffset + BLOCKMAP_HEADER_SIZE + 4 * (nBlockYOff * nNBPR + nBlockXOff), SEEK_SET );
5441 4 : GUIntBig nBlockOffset = nCurPos - nStartOffset - nIMDATOFF;
5442 4 : GUInt32 nBlockOffset32 = (GUInt32)nBlockOffset;
5443 4 : if (nBlockOffset == (GUIntBig)nBlockOffset32)
5444 : {
5445 4 : CPL_MSBPTR32( &nBlockOffset32 );
5446 4 : VSIFWriteL( &nBlockOffset32, 1, 4, fp );
5447 : }
5448 : else
5449 : {
5450 : CPLError(CE_Failure, CPLE_AppDefined,
5451 : "Offset for block (%d, %d) = " CPL_FRMT_GUIB ". Cannot fit into 32 bits...",
5452 0 : nBlockXOff, nBlockYOff, nBlockOffset);
5453 :
5454 0 : nBlockOffset32 = 0xffffffff;
5455 : int i;
5456 0 : for(i=nBlockYOff * nNBPR + nBlockXOff; i < nNBPC * nNBPR; i++)
5457 : {
5458 0 : VSIFWriteL( &nBlockOffset32, 1, 4, fp );
5459 : }
5460 0 : return FALSE;
5461 : }
5462 4 : VSIFSeekL( fp, nCurPos, SEEK_SET );
5463 : }
5464 :
5465 11 : if (!NITFWriteJPEGBlock(poSrcDS, fp,
5466 : nBlockXOff, nBlockYOff,
5467 : nNPPBH, nNPPBV,
5468 : bProgressive, nQuality,
5469 : (nBlockXOff == 0 && nBlockYOff == 0) ? abyAPP6 : NULL,
5470 : nRestartInterval,
5471 : pfnProgress, pProgressData))
5472 : {
5473 0 : return FALSE;
5474 : }
5475 : }
5476 : }
5477 5 : return TRUE;
5478 : }
5479 :
5480 : #endif /* def JPEG_SUPPORTED */
5481 :
5482 : /************************************************************************/
5483 : /* GDALRegister_NITF() */
5484 : /************************************************************************/
5485 :
5486 : typedef struct
5487 : {
5488 : int nMaxLen;
5489 : const char* pszName;
5490 : const char* pszDescription;
5491 : } NITFFieldDescription;
5492 :
5493 : /* Keep in sync with NITFCreate */
5494 : static const NITFFieldDescription asFieldDescription [] =
5495 : {
5496 : { 2, "CLEVEL", "Complexity level" } ,
5497 : { 10, "OSTAID", "Originating Station ID" } ,
5498 : { 14, "FDT", "File Date and Time" } ,
5499 : { 80, "FTITLE", "File Title" } ,
5500 : { 1, "FSCLAS", "File Security Classification" } ,
5501 : { 2, "FSCLSY", "File Classification Security System" } ,
5502 : { 11, "FSCODE", "File Codewords" } ,
5503 : { 2, "FSCTLH", "File Control and Handling" } ,
5504 : { 20, "FSREL", "File Releasing Instructions" } ,
5505 : { 2, "FSDCTP", "File Declassification Type" } ,
5506 : { 8, "FSDCDT", "File Declassification Date" } ,
5507 : { 4, "FSDCXM", "File Declassification Exemption" } ,
5508 : { 1, "FSDG", "File Downgrade" } ,
5509 : { 8, "FSDGDT", "File Downgrade Date" } ,
5510 : { 43, "FSCLTX", "File Classification Text" } ,
5511 : { 1, "FSCATP", "File Classification Authority Type" } ,
5512 : { 40, "FSCAUT", "File Classification Authority" } ,
5513 : { 1, "FSCRSN", "File Classification Reason" } ,
5514 : { 8, "FSSRDT", "File Security Source Date" } ,
5515 : { 15, "FSCTLN", "File Security Control Number" } ,
5516 : { 5, "FSCOP", "File Copy Number" } ,
5517 : { 5, "FSCPYS", "File Number of Copies" } ,
5518 : { 24, "ONAME", "Originator Name" } ,
5519 : { 18, "OPHONE", "Originator Phone Number" } ,
5520 : { 10, "IID1", "Image Identifier 1" } ,
5521 : { 14, "IDATIM", "Image Date and Time" } ,
5522 : { 17, "TGTID", "Target Identifier" } ,
5523 : { 80, "IID2", "Image Identifier 2" } ,
5524 : { 1, "ISCLAS", "Image Security Classification" } ,
5525 : { 2, "ISCLSY", "Image Classification Security System" } ,
5526 : { 11, "ISCODE", "Image Codewords" } ,
5527 : { 2, "ISCTLH", "Image Control and Handling" } ,
5528 : { 20, "ISREL", "Image Releasing Instructions" } ,
5529 : { 2, "ISDCTP", "Image Declassification Type" } ,
5530 : { 8, "ISDCDT", "Image Declassification Date" } ,
5531 : { 4, "ISDCXM", "Image Declassification Exemption" } ,
5532 : { 1, "ISDG", "Image Downgrade" } ,
5533 : { 8, "ISDGDT", "Image Downgrade Date" } ,
5534 : { 43, "ISCLTX", "Image Classification Text" } ,
5535 : { 1, "ISCATP", "Image Classification Authority Type" } ,
5536 : { 40, "ISCAUT", "Image Classification Authority" } ,
5537 : { 1, "ISCRSN", "Image Classification Reason" } ,
5538 : { 8, "ISSRDT", "Image Security Source Date" } ,
5539 : { 15, "ISCTLN", "Image Security Control Number" } ,
5540 : { 42, "ISORCE", "Image Source" } ,
5541 : { 8, "ICAT", "Image Category" } ,
5542 : { 2, "ABPP", "Actual Bits-Per-Pixel Per Band" } ,
5543 : { 1, "PJUST", "Pixel Justification" } ,
5544 : {780, "ICOM", "Image Comments (up to 9x80 characters)" } ,
5545 : };
5546 :
5547 : /* Keep in sync with NITFWriteBLOCKA */
5548 : static const char *apszFieldsBLOCKA[] = {
5549 : "BLOCK_INSTANCE", "0", "2",
5550 : "N_GRAY", "2", "5",
5551 : "L_LINES", "7", "5",
5552 : "LAYOVER_ANGLE", "12", "3",
5553 : "SHADOW_ANGLE", "15", "3",
5554 : "BLANKS", "18", "16",
5555 : "FRLC_LOC", "34", "21",
5556 : "LRLC_LOC", "55", "21",
5557 : "LRFC_LOC", "76", "21",
5558 : "FRFC_LOC", "97", "21",
5559 : NULL, NULL, NULL };
5560 :
5561 610 : void GDALRegister_NITF()
5562 :
5563 : {
5564 : GDALDriver *poDriver;
5565 :
5566 610 : if( GDALGetDriverByName( "NITF" ) == NULL )
5567 : {
5568 : unsigned int i;
5569 588 : CPLString osCreationOptions;
5570 :
5571 : osCreationOptions =
5572 : "<CreationOptionList>"
5573 : " <Option name='IC' type='string-select' default='NC' description='Compression mode. NC=no compression. "
5574 : #ifdef JPEG_SUPPORTED
5575 : "C3/M3=JPEG compression. "
5576 : #endif
5577 : "C8=JP2 compression through the JP2ECW driver"
5578 : "'>"
5579 : " <Value>NC</Value>"
5580 : #ifdef JPEG_SUPPORTED
5581 : " <Value>C3</Value>"
5582 : " <Value>M3</Value>"
5583 : #endif
5584 : " <Value>C8</Value>"
5585 : " </Option>"
5586 : #ifdef JPEG_SUPPORTED
5587 : " <Option name='QUALITY' type='int' description='JPEG quality 10-100' default='75'/>"
5588 : " <Option name='PROGRESSIVE' type='boolean' description='JPEG progressive mode'/>"
5589 : " <Option name='RESTART_INTERVAL' type='int' description='Restart interval (in MCUs). -1 for auto, 0 for none, > 0 for user specified' default='-1'/>"
5590 : #endif
5591 : " <Option name='NUMI' type='int' default='1' description='Number of images to create (1-999). Only works with IC=NC'/>"
5592 : " <Option name='TARGET' type='float' description='For JP2 only. Compression Percentage'/>"
5593 : " <Option name='PROFILE' type='string-select' description='For JP2 only.'>"
5594 : " <Value>BASELINE_0</Value>"
5595 : " <Value>BASELINE_1</Value>"
5596 : " <Value>BASELINE_2</Value>"
5597 : " <Value>NPJE</Value>"
5598 : " <Value>EPJE</Value>"
5599 : " </Option>"
5600 : " <Option name='ICORDS' type='string-select' description='To ensure that space will be reserved for geographic corner coordinates in DMS (G), in decimal degrees (D), UTM North (N) or UTM South (S)'>"
5601 : " <Value>G</Value>"
5602 : " <Value>D</Value>"
5603 : " <Value>N</Value>"
5604 : " <Value>S</Value>"
5605 : " </Option>"
5606 : " <Option name='FHDR' type='string-select' description='File version' default='NITF02.10'>"
5607 : " <Value>NITF02.10</Value>"
5608 : " <Value>NSIF01.00</Value>"
5609 : " </Option>"
5610 : " <Option name='IREP' type='string' description='Set to RGB/LUT to reserve space for a color table for each output band. (Only needed for Create() method, not CreateCopy())'/>"
5611 : " <Option name='IREPBAND' type='string' description='Comma separated list of band IREPBANDs in band order'/>"
5612 : " <Option name='ISUBCAT' type='string' description='Comma separated list of band ISUBCATs in band order'/>"
5613 : " <Option name='LUT_SIZE' type='integer' description='Set to control the size of pseudocolor tables for RGB/LUT bands' default='256'/>"
5614 : " <Option name='BLOCKXSIZE' type='int' description='Set the block width'/>"
5615 : " <Option name='BLOCKYSIZE' type='int' description='Set the block height'/>"
5616 : " <Option name='BLOCKSIZE' type='int' description='Set the block with and height. Overridden by BLOCKXSIZE and BLOCKYSIZE'/>"
5617 : " <Option name='TEXT' type='string' description='TEXT options as text-option-name=text-option-content'/>"
5618 588 : " <Option name='CGM' type='string' description='CGM options in cgm-option-name=cgm-option-content'/>";
5619 :
5620 29400 : for(i=0;i<sizeof(asFieldDescription) / sizeof(asFieldDescription[0]); i++)
5621 : {
5622 : osCreationOptions += CPLString().Printf(" <Option name='%s' type='string' description='%s' maxsize='%d'/>",
5623 28812 : asFieldDescription[i].pszName, asFieldDescription[i].pszDescription, asFieldDescription[i].nMaxLen);
5624 : }
5625 :
5626 : osCreationOptions +=
5627 : " <Option name='TRE' type='string' description='Under the format TRE=tre-name,tre-contents'/>"
5628 : " <Option name='FILE_TRE' type='string' description='Under the format FILE_TRE=tre-name,tre-contents'/>"
5629 588 : " <Option name='BLOCKA_BLOCK_COUNT' type='int'/>";
5630 :
5631 6468 : for(i=0; apszFieldsBLOCKA[i] != NULL; i+=3)
5632 : {
5633 : char szFieldDescription[128];
5634 : sprintf(szFieldDescription, " <Option name='BLOCKA_%s_*' type='string' maxsize='%d'/>",
5635 5880 : apszFieldsBLOCKA[i], atoi(apszFieldsBLOCKA[i+2]));
5636 5880 : osCreationOptions += szFieldDescription;
5637 : }
5638 : osCreationOptions +=
5639 588 : " <Option name='SDE_TRE' type='boolean' description='Write GEOLOB and GEOPSB TREs (only geographic SRS for now)' default='NO'/>";
5640 588 : osCreationOptions += "</CreationOptionList>";
5641 :
5642 588 : poDriver = new GDALDriver();
5643 :
5644 588 : poDriver->SetDescription( "NITF" );
5645 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
5646 588 : "National Imagery Transmission Format" );
5647 :
5648 588 : poDriver->pfnIdentify = NITFDataset::Identify;
5649 588 : poDriver->pfnOpen = NITFDataset::Open;
5650 588 : poDriver->pfnCreate = NITFDataset::NITFDatasetCreate;
5651 588 : poDriver->pfnCreateCopy = NITFDataset::NITFCreateCopy;
5652 :
5653 588 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_nitf.html" );
5654 588 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "ntf" );
5655 588 : poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" );
5656 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
5657 588 : "Byte UInt16 Int16 UInt32 Int32 Float32" );
5658 :
5659 588 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, osCreationOptions);
5660 588 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
5661 :
5662 588 : GetGDALDriverManager()->RegisterDriver( poDriver );
5663 : }
5664 610 : }
|