ultimatepp/bazaar/plugin/gdal/frmts/nitf/nitfimage.c
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

3871 lines
150 KiB
C

/******************************************************************************
* $Id: nitfimage.c 28039 2014-11-30 18:24:59Z rouault $
*
* Project: NITF Read/Write Library
* Purpose: Module responsible for implementation of most NITFImage
* implementation.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
**********************************************************************
* Copyright (c) 2002, Frank Warmerdam
* Copyright (c) 2007-2013, Even Rouault <even dot rouault at mines-paris dot org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include "gdal.h"
#include "nitflib.h"
#include "mgrs.h"
#include "cpl_vsi.h"
#include "cpl_conv.h"
#include "cpl_string.h"
CPL_CVSID("$Id: nitfimage.c 28039 2014-11-30 18:24:59Z rouault $");
static int NITFReadIMRFCA( NITFImage *psImage, NITFRPC00BInfo *psRPC );
static char *NITFTrimWhite( char * );
#ifdef CPL_LSB
static void NITFSwapWords( NITFImage *psImage, void *pData, int nWordCount );
#endif
static void NITFLoadLocationTable( NITFImage *psImage );
static void NITFLoadColormapSubSection( NITFImage *psImage );
static void NITFLoadSubframeMaskTable( NITFImage *psImage );
static int NITFLoadVQTables( NITFImage *psImage, int bTryGuessingOffset );
static int NITFReadGEOLOB( NITFImage *psImage );
static void NITFLoadAttributeSection( NITFImage *psImage );
static void NITFPossibleIGEOLOReorientation( NITFImage *psImage );
void NITFGetGCP ( const char* pachCoord, double *pdfXYs, int iCoord );
int NITFReadBLOCKA_GCPs ( NITFImage *psImage );
#define GOTO_header_too_small() do { nFaultyLine = __LINE__; goto header_too_small; } while(0)
/************************************************************************/
/* NITFImageAccess() */
/************************************************************************/
NITFImage *NITFImageAccess( NITFFile *psFile, int iSegment )
{
NITFImage *psImage;
char *pachHeader;
NITFSegmentInfo *psSegInfo;
char szTemp[128];
int nOffset, iBand, i;
int nNICOM;
const char* pszIID1;
int nFaultyLine = -1;
int bGotWrongOffset = FALSE;
/* -------------------------------------------------------------------- */
/* Verify segment, and return existing image accessor if there */
/* is one. */
/* -------------------------------------------------------------------- */
if( iSegment < 0 || iSegment >= psFile->nSegmentCount )
return NULL;
psSegInfo = psFile->pasSegmentInfo + iSegment;
if( !EQUAL(psSegInfo->szSegmentType,"IM") )
return NULL;
if( psSegInfo->hAccess != NULL )
return (NITFImage *) psSegInfo->hAccess;
/* -------------------------------------------------------------------- */
/* Read the image subheader. */
/* -------------------------------------------------------------------- */
if (psSegInfo->nSegmentHeaderSize < 370 + 1)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Image header too small");
return NULL;
}
pachHeader = (char*) VSIMalloc(psSegInfo->nSegmentHeaderSize);
if (pachHeader == NULL)
{
CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate memory for segment header");
return NULL;
}
if( VSIFSeekL( psFile->fp, psSegInfo->nSegmentHeaderStart,
SEEK_SET ) != 0
|| VSIFReadL( pachHeader, 1, psSegInfo->nSegmentHeaderSize,
psFile->fp ) != psSegInfo->nSegmentHeaderSize )
{
CPLError( CE_Failure, CPLE_FileIO,
"Failed to read %u byte image subheader from " CPL_FRMT_GUIB ".",
psSegInfo->nSegmentHeaderSize,
psSegInfo->nSegmentHeaderStart );
CPLFree(pachHeader);
return NULL;
}
/* -------------------------------------------------------------------- */
/* Initialize image object. */
/* -------------------------------------------------------------------- */
psImage = (NITFImage *) CPLCalloc(sizeof(NITFImage),1);
psImage->psFile = psFile;
psImage->iSegment = iSegment;
psImage->pachHeader = pachHeader;
psSegInfo->hAccess = psImage;
/* -------------------------------------------------------------------- */
/* Collect a variety of information as metadata. */
/* -------------------------------------------------------------------- */
#define GetMD( target, hdr, start, length, name ) \
NITFExtractMetadata( &(target->papszMetadata), hdr, \
start, length, \
"NITF_" #name );
if( EQUAL(psFile->szVersion,"NITF02.10")
|| EQUAL(psFile->szVersion,"NSIF01.00") )
{
GetMD( psImage, pachHeader, 2, 10, IID1 );
GetMD( psImage, pachHeader, 12, 14, IDATIM );
GetMD( psImage, pachHeader, 26, 17, TGTID );
GetMD( psImage, pachHeader, 43, 80, IID2 );
GetMD( psImage, pachHeader, 123, 1, ISCLAS );
GetMD( psImage, pachHeader, 124, 2, ISCLSY );
GetMD( psImage, pachHeader, 126, 11, ISCODE );
GetMD( psImage, pachHeader, 137, 2, ISCTLH );
GetMD( psImage, pachHeader, 139, 20, ISREL );
GetMD( psImage, pachHeader, 159, 2, ISDCTP );
GetMD( psImage, pachHeader, 161, 8, ISDCDT );
GetMD( psImage, pachHeader, 169, 4, ISDCXM );
GetMD( psImage, pachHeader, 173, 1, ISDG );
GetMD( psImage, pachHeader, 174, 8, ISDGDT );
GetMD( psImage, pachHeader, 182, 43, ISCLTX );
GetMD( psImage, pachHeader, 225, 1, ISCATP );
GetMD( psImage, pachHeader, 226, 40, ISCAUT );
GetMD( psImage, pachHeader, 266, 1, ISCRSN );
GetMD( psImage, pachHeader, 267, 8, ISSRDT );
GetMD( psImage, pachHeader, 275, 15, ISCTLN );
/* skip ENCRYPT - 1 character */
GetMD( psImage, pachHeader, 291, 42, ISORCE );
/* skip NROWS (8), and NCOLS (8) */
GetMD( psImage, pachHeader, 349, 3, PVTYPE );
GetMD( psImage, pachHeader, 352, 8, IREP );
GetMD( psImage, pachHeader, 360, 8, ICAT );
GetMD( psImage, pachHeader, 368, 2, ABPP );
GetMD( psImage, pachHeader, 370, 1, PJUST );
}
else if( EQUAL(psFile->szVersion,"NITF02.00") )
{
int nOffset = 0;
GetMD( psImage, pachHeader, 2, 10, IID1 );
GetMD( psImage, pachHeader, 12, 14, IDATIM );
GetMD( psImage, pachHeader, 26, 17, TGTID );
GetMD( psImage, pachHeader, 43, 80, ITITLE );
GetMD( psImage, pachHeader, 123, 1, ISCLAS );
GetMD( psImage, pachHeader, 124, 40, ISCODE );
GetMD( psImage, pachHeader, 164, 40, ISCTLH );
GetMD( psImage, pachHeader, 204, 40, ISREL );
GetMD( psImage, pachHeader, 244, 20, ISCAUT );
GetMD( psImage, pachHeader, 264, 20, ISCTLN );
GetMD( psImage, pachHeader, 284, 6, ISDWNG );
if( EQUALN(pachHeader+284,"999998",6) )
{
if (psSegInfo->nSegmentHeaderSize < 370 + 40 + 1)
GOTO_header_too_small();
GetMD( psImage, pachHeader, 290, 40, ISDEVT );
nOffset += 40;
}
/* skip ENCRYPT - 1 character */
GetMD( psImage, pachHeader, 291+nOffset, 42, ISORCE );
/* skip NROWS (8), and NCOLS (8) */
GetMD( psImage, pachHeader, 349+nOffset, 3, PVTYPE );
GetMD( psImage, pachHeader, 352+nOffset, 8, IREP );
GetMD( psImage, pachHeader, 360+nOffset, 8, ICAT );
GetMD( psImage, pachHeader, 368+nOffset, 2, ABPP );
GetMD( psImage, pachHeader, 370+nOffset, 1, PJUST );
}
/* -------------------------------------------------------------------- */
/* Does this header have the FSDEVT field? */
/* -------------------------------------------------------------------- */
nOffset = 333;
if( EQUALN(psFile->szVersion,"NITF01.",7)
|| EQUALN(pachHeader+284,"999998",6) )
nOffset += 40;
/* -------------------------------------------------------------------- */
/* Read lots of header fields. */
/* -------------------------------------------------------------------- */
if( !EQUALN(psFile->szVersion,"NITF01.",7) )
{
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 35+2)
GOTO_header_too_small();
psImage->nRows = atoi(NITFGetField(szTemp,pachHeader,nOffset,8));
psImage->nCols = atoi(NITFGetField(szTemp,pachHeader,nOffset+8,8));
NITFTrimWhite( NITFGetField( psImage->szPVType, pachHeader,
nOffset+16, 3) );
NITFTrimWhite( NITFGetField( psImage->szIREP, pachHeader,
nOffset+19, 8) );
NITFTrimWhite( NITFGetField( psImage->szICAT, pachHeader,
nOffset+27, 8) );
psImage->nABPP = atoi(NITFGetField(szTemp,pachHeader,nOffset+35,2));
}
nOffset += 38;
/* -------------------------------------------------------------------- */
/* Do we have IGEOLO information? In NITF 2.0 (and 1.x) 'N' means */
/* no information, while in 2.1 this is indicated as ' ', and 'N' */
/* means UTM (north). So for 2.0 products we change 'N' to ' ' */
/* to conform to 2.1 conventions. */
/* -------------------------------------------------------------------- */
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 1)
GOTO_header_too_small();
GetMD( psImage, pachHeader, nOffset, 1, ICORDS );
psImage->chICORDS = pachHeader[nOffset++];
psImage->bHaveIGEOLO = FALSE;
if( (EQUALN(psFile->szVersion,"NITF02.0",8)
|| EQUALN(psFile->szVersion,"NITF01.",7))
&& psImage->chICORDS == 'N' )
psImage->chICORDS = ' ';
/* -------------------------------------------------------------------- */
/* Read the image bounds. */
/* -------------------------------------------------------------------- */
if( psImage->chICORDS != ' ' )
{
int iCoord;
psImage->bHaveIGEOLO = TRUE;
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 4 * 15)
GOTO_header_too_small();
GetMD( psImage, pachHeader, nOffset, 60, IGEOLO );
psImage->bIsBoxCenterOfPixel = TRUE;
for( iCoord = 0; iCoord < 4; iCoord++ )
{
const char *pszCoordPair = pachHeader + nOffset + iCoord*15;
double *pdfXY = &(psImage->dfULX) + iCoord*2;
if( psImage->chICORDS == 'N' || psImage->chICORDS == 'S' )
{
psImage->nZone =
atoi(NITFGetField( szTemp, pszCoordPair, 0, 2 ));
pdfXY[0] = CPLAtof(NITFGetField( szTemp, pszCoordPair, 2, 6 ));
pdfXY[1] = CPLAtof(NITFGetField( szTemp, pszCoordPair, 8, 7 ));
}
else if( psImage->chICORDS == 'G' || psImage->chICORDS == 'C' )
{
pdfXY[1] =
CPLAtof(NITFGetField( szTemp, pszCoordPair, 0, 2 ))
+ CPLAtof(NITFGetField( szTemp, pszCoordPair, 2, 2 )) / 60.0
+ CPLAtof(NITFGetField( szTemp, pszCoordPair, 4, 2 )) / 3600.0;
if( pszCoordPair[6] == 's' || pszCoordPair[6] == 'S' )
pdfXY[1] *= -1;
pdfXY[0] =
CPLAtof(NITFGetField( szTemp, pszCoordPair, 7, 3 ))
+ CPLAtof(NITFGetField( szTemp, pszCoordPair,10, 2 )) / 60.0
+ CPLAtof(NITFGetField( szTemp, pszCoordPair,12, 2 )) / 3600.0;
if( pszCoordPair[14] == 'w' || pszCoordPair[14] == 'W' )
pdfXY[0] *= -1;
}
else if( psImage->chICORDS == 'D' )
{ /* 'D' is Decimal Degrees */
pdfXY[1] = CPLAtof(NITFGetField( szTemp, pszCoordPair, 0, 7 ));
pdfXY[0] = CPLAtof(NITFGetField( szTemp, pszCoordPair, 7, 8 ));
}
else if( psImage->chICORDS == 'U' )
{
/* int err; */
long nZone;
char chHemisphere;
NITFGetField( szTemp, pszCoordPair, 0, 15 );
CPLDebug( "NITF", "IGEOLO = %15.15s", pszCoordPair );
/* err = */ Convert_MGRS_To_UTM( szTemp, &nZone, &chHemisphere,
pdfXY+0, pdfXY+1 );
if( chHemisphere == 'S' )
nZone = -1 * nZone;
if( psImage->nZone != 0 && psImage->nZone != -100 )
{
if( nZone != psImage->nZone )
{
CPLError( CE_Warning, CPLE_AppDefined,
"Some IGEOLO points are in different UTM\n"
"zones, but this configuration isn't currently\n"
"supported by GDAL, ignoring IGEOLO." );
psImage->nZone = -100;
}
}
else if( psImage->nZone == 0 )
{
psImage->nZone = nZone;
}
}
}
if( psImage->nZone == -100 )
psImage->nZone = 0;
nOffset += 60;
}
/* -------------------------------------------------------------------- */
/* Should we reorient the IGEOLO points in an attempt to handle */
/* files where they were written in the wrong order? */
/* -------------------------------------------------------------------- */
if( psImage->bHaveIGEOLO )
NITFPossibleIGEOLOReorientation( psImage );
/* -------------------------------------------------------------------- */
/* Read the image comments. */
/* -------------------------------------------------------------------- */
{
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 1 )
GOTO_header_too_small();
nNICOM = atoi(NITFGetField( szTemp, pachHeader, nOffset++, 1));
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 80 * nNICOM )
GOTO_header_too_small();
psImage->pszComments = (char *) CPLMalloc(nNICOM*80+1);
NITFGetField( psImage->pszComments, pachHeader,
nOffset, 80 * nNICOM );
nOffset += nNICOM * 80;
}
/* -------------------------------------------------------------------- */
/* Read more stuff. */
/* -------------------------------------------------------------------- */
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 2 )
GOTO_header_too_small();
NITFGetField( psImage->szIC, pachHeader, nOffset, 2 );
nOffset += 2;
if( psImage->szIC[0] != 'N' )
{
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 4 )
GOTO_header_too_small();
NITFGetField( psImage->szCOMRAT, pachHeader, nOffset, 4 );
nOffset += 4;
}
/* NBANDS */
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 1 )
GOTO_header_too_small();
psImage->nBands = atoi(NITFGetField(szTemp,pachHeader,nOffset,1));
nOffset++;
/* XBANDS */
if( psImage->nBands == 0 )
{
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 5 )
GOTO_header_too_small();
psImage->nBands = atoi(NITFGetField(szTemp,pachHeader,nOffset,5));
nOffset += 5;
}
if (psImage->nBands <= 0)
{
CPLError(CE_Failure, CPLE_AppDefined, "Invalid band number");
NITFImageDeaccess(psImage);
return NULL;
}
/* -------------------------------------------------------------------- */
/* Read per-band information. */
/* -------------------------------------------------------------------- */
psImage->pasBandInfo = (NITFBandInfo *)
VSICalloc(sizeof(NITFBandInfo),psImage->nBands);
if (psImage->pasBandInfo == NULL)
{
CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate memory for band info");
NITFImageDeaccess(psImage);
return NULL;
}
for( iBand = 0; iBand < psImage->nBands; iBand++ )
{
NITFBandInfo *psBandInfo = psImage->pasBandInfo + iBand;
int nLUTS;
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 2 + 6 + 4 + 1 + 5)
GOTO_header_too_small();
NITFTrimWhite(
NITFGetField( psBandInfo->szIREPBAND, pachHeader, nOffset, 2 ) );
nOffset += 2;
NITFTrimWhite(
NITFGetField( psBandInfo->szISUBCAT, pachHeader, nOffset, 6 ) );
nOffset += 6;
nOffset += 4; /* Skip IFCn and IMFLTn */
nLUTS = atoi(NITFGetField( szTemp, pachHeader, nOffset, 1 ));
nOffset += 1;
if( nLUTS == 0 )
continue;
psBandInfo->nSignificantLUTEntries =
atoi(NITFGetField( szTemp, pachHeader, nOffset, 5 ));
nOffset += 5;
if (psBandInfo->nSignificantLUTEntries < 0 ||
psBandInfo->nSignificantLUTEntries > 256)
{
CPLError( CE_Warning, CPLE_AppDefined,
"LUT for band %d is corrupted : nSignificantLUTEntries=%d. Truncating to 256",
iBand + 1, psBandInfo->nSignificantLUTEntries);
psBandInfo->nSignificantLUTEntries = 256;
}
psBandInfo->nLUTLocation = nOffset +
(int)psSegInfo->nSegmentHeaderStart;
psBandInfo->pabyLUT = (unsigned char *) CPLCalloc(768,1);
if ( (int)psSegInfo->nSegmentHeaderSize <
nOffset + nLUTS * psBandInfo->nSignificantLUTEntries )
GOTO_header_too_small();
memcpy( psBandInfo->pabyLUT, pachHeader + nOffset,
psBandInfo->nSignificantLUTEntries );
nOffset += psBandInfo->nSignificantLUTEntries;
if( nLUTS == 3 )
{
memcpy( psBandInfo->pabyLUT+256, pachHeader + nOffset,
psBandInfo->nSignificantLUTEntries );
nOffset += psBandInfo->nSignificantLUTEntries;
memcpy( psBandInfo->pabyLUT+512, pachHeader + nOffset,
psBandInfo->nSignificantLUTEntries );
nOffset += psBandInfo->nSignificantLUTEntries;
}
else if( (nLUTS == 2) && (EQUALN(psImage->szIREP,"MONO",4)) &&
((EQUALN(psBandInfo->szIREPBAND, "M", 1)) || (EQUALN(psBandInfo->szIREPBAND, "LU", 2))) )
{
int iLUTEntry;
double scale = 255.0/65535.0;
unsigned char *pMSB = NULL;
unsigned char *pLSB = NULL;
unsigned char *p3rdLUT = NULL;
unsigned char scaledVal = 0;
unsigned short *pLUTVal = NULL;
/* In this case, we have two LUTs. The first and second LUTs should map respectively to the most */
/* significant byte and the least significant byte of the 16 bit values. */
memcpy( psBandInfo->pabyLUT+256, pachHeader + nOffset,
psBandInfo->nSignificantLUTEntries );
nOffset += psBandInfo->nSignificantLUTEntries;
pMSB = psBandInfo->pabyLUT;
pLSB = psBandInfo->pabyLUT + 256;
p3rdLUT = psBandInfo->pabyLUT + 512;
/* E. Rouault: Why 255 and not 256 ? */
pLUTVal = (unsigned short*) CPLMalloc(sizeof(short)*255);
for( iLUTEntry = 0; iLUTEntry < 255; ++iLUTEntry )
{
/* E. Rouault: I don't understand why the following logic is endianness dependant */
pLUTVal[iLUTEntry] = ((pMSB[iLUTEntry] << 8) | pLSB[iLUTEntry]);
#ifdef CPL_LSB
pLUTVal[iLUTEntry] = ((pLUTVal[iLUTEntry] >> 8) | (pLUTVal[iLUTEntry] << 8));
#endif
}
for( iLUTEntry = 0; iLUTEntry < 255; ++iLUTEntry )
{
scaledVal = (unsigned char) ceil((double) (pLUTVal[iLUTEntry]*scale));
pMSB[iLUTEntry] = scaledVal;
pLSB[iLUTEntry] = scaledVal;
p3rdLUT[iLUTEntry] = scaledVal;
}
CPLFree(pLUTVal);
}
else
{
/* morph greyscale lut into RGB LUT. */
memcpy( psBandInfo->pabyLUT+256, psBandInfo->pabyLUT, 256 );
memcpy( psBandInfo->pabyLUT+512, psBandInfo->pabyLUT, 256 );
}
}
/* -------------------------------------------------------------------- */
/* Some files (ie NSIF datasets) have truncated image */
/* headers. This has been observed with jpeg compressed */
/* files. In this case guess reasonable values for these */
/* fields. */
/* -------------------------------------------------------------------- */
if( nOffset + 40 > (int)psSegInfo->nSegmentHeaderSize )
{
psImage->chIMODE = 'B';
psImage->nBlocksPerRow = 1;
psImage->nBlocksPerColumn = 1;
psImage->nBlockWidth = psImage->nCols;
psImage->nBlockHeight = psImage->nRows;
psImage->nBitsPerSample = psImage->nABPP;
psImage->nIDLVL = 0;
psImage->nIALVL = 0;
psImage->nILOCRow = 0;
psImage->nILOCColumn = 0;
psImage->szIMAG[0] = '\0';
nOffset += 40;
}
/* -------------------------------------------------------------------- */
/* Read more header fields. */
/* -------------------------------------------------------------------- */
else
{
psImage->chIMODE = pachHeader[nOffset + 1];
psImage->nBlocksPerRow =
atoi(NITFGetField(szTemp, pachHeader, nOffset+2, 4));
psImage->nBlocksPerColumn =
atoi(NITFGetField(szTemp, pachHeader, nOffset+6, 4));
psImage->nBlockWidth =
atoi(NITFGetField(szTemp, pachHeader, nOffset+10, 4));
psImage->nBlockHeight =
atoi(NITFGetField(szTemp, pachHeader, nOffset+14, 4));
/* See MIL-STD-2500-C, paragraph 5.4.2.2-d (#3263) */
if (EQUAL(psImage->szIC, "NC"))
{
if (psImage->nBlocksPerRow == 1 &&
psImage->nBlockWidth == 0)
{
psImage->nBlockWidth = psImage->nCols;
}
if (psImage->nBlocksPerColumn == 1 &&
psImage->nBlockHeight == 0)
{
psImage->nBlockHeight = psImage->nRows;
}
}
psImage->nBitsPerSample =
atoi(NITFGetField(szTemp, pachHeader, nOffset+18, 2));
if( psImage->nABPP == 0 )
psImage->nABPP = psImage->nBitsPerSample;
nOffset += 20;
/* capture image inset information */
psImage->nIDLVL = atoi(NITFGetField(szTemp,pachHeader, nOffset+0, 3));
psImage->nIALVL = atoi(NITFGetField(szTemp,pachHeader, nOffset+3, 3));
psImage->nILOCRow = atoi(NITFGetField(szTemp,pachHeader,nOffset+6,5));
psImage->nILOCColumn =
atoi(NITFGetField(szTemp,pachHeader, nOffset+11,5));
memcpy( psImage->szIMAG, pachHeader+nOffset+16, 4 );
psImage->szIMAG[4] = '\0';
nOffset += 3; /* IDLVL */
nOffset += 3; /* IALVL */
nOffset += 10; /* ILOC */
nOffset += 4; /* IMAG */
}
if (psImage->nBitsPerSample <= 0 ||
psImage->nBlocksPerRow <= 0 ||
psImage->nBlocksPerColumn <= 0 ||
psImage->nBlockWidth <= 0 ||
psImage->nBlockHeight <= 0 ||
psImage->nBlocksPerRow > INT_MAX / psImage->nBlockWidth ||
psImage->nBlocksPerColumn > INT_MAX / psImage->nBlockHeight ||
psImage->nCols > psImage->nBlocksPerRow * psImage->nBlockWidth ||
psImage->nRows > psImage->nBlocksPerColumn * psImage->nBlockHeight ||
psImage->nBlocksPerRow > INT_MAX / psImage->nBlocksPerColumn ||
psImage->nBlocksPerRow * psImage->nBlocksPerColumn > INT_MAX / psImage->nBands)
{
CPLError(CE_Failure, CPLE_AppDefined, "Invalid values for block dimension/number");
NITFImageDeaccess(psImage);
return NULL;
}
/* -------------------------------------------------------------------- */
/* Override nCols and nRows for NITF 1.1 (not sure why!) */
/* -------------------------------------------------------------------- */
if( EQUALN(psFile->szVersion,"NITF01.",7) )
{
psImage->nCols = psImage->nBlocksPerRow * psImage->nBlockWidth;
psImage->nRows = psImage->nBlocksPerColumn * psImage->nBlockHeight;
}
/* -------------------------------------------------------------------- */
/* Read TREs if we have them. */
/* -------------------------------------------------------------------- */
else if( nOffset+10 <= (int)psSegInfo->nSegmentHeaderSize )
{
int nUserTREBytes, nExtendedTREBytes;
/* -------------------------------------------------------------------- */
/* Are there user TRE bytes to skip? */
/* -------------------------------------------------------------------- */
nUserTREBytes = atoi(NITFGetField( szTemp, pachHeader, nOffset, 5 ));
nOffset += 5;
if( nUserTREBytes > 3 )
{
if( (int)psSegInfo->nSegmentHeaderSize < nOffset + nUserTREBytes )
GOTO_header_too_small();
psImage->nTREBytes = nUserTREBytes - 3;
psImage->pachTRE = (char *) CPLMalloc(psImage->nTREBytes);
memcpy( psImage->pachTRE, pachHeader + nOffset + 3,
psImage->nTREBytes );
nOffset += nUserTREBytes;
}
else
{
psImage->nTREBytes = 0;
psImage->pachTRE = NULL;
if (nUserTREBytes > 0)
nOffset += nUserTREBytes;
}
/* -------------------------------------------------------------------- */
/* Are there managed TRE bytes to recognise? */
/* -------------------------------------------------------------------- */
if ( (int)psSegInfo->nSegmentHeaderSize < nOffset + 5 )
GOTO_header_too_small();
nExtendedTREBytes = atoi(NITFGetField(szTemp,pachHeader,nOffset,5));
nOffset += 5;
if( nExtendedTREBytes > 3 )
{
if( (int)psSegInfo->nSegmentHeaderSize <
nOffset + nExtendedTREBytes )
GOTO_header_too_small();
psImage->pachTRE = (char *)
CPLRealloc( psImage->pachTRE,
psImage->nTREBytes + nExtendedTREBytes - 3 );
memcpy( psImage->pachTRE + psImage->nTREBytes,
pachHeader + nOffset + 3,
nExtendedTREBytes - 3 );
psImage->nTREBytes += (nExtendedTREBytes - 3);
nOffset += nExtendedTREBytes;
}
}
/* -------------------------------------------------------------------- */
/* Is there a location table to load? */
/* -------------------------------------------------------------------- */
NITFLoadLocationTable( psImage );
/* Fix bug #1744 */
if (psImage->nBands == 1)
NITFLoadColormapSubSection ( psImage );
/* -------------------------------------------------------------------- */
/* Setup some image access values. Some of these may not apply */
/* for compressed images, or band interleaved by block images. */
/* -------------------------------------------------------------------- */
psImage->nWordSize = psImage->nBitsPerSample / 8;
if( psImage->chIMODE == 'S' )
{
psImage->nPixelOffset = psImage->nWordSize;
psImage->nLineOffset =
((GIntBig) psImage->nBlockWidth * psImage->nBitsPerSample) / 8;
psImage->nBlockOffset = psImage->nLineOffset * psImage->nBlockHeight;
psImage->nBandOffset = psImage->nBlockOffset * psImage->nBlocksPerRow
* psImage->nBlocksPerColumn;
}
else if( psImage->chIMODE == 'P' )
{
psImage->nPixelOffset = psImage->nWordSize * psImage->nBands;
psImage->nLineOffset =
((GIntBig) psImage->nBlockWidth * psImage->nBitsPerSample * psImage->nBands) / 8;
psImage->nBandOffset = psImage->nWordSize;
psImage->nBlockOffset = psImage->nLineOffset * psImage->nBlockHeight;
}
else if( psImage->chIMODE == 'R' )
{
psImage->nPixelOffset = psImage->nWordSize;
psImage->nBandOffset =
((GIntBig) psImage->nBlockWidth * psImage->nBitsPerSample) / 8;
psImage->nLineOffset = psImage->nBandOffset * psImage->nBands;
psImage->nBlockOffset = psImage->nLineOffset * psImage->nBlockHeight;
}
else if( psImage->chIMODE == 'B' )
{
psImage->nPixelOffset = psImage->nWordSize;
psImage->nLineOffset =
((GIntBig) psImage->nBlockWidth * psImage->nBitsPerSample) / 8;
psImage->nBandOffset = psImage->nBlockHeight * psImage->nLineOffset;
psImage->nBlockOffset = psImage->nBandOffset * psImage->nBands;
}
else
{
psImage->nPixelOffset = psImage->nWordSize;
psImage->nLineOffset =
((GIntBig) psImage->nBlockWidth * psImage->nBitsPerSample) / 8;
psImage->nBandOffset = psImage->nBlockHeight * psImage->nLineOffset;
psImage->nBlockOffset = psImage->nBandOffset * psImage->nBands;
}
/* -------------------------------------------------------------------- */
/* Setup block map. */
/* -------------------------------------------------------------------- */
/* Int overflow already checked above */
psImage->panBlockStart = (GUIntBig *)
VSICalloc( psImage->nBlocksPerRow * psImage->nBlocksPerColumn
* psImage->nBands, sizeof(GUIntBig) );
if (psImage->panBlockStart == NULL)
{
CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate block map");
NITFImageDeaccess(psImage);
return NULL;
}
/* -------------------------------------------------------------------- */
/* Offsets to VQ compressed tiles are based on a fixed block */
/* size, and are offset from the spatial data location kept in */
/* the location table ... which is generally not the beginning */
/* of the image data segment. */
/* -------------------------------------------------------------------- */
if( EQUAL(psImage->szIC,"C4") )
{
GUIntBig nLocBase = psSegInfo->nSegmentStart;
for( i = 0; i < psImage->nLocCount; i++ )
{
if( psImage->pasLocations[i].nLocId == LID_SpatialDataSubsection )
nLocBase = psImage->pasLocations[i].nLocOffset;
}
if( nLocBase == psSegInfo->nSegmentStart )
CPLError( CE_Warning, CPLE_AppDefined,
"Failed to find spatial data location, guessing." );
for( i=0; i < psImage->nBlocksPerRow * psImage->nBlocksPerColumn; i++ )
psImage->panBlockStart[i] = nLocBase + 6144 * i;
}
/* -------------------------------------------------------------------- */
/* If there is no block map, just compute directly assuming the */
/* blocks start at the beginning of the image segment, and are */
/* packed tightly with the IMODE organization. */
/* -------------------------------------------------------------------- */
else if( psImage->szIC[0] != 'M' && psImage->szIC[1] != 'M' )
{
int iBlockX, iBlockY, iBand;
for( iBlockY = 0; iBlockY < psImage->nBlocksPerColumn; iBlockY++ )
{
for( iBlockX = 0; iBlockX < psImage->nBlocksPerRow; iBlockX++ )
{
for( iBand = 0; iBand < psImage->nBands; iBand++ )
{
int iBlock;
iBlock = iBlockX + iBlockY * psImage->nBlocksPerRow
+ iBand * psImage->nBlocksPerRow
* psImage->nBlocksPerColumn;
psImage->panBlockStart[iBlock] =
psSegInfo->nSegmentStart
+ ((iBlockX + iBlockY * psImage->nBlocksPerRow)
* psImage->nBlockOffset)
+ (iBand * psImage->nBandOffset );
}
}
}
}
/* -------------------------------------------------------------------- */
/* Otherwise we need to read the block map from the beginning */
/* of the image segment. */
/* -------------------------------------------------------------------- */
else
{
GUInt32 nIMDATOFF;
GUInt16 nBMRLNTH, nTMRLNTH, nTPXCDLNTH;
int nBlockCount;
nBlockCount = psImage->nBlocksPerRow * psImage->nBlocksPerColumn
* psImage->nBands;
CPLAssert( psImage->szIC[0] == 'M' || psImage->szIC[1] == 'M' );
VSIFSeekL( psFile->fp, psSegInfo->nSegmentStart, SEEK_SET );
VSIFReadL( &nIMDATOFF, 1, 4, psFile->fp );
VSIFReadL( &nBMRLNTH, 1, 2, psFile->fp );
VSIFReadL( &nTMRLNTH, 1, 2, psFile->fp );
VSIFReadL( &nTPXCDLNTH, 1, 2, psFile->fp );
CPL_MSBPTR32( &nIMDATOFF );
CPL_MSBPTR16( &nBMRLNTH );
CPL_MSBPTR16( &nTMRLNTH );
CPL_MSBPTR16( &nTPXCDLNTH );
if( nTPXCDLNTH == 8 )
{
GByte byNodata;
psImage->bNoDataSet = TRUE;
VSIFReadL( &byNodata, 1, 1, psFile->fp );
psImage->nNoDataValue = byNodata;
}
else
VSIFSeekL( psFile->fp, (nTPXCDLNTH+7)/8, SEEK_CUR );
if( nBMRLNTH == 4 && psImage->chIMODE == 'P' )
{
int nStoredBlocks = psImage->nBlocksPerRow
* psImage->nBlocksPerColumn;
int iBand;
for( i = 0; i < nStoredBlocks; i++ )
{
GUInt32 nOffset;
VSIFReadL( &nOffset, 4, 1, psFile->fp );
CPL_MSBPTR32( &nOffset );
psImage->panBlockStart[i] = nOffset;
if( psImage->panBlockStart[i] != 0xffffffff )
{
psImage->panBlockStart[i]
+= psSegInfo->nSegmentStart + nIMDATOFF;
for( iBand = 1; iBand < psImage->nBands; iBand++ )
{
psImage->panBlockStart[i + iBand * nStoredBlocks] =
psImage->panBlockStart[i]
+ iBand * psImage->nBandOffset;
}
}
else
{
for( iBand = 1; iBand < psImage->nBands; iBand++ )
psImage->panBlockStart[i + iBand * nStoredBlocks] =
0xffffffff;
}
}
}
else if( nBMRLNTH == 4 )
{
int isM4 = EQUAL(psImage->szIC,"M4");
for( i=0; i < nBlockCount; i++ )
{
GUInt32 nOffset;
VSIFReadL( &nOffset, 4, 1, psFile->fp );
CPL_MSBPTR32( &nOffset );
psImage->panBlockStart[i] = nOffset;
if( psImage->panBlockStart[i] != 0xffffffff )
{
if (isM4 && (psImage->panBlockStart[i] % 6144) != 0)
{
break;
}
psImage->panBlockStart[i]
+= psSegInfo->nSegmentStart + nIMDATOFF;
}
}
/* This is a fix for a problem with rpf/cjga/cjgaz01/0105f033.ja1 and */
/* rpf/cjga/cjgaz03/0034t0b3.ja3 CADRG products (bug 1754). */
/* These products have the strange particularity that their block start table begins */
/* one byte after its theoretical beginning, for an unknown reason */
/* We detect this situation when the block start offset is not a multiple of 6144 */
/* Hopefully there's something in the NITF/CADRG standard that can account for it, */
/* but I've not found it */
if (isM4 && i != nBlockCount)
{
bGotWrongOffset = TRUE;
CPLError( CE_Warning, CPLE_AppDefined,
"Block start for block %d is wrong. Retrying with one extra byte shift...", i);
VSIFSeekL( psFile->fp, psSegInfo->nSegmentStart +
4 + /* nIMDATOFF */
2 + /* nBMRLNTH */
2 + /* nTMRLNTH */
2 + /* nTPXCDLNTH */
(nTPXCDLNTH+7)/8 +
1, /* MAGIC here ! One byte shift... */
SEEK_SET );
for( i=0; i < nBlockCount; i++ )
{
GUInt32 nOffset;
VSIFReadL( &nOffset, 4, 1, psFile->fp );
CPL_MSBPTR32( &nOffset );
psImage->panBlockStart[i] = nOffset;
if( psImage->panBlockStart[i] != 0xffffffff )
{
if ((psImage->panBlockStart[i] % 6144) != 0)
{
CPLError( CE_Warning, CPLE_AppDefined,
"Block start for block %d is still wrong. Display will be wrong.", i );
break;
}
psImage->panBlockStart[i]
+= psSegInfo->nSegmentStart + nIMDATOFF;
}
}
}
}
else
{
if( EQUAL(psImage->szIC,"M4") )
{
for( i=0; i < nBlockCount; i++ )
psImage->panBlockStart[i] = 6144 * i
+ psSegInfo->nSegmentStart + nIMDATOFF;
}
else if( EQUAL(psImage->szIC,"NM") )
{
int iBlockX, iBlockY, iBand;
for( iBlockY = 0; iBlockY < psImage->nBlocksPerColumn; iBlockY++ )
{
for( iBlockX = 0; iBlockX < psImage->nBlocksPerRow; iBlockX++ )
{
for( iBand = 0; iBand < psImage->nBands; iBand++ )
{
int iBlock;
iBlock = iBlockX + iBlockY * psImage->nBlocksPerRow
+ iBand * psImage->nBlocksPerRow
* psImage->nBlocksPerColumn;
psImage->panBlockStart[iBlock] =
psSegInfo->nSegmentStart + nIMDATOFF
+ ((iBlockX + iBlockY * psImage->nBlocksPerRow)
* psImage->nBlockOffset)
+ (iBand * psImage->nBandOffset );
}
}
}
}
else
{
CPLError( CE_Warning, CPLE_AppDefined,
"Unsupported IC value '%s', image access will likely fail.",
psImage->szIC );
}
}
}
/* -------------------------------------------------------------------- */
/* Load subframe mask table if present (typically, for CADRG/CIB */
/* images with IC=C4/M4) */
/* -------------------------------------------------------------------- */
if (!bGotWrongOffset)
NITFLoadSubframeMaskTable ( psImage );
/* -------------------------------------------------------------------- */
/* Bug #1751: Add a transparent color if there are none. Absent */
/* subblocks will be then transparent. */
/* -------------------------------------------------------------------- */
if( !psImage->bNoDataSet
&& psImage->nBands == 1
&& psImage->nBitsPerSample == 8 )
{
NITFBandInfo *psBandInfo = psImage->pasBandInfo;
if (psBandInfo->nSignificantLUTEntries < 256-1
&& psBandInfo->pabyLUT != NULL )
{
if (psBandInfo->nSignificantLUTEntries == 217 &&
psBandInfo->pabyLUT[216] == 0 &&
psBandInfo->pabyLUT[256+216] == 0 &&
psBandInfo->pabyLUT[512+216] == 0)
{
psImage->bNoDataSet = TRUE;
psImage->nNoDataValue = psBandInfo->nSignificantLUTEntries - 1;
}
else
{
psBandInfo->pabyLUT[0+psBandInfo->nSignificantLUTEntries] = 0;
psBandInfo->pabyLUT[256+psBandInfo->nSignificantLUTEntries] = 0;
psBandInfo->pabyLUT[512+psBandInfo->nSignificantLUTEntries] = 0;
psImage->bNoDataSet = TRUE;
psImage->nNoDataValue = psBandInfo->nSignificantLUTEntries;
}
}
}
/* -------------------------------------------------------------------- */
/* We override the coordinates found in IGEOLO in case a BLOCKA is */
/* present. According to the BLOCKA specification, it repeats earth */
/* coordinates image corner locations described by IGEOLO in the NITF */
/* image subheader, but provide higher precision. */
/* -------------------------------------------------------------------- */
NITFReadBLOCKA_GCPs( psImage );
/* -------------------------------------------------------------------- */
/* We override the coordinates found in IGEOLO in case a GEOLOB is */
/* present. It provides higher precision lat/long values. */
/* -------------------------------------------------------------------- */
NITFReadGEOLOB( psImage );
/* -------------------------------------------------------------------- */
/* If we have an RPF CoverageSectionSubheader, read the more */
/* precise bounds from it. */
/* -------------------------------------------------------------------- */
for( i = 0; i < psImage->nLocCount; i++ )
{
if( psImage->pasLocations[i].nLocId == LID_CoverageSectionSubheader )
{
double adfTarget[8];
VSIFSeekL( psFile->fp, psImage->pasLocations[i].nLocOffset,
SEEK_SET );
VSIFReadL( adfTarget, 8, 8, psFile->fp );
for( i = 0; i < 8; i++ )
CPL_MSBPTR64( (adfTarget + i) );
psImage->dfULX = adfTarget[1];
psImage->dfULY = adfTarget[0];
psImage->dfLLX = adfTarget[3];
psImage->dfLLY = adfTarget[2];
psImage->dfURX = adfTarget[5];
psImage->dfURY = adfTarget[4];
psImage->dfLRX = adfTarget[7];
psImage->dfLRY = adfTarget[6];
psImage->bIsBoxCenterOfPixel = FALSE; // edge of pixel
CPLDebug( "NITF", "Got spatial info from CoverageSection" );
break;
}
}
/* Bug #1750, #2135 and #3383 */
/* Fix CADRG products like cjnc/cjncz01/000k1023.jn1 (and similar) from NIMA GNCJNCN CDROM: */
/* this product is crossing meridian 180deg and the upper and lower right longitudes are negative */
/* while the upper and lower left longitudes are positive which causes problems in OpenEV, etc... */
/* So we are adjusting the upper and lower right longitudes by setting them above +180 */
/* Make this test only CADRG specific are there are other NITF profiles where non north-up imagery */
/* is valid */
pszIID1 = CSLFetchNameValue(psImage->papszMetadata, "NITF_IID1");
if( (psImage->chICORDS == 'G' || psImage->chICORDS == 'D') &&
pszIID1 != NULL && EQUAL(pszIID1, "CADRG") &&
(psImage->dfULX > psImage->dfURX && psImage->dfLLX > psImage->dfLRX &&
psImage->dfULY > psImage->dfLLY && psImage->dfURY > psImage->dfLRY) )
{
psImage->dfURX += 360;
psImage->dfLRX += 360;
}
/* -------------------------------------------------------------------- */
/* Load RPF attribute metadata if we have it. */
/* -------------------------------------------------------------------- */
NITFLoadAttributeSection( psImage );
/* -------------------------------------------------------------------- */
/* Are the VQ tables to load up? */
/* -------------------------------------------------------------------- */
NITFLoadVQTables( psImage, TRUE );
return psImage;
header_too_small:
CPLError(CE_Failure, CPLE_AppDefined, "Image header too small (called from line %d)",
nFaultyLine);
NITFImageDeaccess(psImage);
return NULL;
}
/************************************************************************/
/* NITFImageDeaccess() */
/************************************************************************/
void NITFImageDeaccess( NITFImage *psImage )
{
int iBand;
CPLAssert( psImage->psFile->pasSegmentInfo[psImage->iSegment].hAccess
== psImage );
psImage->psFile->pasSegmentInfo[psImage->iSegment].hAccess = NULL;
if ( psImage->pasBandInfo)
{
for( iBand = 0; iBand < psImage->nBands; iBand++ )
CPLFree( psImage->pasBandInfo[iBand].pabyLUT );
}
CPLFree( psImage->pasBandInfo );
CPLFree( psImage->panBlockStart );
CPLFree( psImage->pszComments );
CPLFree( psImage->pachHeader );
CPLFree( psImage->pachTRE );
CSLDestroy( psImage->papszMetadata );
CPLFree( psImage->pasLocations );
for( iBand = 0; iBand < 4; iBand++ )
CPLFree( psImage->apanVQLUT[iBand] );
CPLFree( psImage );
}
/************************************************************************/
/* NITFUncompressVQTile() */
/* */
/* This code was derived from OSSIM which in turn derived it */
/* from OpenMap ... open source means sharing! */
/************************************************************************/
static void NITFUncompressVQTile( NITFImage *psImage,
GByte *pabyVQBuf,
GByte *pabyResult )
{
int i, j, t, iSrcByte = 0;
for (i = 0; i < 256; i += 4)
{
for (j = 0; j < 256; j += 8)
{
GUInt16 firstByte = pabyVQBuf[iSrcByte++];
GUInt16 secondByte = pabyVQBuf[iSrcByte++];
GUInt16 thirdByte = pabyVQBuf[iSrcByte++];
/*
* because dealing with half-bytes is hard, we
* uncompress two 4x4 tiles at the same time. (a
* 4x4 tile compressed is 12 bits )
* this little code was grabbed from openmap software.
*/
/* Get first 12-bit value as index into VQ table */
GUInt16 val1 = (firstByte << 4) | (secondByte >> 4);
/* Get second 12-bit value as index into VQ table*/
GUInt16 val2 = ((secondByte & 0x000F) << 8) | thirdByte;
for ( t = 0; t < 4; ++t)
{
GByte *pabyTarget = pabyResult + (i+t) * 256 + j;
memcpy( pabyTarget, psImage->apanVQLUT[t] + val1, 4 );
memcpy( pabyTarget+4, psImage->apanVQLUT[t] + val2, 4);
}
} /* for j */
} /* for i */
}
/************************************************************************/
/* NITFReadImageBlock() */
/************************************************************************/
int NITFReadImageBlock( NITFImage *psImage, int nBlockX, int nBlockY,
int nBand, void *pData )
{
int nWrkBufSize;
int iBaseBlock = nBlockX + nBlockY * psImage->nBlocksPerRow;
int iFullBlock = iBaseBlock
+ (nBand-1) * psImage->nBlocksPerRow * psImage->nBlocksPerColumn;
/* -------------------------------------------------------------------- */
/* Special exit conditions. */
/* -------------------------------------------------------------------- */
if( nBand == 0 )
return BLKREAD_FAIL;
if( psImage->panBlockStart[iFullBlock] == 0xffffffff )
return BLKREAD_NULL;
/* -------------------------------------------------------------------- */
/* Special case for 1 bit data. NITFRasterBand::IReadBlock() */
/* already knows how to promote to byte. */
/* -------------------------------------------------------------------- */
if ((EQUAL(psImage->szIC, "NC") || EQUAL(psImage->szIC, "NM")) && psImage->nBitsPerSample == 1)
{
if (nBlockX != 0 || nBlockY != 0)
{
CPLError( CE_Failure, CPLE_AppDefined,
"assert nBlockX == 0 && nBlockY == 0 failed\n");
return BLKREAD_FAIL;
}
VSIFSeekL( psImage->psFile->fp,
psImage->panBlockStart[0] +
(psImage->nBlockWidth * psImage->nBlockHeight + 7) / 8 * (nBand-1),
SEEK_SET );
VSIFReadL( pData, 1, (psImage->nBlockWidth * psImage->nBlockHeight + 7) / 8, psImage->psFile->fp );
return BLKREAD_OK;
}
/* -------------------------------------------------------------------- */
/* Figure out how big the working buffer will need to be. */
/* -------------------------------------------------------------------- */
if( psImage->nBitsPerSample != psImage->nWordSize * 8 )
nWrkBufSize = (int)psImage->nLineOffset * (psImage->nBlockHeight-1)
+ (psImage->nBitsPerSample * (psImage->nBlockWidth) + 7) / 8;
else
nWrkBufSize = (int)psImage->nLineOffset * (psImage->nBlockHeight-1)
+ (int)psImage->nPixelOffset * (psImage->nBlockWidth - 1)
+ psImage->nWordSize;
if (nWrkBufSize == 0)
nWrkBufSize = (psImage->nBlockWidth*psImage->nBlockHeight*psImage->nBitsPerSample+7)/8;
/* -------------------------------------------------------------------- */
/* Can we do a direct read into our buffer? */
/* -------------------------------------------------------------------- */
if( (size_t)psImage->nWordSize == psImage->nPixelOffset
&& (size_t)((psImage->nBitsPerSample * psImage->nBlockWidth + 7) / 8)
== psImage->nLineOffset
&& psImage->szIC[0] != 'C' && psImage->szIC[0] != 'M'
&& psImage->chIMODE != 'P' )
{
if( VSIFSeekL( psImage->psFile->fp,
psImage->panBlockStart[iFullBlock],
SEEK_SET ) != 0
|| (int) VSIFReadL( pData, 1, nWrkBufSize,
psImage->psFile->fp ) != nWrkBufSize )
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to read %d byte block from " CPL_FRMT_GUIB ".",
nWrkBufSize, psImage->panBlockStart[iFullBlock] );
return BLKREAD_FAIL;
}
else
{
#ifdef CPL_LSB
if( psImage->nWordSize * 8 == psImage->nBitsPerSample )
{
NITFSwapWords( psImage, pData,
psImage->nBlockWidth * psImage->nBlockHeight);
}
#endif
return BLKREAD_OK;
}
}
if( psImage->szIC[0] == 'N' )
{
/* read all the data needed to get our requested band-block */
if( psImage->nBitsPerSample != psImage->nWordSize * 8 )
{
if( psImage->chIMODE == 'S' || (psImage->chIMODE == 'B' && psImage->nBands == 1) )
{
nWrkBufSize = ((psImage->nBlockWidth * psImage->nBlockHeight * psImage->nBitsPerSample) + 7) / 8;
if( VSIFSeekL( psImage->psFile->fp, psImage->panBlockStart[iFullBlock], SEEK_SET ) != 0
|| (int) VSIFReadL( pData, 1, nWrkBufSize, psImage->psFile->fp ) != nWrkBufSize )
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to read %d byte block from %d.",
(int) nWrkBufSize,
(int) psImage->panBlockStart[iFullBlock] );
return BLKREAD_FAIL;
}
return BLKREAD_OK;
}
}
}
/* -------------------------------------------------------------------- */
/* Read the requested information into a temporary buffer and */
/* pull out what we want. */
/* -------------------------------------------------------------------- */
if( psImage->szIC[0] == 'N' )
{
GByte *pabyWrkBuf = (GByte *) VSIMalloc(nWrkBufSize);
int iPixel, iLine;
if (pabyWrkBuf == NULL)
{
CPLError( CE_Failure, CPLE_OutOfMemory,
"Cannot allocate working buffer" );
return BLKREAD_FAIL;
}
/* read all the data needed to get our requested band-block */
if( VSIFSeekL( psImage->psFile->fp, psImage->panBlockStart[iFullBlock],
SEEK_SET ) != 0
|| (int) VSIFReadL( pabyWrkBuf, 1, nWrkBufSize,
psImage->psFile->fp ) != nWrkBufSize )
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to read %d byte block from " CPL_FRMT_GUIB ".",
nWrkBufSize, psImage->panBlockStart[iFullBlock] );
CPLFree( pabyWrkBuf );
return BLKREAD_FAIL;
}
for( iLine = 0; iLine < psImage->nBlockHeight; iLine++ )
{
GByte *pabySrc, *pabyDst;
pabySrc = pabyWrkBuf + iLine * psImage->nLineOffset;
pabyDst = ((GByte *) pData)
+ iLine * (psImage->nWordSize * psImage->nBlockWidth);
for( iPixel = 0; iPixel < psImage->nBlockWidth; iPixel++ )
{
memcpy( pabyDst + iPixel * psImage->nWordSize,
pabySrc + iPixel * psImage->nPixelOffset,
psImage->nWordSize );
}
}
#ifdef CPL_LSB
NITFSwapWords( psImage, pData,
psImage->nBlockWidth * psImage->nBlockHeight);
#endif
CPLFree( pabyWrkBuf );
return BLKREAD_OK;
}
/* -------------------------------------------------------------------- */
/* Handle VQ compression. The VQ compression basically keeps a */
/* 64x64 array of 12bit code words. Each code word expands to */
/* a predefined 4x4 8 bit per pixel pattern. */
/* -------------------------------------------------------------------- */
else if( EQUAL(psImage->szIC,"C4") || EQUAL(psImage->szIC,"M4") )
{
GByte abyVQCoded[6144];
if( psImage->apanVQLUT[0] == NULL )
{
CPLError( CE_Failure, CPLE_NotSupported,
"File lacks VQ LUTs, unable to decode imagery." );
return BLKREAD_FAIL;
}
/* Read the codewords */
if( VSIFSeekL(psImage->psFile->fp, psImage->panBlockStart[iFullBlock],
SEEK_SET ) != 0
|| VSIFReadL(abyVQCoded, 1, sizeof(abyVQCoded),
psImage->psFile->fp ) != sizeof(abyVQCoded) )
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to read %d byte block from " CPL_FRMT_GUIB ".",
(int) sizeof(abyVQCoded),
psImage->panBlockStart[iFullBlock] );
return BLKREAD_FAIL;
}
NITFUncompressVQTile( psImage, abyVQCoded, pData );
return BLKREAD_OK;
}
/* -------------------------------------------------------------------- */
/* Handle ARIDPCM compression. */
/* -------------------------------------------------------------------- */
else if( EQUAL(psImage->szIC,"C2") || EQUAL(psImage->szIC,"M2") )
{
size_t nRawBytes;
NITFSegmentInfo *psSegInfo;
int success;
GByte *pabyRawData;
if (psImage->nBitsPerSample != 8)
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unsupported bits per sample value (%d) for C2/M2 compression",
psImage->nBitsPerSample);
return BLKREAD_FAIL;
}
if( iFullBlock < psImage->nBlocksPerRow * psImage->nBlocksPerColumn-1 )
nRawBytes = (size_t)( psImage->panBlockStart[iFullBlock+1]
- psImage->panBlockStart[iFullBlock] );
else
{
psSegInfo = psImage->psFile->pasSegmentInfo + psImage->iSegment;
nRawBytes = (size_t)(psSegInfo->nSegmentStart
+ psSegInfo->nSegmentSize
- psImage->panBlockStart[iFullBlock]);
}
pabyRawData = (GByte *) VSIMalloc( nRawBytes );
if (pabyRawData == NULL)
{
CPLError( CE_Failure, CPLE_OutOfMemory,
"Cannot allocate working buffer" );
return BLKREAD_FAIL;
}
/* Read the codewords */
if( VSIFSeekL(psImage->psFile->fp, psImage->panBlockStart[iFullBlock],
SEEK_SET ) != 0
|| VSIFReadL(pabyRawData, 1, nRawBytes, psImage->psFile->fp ) !=
nRawBytes )
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to read %d byte block from " CPL_FRMT_GUIB ".",
(int) nRawBytes, psImage->panBlockStart[iFullBlock] );
CPLFree( pabyRawData );
return BLKREAD_FAIL;
}
success = NITFUncompressARIDPCM( psImage, pabyRawData, nRawBytes, pData );
CPLFree( pabyRawData );
if( success )
return BLKREAD_OK;
else
return BLKREAD_FAIL;
}
/* -------------------------------------------------------------------- */
/* Handle BILEVEL (C1) compression. */
/* -------------------------------------------------------------------- */
else if( EQUAL(psImage->szIC,"C1") || EQUAL(psImage->szIC,"M1") )
{
size_t nRawBytes;
NITFSegmentInfo *psSegInfo;
int success;
GByte *pabyRawData;
if (psImage->nBitsPerSample != 1)
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid bits per sample value (%d) for C1/M1 compression",
psImage->nBitsPerSample);
return BLKREAD_FAIL;
}
if( iFullBlock < psImage->nBlocksPerRow * psImage->nBlocksPerColumn-1 )
nRawBytes = (size_t)( psImage->panBlockStart[iFullBlock+1]
- psImage->panBlockStart[iFullBlock] );
else
{
psSegInfo = psImage->psFile->pasSegmentInfo + psImage->iSegment;
nRawBytes = (size_t)( psSegInfo->nSegmentStart
+ psSegInfo->nSegmentSize
- psImage->panBlockStart[iFullBlock] );
}
pabyRawData = (GByte *) VSIMalloc( nRawBytes );
if (pabyRawData == NULL)
{
CPLError( CE_Failure, CPLE_OutOfMemory,
"Cannot allocate working buffer" );
return BLKREAD_FAIL;
}
/* Read the codewords */
if( VSIFSeekL(psImage->psFile->fp, psImage->panBlockStart[iFullBlock],
SEEK_SET ) != 0
|| VSIFReadL(pabyRawData, 1, nRawBytes, psImage->psFile->fp ) !=
nRawBytes )
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to read %d byte block from " CPL_FRMT_GUIB ".",
(int) nRawBytes, psImage->panBlockStart[iFullBlock] );
return BLKREAD_FAIL;
}
success = NITFUncompressBILEVEL( psImage, pabyRawData, (int)nRawBytes,
pData );
CPLFree( pabyRawData );
if( success )
return BLKREAD_OK;
else
return BLKREAD_FAIL;
}
/* -------------------------------------------------------------------- */
/* Report unsupported compression scheme(s). */
/* -------------------------------------------------------------------- */
else if( atoi(psImage->szIC + 1) > 0 )
{
CPLError( CE_Failure, CPLE_NotSupported,
"Unsupported imagery compression format %s in NITF library.",
psImage->szIC );
return BLKREAD_FAIL;
}
return BLKREAD_FAIL;
}
/************************************************************************/
/* NITFWriteImageBlock() */
/************************************************************************/
int NITFWriteImageBlock( NITFImage *psImage, int nBlockX, int nBlockY,
int nBand, void *pData )
{
GUIntBig nWrkBufSize;
int iBaseBlock = nBlockX + nBlockY * psImage->nBlocksPerRow;
int iFullBlock = iBaseBlock
+ (nBand-1) * psImage->nBlocksPerRow * psImage->nBlocksPerColumn;
if( nBand == 0 )
return BLKREAD_FAIL;
nWrkBufSize = psImage->nLineOffset * (psImage->nBlockHeight-1)
+ psImage->nPixelOffset * (psImage->nBlockWidth-1)
+ psImage->nWordSize;
if (nWrkBufSize == 0)
nWrkBufSize = (psImage->nBlockWidth*psImage->nBlockHeight*psImage->nBitsPerSample+7)/8;
/* -------------------------------------------------------------------- */
/* Can we do a direct read into our buffer? */
/* -------------------------------------------------------------------- */
if( (size_t)psImage->nWordSize == psImage->nPixelOffset
&& (size_t)(psImage->nWordSize * psImage->nBlockWidth) == psImage->nLineOffset
&& psImage->szIC[0] != 'C' && psImage->szIC[0] != 'M' )
{
#ifdef CPL_LSB
NITFSwapWords( psImage, pData,
psImage->nBlockWidth * psImage->nBlockHeight);
#endif
if( VSIFSeekL( psImage->psFile->fp, psImage->panBlockStart[iFullBlock],
SEEK_SET ) != 0
|| (GUIntBig) VSIFWriteL( pData, 1, (size_t)nWrkBufSize,
psImage->psFile->fp ) != nWrkBufSize )
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to write " CPL_FRMT_GUIB " byte block from " CPL_FRMT_GUIB ".",
nWrkBufSize, psImage->panBlockStart[iFullBlock] );
return BLKREAD_FAIL;
}
else
{
#ifdef CPL_LSB
/* restore byte order to original */
NITFSwapWords( psImage, pData,
psImage->nBlockWidth * psImage->nBlockHeight);
#endif
return BLKREAD_OK;
}
}
/* -------------------------------------------------------------------- */
/* Other forms not supported at this time. */
/* -------------------------------------------------------------------- */
CPLError( CE_Failure, CPLE_NotSupported,
"Mapped, interleaved and compressed NITF forms not supported\n"
"for writing at this time." );
return BLKREAD_FAIL;
}
/************************************************************************/
/* NITFReadImageLine() */
/************************************************************************/
int NITFReadImageLine( NITFImage *psImage, int nLine, int nBand, void *pData )
{
GUIntBig nLineOffsetInFile;
size_t nLineSize;
unsigned char *pabyLineBuf;
if( nBand == 0 )
return BLKREAD_FAIL;
if( psImage->nBlocksPerRow != 1 || psImage->nBlocksPerColumn != 1 )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Scanline access not supported on tiled NITF files." );
return BLKREAD_FAIL;
}
if( psImage->nBlockWidth < psImage->nCols)
{
CPLError( CE_Failure, CPLE_AppDefined,
"For scanline access, block width cannot be lesser than the number of columns." );
return BLKREAD_FAIL;
}
if( !EQUAL(psImage->szIC,"NC") )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Scanline access not supported on compressed NITF files." );
return BLKREAD_FAIL;
}
/* -------------------------------------------------------------------- */
/* Workout location and size of data in file. */
/* -------------------------------------------------------------------- */
nLineOffsetInFile = psImage->panBlockStart[0]
+ psImage->nLineOffset * nLine
+ psImage->nBandOffset * (nBand-1);
nLineSize = (size_t)psImage->nPixelOffset * (psImage->nBlockWidth - 1)
+ psImage->nWordSize;
if (nLineSize == 0 || psImage->nWordSize * 8 != psImage->nBitsPerSample)
nLineSize = (psImage->nBlockWidth*psImage->nBitsPerSample+7)/8;
VSIFSeekL( psImage->psFile->fp, nLineOffsetInFile, SEEK_SET );
/* -------------------------------------------------------------------- */
/* Can we do a direct read into our buffer. */
/* -------------------------------------------------------------------- */
if( (psImage->nBitsPerSample % 8) != 0 ||
((size_t)psImage->nWordSize == psImage->nPixelOffset
&& (size_t)(psImage->nWordSize * psImage->nBlockWidth) == psImage->nLineOffset) )
{
if( VSIFReadL( pData, 1, nLineSize, psImage->psFile->fp ) !=
nLineSize )
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to read %d bytes for line %d.", (int) nLineSize, nLine );
return BLKREAD_FAIL;
}
#ifdef CPL_LSB
NITFSwapWords( psImage, pData, psImage->nBlockWidth);
#endif
return BLKREAD_OK;
}
/* -------------------------------------------------------------------- */
/* Allocate a buffer for all the interleaved data, and read */
/* it. */
/* -------------------------------------------------------------------- */
pabyLineBuf = (unsigned char *) VSIMalloc(nLineSize);
if (pabyLineBuf == NULL)
{
CPLError( CE_Failure, CPLE_OutOfMemory,
"Cannot allocate working buffer" );
return BLKREAD_FAIL;
}
if( VSIFReadL( pabyLineBuf, 1, nLineSize, psImage->psFile->fp ) !=
nLineSize )
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to read %d bytes for line %d.", (int) nLineSize, nLine );
CPLFree(pabyLineBuf);
return BLKREAD_FAIL;
}
/* -------------------------------------------------------------------- */
/* Copy the desired data out of the interleaved buffer. */
/* -------------------------------------------------------------------- */
{
GByte *pabySrc, *pabyDst;
int iPixel;
pabySrc = pabyLineBuf;
pabyDst = ((GByte *) pData);
for( iPixel = 0; iPixel < psImage->nBlockWidth; iPixel++ )
{
memcpy( pabyDst + iPixel * psImage->nWordSize,
pabySrc + iPixel * psImage->nPixelOffset,
psImage->nWordSize );
}
#ifdef CPL_LSB
NITFSwapWords( psImage, pabyDst, psImage->nBlockWidth);
#endif
}
CPLFree( pabyLineBuf );
return BLKREAD_OK;
}
/************************************************************************/
/* NITFWriteImageLine() */
/************************************************************************/
int NITFWriteImageLine( NITFImage *psImage, int nLine, int nBand, void *pData )
{
GUIntBig nLineOffsetInFile;
size_t nLineSize;
unsigned char *pabyLineBuf;
if( nBand == 0 )
return BLKREAD_FAIL;
if( psImage->nBlocksPerRow != 1 || psImage->nBlocksPerColumn != 1 )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Scanline access not supported on tiled NITF files." );
return BLKREAD_FAIL;
}
if( psImage->nBlockWidth < psImage->nCols)
{
CPLError( CE_Failure, CPLE_AppDefined,
"For scanline access, block width cannot be lesser than the number of columns." );
return BLKREAD_FAIL;
}
if( !EQUAL(psImage->szIC,"NC") )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Scanline access not supported on compressed NITF files." );
return BLKREAD_FAIL;
}
/* -------------------------------------------------------------------- */
/* Workout location and size of data in file. */
/* -------------------------------------------------------------------- */
nLineOffsetInFile = psImage->panBlockStart[0]
+ psImage->nLineOffset * nLine
+ psImage->nBandOffset * (nBand-1);
nLineSize = (size_t)psImage->nPixelOffset * (psImage->nBlockWidth - 1)
+ psImage->nWordSize;
VSIFSeekL( psImage->psFile->fp, nLineOffsetInFile, SEEK_SET );
/* -------------------------------------------------------------------- */
/* Can we do a direct write into our buffer. */
/* -------------------------------------------------------------------- */
if( (size_t)psImage->nWordSize == psImage->nPixelOffset
&& (size_t)(psImage->nWordSize * psImage->nBlockWidth) == psImage->nLineOffset )
{
#ifdef CPL_LSB
NITFSwapWords( psImage, pData, psImage->nBlockWidth );
#endif
VSIFWriteL( pData, 1, nLineSize, psImage->psFile->fp );
#ifdef CPL_LSB
NITFSwapWords( psImage, pData, psImage->nBlockWidth );
#endif
return BLKREAD_OK;
}
/* -------------------------------------------------------------------- */
/* Allocate a buffer for all the interleaved data, and read */
/* it. */
/* -------------------------------------------------------------------- */
pabyLineBuf = (unsigned char *) VSIMalloc(nLineSize);
if (pabyLineBuf == NULL)
{
CPLError( CE_Failure, CPLE_OutOfMemory,
"Cannot allocate working buffer" );
return BLKREAD_FAIL;
}
VSIFReadL( pabyLineBuf, 1, nLineSize, psImage->psFile->fp );
/* -------------------------------------------------------------------- */
/* Copy the desired data into the interleaved buffer. */
/* -------------------------------------------------------------------- */
{
GByte *pabySrc, *pabyDst;
int iPixel;
pabyDst = pabyLineBuf;
pabySrc = ((GByte *) pData);
#ifdef CPL_LSB
NITFSwapWords( psImage, pData, psImage->nBlockWidth );
#endif
for( iPixel = 0; iPixel < psImage->nBlockWidth; iPixel++ )
{
memcpy( pabyDst + iPixel * psImage->nPixelOffset,
pabySrc + iPixel * psImage->nWordSize,
psImage->nWordSize );
}
#ifdef CPL_LSB
NITFSwapWords( psImage, pData, psImage->nBlockWidth );
#endif
}
/* -------------------------------------------------------------------- */
/* Write the results back out. */
/* -------------------------------------------------------------------- */
VSIFSeekL( psImage->psFile->fp, nLineOffsetInFile, SEEK_SET );
VSIFWriteL( pabyLineBuf, 1, nLineSize, psImage->psFile->fp );
CPLFree( pabyLineBuf );
return BLKREAD_OK;
}
/************************************************************************/
/* NITFEncodeDMSLoc() */
/************************************************************************/
static void NITFEncodeDMSLoc( char *pszTarget, double dfValue,
const char *pszAxis )
{
char chHemisphere;
int nDegrees, nMinutes, nSeconds;
if( EQUAL(pszAxis,"Lat") )
{
if( dfValue < 0.0 )
chHemisphere = 'S';
else
chHemisphere = 'N';
}
else
{
if( dfValue < 0.0 )
chHemisphere = 'W';
else
chHemisphere = 'E';
}
dfValue = fabs(dfValue);
nDegrees = (int) dfValue;
dfValue = (dfValue-nDegrees) * 60.0;
nMinutes = (int) dfValue;
dfValue = (dfValue-nMinutes) * 60.0;
/* -------------------------------------------------------------------- */
/* Do careful rounding on seconds so that 59.9->60 is properly */
/* rolled into minutes and degrees. */
/* -------------------------------------------------------------------- */
nSeconds = (int) (dfValue + 0.5);
if (nSeconds == 60)
{
nSeconds = 0;
nMinutes += 1;
if (nMinutes == 60)
{
nMinutes = 0;
nDegrees += 1;
}
}
if( EQUAL(pszAxis,"Lat") )
sprintf( pszTarget, "%02d%02d%02d%c",
nDegrees, nMinutes, nSeconds, chHemisphere );
else
sprintf( pszTarget, "%03d%02d%02d%c",
nDegrees, nMinutes, nSeconds, chHemisphere );
}
/************************************************************************/
/* NITFWriteIGEOLO() */
/************************************************************************/
/* Check that easting can be represented as a 6 character string */
#define CHECK_IGEOLO_UTM_X(name, x) \
if ((int) floor((x)+0.5) <= -100000 || (int) floor((x)+0.5) >= 1000000) \
{ \
CPLError( CE_Failure, CPLE_AppDefined, \
"Attempt to write UTM easting %s=%d which is outside of valid range.", name, (int) floor((x)+0.5) ); \
return FALSE; \
}
/* Check that northing can be represented as a 7 character string */
#define CHECK_IGEOLO_UTM_Y(name, y) \
if ((int) floor((y)+0.5) <= -1000000 || (int) floor((y)+0.5) >= 10000000) \
{ \
CPLError( CE_Failure, CPLE_AppDefined, \
"Attempt to write UTM northing %s=%d which is outside of valid range.", name, (int) floor((y)+0.5) ); \
return FALSE; \
}
int NITFWriteIGEOLO( NITFImage *psImage, char chICORDS,
int nZone,
double dfULX, double dfULY,
double dfURX, double dfURY,
double dfLRX, double dfLRY,
double dfLLX, double dfLLY )
{
char szIGEOLO[61];
/* -------------------------------------------------------------------- */
/* Do some checking. */
/* -------------------------------------------------------------------- */
if( psImage->chICORDS == ' ' )
{
CPLError(CE_Failure, CPLE_NotSupported,
"Apparently no space reserved for IGEOLO info in NITF file.\n"
"NITFWriteIGEOGLO() fails." );
return FALSE;
}
if( chICORDS != 'G' && chICORDS != 'N' && chICORDS != 'S' && chICORDS != 'D')
{
CPLError( CE_Failure, CPLE_NotSupported,
"Invalid ICOORDS value (%c) for NITFWriteIGEOLO().", chICORDS );
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Format geographic coordinates in DMS */
/* -------------------------------------------------------------------- */
if( chICORDS == 'G' )
{
if( fabs(dfULX) > 180 || fabs(dfURX) > 180
|| fabs(dfLRX) > 180 || fabs(dfLLX) > 180
|| fabs(dfULY) > 90 || fabs(dfURY) > 90
|| fabs(dfLRY) > 90 || fabs(dfLLY) > 90 )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Attempt to write geographic bound outside of legal range." );
return FALSE;
}
NITFEncodeDMSLoc( szIGEOLO + 0, dfULY, "Lat" );
NITFEncodeDMSLoc( szIGEOLO + 7, dfULX, "Long" );
NITFEncodeDMSLoc( szIGEOLO + 15, dfURY, "Lat" );
NITFEncodeDMSLoc( szIGEOLO + 22, dfURX, "Long" );
NITFEncodeDMSLoc( szIGEOLO + 30, dfLRY, "Lat" );
NITFEncodeDMSLoc( szIGEOLO + 37, dfLRX, "Long" );
NITFEncodeDMSLoc( szIGEOLO + 45, dfLLY, "Lat" );
NITFEncodeDMSLoc( szIGEOLO + 52, dfLLX, "Long" );
}
/* -------------------------------------------------------------------- */
/* Format geographic coordinates in decimal degrees */
/* -------------------------------------------------------------------- */
else if( chICORDS == 'D' )
{
if( fabs(dfULX) > 180 || fabs(dfURX) > 180
|| fabs(dfLRX) > 180 || fabs(dfLLX) > 180
|| fabs(dfULY) > 90 || fabs(dfURY) > 90
|| fabs(dfLRY) > 90 || fabs(dfLLY) > 90 )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Attempt to write geographic bound outside of legal range." );
return FALSE;
}
CPLsprintf(szIGEOLO + 0, "%+#07.3f%+#08.3f", dfULY, dfULX);
CPLsprintf(szIGEOLO + 15, "%+#07.3f%+#08.3f", dfURY, dfURX);
CPLsprintf(szIGEOLO + 30, "%+#07.3f%+#08.3f", dfLRY, dfLRX);
CPLsprintf(szIGEOLO + 45, "%+#07.3f%+#08.3f", dfLLY, dfLLX);
}
/* -------------------------------------------------------------------- */
/* Format UTM coordinates. */
/* -------------------------------------------------------------------- */
else if( chICORDS == 'N' || chICORDS == 'S' )
{
CHECK_IGEOLO_UTM_X("dfULX", dfULX);
CHECK_IGEOLO_UTM_Y("dfULY", dfULY);
CHECK_IGEOLO_UTM_X("dfURX", dfURX);
CHECK_IGEOLO_UTM_Y("dfURY", dfURY);
CHECK_IGEOLO_UTM_X("dfLRX", dfLRX);
CHECK_IGEOLO_UTM_Y("dfLRY", dfLRY);
CHECK_IGEOLO_UTM_X("dfLLX", dfLLX);
CHECK_IGEOLO_UTM_Y("dfLLY", dfLLY);
CPLsprintf( szIGEOLO + 0, "%02d%06d%07d",
nZone, (int) floor(dfULX+0.5), (int) floor(dfULY+0.5) );
CPLsprintf( szIGEOLO + 15, "%02d%06d%07d",
nZone, (int) floor(dfURX+0.5), (int) floor(dfURY+0.5) );
CPLsprintf( szIGEOLO + 30, "%02d%06d%07d",
nZone, (int) floor(dfLRX+0.5), (int) floor(dfLRY+0.5) );
CPLsprintf( szIGEOLO + 45, "%02d%06d%07d",
nZone, (int) floor(dfLLX+0.5), (int) floor(dfLLY+0.5) );
}
/* -------------------------------------------------------------------- */
/* Write IGEOLO data to disk. */
/* -------------------------------------------------------------------- */
if( VSIFSeekL( psImage->psFile->fp,
psImage->psFile->pasSegmentInfo[psImage->iSegment].nSegmentHeaderStart + 372, SEEK_SET ) == 0
&& VSIFWriteL( szIGEOLO, 1, 60, psImage->psFile->fp ) == 60 )
{
return TRUE;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"I/O Error writing IGEOLO segment.\n%s",
VSIStrerror( errno ) );
return FALSE;
}
}
/************************************************************************/
/* NITFWriteLUT() */
/************************************************************************/
int NITFWriteLUT( NITFImage *psImage, int nBand, int nColors,
unsigned char *pabyLUT )
{
NITFBandInfo *psBandInfo;
int bSuccess = TRUE;
if( nBand < 1 || nBand > psImage->nBands )
return FALSE;
psBandInfo = psImage->pasBandInfo + (nBand-1);
if( nColors > psBandInfo->nSignificantLUTEntries )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unable to write all %d LUT entries, only able to write %d.",
nColors, psBandInfo->nSignificantLUTEntries );
nColors = psBandInfo->nSignificantLUTEntries;
bSuccess = FALSE;
}
VSIFSeekL( psImage->psFile->fp, psBandInfo->nLUTLocation, SEEK_SET );
VSIFWriteL( pabyLUT, 1, nColors, psImage->psFile->fp );
VSIFSeekL( psImage->psFile->fp,
psBandInfo->nLUTLocation + psBandInfo->nSignificantLUTEntries,
SEEK_SET );
VSIFWriteL( pabyLUT+256, 1, nColors, psImage->psFile->fp );
VSIFSeekL( psImage->psFile->fp,
psBandInfo->nLUTLocation + 2*psBandInfo->nSignificantLUTEntries,
SEEK_SET );
VSIFWriteL( pabyLUT+512, 1, nColors, psImage->psFile->fp );
return bSuccess;
}
/************************************************************************/
/* NITFTrimWhite() */
/* */
/* Trim any white space off the white of the passed string in */
/* place. */
/************************************************************************/
char *NITFTrimWhite( char *pszTarget )
{
int i;
i = strlen(pszTarget)-1;
while( i >= 0 && pszTarget[i] == ' ' )
pszTarget[i--] = '\0';
return pszTarget;
}
/************************************************************************/
/* NITFSwapWords() */
/************************************************************************/
#ifdef CPL_LSB
static void NITFSwapWordsInternal( void *pData, int nWordSize, int nWordCount,
int nWordSkip )
{
int i;
GByte *pabyData = (GByte *) pData;
switch( nWordSize )
{
case 1:
break;
case 2:
for( i = 0; i < nWordCount; i++ )
{
GByte byTemp;
byTemp = pabyData[0];
pabyData[0] = pabyData[1];
pabyData[1] = byTemp;
pabyData += nWordSkip;
}
break;
case 4:
for( i = 0; i < nWordCount; i++ )
{
GByte byTemp;
byTemp = pabyData[0];
pabyData[0] = pabyData[3];
pabyData[3] = byTemp;
byTemp = pabyData[1];
pabyData[1] = pabyData[2];
pabyData[2] = byTemp;
pabyData += nWordSkip;
}
break;
case 8:
for( i = 0; i < nWordCount; i++ )
{
GByte byTemp;
byTemp = pabyData[0];
pabyData[0] = pabyData[7];
pabyData[7] = byTemp;
byTemp = pabyData[1];
pabyData[1] = pabyData[6];
pabyData[6] = byTemp;
byTemp = pabyData[2];
pabyData[2] = pabyData[5];
pabyData[5] = byTemp;
byTemp = pabyData[3];
pabyData[3] = pabyData[4];
pabyData[4] = byTemp;
pabyData += nWordSkip;
}
break;
default:
break;
}
}
/* Swap real or complex types */
static void NITFSwapWords( NITFImage *psImage, void *pData, int nWordCount )
{
if( EQUAL(psImage->szPVType,"C") )
{
/* According to http://jitc.fhu.disa.mil/nitf/tag_reg/imagesubheader/pvtype.html */
/* "C values shall be represented with the Real and Imaginary parts, each represented */
/* in IEEE 32 or 64-bit floating point representation (IEEE 754) and appearing in */
/* adjacent four or eight-byte blocks, first Real, then Imaginary" */
NITFSwapWordsInternal( pData,
psImage->nWordSize / 2,
2 * nWordCount,
psImage->nWordSize / 2 );
}
else
{
NITFSwapWordsInternal( pData,
psImage->nWordSize,
nWordCount,
psImage->nWordSize );
}
}
#endif /* def CPL_LSB */
/************************************************************************/
/* NITFReadCSEXRA() */
/* */
/* Read a CSEXRA TRE and return contents as metadata strings. */
/************************************************************************/
char **NITFReadCSEXRA( NITFImage *psImage )
{
return NITFGenericMetadataRead(NULL, NULL, psImage, "CSEXRA");
}
/************************************************************************/
/* NITFReadPIAIMC() */
/* */
/* Read a PIAIMC TRE and return contents as metadata strings. */
/************************************************************************/
char **NITFReadPIAIMC( NITFImage *psImage )
{
return NITFGenericMetadataRead(NULL, NULL, psImage, "PIAIMC");
}
/************************************************************************/
/* NITFReadRPC00B() */
/* */
/* Read an RPC00A or RPC00B structure if the TRE is available. */
/* RPC00A is remapped into RPC00B organization. */
/************************************************************************/
int NITFReadRPC00B( NITFImage *psImage, NITFRPC00BInfo *psRPC )
{
static const int anRPC00AMap[] = /* See ticket #2040 */
{0, 1, 2, 3, 4, 5, 6 , 10, 7, 8, 9, 11, 14, 17, 12, 15, 18, 13, 16, 19};
const char *pachTRE;
char szTemp[100];
int i;
int bRPC00A = FALSE;
int nTRESize;
psRPC->SUCCESS = 0;
/* -------------------------------------------------------------------- */
/* Do we have the TRE? */
/* -------------------------------------------------------------------- */
pachTRE = NITFFindTRE( psImage->pachTRE, psImage->nTREBytes,
"RPC00B", &nTRESize );
if( pachTRE == NULL )
{
pachTRE = NITFFindTRE( psImage->pachTRE, psImage->nTREBytes,
"RPC00A", &nTRESize );
if( pachTRE )
bRPC00A = TRUE;
}
if( pachTRE == NULL )
{
/* No RPC00 tag. Check to see if we have the IMASDA and IMRFCA
tags (DPPDB data) before returning. */
return NITFReadIMRFCA( psImage, psRPC );
}
if (nTRESize < 801 + 19*12 + 12)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Cannot read RPC00A/RPC00B TRE. Not enough bytes");
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Parse out field values. */
/* -------------------------------------------------------------------- */
psRPC->SUCCESS = atoi(NITFGetField(szTemp, pachTRE, 0, 1 ));
if ( !psRPC->SUCCESS )
fprintf( stdout, "RPC Extension not Populated!\n");
psRPC->ERR_BIAS = CPLAtof(NITFGetField(szTemp, pachTRE, 1, 7 ));
psRPC->ERR_RAND = CPLAtof(NITFGetField(szTemp, pachTRE, 8, 7 ));
psRPC->LINE_OFF = CPLAtof(NITFGetField(szTemp, pachTRE, 15, 6 ));
psRPC->SAMP_OFF = CPLAtof(NITFGetField(szTemp, pachTRE, 21, 5 ));
psRPC->LAT_OFF = CPLAtof(NITFGetField(szTemp, pachTRE, 26, 8 ));
psRPC->LONG_OFF = CPLAtof(NITFGetField(szTemp, pachTRE, 34, 9 ));
psRPC->HEIGHT_OFF = CPLAtof(NITFGetField(szTemp, pachTRE, 43, 5 ));
psRPC->LINE_SCALE = CPLAtof(NITFGetField(szTemp, pachTRE, 48, 6 ));
psRPC->SAMP_SCALE = CPLAtof(NITFGetField(szTemp, pachTRE, 54, 5 ));
psRPC->LAT_SCALE = CPLAtof(NITFGetField(szTemp, pachTRE, 59, 8 ));
psRPC->LONG_SCALE = CPLAtof(NITFGetField(szTemp, pachTRE, 67, 9 ));
psRPC->HEIGHT_SCALE = CPLAtof(NITFGetField(szTemp, pachTRE, 76, 5 ));
/* -------------------------------------------------------------------- */
/* Parse out coefficients. */
/* -------------------------------------------------------------------- */
for( i = 0; i < 20; i++ )
{
int iSrcCoef = i;
if( bRPC00A )
iSrcCoef = anRPC00AMap[i];
psRPC->LINE_NUM_COEFF[i] =
CPLAtof(NITFGetField(szTemp, pachTRE, 81+iSrcCoef*12, 12));
psRPC->LINE_DEN_COEFF[i] =
CPLAtof(NITFGetField(szTemp, pachTRE, 321+iSrcCoef*12, 12));
psRPC->SAMP_NUM_COEFF[i] =
CPLAtof(NITFGetField(szTemp, pachTRE, 561+iSrcCoef*12, 12));
psRPC->SAMP_DEN_COEFF[i] =
CPLAtof(NITFGetField(szTemp, pachTRE, 801+iSrcCoef*12, 12));
}
return TRUE;
}
/************************************************************************/
/* NITFReadICHIPB() */
/* */
/* Read an ICHIPB structure if the TRE is available. */
/************************************************************************/
int NITFReadICHIPB( NITFImage *psImage, NITFICHIPBInfo *psICHIP )
{
const char *pachTRE;
char szTemp[32];
int nTRESize;
/* -------------------------------------------------------------------- */
/* Do we have the TRE? */
/* -------------------------------------------------------------------- */
pachTRE = NITFFindTRE( psImage->pachTRE, psImage->nTREBytes,
"ICHIPB", &nTRESize );
if( pachTRE == NULL )
{
pachTRE = NITFFindTRE( psImage->pachTRE, psImage->nTREBytes,
"ICHIPA", &nTRESize );
}
if( pachTRE == NULL )
{
return FALSE;
}
if (nTRESize < 2)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Cannot read ICHIPA/ICHIPB TRE. Not enough bytes");
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Parse out field values. */
/* -------------------------------------------------------------------- */
psICHIP->XFRM_FLAG = atoi(NITFGetField(szTemp, pachTRE, 0, 2 ));
if ( psICHIP->XFRM_FLAG == 0 )
{
if (nTRESize < 216 + 8)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Cannot read ICHIPA/ICHIPB TRE. Not enough bytes");
return FALSE;
}
psICHIP->SCALE_FACTOR = CPLAtof(NITFGetField(szTemp, pachTRE, 2, 10 ));
psICHIP->ANAMORPH_CORR = atoi(NITFGetField(szTemp, pachTRE, 12, 2 ));
psICHIP->SCANBLK_NUM = atoi(NITFGetField(szTemp, pachTRE, 14, 2 ));
psICHIP->OP_ROW_11 = CPLAtof(NITFGetField(szTemp, pachTRE, 16, 12 ));
psICHIP->OP_COL_11 = CPLAtof(NITFGetField(szTemp, pachTRE, 28, 12 ));
psICHIP->OP_ROW_12 = CPLAtof(NITFGetField(szTemp, pachTRE, 40, 12 ));
psICHIP->OP_COL_12 = CPLAtof(NITFGetField(szTemp, pachTRE, 52, 12 ));
psICHIP->OP_ROW_21 = CPLAtof(NITFGetField(szTemp, pachTRE, 64, 12 ));
psICHIP->OP_COL_21 = CPLAtof(NITFGetField(szTemp, pachTRE, 76, 12 ));
psICHIP->OP_ROW_22 = CPLAtof(NITFGetField(szTemp, pachTRE, 88, 12 ));
psICHIP->OP_COL_22 = CPLAtof(NITFGetField(szTemp, pachTRE, 100, 12 ));
psICHIP->FI_ROW_11 = CPLAtof(NITFGetField(szTemp, pachTRE, 112, 12 ));
psICHIP->FI_COL_11 = CPLAtof(NITFGetField(szTemp, pachTRE, 124, 12 ));
psICHIP->FI_ROW_12 = CPLAtof(NITFGetField(szTemp, pachTRE, 136, 12 ));
psICHIP->FI_COL_12 = CPLAtof(NITFGetField(szTemp, pachTRE, 148, 12 ));
psICHIP->FI_ROW_21 = CPLAtof(NITFGetField(szTemp, pachTRE, 160, 12 ));
psICHIP->FI_COL_21 = CPLAtof(NITFGetField(szTemp, pachTRE, 172, 12 ));
psICHIP->FI_ROW_22 = CPLAtof(NITFGetField(szTemp, pachTRE, 184, 12 ));
psICHIP->FI_COL_22 = CPLAtof(NITFGetField(szTemp, pachTRE, 196, 12 ));
psICHIP->FI_ROW = atoi(NITFGetField(szTemp, pachTRE, 208, 8 ));
psICHIP->FI_COL = atoi(NITFGetField(szTemp, pachTRE, 216, 8 ));
}
else
{
fprintf( stdout, "Chip is already de-warpped?\n" );
}
return TRUE;
}
/************************************************************************/
/* NITFReadUSE00A() */
/* */
/* Read a USE00A TRE and return contents as metadata strings. */
/************************************************************************/
char **NITFReadUSE00A( NITFImage *psImage )
{
return NITFGenericMetadataRead(NULL, NULL, psImage, "USE00A");
}
/************************************************************************/
/* NITFReadBLOCKA() */
/* */
/* Read a BLOCKA SDE and return contents as metadata strings. */
/************************************************************************/
char **NITFReadBLOCKA( NITFImage *psImage )
{
const char *pachTRE;
int nTRESize;
char **papszMD = NULL;
int nBlockaCount = 0;
char szTemp[128];
while ( TRUE )
{
/* -------------------------------------------------------------------- */
/* Do we have the TRE? */
/* -------------------------------------------------------------------- */
pachTRE = NITFFindTREByIndex( psImage->pachTRE, psImage->nTREBytes,
"BLOCKA", nBlockaCount,
&nTRESize );
if( pachTRE == NULL )
break;
if( nTRESize != 123 )
{
CPLError( CE_Warning, CPLE_AppDefined,
"BLOCKA TRE wrong size, ignoring." );
break;
}
nBlockaCount++;
/* -------------------------------------------------------------------- */
/* Parse out field values. */
/* -------------------------------------------------------------------- */
sprintf( szTemp, "NITF_BLOCKA_BLOCK_INSTANCE_%02d", nBlockaCount );
NITFExtractMetadata( &papszMD, pachTRE, 0, 2, szTemp );
sprintf( szTemp, "NITF_BLOCKA_N_GRAY_%02d", nBlockaCount );
NITFExtractMetadata( &papszMD, pachTRE, 2, 5, szTemp );
sprintf( szTemp, "NITF_BLOCKA_L_LINES_%02d", nBlockaCount );
NITFExtractMetadata( &papszMD, pachTRE, 7, 5, szTemp );
sprintf( szTemp, "NITF_BLOCKA_LAYOVER_ANGLE_%02d",nBlockaCount );
NITFExtractMetadata( &papszMD, pachTRE, 12, 3, szTemp );
sprintf( szTemp, "NITF_BLOCKA_SHADOW_ANGLE_%02d", nBlockaCount );
NITFExtractMetadata( &papszMD, pachTRE, 15, 3, szTemp );
/* reserved: 16 */
sprintf( szTemp, "NITF_BLOCKA_FRLC_LOC_%02d", nBlockaCount );
NITFExtractMetadata( &papszMD, pachTRE, 34, 21, szTemp );
sprintf( szTemp, "NITF_BLOCKA_LRLC_LOC_%02d", nBlockaCount );
NITFExtractMetadata( &papszMD, pachTRE, 55, 21, szTemp );
sprintf( szTemp, "NITF_BLOCKA_LRFC_LOC_%02d", nBlockaCount );
NITFExtractMetadata( &papszMD, pachTRE, 76, 21, szTemp );
sprintf( szTemp, "NITF_BLOCKA_FRFC_LOC_%02d", nBlockaCount );
NITFExtractMetadata( &papszMD, pachTRE, 97, 21, szTemp );
/* reserved: 5 -> 97 + 21 + 5 = 123 -> OK */
}
if ( nBlockaCount > 0 )
{
sprintf( szTemp, "%02d", nBlockaCount );
papszMD = CSLSetNameValue( papszMD, "NITF_BLOCKA_BLOCK_COUNT", szTemp );
}
return papszMD;
}
/************************************************************************/
/* NITFGetGCP() */
/* */
/* Reads a geographical coordinate (lat, long) from the provided */
/* buffer. */
/************************************************************************/
void NITFGetGCP ( const char* pachCoord, double *pdfXYs, int iCoord )
{
char szTemp[128];
// offset to selected coordinate.
pdfXYs += 2 * iCoord;
if( pachCoord[0] == 'N' || pachCoord[0] == 'n' ||
pachCoord[0] == 'S' || pachCoord[0] == 's' )
{
/* ------------------------------------------------------------ */
/* 0....+....1....+....2 */
/* Coordinates are in the form Xddmmss.ssYdddmmss.ss: */
/* The format Xddmmss.cc represents degrees (00 to 89), minutes */
/* (00 to 59), seconds (00 to 59), and hundredths of seconds */
/* (00 to 99) of latitude, with X = N for north or S for south, */
/* and Ydddmmss.cc represents degrees (000 to 179), minutes */
/* (00 to 59), seconds (00 to 59), and hundredths of seconds */
/* (00 to 99) of longitude, with Y = E for east or W for west. */
/* ------------------------------------------------------------ */
pdfXYs[1] =
CPLAtof(NITFGetField( szTemp, pachCoord, 1, 2 ))
+ CPLAtof(NITFGetField( szTemp, pachCoord, 3, 2 )) / 60.0
+ CPLAtof(NITFGetField( szTemp, pachCoord, 5, 5 )) / 3600.0;
if( pachCoord[0] == 's' || pachCoord[0] == 'S' )
pdfXYs[1] *= -1;
pdfXYs[0] =
CPLAtof(NITFGetField( szTemp, pachCoord,11, 3 ))
+ CPLAtof(NITFGetField( szTemp, pachCoord,14, 2 )) / 60.0
+ CPLAtof(NITFGetField( szTemp, pachCoord,16, 5 )) / 3600.0;
if( pachCoord[10] == 'w' || pachCoord[10] == 'W' )
pdfXYs[0] *= -1;
}
else
{
/* ------------------------------------------------------------ */
/* 0....+....1....+....2 */
/* Coordinates are in the form ±dd.dddddd±ddd.dddddd: */
/* The format ±dd.dddddd indicates degrees of latitude (north */
/* is positive), and ±ddd.dddddd represents degrees of */
/* longitude (east is positive). */
/* ------------------------------------------------------------ */
pdfXYs[1] = CPLAtof(NITFGetField( szTemp, pachCoord, 0, 10 ));
pdfXYs[0] = CPLAtof(NITFGetField( szTemp, pachCoord,10, 11 ));
}
}
/************************************************************************/
/* NITFReadBLOCKA_GCPs() */
/* */
/* The BLOCKA repeat earth coordinates image corner locations described */
/* by IGEOLO in the NITF image subheader, but provide higher precision. */
/************************************************************************/
int NITFReadBLOCKA_GCPs( NITFImage *psImage )
{
const char *pachTRE;
int nTRESize;
int nBlockaLines;
char szTemp[128];
/* -------------------------------------------------------------------- */
/* Do we have the TRE? */
/* -------------------------------------------------------------------- */
pachTRE = NITFFindTRE( psImage->pachTRE, psImage->nTREBytes,
"BLOCKA", &nTRESize );
if( pachTRE == NULL )
return FALSE;
if( nTRESize != 123 )
{
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Parse out field values. */
/* -------------------------------------------------------------------- */
/* ---------------------------------------------------------------- */
/* Make sure the BLOCKA geo coordinates are set. Spaces indicate */
/* the value of a coordinate is unavailable or inapplicable. */
/* ---------------------------------------------------------------- */
if( pachTRE[34] == ' ' || pachTRE[55] == ' ' ||
pachTRE[76] == ' ' || pachTRE[97] == ' ' )
{
return FALSE;
}
/* ---------------------------------------------------------------- */
/* Extract the L_LINES field of BLOCKA and see if this instance */
/* covers the whole image. This is the case if L_LINES is equal to */
/* the no of rows of this image. */
/* We use the BLOCKA only in that case! */
/* ---------------------------------------------------------------- */
nBlockaLines = atoi(NITFGetField( szTemp, pachTRE, 7, 5 ));
if( psImage->nRows != nBlockaLines )
{
return FALSE;
}
/* ---------------------------------------------------------------- */
/* Note that the order of these coordinates is different from */
/* IGEOLO/NITFImage. */
/* IGEOLO BLOCKA */
/* 0, 0 0, MaxCol */
/* 0, MaxCol MaxRow, MaxCol */
/* MaxRow, MaxCol MaxRow, 0 */
/* MaxRow, 0 0, 0 */
/* ---------------------------------------------------------------- */
{
double *pdfXYs = &(psImage->dfULX);
NITFGetGCP ( pachTRE + 34, pdfXYs, 1 );
NITFGetGCP ( pachTRE + 55, pdfXYs, 2 );
NITFGetGCP ( pachTRE + 76, pdfXYs, 3 );
NITFGetGCP ( pachTRE + 97, pdfXYs, 0 );
psImage->bIsBoxCenterOfPixel = TRUE;
}
/* ---------------------------------------------------------------- */
/* Regardless of the former value of ICORDS, the values are now in */
/* decimal degrees. */
/* ---------------------------------------------------------------- */
psImage->chICORDS = 'D';
return TRUE;
}
/************************************************************************/
/* NITFReadGEOLOB() */
/* */
/* The GEOLOB contains high precision lat/long geotransform */
/* values. */
/************************************************************************/
static int NITFReadGEOLOB( NITFImage *psImage )
{
const char *pachTRE;
int nTRESize;
char szTemp[128];
/* -------------------------------------------------------------------- */
/* Do we have the TRE? */
/* -------------------------------------------------------------------- */
pachTRE = NITFFindTRE( psImage->pachTRE, psImage->nTREBytes,
"GEOLOB", &nTRESize );
if( pachTRE == NULL )
return FALSE;
if( !CSLTestBoolean(CPLGetConfigOption( "NITF_USEGEOLOB", "YES" )) )
{
CPLDebug( "NITF", "GEOLOB available, but ignored by request." );
return FALSE;
}
if( nTRESize != 48 )
{
CPLError(CE_Failure, CPLE_AppDefined,
"Cannot read GEOLOB TRE. Wrong size.");
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Parse out field values. */
/* -------------------------------------------------------------------- */
{
double dfARV = atoi(NITFGetField( szTemp, pachTRE, 0, 9 ));
double dfBRV = atoi(NITFGetField( szTemp, pachTRE, 9, 9 ));
double dfLSO = CPLAtof(NITFGetField( szTemp, pachTRE, 18, 15 ));
double dfPSO = CPLAtof(NITFGetField( szTemp, pachTRE, 33, 15 ));
double dfPixelWidth = 360.0 / dfARV;
double dfPixelHeight = 360.0 / dfBRV;
psImage->dfULX = dfLSO;
psImage->dfURX = psImage->dfULX + psImage->nCols * dfPixelWidth;
psImage->dfLLX = psImage->dfULX;
psImage->dfLRX = psImage->dfURX;
psImage->dfULY = dfPSO;
psImage->dfURY = psImage->dfULY;
psImage->dfLLY = psImage->dfULY - psImage->nRows * dfPixelHeight;
psImage->dfLRY = psImage->dfLLY;
psImage->bIsBoxCenterOfPixel = FALSE; // GEOLOB is edge of pixel.
psImage->chICORDS = 'G';
CPLDebug( "NITF", "IGEOLO bounds overridden by GEOLOB TRE." );
}
return TRUE;
}
/************************************************************************/
/* NITFFetchAttribute() */
/* */
/* Load one attribute given the attribute id, and the parameter */
/* id and the number of bytes to fetch. */
/************************************************************************/
static int NITFFetchAttribute( GByte *pabyAttributeSubsection,
GUInt32 nASSSize, int nAttrCount,
int nAttrID, int nParamID, GUInt32 nBytesToFetch,
GByte *pabyBuffer )
{
int i;
GUInt32 nAttrOffset = 0;
/* -------------------------------------------------------------------- */
/* Scan the attribute offset table */
/* -------------------------------------------------------------------- */
for( i = 0; i < nAttrCount; i++ )
{
GByte *pabyOffsetRec = i*8 + pabyAttributeSubsection;
if( (pabyOffsetRec[0] * 256 + pabyOffsetRec[1]) == nAttrID
&& pabyOffsetRec[2] == nParamID )
{
memcpy( &nAttrOffset, pabyOffsetRec+4, 4 );
CPL_MSBPTR32( &nAttrOffset );
break;
}
}
/* -------------------------------------------------------------------- */
/* Extract the attribute value. */
/* -------------------------------------------------------------------- */
if( nAttrOffset == 0 )
return FALSE;
if( nAttrOffset + nBytesToFetch > nASSSize )
return FALSE;
memcpy( pabyBuffer, pabyAttributeSubsection + nAttrOffset, nBytesToFetch );
return TRUE;
}
/************************************************************************/
/* NITFLoadAttributeSection() */
/* */
/* Load metadata items from selected attributes in the RPF */
/* attributes subsection. The items are defined in */
/* MIL-STD-2411-1 section 5.3.2. */
/************************************************************************/
static void NITFLoadAttributeSection( NITFImage *psImage )
{
int i;
GUInt32 nASHOffset=0, /* nASHSize=0, */ nASSOffset=0, nASSSize=0, nNextOffset=0;
GInt16 nAttrCount;
GByte *pabyAttributeSubsection;
GByte abyBuffer[128];
for( i = 0; i < psImage->nLocCount; i++ )
{
if( psImage->pasLocations[i].nLocId == LID_AttributeSectionSubheader )
{
nASHOffset = psImage->pasLocations[i].nLocOffset;
/* nASHSize = psImage->pasLocations[i].nLocSize; */
}
else if( psImage->pasLocations[i].nLocId == LID_AttributeSubsection )
{
nASSOffset = psImage->pasLocations[i].nLocOffset;
nASSSize = psImage->pasLocations[i].nLocSize;
}
}
if( nASSOffset == 0 || nASHOffset == 0 )
return;
/* -------------------------------------------------------------------- */
/* How many attribute records do we have? */
/* -------------------------------------------------------------------- */
VSIFSeekL( psImage->psFile->fp, nASHOffset, SEEK_SET );
VSIFReadL( &nAttrCount, 2, 1, psImage->psFile->fp );
CPL_MSBPTR16( &nAttrCount );
/* -------------------------------------------------------------------- */
/* nASSSize Hack */
/* -------------------------------------------------------------------- */
/* OK, now, as often with RPF/CADRG, here is the necessary dirty hack */
/* -- Begin of lengthy explanation -- */
/* A lot of CADRG files have a nASSSize value that reports a size */
/* smaller than the genuine size of the attribute subsection in the */
/* file, so if we trust the nASSSize value, we'll reject existing */
/* attributes. This is for example the case for */
/* http://download.osgeo.org/gdal/data/nitf/0000M033.GN3 */
/* where nASSSize is reported to be 302 bytes for 52 attributes (which */
/* is odd since 52 * 8 < 302), but a binary inspection of the attribute */
/* subsection shows that the actual size is 608 bytes, which is also confirmed*/
/* by the fact that the next subsection (quite often LID_ExplicitArealCoverageTable but not always) */
/* begins right after. So if this next subsection is found and that the */
/* difference in offset is larger than the original nASSSize, use it. */
/* I have observed that nowhere in the NITF driver we make use of the .nLocSize field */
/* -- End of lengthy explanation -- */
for( i = 0; i < psImage->nLocCount; i++ )
{
if( psImage->pasLocations[i].nLocOffset > nASSOffset )
{
if( nNextOffset == 0
|| nNextOffset > psImage->pasLocations[i].nLocOffset )
nNextOffset = psImage->pasLocations[i].nLocOffset;
}
}
if (nNextOffset > 0 && nNextOffset - nASSOffset > nASSSize)
nASSSize = nNextOffset - nASSOffset;
/* -------------------------------------------------------------------- */
/* Be sure that the attribute subsection is large enough to */
/* hold the offset table (otherwise NITFFetchAttribute coud */
/* read out of the buffer) */
/* -------------------------------------------------------------------- */
if (nASSSize < (size_t)(8 * nAttrCount))
{
CPLError( CE_Warning, CPLE_AppDefined,
"Attribute subsection not large enough (%d bytes) to contain %d attributes.",
nASSSize, nAttrCount );
return;
}
/* -------------------------------------------------------------------- */
/* Load the attribute table. */
/* -------------------------------------------------------------------- */
pabyAttributeSubsection = (GByte *) VSIMalloc(nASSSize);
if( pabyAttributeSubsection == NULL )
{
CPLError( CE_Warning, CPLE_AppDefined,
"Out of memory failure reading %d bytes of attribute subsection. ",
nASSSize );
return;
}
VSIFSeekL( psImage->psFile->fp, nASSOffset, SEEK_SET );
VSIFReadL( pabyAttributeSubsection, nASSSize, 1, psImage->psFile->fp );
/* -------------------------------------------------------------------- */
/* Scan for some particular attributes we would like. */
/* -------------------------------------------------------------------- */
if( NITFFetchAttribute( pabyAttributeSubsection, nASSSize, nAttrCount,
1, 1, 8, abyBuffer ) )
NITFExtractMetadata( &(psImage->papszMetadata), (char*)abyBuffer, 0, 8,
"NITF_RPF_CurrencyDate" );
if( NITFFetchAttribute( pabyAttributeSubsection, nASSSize, nAttrCount,
2, 1, 8, abyBuffer ) )
NITFExtractMetadata( &(psImage->papszMetadata), (char*)abyBuffer, 0, 8,
"NITF_RPF_ProductionDate" );
if( NITFFetchAttribute( pabyAttributeSubsection, nASSSize, nAttrCount,
3, 1, 8, abyBuffer ) )
NITFExtractMetadata( &(psImage->papszMetadata), (char*)abyBuffer, 0, 8,
"NITF_RPF_SignificantDate" );
CPLFree( pabyAttributeSubsection );
}
/************************************************************************/
/* NITFLoadColormapSubSection() */
/************************************************************************/
/* This function is directly inspired by function parse_clut coming from ogdi/driver/rpf/utils.c
and placed under the following copyright */
/*
******************************************************************************
* Copyright (C) 1995 Logiciels et Applications Scientifiques (L.A.S.) Inc
* Permission to use, copy, modify and distribute this software and
* its documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies, that
* both the copyright notice and this permission notice appear in
* supporting documentation, and that the name of L.A.S. Inc not be used
* in advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. L.A.S. Inc. makes no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
******************************************************************************
*/
static void NITFLoadColormapSubSection( NITFImage *psImage )
{
int nLocBaseColorGrayscaleSection = 0;
int nLocBaseColormapSubSection = 0;
/* int colorGrayscaleSectionSize = 0; */
/* int colormapSubSectionSize = 0; */
NITFFile *psFile = psImage->psFile;
unsigned int i, j;
unsigned char nOffsetRecs;
NITFColormapRecord* colormapRecords;
unsigned int colormapOffsetTableOffset;
unsigned short offsetRecLen;
NITFBandInfo *psBandInfo = psImage->pasBandInfo;
for( i = 0; (int)i < psImage->nLocCount; i++ )
{
if( psImage->pasLocations[i].nLocId == LID_ColorGrayscaleSectionSubheader )
{
nLocBaseColorGrayscaleSection = psImage->pasLocations[i].nLocOffset;
/* colorGrayscaleSectionSize = psImage->pasLocations[i].nLocSize; */
}
else if( psImage->pasLocations[i].nLocId == LID_ColormapSubsection )
{
nLocBaseColormapSubSection = psImage->pasLocations[i].nLocOffset;
/* colormapSubSectionSize = psImage->pasLocations[i].nLocSize; */
}
}
if (nLocBaseColorGrayscaleSection == 0)
{
return;
}
if (nLocBaseColormapSubSection == 0)
{
return;
}
if( VSIFSeekL( psFile->fp, nLocBaseColorGrayscaleSection,
SEEK_SET ) != 0 )
{
CPLError( CE_Failure, CPLE_FileIO,
"Failed to seek to %d.",
nLocBaseColorGrayscaleSection );
return;
}
VSIFReadL( &nOffsetRecs, 1, 1, psFile->fp );
if( VSIFSeekL( psFile->fp, nLocBaseColormapSubSection,
SEEK_SET ) != 0 )
{
CPLError( CE_Failure, CPLE_FileIO,
"Failed to seek to %d.",
nLocBaseColormapSubSection );
return;
}
colormapRecords = (NITFColormapRecord*)CPLMalloc(nOffsetRecs * sizeof(NITFColormapRecord));
/* colormap offset table offset length */
VSIFReadL( &colormapOffsetTableOffset, 1, sizeof(colormapOffsetTableOffset), psFile->fp );
CPL_MSBPTR32( &colormapOffsetTableOffset );
/* offset record length */
VSIFReadL( &offsetRecLen, 1, sizeof(offsetRecLen), psFile->fp );
CPL_MSBPTR16( &offsetRecLen );
for (i = 0; i < nOffsetRecs; i++)
{
VSIFReadL( &colormapRecords[i].tableId, 1, sizeof(colormapRecords[i].tableId), psFile->fp );
CPL_MSBPTR16( &colormapRecords[i].tableId );
VSIFReadL( &colormapRecords[i].nRecords, 1, sizeof(colormapRecords[i].nRecords), psFile->fp );
CPL_MSBPTR32( &colormapRecords[i].nRecords );
VSIFReadL( &colormapRecords[i].elementLength, 1, sizeof(colormapRecords[i].elementLength), psFile->fp );
VSIFReadL( &colormapRecords[i].histogramRecordLength, 1, sizeof(colormapRecords[i].histogramRecordLength), psFile->fp );
CPL_MSBPTR16( &colormapRecords[i].histogramRecordLength );
VSIFReadL( &colormapRecords[i].colorTableOffset, 1, sizeof(colormapRecords[i].colorTableOffset), psFile->fp );
CPL_MSBPTR32( &colormapRecords[i].colorTableOffset );
VSIFReadL( &colormapRecords[i].histogramTableOffset, 1, sizeof(colormapRecords[i].histogramTableOffset), psFile->fp );
CPL_MSBPTR32( &colormapRecords[i].histogramTableOffset );
}
for (i=0; i<nOffsetRecs; i++)
{
if( VSIFSeekL( psFile->fp, nLocBaseColormapSubSection + colormapRecords[i].colorTableOffset,
SEEK_SET ) != 0 )
{
CPLError( CE_Failure, CPLE_FileIO,
"Failed to seek to %d.",
nLocBaseColormapSubSection + colormapRecords[i].colorTableOffset );
CPLFree(colormapRecords);
return;
}
/* This test is very CADRG specific. See MIL-C-89038, paragraph 3.12.5.a */
if (i == 0 &&
colormapRecords[i].tableId == 2 &&
colormapRecords[i].elementLength == 4 &&
colormapRecords[i].nRecords == 216) /* read, use colortable */
{
GByte* rgbm = (GByte*)CPLMalloc(colormapRecords[i].nRecords * 4);
if (VSIFReadL(rgbm, 1, colormapRecords[i].nRecords * 4,
psFile->fp ) != colormapRecords[i].nRecords * 4 )
{
CPLError( CE_Failure, CPLE_FileIO,
"Failed to read %d byte rgbm.",
colormapRecords[i].nRecords * 4);
CPLFree(rgbm);
CPLFree(colormapRecords);
return;
}
for (j = 0; j < colormapRecords[i].nRecords; j++)
{
psBandInfo->pabyLUT[j] = rgbm[4*j];
psBandInfo->pabyLUT[j+256] = rgbm[4*j+1];
psBandInfo->pabyLUT[j+512] = rgbm[4*j+2];
}
CPLFree(rgbm);
}
}
CPLFree(colormapRecords);
}
/************************************************************************/
/* NITFLoadSubframeMaskTable() */
/************************************************************************/
/* Fixes bug #913 */
static void NITFLoadSubframeMaskTable( NITFImage *psImage )
{
int i;
NITFFile *psFile = psImage->psFile;
NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + psImage->iSegment;
GUIntBig nLocBaseSpatialDataSubsection = psSegInfo->nSegmentStart;
GUInt32 nLocBaseMaskSubsection = 0;
GUInt16 subframeSequenceRecordLength, transparencySequenceRecordLength, transparencyOutputPixelCodeLength;
for( i = 0; i < psImage->nLocCount; i++ )
{
if( psImage->pasLocations[i].nLocId == LID_SpatialDataSubsection )
{
nLocBaseSpatialDataSubsection = psImage->pasLocations[i].nLocOffset;
}
else if( psImage->pasLocations[i].nLocId == LID_MaskSubsection )
{
nLocBaseMaskSubsection = psImage->pasLocations[i].nLocOffset;
}
}
if (nLocBaseMaskSubsection == 0)
{
//fprintf(stderr, "nLocBase(LID_MaskSubsection) == 0\n");
return;
}
//fprintf(stderr, "nLocBaseMaskSubsection = %d\n", nLocBaseMaskSubsection);
if( VSIFSeekL( psFile->fp, nLocBaseMaskSubsection,
SEEK_SET ) != 0 )
{
CPLError( CE_Failure, CPLE_FileIO,
"Failed to seek to %d.",
nLocBaseMaskSubsection );
return;
}
VSIFReadL( &subframeSequenceRecordLength, 1, sizeof(subframeSequenceRecordLength), psFile->fp );
CPL_MSBPTR16( &subframeSequenceRecordLength );
VSIFReadL( &transparencySequenceRecordLength, 1, sizeof(transparencySequenceRecordLength), psFile->fp );
CPL_MSBPTR16( &transparencySequenceRecordLength );
/* in bits */
VSIFReadL( &transparencyOutputPixelCodeLength, 1, sizeof(transparencyOutputPixelCodeLength), psFile->fp );
CPL_MSBPTR16( &transparencyOutputPixelCodeLength );
//fprintf(stderr, "transparencyOutputPixelCodeLength=%d\n", transparencyOutputPixelCodeLength);
if( transparencyOutputPixelCodeLength == 8 )
{
GByte byNodata;
psImage->bNoDataSet = TRUE;
VSIFReadL( &byNodata, 1, 1, psFile->fp );
psImage->nNoDataValue = byNodata;
}
else
{
VSIFSeekL( psFile->fp, (transparencyOutputPixelCodeLength+7)/8, SEEK_CUR );
}
/* Fix for rpf/cjnc/cjncz01/0001f023.jn1 */
if (subframeSequenceRecordLength != 4)
{
//fprintf(stderr, "subframeSequenceRecordLength=%d\n", subframeSequenceRecordLength);
return;
}
for( i=0; i < psImage->nBlocksPerRow * psImage->nBlocksPerColumn; i++ )
{
unsigned int offset;
VSIFReadL( &offset, 1, sizeof(offset), psFile->fp );
CPL_MSBPTR32( &offset );
//fprintf(stderr, "%d : %d\n", i, offset);
if (offset == 0xffffffff)
psImage->panBlockStart[i] = 0xffffffff;
else
psImage->panBlockStart[i] = nLocBaseSpatialDataSubsection + offset;
}
}
static GUInt16 NITFReadMSBGUInt16(VSILFILE* fp, int* pbSuccess)
{
GUInt16 nVal;
if (VSIFReadL(&nVal, 1, sizeof(nVal), fp) != sizeof(nVal))
{
*pbSuccess = FALSE;
return 0;
}
CPL_MSBPTR16( &nVal );
return nVal;
}
static GUInt32 NITFReadMSBGUInt32(VSILFILE* fp, int* pbSuccess)
{
GUInt32 nVal;
if (VSIFReadL(&nVal, 1, sizeof(nVal), fp) != sizeof(nVal))
{
*pbSuccess = FALSE;
return 0;
}
CPL_MSBPTR32( &nVal );
return nVal;
}
/************************************************************************/
/* NITFReadRPFLocationTable() */
/************************************************************************/
NITFLocation* NITFReadRPFLocationTable(VSILFILE* fp, int* pnLocCount)
{
/* GUInt16 nLocSectionLength; */
GUInt32 nLocSectionOffset;
GUInt16 iLoc;
GUInt16 nLocCount;
GUInt16 nLocRecordLength;
/* GUInt32 nLocComponentAggregateLength; */
NITFLocation* pasLocations = NULL;
int bSuccess;
GUIntBig nCurOffset;
if (fp == NULL || pnLocCount == NULL)
return NULL;
*pnLocCount = 0;
nCurOffset = VSIFTellL(fp);
bSuccess = TRUE;
/* nLocSectionLength = */ NITFReadMSBGUInt16(fp, &bSuccess);
nLocSectionOffset = NITFReadMSBGUInt32(fp, &bSuccess);
if (nLocSectionOffset != 14)
{
CPLDebug("NITF", "Unusual location section offset : %d", nLocSectionOffset);
}
nLocCount = NITFReadMSBGUInt16(fp, &bSuccess);
if (!bSuccess || nLocCount == 0)
{
return NULL;
}
nLocRecordLength = NITFReadMSBGUInt16(fp, &bSuccess);
if (nLocRecordLength != 10)
{
CPLError(CE_Failure, CPLE_AppDefined,
"Did not get expected record length : %d", nLocRecordLength);
return NULL;
}
/* nLocComponentAggregateLength = */ NITFReadMSBGUInt32(fp, &bSuccess);
VSIFSeekL(fp, nCurOffset + nLocSectionOffset, SEEK_SET);
pasLocations = (NITFLocation *) VSICalloc(sizeof(NITFLocation), nLocCount);
if (pasLocations == NULL)
{
CPLError(CE_Failure, CPLE_OutOfMemory,
"Cannot allocate memory for location table");
return NULL;
}
/* -------------------------------------------------------------------- */
/* Process the locations. */
/* -------------------------------------------------------------------- */
for( iLoc = 0; iLoc < nLocCount; iLoc++ )
{
pasLocations[iLoc].nLocId = NITFReadMSBGUInt16(fp, &bSuccess);
pasLocations[iLoc].nLocSize = NITFReadMSBGUInt32(fp, &bSuccess);
pasLocations[iLoc].nLocOffset = NITFReadMSBGUInt32(fp, &bSuccess);
}
if (!bSuccess)
{
CPLFree(pasLocations);
return NULL;
}
*pnLocCount = nLocCount;
return pasLocations;
}
/************************************************************************/
/* NITFLoadLocationTable() */
/************************************************************************/
static void NITFLoadLocationTable( NITFImage *psImage )
{
/* -------------------------------------------------------------------- */
/* Get the location table out of the RPFIMG TRE on the image. */
/* -------------------------------------------------------------------- */
const char *pszTRE;
GUInt32 nHeaderOffset = 0;
int i;
int nTRESize;
char szTempFileName[32];
VSILFILE* fpTemp;
pszTRE = NITFFindTRE(psImage->pachTRE, psImage->nTREBytes, "RPFIMG", &nTRESize);
if( pszTRE == NULL )
return;
sprintf(szTempFileName, "/vsimem/%p", pszTRE);
fpTemp = VSIFileFromMemBuffer( szTempFileName, (GByte*) pszTRE, nTRESize, FALSE);
psImage->pasLocations = NITFReadRPFLocationTable(fpTemp, &psImage->nLocCount);
VSIFCloseL(fpTemp);
VSIUnlink(szTempFileName);
if (psImage->nLocCount == 0)
return;
/* -------------------------------------------------------------------- */
/* It seems that sometimes (at least for bug #1313 and #1714) */
/* the RPF headers are improperly placed. We check by looking */
/* to see if the RPFHDR is where it should be. If not, we */
/* disregard the location table. */
/* */
/* The NITF21_CGM_ANNO_Uncompressed_unmasked.ntf sample data */
/* file (see gdal data downloads) is an example of this. */
/* -------------------------------------------------------------------- */
for( i = 0; i < psImage->nLocCount; i++ )
{
if( psImage->pasLocations[i].nLocId == LID_HeaderComponent )
{
nHeaderOffset = psImage->pasLocations[i].nLocOffset;
break;
}
}
if( nHeaderOffset != 0 )
{
char achHeaderChunk[1000];
VSIFSeekL( psImage->psFile->fp, nHeaderOffset - 11, SEEK_SET );
VSIFReadL( achHeaderChunk, 1, sizeof(achHeaderChunk),
psImage->psFile->fp );
/* You can define NITF_DISABLE_RPF_LOCATION_TABLE_SANITY_TESTS to TRUE */
/* to blindly trust the RPF location table even if it doesn't look */
/* sane. Necessary for dataset attached to http://trac.osgeo.org/gdal/ticket/3930 */
if( !EQUALN(achHeaderChunk,"RPFHDR",6) &&
!CSLTestBoolean(CPLGetConfigOption("NITF_DISABLE_RPF_LOCATION_TABLE_SANITY_TESTS", "FALSE")) )
{
/* Image of http://trac.osgeo.org/gdal/ticket/3848 has incorrect */
/* RPFHDR offset, but all other locations are correct... */
/* So if we find LID_CoverageSectionSubheader and LID_CompressionLookupSubsection */
/* we check weither their content is valid */
int bFoundValidLocation = FALSE;
for( i = 0; i < psImage->nLocCount; i++ )
{
if( psImage->pasLocations[i].nLocId == LID_CoverageSectionSubheader &&
(psImage->chICORDS == 'G' || psImage->chICORDS == 'D'))
{
/* Does that look like valid latitude/longitude values ? */
/* We test that they are close enough from the values of the IGEOLO record */
double adfTarget[8];
VSIFSeekL( psImage->psFile->fp, psImage->pasLocations[i].nLocOffset,
SEEK_SET );
VSIFReadL( adfTarget, 8, 8, psImage->psFile->fp );
for( i = 0; i < 8; i++ )
CPL_MSBPTR64( (adfTarget + i) );
if ( fabs(psImage->dfULX - adfTarget[1]) < 0.1 &&
fabs(psImage->dfULY - adfTarget[0]) < 0.1 &&
fabs(psImage->dfLLX - adfTarget[3]) < 0.1 &&
fabs(psImage->dfLLY - adfTarget[2]) < 0.1 &&
fabs(psImage->dfURX - adfTarget[5]) < 0.1 &&
fabs(psImage->dfURY - adfTarget[4]) < 0.1 &&
fabs(psImage->dfLRX - adfTarget[7]) < 0.1 &&
fabs(psImage->dfLRY - adfTarget[6]) < 0.1 )
{
bFoundValidLocation = TRUE;
}
else
{
CPLDebug("NITF", "The CoverageSectionSubheader content isn't consistent");
bFoundValidLocation = FALSE;
break;
}
}
else if( psImage->pasLocations[i].nLocId == LID_CompressionLookupSubsection)
{
if (NITFLoadVQTables(psImage, FALSE))
{
bFoundValidLocation = TRUE;
}
else
{
CPLDebug("NITF", "The VQ tables content aren't consistent");
bFoundValidLocation = FALSE;
break;
}
}
}
if (bFoundValidLocation)
{
CPLDebug("NITF", "RPFHDR is not correctly placed, but other locations seem correct. Going on...");
}
else
{
CPLError( CE_Warning, CPLE_AppDefined,
"Ignoring NITF RPF Location table since it seems to be corrupt." );
CPLFree( psImage->pasLocations );
psImage->pasLocations = NULL;
psImage->nLocCount = 0;
}
}
}
}
/************************************************************************/
/* NITFLoadVQTables() */
/************************************************************************/
static int NITFLoadVQTables( NITFImage *psImage, int bTryGuessingOffset )
{
int i;
GUInt32 nVQOffset=0 /*, nVQSize=0 */;
GByte abyTestChunk[1000];
GByte abySignature[6];
/* -------------------------------------------------------------------- */
/* Do we already have the VQ tables? */
/* -------------------------------------------------------------------- */
if( psImage->apanVQLUT[0] != NULL )
return TRUE;
/* -------------------------------------------------------------------- */
/* Do we have the location information? */
/* -------------------------------------------------------------------- */
for( i = 0; i < psImage->nLocCount; i++ )
{
if( psImage->pasLocations[i].nLocId == LID_CompressionLookupSubsection)
{
nVQOffset = psImage->pasLocations[i].nLocOffset;
/* nVQSize = psImage->pasLocations[i].nLocSize; */
}
}
if( nVQOffset == 0 )
return FALSE;
/* -------------------------------------------------------------------- */
/* Does it look like we have the tables properly identified? */
/* -------------------------------------------------------------------- */
abySignature[0] = 0x00;
abySignature[1] = 0x00;
abySignature[2] = 0x00;
abySignature[3] = 0x06;
abySignature[4] = 0x00;
abySignature[5] = 0x0E;
VSIFSeekL( psImage->psFile->fp, nVQOffset, SEEK_SET );
VSIFReadL( abyTestChunk, 1, sizeof(abyTestChunk), psImage->psFile->fp );
if( memcmp(abyTestChunk,abySignature,sizeof(abySignature)) != 0 )
{
int bFoundSignature = FALSE;
if (!bTryGuessingOffset)
return FALSE;
for( i = 0; (size_t)i < sizeof(abyTestChunk) - sizeof(abySignature); i++ )
{
if( memcmp(abyTestChunk+i,abySignature,sizeof(abySignature)) == 0 )
{
bFoundSignature = TRUE;
nVQOffset += i;
CPLDebug( "NITF",
"VQ CompressionLookupSubsection offsets off by %d bytes, adjusting accordingly.",
i );
break;
}
}
if (!bFoundSignature)
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Load the tables. */
/* -------------------------------------------------------------------- */
for( i = 0; i < 4; i++ )
{
GUInt32 nVQVector;
psImage->apanVQLUT[i] = (GUInt32 *) CPLCalloc(4096,sizeof(GUInt32));
VSIFSeekL( psImage->psFile->fp, nVQOffset + 6 + i*14 + 10, SEEK_SET );
VSIFReadL( &nVQVector, 1, 4, psImage->psFile->fp );
nVQVector = CPL_MSBWORD32( nVQVector );
VSIFSeekL( psImage->psFile->fp, nVQOffset + nVQVector, SEEK_SET );
VSIFReadL( psImage->apanVQLUT[i], 4, 4096, psImage->psFile->fp );
}
return TRUE;
}
/************************************************************************/
/* NITFReadSTDIDC() */
/* */
/* Read a STDIDC TRE and return contents as metadata strings. */
/************************************************************************/
char **NITFReadSTDIDC( NITFImage *psImage )
{
return NITFGenericMetadataRead(NULL, NULL, psImage, "STDIDC");
}
/************************************************************************/
/* NITFRPCGeoToImage() */
/************************************************************************/
int NITFRPCGeoToImage( NITFRPC00BInfo *psRPC,
double dfLong, double dfLat, double dfHeight,
double *pdfPixel, double *pdfLine )
{
double dfLineNumerator, dfLineDenominator,
dfPixelNumerator, dfPixelDenominator;
double dfPolyTerm[20];
int i;
/* -------------------------------------------------------------------- */
/* Normalize Lat/Long position. */
/* -------------------------------------------------------------------- */
dfLong = (dfLong - psRPC->LONG_OFF) / psRPC->LONG_SCALE;
dfLat = (dfLat - psRPC->LAT_OFF) / psRPC->LAT_SCALE;
dfHeight = (dfHeight - psRPC->HEIGHT_OFF) / psRPC->HEIGHT_SCALE;
/* -------------------------------------------------------------------- */
/* Compute the 20 terms. */
/* -------------------------------------------------------------------- */
dfPolyTerm[0] = 1.0;
dfPolyTerm[1] = dfLong;
dfPolyTerm[2] = dfLat;
dfPolyTerm[3] = dfHeight;
dfPolyTerm[4] = dfLong * dfLat;
dfPolyTerm[5] = dfLong * dfHeight;
dfPolyTerm[6] = dfLat * dfHeight;
dfPolyTerm[7] = dfLong * dfLong;
dfPolyTerm[8] = dfLat * dfLat;
dfPolyTerm[9] = dfHeight * dfHeight;
dfPolyTerm[10] = dfLong * dfLat * dfHeight;
dfPolyTerm[11] = dfLong * dfLong * dfLong;
dfPolyTerm[12] = dfLong * dfLat * dfLat;
dfPolyTerm[13] = dfLong * dfHeight * dfHeight;
dfPolyTerm[14] = dfLong * dfLong * dfLat;
dfPolyTerm[15] = dfLat * dfLat * dfLat;
dfPolyTerm[16] = dfLat * dfHeight * dfHeight;
dfPolyTerm[17] = dfLong * dfLong * dfHeight;
dfPolyTerm[18] = dfLat * dfLat * dfHeight;
dfPolyTerm[19] = dfHeight * dfHeight * dfHeight;
/* -------------------------------------------------------------------- */
/* Compute numerator and denominator sums. */
/* -------------------------------------------------------------------- */
dfPixelNumerator = 0.0;
dfPixelDenominator = 0.0;
dfLineNumerator = 0.0;
dfLineDenominator = 0.0;
for( i = 0; i < 20; i++ )
{
dfPixelNumerator += psRPC->SAMP_NUM_COEFF[i] * dfPolyTerm[i];
dfPixelDenominator += psRPC->SAMP_DEN_COEFF[i] * dfPolyTerm[i];
dfLineNumerator += psRPC->LINE_NUM_COEFF[i] * dfPolyTerm[i];
dfLineDenominator += psRPC->LINE_DEN_COEFF[i] * dfPolyTerm[i];
}
/* -------------------------------------------------------------------- */
/* Compute normalized pixel and line values. */
/* -------------------------------------------------------------------- */
*pdfPixel = dfPixelNumerator / dfPixelDenominator;
*pdfLine = dfLineNumerator / dfLineDenominator;
/* -------------------------------------------------------------------- */
/* Denormalize. */
/* -------------------------------------------------------------------- */
*pdfPixel = *pdfPixel * psRPC->SAMP_SCALE + psRPC->SAMP_OFF;
*pdfLine = *pdfLine * psRPC->LINE_SCALE + psRPC->LINE_OFF;
return TRUE;
}
/************************************************************************/
/* NITFIHFieldOffset() */
/* */
/* Find the file offset for the beginning of a particular field */
/* in this image header. Only implemented for selected fields. */
/************************************************************************/
GUIntBig NITFIHFieldOffset( NITFImage *psImage, const char *pszFieldName )
{
char szTemp[128];
int nNICOM;
GUIntBig nWrkOffset;
GUIntBig nIMOffset =
psImage->psFile->pasSegmentInfo[psImage->iSegment].nSegmentHeaderStart;
// We only support files we created.
if( !EQUALN(psImage->psFile->szVersion,"NITF02.1",8) )
{
CPLError(CE_Failure, CPLE_AppDefined,
"NITFIHFieldOffset() only works with NITF 2.1 images");
return 0;
}
if( EQUAL(pszFieldName,"IM") )
return nIMOffset;
if( EQUAL(pszFieldName,"PJUST") )
return nIMOffset + 370;
if( EQUAL(pszFieldName,"ICORDS") )
return nIMOffset + 371;
if( EQUAL(pszFieldName,"IGEOLO") )
{
if( !psImage->bHaveIGEOLO )
return 0;
else
return nIMOffset + 372;
}
/* -------------------------------------------------------------------- */
/* Keep working offset from here on in since everything else is */
/* variable. */
/* -------------------------------------------------------------------- */
nWrkOffset = 372 + nIMOffset;
if( psImage->bHaveIGEOLO )
nWrkOffset += 60;
/* -------------------------------------------------------------------- */
/* Comments. */
/* -------------------------------------------------------------------- */
nNICOM = atoi(NITFGetField(szTemp,psImage->pachHeader,
(int)(nWrkOffset - nIMOffset),1));
if( EQUAL(pszFieldName,"NICOM") )
return nWrkOffset;
nWrkOffset++;
if( EQUAL(pszFieldName,"ICOM") )
return nWrkOffset;
nWrkOffset += 80 * nNICOM;
/* -------------------------------------------------------------------- */
/* IC */
/* -------------------------------------------------------------------- */
if( EQUAL(pszFieldName,"IC") )
return nWrkOffset;
nWrkOffset += 2;
/* -------------------------------------------------------------------- */
/* COMRAT */
/* -------------------------------------------------------------------- */
if( psImage->szIC[0] != 'N' )
{
if( EQUAL(pszFieldName,"COMRAT") )
return nWrkOffset;
nWrkOffset += 4;
}
/* -------------------------------------------------------------------- */
/* NBANDS */
/* -------------------------------------------------------------------- */
if( EQUAL(pszFieldName,"NBANDS") )
return nWrkOffset;
nWrkOffset += 1;
/* -------------------------------------------------------------------- */
/* XBANDS */
/* -------------------------------------------------------------------- */
if( EQUAL(pszFieldName,"XBANDS") )
return nWrkOffset;
if( psImage->nBands > 9 )
nWrkOffset += 5;
/* -------------------------------------------------------------------- */
/* IREPBAND */
/* -------------------------------------------------------------------- */
if( EQUAL(pszFieldName,"IREPBAND") )
return nWrkOffset;
// nWrkOffset += 2 * psImage->nBands;
return 0;
}
/************************************************************************/
/* NITFDoLinesIntersect() */
/************************************************************************/
static int NITFDoLinesIntersect( double dfL1X1, double dfL1Y1,
double dfL1X2, double dfL1Y2,
double dfL2X1, double dfL2Y1,
double dfL2X2, double dfL2Y2 )
{
double dfL1M, dfL1B, dfL2M, dfL2B;
if( dfL1X1 == dfL1X2 )
{
dfL1M = 1e10;
dfL1B = 0.0;
}
else
{
dfL1M = (dfL1Y2 - dfL1Y1 ) / (dfL1X2 - dfL1X1);
dfL1B = dfL1Y2 - dfL1M * dfL1X2;
}
if( dfL2X1 == dfL2X2 )
{
dfL2M = 1e10;
dfL2B = 0.0;
}
else
{
dfL2M = (dfL2Y2 - dfL2Y1 ) / (dfL2X2 - dfL2X1);
dfL2B = dfL2Y2 - dfL2M * dfL2X2;
}
if( dfL2M == dfL1M )
{
// parallel .. no meaningful intersection.
return FALSE;
}
else
{
double dfX /*, dfY*/;
dfX = (dfL2B - dfL1B) / (dfL1M-dfL2M);
/* dfY = dfL2M * dfX + dfL2B; */
/*
** Is this intersection on the line between
** our corner points or "out somewhere" else?
*/
return ((dfX >= dfL1X1 && dfX <= dfL1X2)
|| (dfX >= dfL1X2 && dfX <= dfL1X1))
&& ((dfX >= dfL2X1 && dfX <= dfL2X2)
|| (dfX >= dfL2X2 && dfX <= dfL2X1));
}
}
/************************************************************************/
/* NITFPossibleIGEOLOReorientation() */
/************************************************************************/
static void NITFPossibleIGEOLOReorientation( NITFImage *psImage )
{
/* -------------------------------------------------------------------- */
/* Check whether the vector from top left to bottom left */
/* intersects the line from top right to bottom right. If this */
/* is true, then we believe the corner coordinate order was */
/* written improperly. */
/* -------------------------------------------------------------------- */
#if 1
if( !NITFDoLinesIntersect( psImage->dfULX, psImage->dfULY,
psImage->dfLLX, psImage->dfLLY,
psImage->dfURX, psImage->dfURY,
psImage->dfLRX, psImage->dfLRY ) )
return;
else
CPLDebug( "NITF", "It appears the IGEOLO corner coordinates were written improperly!" );
#endif
/* -------------------------------------------------------------------- */
/* Divide the lat/long extents of this image into four */
/* quadrants and assign the corners based on which point falls */
/* into which quadrant. This is intended to correct images */
/* with the corner points written improperly. Unfortunately it */
/* also breaks images which are mirrored, or rotated more than */
/* 90 degrees from simple north up. */
/* -------------------------------------------------------------------- */
{
double dfXMax = MAX(MAX(psImage->dfULX,psImage->dfURX),
MAX(psImage->dfLRX,psImage->dfLLX));
double dfXMin = MIN(MIN(psImage->dfULX,psImage->dfURX),
MIN(psImage->dfLRX,psImage->dfLLX));
double dfYMax = MAX(MAX(psImage->dfULY,psImage->dfURY),
MAX(psImage->dfLRY,psImage->dfLLY));
double dfYMin = MIN(MIN(psImage->dfULY,psImage->dfURY),
MIN(psImage->dfLRY,psImage->dfLLY));
double dfXPivot = (dfXMax + dfXMin) * 0.5;
double dfYPivot = (dfYMax + dfYMin) * 0.5;
double dfNewULX = 0., dfNewULY = 0., dfNewURX = 0., dfNewURY = 0.,
dfNewLLX = 0., dfNewLLY = 0., dfNewLRX = 0., dfNewLRY = 0.;
int bGotUL = FALSE, bGotUR = FALSE,
bGotLL = FALSE, bGotLR = FALSE;
int iCoord, bChange = FALSE;
for( iCoord = 0; iCoord < 4; iCoord++ )
{
double *pdfXY = &(psImage->dfULX) + iCoord*2;
if( pdfXY[0] < dfXPivot && pdfXY[1] < dfYPivot )
{
bGotLL = TRUE;
dfNewLLX = pdfXY[0];
dfNewLLY = pdfXY[1];
bChange |= iCoord != 3;
}
else if( pdfXY[0] > dfXPivot && pdfXY[1] < dfYPivot )
{
bGotLR = TRUE;
dfNewLRX = pdfXY[0];
dfNewLRY = pdfXY[1];
bChange |= iCoord != 2;
}
else if( pdfXY[0] > dfXPivot && pdfXY[1] > dfYPivot )
{
bGotUR = TRUE;
dfNewURX = pdfXY[0];
dfNewURY = pdfXY[1];
bChange |= iCoord != 1;
}
else
{
bGotUL = TRUE;
dfNewULX = pdfXY[0];
dfNewULY = pdfXY[1];
bChange |= iCoord != 0;
}
}
if( !bGotUL || !bGotUR || !bGotLL || !bGotLR )
{
CPLDebug( "NITF",
"Unable to reorient corner points sensibly in NITFPossibleIGEOLOReorganization(), discarding IGEOLO locations." );
psImage->bHaveIGEOLO = FALSE;
return;
}
if( !bChange )
return;
psImage->dfULX = dfNewULX;
psImage->dfULY = dfNewULY;
psImage->dfURX = dfNewURX;
psImage->dfURY = dfNewURY;
psImage->dfLRX = dfNewLRX;
psImage->dfLRY = dfNewLRY;
psImage->dfLLX = dfNewLLX;
psImage->dfLLY = dfNewLLY;
CPLDebug( "NITF",
"IGEOLO corners have been reoriented by NITFPossibleIGEOLOReorientation()." );
}
}
/************************************************************************/
/* NITFReadIMRFCA() */
/* */
/* Read DPPDB IMRFCA TRE (and the associated IMASDA TRE) if it is */
/* available. IMRFCA RPC coefficients are remapped into RPC00B */
/* organization. */
/************************************************************************/
int NITFReadIMRFCA( NITFImage *psImage, NITFRPC00BInfo *psRPC )
{
char szTemp[100];
const char *pachTreIMASDA = NULL;
const char *pachTreIMRFCA = NULL;
double dfTolerance = 1.0e-10;
int count = 0;
int nTreIMASDASize = 0;
int nTreIMRFCASize = 0;
if( (psImage == NULL) || (psRPC == NULL) ) return FALSE;
/* Check to see if we have the IMASDA and IMRFCA tag (DPPDB data). */
pachTreIMASDA = NITFFindTRE( psImage->pachTRE, psImage->nTREBytes, "IMASDA", &nTreIMASDASize );
pachTreIMRFCA = NITFFindTRE( psImage->pachTRE, psImage->nTREBytes, "IMRFCA", &nTreIMRFCASize );
if ( (pachTreIMASDA == NULL) || (pachTreIMRFCA == NULL) ) return FALSE;
if( nTreIMASDASize < 242 || nTreIMRFCASize < 1760 )
{
CPLError( CE_Failure, CPLE_AppDefined, "Cannot read DPPDB IMASDA/IMRFCA TREs; not enough bytes." );
return FALSE;
}
/* Parse out the field values. */
/* Set the errors to 0.0 for now. */
psRPC->ERR_BIAS = 0.0;
psRPC->ERR_RAND = 0.0;
psRPC->LONG_OFF = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 0, 22) );
psRPC->LAT_OFF = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 22, 22) );
psRPC->HEIGHT_OFF = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 44, 22) );
psRPC->LONG_SCALE = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 66, 22) );
psRPC->LAT_SCALE = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 88, 22) );
psRPC->HEIGHT_SCALE = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 110, 22) );
psRPC->SAMP_OFF = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 132, 22) );
psRPC->LINE_OFF = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 154, 22) );
psRPC->SAMP_SCALE = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 176, 22) );
psRPC->LINE_SCALE = CPLAtof( NITFGetField(szTemp, pachTreIMASDA, 198, 22) );
if (psRPC->HEIGHT_SCALE == 0.0 ) psRPC->HEIGHT_SCALE = dfTolerance;
if (psRPC->LAT_SCALE == 0.0 ) psRPC->LAT_SCALE = dfTolerance;
if (psRPC->LINE_SCALE == 0.0 ) psRPC->LINE_SCALE = dfTolerance;
if (psRPC->LONG_SCALE == 0.0 ) psRPC->LONG_SCALE = dfTolerance;
if (psRPC->SAMP_SCALE == 0.0 ) psRPC->SAMP_SCALE = dfTolerance;
psRPC->HEIGHT_SCALE = 1.0/psRPC->HEIGHT_SCALE;
psRPC->LAT_SCALE = 1.0/psRPC->LAT_SCALE;
psRPC->LINE_SCALE = 1.0/psRPC->LINE_SCALE;
psRPC->LONG_SCALE = 1.0/psRPC->LONG_SCALE;
psRPC->SAMP_SCALE = 1.0/psRPC->SAMP_SCALE;
/* Parse out the RPC coefficients. */
for( count = 0; count < 20; ++count )
{
psRPC->LINE_NUM_COEFF[count] = CPLAtof( NITFGetField(szTemp, pachTreIMRFCA, count*22, 22) );
psRPC->LINE_DEN_COEFF[count] = CPLAtof( NITFGetField(szTemp, pachTreIMRFCA, 440+count*22, 22) );
psRPC->SAMP_NUM_COEFF[count] = CPLAtof( NITFGetField(szTemp, pachTreIMRFCA, 880+count*22, 22) );
psRPC->SAMP_DEN_COEFF[count] = CPLAtof( NITFGetField(szTemp, pachTreIMRFCA, 1320+count*22, 22) );
}
psRPC->SUCCESS = 1;
return TRUE;
}