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