LCOV - code coverage report
Current view: directory - frmts/nitf - nitfdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 2042 1640 80.3 %
Date: 2012-04-28 Functions: 45 41 91.1 %

       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 : }

Generated by: LCOV version 1.7