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