LCOV - code coverage report
Current view: directory - frmts/nitf - nitfdataset.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 1992 1576 79.1 %
Date: 2011-12-18 Functions: 44 38 86.4 %

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

Generated by: LCOV version 1.7