mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 06:06:00 -06:00
3386 lines
120 KiB
C++
3386 lines
120 KiB
C++
/******************************************************************************
|
|
* $Id: gdal_misc.cpp 29326 2015-06-10 20:36:31Z rouault $
|
|
*
|
|
* Project: GDAL Core
|
|
* Purpose: Free standing functions for GDAL.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 1999, 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_priv.h"
|
|
#include "cpl_string.h"
|
|
#include "cpl_minixml.h"
|
|
#include "cpl_multiproc.h"
|
|
#include <ctype.h>
|
|
#include <string>
|
|
|
|
CPL_CVSID("$Id: gdal_misc.cpp 29326 2015-06-10 20:36:31Z rouault $");
|
|
|
|
#include "ogr_spatialref.h"
|
|
#include "gdal_mdreader.h"
|
|
|
|
/************************************************************************/
|
|
/* __pure_virtual() */
|
|
/* */
|
|
/* The following is a gross hack to remove the last remaining */
|
|
/* dependency on the GNU C++ standard library. */
|
|
/************************************************************************/
|
|
|
|
#ifdef __GNUC__
|
|
|
|
extern "C"
|
|
void __pure_virtual()
|
|
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
/************************************************************************/
|
|
/* GDALDataTypeUnion() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Return the smallest data type that can fully express both input data
|
|
* types.
|
|
*
|
|
* @param eType1 first data type.
|
|
* @param eType2 second data type.
|
|
*
|
|
* @return a data type able to express eType1 and eType2.
|
|
*/
|
|
|
|
GDALDataType CPL_STDCALL
|
|
GDALDataTypeUnion( GDALDataType eType1, GDALDataType eType2 )
|
|
|
|
{
|
|
int bFloating, bComplex, nBits, bSigned;
|
|
|
|
bComplex = GDALDataTypeIsComplex(eType1) | GDALDataTypeIsComplex(eType2);
|
|
|
|
switch( eType1 )
|
|
{
|
|
case GDT_Byte:
|
|
nBits = 8;
|
|
bSigned = FALSE;
|
|
bFloating = FALSE;
|
|
break;
|
|
|
|
case GDT_Int16:
|
|
case GDT_CInt16:
|
|
nBits = 16;
|
|
bSigned = TRUE;
|
|
bFloating = FALSE;
|
|
break;
|
|
|
|
case GDT_UInt16:
|
|
nBits = 16;
|
|
bSigned = FALSE;
|
|
bFloating = FALSE;
|
|
break;
|
|
|
|
case GDT_Int32:
|
|
case GDT_CInt32:
|
|
nBits = 32;
|
|
bSigned = TRUE;
|
|
bFloating = FALSE;
|
|
break;
|
|
|
|
case GDT_UInt32:
|
|
nBits = 32;
|
|
bSigned = FALSE;
|
|
bFloating = FALSE;
|
|
break;
|
|
|
|
case GDT_Float32:
|
|
case GDT_CFloat32:
|
|
nBits = 32;
|
|
bSigned = TRUE;
|
|
bFloating = TRUE;
|
|
break;
|
|
|
|
case GDT_Float64:
|
|
case GDT_CFloat64:
|
|
nBits = 64;
|
|
bSigned = TRUE;
|
|
bFloating = TRUE;
|
|
break;
|
|
|
|
default:
|
|
CPLAssert( FALSE );
|
|
return GDT_Unknown;
|
|
}
|
|
|
|
switch( eType2 )
|
|
{
|
|
case GDT_Byte:
|
|
break;
|
|
|
|
case GDT_Int16:
|
|
case GDT_CInt16:
|
|
nBits = MAX(nBits,16);
|
|
bSigned = TRUE;
|
|
break;
|
|
|
|
case GDT_UInt16:
|
|
nBits = MAX(nBits,16);
|
|
break;
|
|
|
|
case GDT_Int32:
|
|
case GDT_CInt32:
|
|
nBits = MAX(nBits,32);
|
|
bSigned = TRUE;
|
|
break;
|
|
|
|
case GDT_UInt32:
|
|
nBits = MAX(nBits,32);
|
|
break;
|
|
|
|
case GDT_Float32:
|
|
case GDT_CFloat32:
|
|
nBits = MAX(nBits,32);
|
|
bSigned = TRUE;
|
|
bFloating = TRUE;
|
|
break;
|
|
|
|
case GDT_Float64:
|
|
case GDT_CFloat64:
|
|
nBits = MAX(nBits,64);
|
|
bSigned = TRUE;
|
|
bFloating = TRUE;
|
|
break;
|
|
|
|
default:
|
|
CPLAssert( FALSE );
|
|
return GDT_Unknown;
|
|
}
|
|
|
|
if( nBits == 8 )
|
|
return GDT_Byte;
|
|
else if( nBits == 16 && bComplex )
|
|
return GDT_CInt16;
|
|
else if( nBits == 16 && bSigned )
|
|
return GDT_Int16;
|
|
else if( nBits == 16 && !bSigned )
|
|
return GDT_UInt16;
|
|
else if( nBits == 32 && bFloating && bComplex )
|
|
return GDT_CFloat32;
|
|
else if( nBits == 32 && bFloating )
|
|
return GDT_Float32;
|
|
else if( nBits == 32 && bComplex )
|
|
return GDT_CInt32;
|
|
else if( nBits == 32 && bSigned )
|
|
return GDT_Int32;
|
|
else if( nBits == 32 && !bSigned )
|
|
return GDT_UInt32;
|
|
else if( nBits == 64 && bComplex )
|
|
return GDT_CFloat64;
|
|
else
|
|
return GDT_Float64;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* GDALGetDataTypeSize() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Get data type size in bits.
|
|
*
|
|
* Returns the size of a a GDT_* type in bits, <b>not bytes</b>!
|
|
*
|
|
* @param eDataType type, such as GDT_Byte.
|
|
* @return the number of bits or zero if it is not recognised.
|
|
*/
|
|
|
|
int CPL_STDCALL GDALGetDataTypeSize( GDALDataType eDataType )
|
|
|
|
{
|
|
switch( eDataType )
|
|
{
|
|
case GDT_Byte:
|
|
return 8;
|
|
|
|
case GDT_UInt16:
|
|
case GDT_Int16:
|
|
return 16;
|
|
|
|
case GDT_UInt32:
|
|
case GDT_Int32:
|
|
case GDT_Float32:
|
|
case GDT_CInt16:
|
|
return 32;
|
|
|
|
case GDT_Float64:
|
|
case GDT_CInt32:
|
|
case GDT_CFloat32:
|
|
return 64;
|
|
|
|
case GDT_CFloat64:
|
|
return 128;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDataTypeIsComplex() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Is data type complex?
|
|
*
|
|
* @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32,
|
|
* GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary
|
|
* component.
|
|
*/
|
|
|
|
int CPL_STDCALL GDALDataTypeIsComplex( GDALDataType eDataType )
|
|
|
|
{
|
|
switch( eDataType )
|
|
{
|
|
case GDT_CInt16:
|
|
case GDT_CInt32:
|
|
case GDT_CFloat32:
|
|
case GDT_CFloat64:
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGetDataTypeName() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Get name of data type.
|
|
*
|
|
* Returns a symbolic name for the data type. This is essentially the
|
|
* the enumerated item name with the GDT_ prefix removed. So GDT_Byte returns
|
|
* "Byte". The returned strings are static strings and should not be modified
|
|
* or freed by the application. These strings are useful for reporting
|
|
* datatypes in debug statements, errors and other user output.
|
|
*
|
|
* @param eDataType type to get name of.
|
|
* @return string corresponding to existing data type
|
|
* or NULL pointer if invalid type given.
|
|
*/
|
|
|
|
const char * CPL_STDCALL GDALGetDataTypeName( GDALDataType eDataType )
|
|
|
|
{
|
|
switch( eDataType )
|
|
{
|
|
case GDT_Unknown:
|
|
return "Unknown";
|
|
|
|
case GDT_Byte:
|
|
return "Byte";
|
|
|
|
case GDT_UInt16:
|
|
return "UInt16";
|
|
|
|
case GDT_Int16:
|
|
return "Int16";
|
|
|
|
case GDT_UInt32:
|
|
return "UInt32";
|
|
|
|
case GDT_Int32:
|
|
return "Int32";
|
|
|
|
case GDT_Float32:
|
|
return "Float32";
|
|
|
|
case GDT_Float64:
|
|
return "Float64";
|
|
|
|
case GDT_CInt16:
|
|
return "CInt16";
|
|
|
|
case GDT_CInt32:
|
|
return "CInt32";
|
|
|
|
case GDT_CFloat32:
|
|
return "CFloat32";
|
|
|
|
case GDT_CFloat64:
|
|
return "CFloat64";
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGetDataTypeByName() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Get data type by symbolic name.
|
|
*
|
|
* Returns a data type corresponding to the given symbolic name. This
|
|
* function is opposite to the GDALGetDataTypeName().
|
|
*
|
|
* @param pszName string containing the symbolic name of the type.
|
|
*
|
|
* @return GDAL data type.
|
|
*/
|
|
|
|
GDALDataType CPL_STDCALL GDALGetDataTypeByName( const char *pszName )
|
|
|
|
{
|
|
VALIDATE_POINTER1( pszName, "GDALGetDataTypeByName", GDT_Unknown );
|
|
|
|
int iType;
|
|
|
|
for( iType = 1; iType < GDT_TypeCount; iType++ )
|
|
{
|
|
if( GDALGetDataTypeName((GDALDataType)iType) != NULL
|
|
&& EQUAL(GDALGetDataTypeName((GDALDataType)iType), pszName) )
|
|
{
|
|
return (GDALDataType)iType;
|
|
}
|
|
}
|
|
|
|
return GDT_Unknown;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGetAsyncStatusTypeByName() */
|
|
/************************************************************************/
|
|
/**
|
|
* Get AsyncStatusType by symbolic name.
|
|
*
|
|
* Returns a data type corresponding to the given symbolic name. This
|
|
* function is opposite to the GDALGetAsyncStatusTypeName().
|
|
*
|
|
* @param pszName string containing the symbolic name of the type.
|
|
*
|
|
* @return GDAL AsyncStatus type.
|
|
*/
|
|
GDALAsyncStatusType CPL_DLL CPL_STDCALL GDALGetAsyncStatusTypeByName( const char *pszName )
|
|
{
|
|
VALIDATE_POINTER1( pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR);
|
|
|
|
int iType;
|
|
|
|
for( iType = 1; iType < GARIO_TypeCount; iType++ )
|
|
{
|
|
if( GDALGetAsyncStatusTypeName((GDALAsyncStatusType)iType) != NULL
|
|
&& EQUAL(GDALGetAsyncStatusTypeName((GDALAsyncStatusType)iType), pszName) )
|
|
{
|
|
return (GDALAsyncStatusType)iType;
|
|
}
|
|
}
|
|
|
|
return GARIO_ERROR;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* GDALGetAsyncStatusTypeName() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Get name of AsyncStatus data type.
|
|
*
|
|
* Returns a symbolic name for the AsyncStatus data type. This is essentially the
|
|
* the enumerated item name with the GARIO_ prefix removed. So GARIO_COMPLETE returns
|
|
* "COMPLETE". The returned strings are static strings and should not be modified
|
|
* or freed by the application. These strings are useful for reporting
|
|
* datatypes in debug statements, errors and other user output.
|
|
*
|
|
* @param eAsyncStatusType type to get name of.
|
|
* @return string corresponding to type.
|
|
*/
|
|
|
|
const char * CPL_STDCALL GDALGetAsyncStatusTypeName( GDALAsyncStatusType eAsyncStatusType )
|
|
|
|
{
|
|
switch( eAsyncStatusType )
|
|
{
|
|
case GARIO_PENDING:
|
|
return "PENDING";
|
|
|
|
case GARIO_UPDATE:
|
|
return "UPDATE";
|
|
|
|
case GARIO_ERROR:
|
|
return "ERROR";
|
|
|
|
case GARIO_COMPLETE:
|
|
return "COMPLETE";
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGetPaletteInterpretationName() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Get name of palette interpretation
|
|
*
|
|
* Returns a symbolic name for the palette interpretation. This is the
|
|
* the enumerated item name with the GPI_ prefix removed. So GPI_Gray returns
|
|
* "Gray". The returned strings are static strings and should not be modified
|
|
* or freed by the application.
|
|
*
|
|
* @param eInterp palette interpretation to get name of.
|
|
* @return string corresponding to palette interpretation.
|
|
*/
|
|
|
|
const char *GDALGetPaletteInterpretationName( GDALPaletteInterp eInterp )
|
|
|
|
{
|
|
switch( eInterp )
|
|
{
|
|
case GPI_Gray:
|
|
return "Gray";
|
|
|
|
case GPI_RGB:
|
|
return "RGB";
|
|
|
|
case GPI_CMYK:
|
|
return "CMYK";
|
|
|
|
case GPI_HLS:
|
|
return "HLS";
|
|
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGetColorInterpretationName() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Get name of color interpretation
|
|
*
|
|
* Returns a symbolic name for the color interpretation. This is derived from
|
|
* the enumerated item name with the GCI_ prefix removed, but there are some
|
|
* variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red".
|
|
* The returned strings are static strings and should not be modified
|
|
* or freed by the application.
|
|
*
|
|
* @param eInterp color interpretation to get name of.
|
|
* @return string corresponding to color interpretation
|
|
* or NULL pointer if invalid enumerator given.
|
|
*/
|
|
|
|
const char *GDALGetColorInterpretationName( GDALColorInterp eInterp )
|
|
|
|
{
|
|
switch( eInterp )
|
|
{
|
|
case GCI_Undefined:
|
|
return "Undefined";
|
|
|
|
case GCI_GrayIndex:
|
|
return "Gray";
|
|
|
|
case GCI_PaletteIndex:
|
|
return "Palette";
|
|
|
|
case GCI_RedBand:
|
|
return "Red";
|
|
|
|
case GCI_GreenBand:
|
|
return "Green";
|
|
|
|
case GCI_BlueBand:
|
|
return "Blue";
|
|
|
|
case GCI_AlphaBand:
|
|
return "Alpha";
|
|
|
|
case GCI_HueBand:
|
|
return "Hue";
|
|
|
|
case GCI_SaturationBand:
|
|
return "Saturation";
|
|
|
|
case GCI_LightnessBand:
|
|
return "Lightness";
|
|
|
|
case GCI_CyanBand:
|
|
return "Cyan";
|
|
|
|
case GCI_MagentaBand:
|
|
return "Magenta";
|
|
|
|
case GCI_YellowBand:
|
|
return "Yellow";
|
|
|
|
case GCI_BlackBand:
|
|
return "Black";
|
|
|
|
case GCI_YCbCr_YBand:
|
|
return "YCbCr_Y";
|
|
|
|
case GCI_YCbCr_CbBand:
|
|
return "YCbCr_Cb";
|
|
|
|
case GCI_YCbCr_CrBand:
|
|
return "YCbCr_Cr";
|
|
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGetColorInterpretationByName() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Get color interpreation by symbolic name.
|
|
*
|
|
* Returns a color interpreation corresponding to the given symbolic name. This
|
|
* function is opposite to the GDALGetColorInterpretationName().
|
|
*
|
|
* @param pszName string containing the symbolic name of the color interpretation.
|
|
*
|
|
* @return GDAL color interpretation.
|
|
*
|
|
* @since GDAL 1.7.0
|
|
*/
|
|
|
|
GDALColorInterp GDALGetColorInterpretationByName( const char *pszName )
|
|
|
|
{
|
|
VALIDATE_POINTER1( pszName, "GDALGetColorInterpretationByName", GCI_Undefined );
|
|
|
|
int iType;
|
|
|
|
for( iType = 0; iType <= GCI_Max; iType++ )
|
|
{
|
|
if( EQUAL(GDALGetColorInterpretationName((GDALColorInterp)iType), pszName) )
|
|
{
|
|
return (GDALColorInterp)iType;
|
|
}
|
|
}
|
|
|
|
return GCI_Undefined;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGetRandomRasterSample() */
|
|
/************************************************************************/
|
|
|
|
int CPL_STDCALL
|
|
GDALGetRandomRasterSample( GDALRasterBandH hBand, int nSamples,
|
|
float *pafSampleBuf )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hBand, "GDALGetRandomRasterSample", 0 );
|
|
|
|
GDALRasterBand *poBand;
|
|
|
|
poBand = (GDALRasterBand *) GDALGetRasterSampleOverview( hBand, nSamples );
|
|
CPLAssert( NULL != poBand );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Figure out the ratio of blocks we will read to get an */
|
|
/* approximate value. */
|
|
/* -------------------------------------------------------------------- */
|
|
int nBlockXSize, nBlockYSize;
|
|
int nBlocksPerRow, nBlocksPerColumn;
|
|
int nSampleRate;
|
|
int bGotNoDataValue;
|
|
double dfNoDataValue;
|
|
int nActualSamples = 0;
|
|
int nBlockSampleRate;
|
|
int nBlockPixels, nBlockCount;
|
|
|
|
dfNoDataValue = poBand->GetNoDataValue( &bGotNoDataValue );
|
|
|
|
poBand->GetBlockSize( &nBlockXSize, &nBlockYSize );
|
|
|
|
nBlocksPerRow = (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize;
|
|
nBlocksPerColumn = (poBand->GetYSize() + nBlockYSize - 1) / nBlockYSize;
|
|
|
|
nBlockPixels = nBlockXSize * nBlockYSize;
|
|
nBlockCount = nBlocksPerRow * nBlocksPerColumn;
|
|
|
|
if( nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0
|
|
|| nBlockCount == 0 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"GDALGetRandomRasterSample(): returning because band"
|
|
" appears degenerate." );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
nSampleRate = (int) MAX(1,sqrt((double) nBlockCount)-2.0);
|
|
|
|
if( nSampleRate == nBlocksPerRow && nSampleRate > 1 )
|
|
nSampleRate--;
|
|
|
|
while( nSampleRate > 1
|
|
&& ((nBlockCount-1) / nSampleRate + 1) * nBlockPixels < nSamples )
|
|
nSampleRate--;
|
|
|
|
if ((nSamples / ((nBlockCount-1) / nSampleRate + 1)) == 0)
|
|
nBlockSampleRate = 1;
|
|
else
|
|
nBlockSampleRate =
|
|
MAX(1,nBlockPixels / (nSamples / ((nBlockCount-1) / nSampleRate + 1)));
|
|
|
|
for( int iSampleBlock = 0;
|
|
iSampleBlock < nBlockCount;
|
|
iSampleBlock += nSampleRate )
|
|
{
|
|
double dfValue = 0.0, dfReal, dfImag;
|
|
int iXBlock, iYBlock, iX, iY, iXValid, iYValid, iRemainder = 0;
|
|
GDALRasterBlock *poBlock;
|
|
|
|
iYBlock = iSampleBlock / nBlocksPerRow;
|
|
iXBlock = iSampleBlock - nBlocksPerRow * iYBlock;
|
|
|
|
poBlock = poBand->GetLockedBlockRef( iXBlock, iYBlock );
|
|
if( poBlock == NULL )
|
|
continue;
|
|
if( poBlock->GetDataRef() == NULL )
|
|
{
|
|
poBlock->DropLock();
|
|
continue;
|
|
}
|
|
|
|
if( (iXBlock + 1) * nBlockXSize > poBand->GetXSize() )
|
|
iXValid = poBand->GetXSize() - iXBlock * nBlockXSize;
|
|
else
|
|
iXValid = nBlockXSize;
|
|
|
|
if( (iYBlock + 1) * nBlockYSize > poBand->GetYSize() )
|
|
iYValid = poBand->GetYSize() - iYBlock * nBlockYSize;
|
|
else
|
|
iYValid = nBlockYSize;
|
|
|
|
for( iY = 0; iY < iYValid; iY++ )
|
|
{
|
|
for( iX = iRemainder; iX < iXValid; iX += nBlockSampleRate )
|
|
{
|
|
int iOffset;
|
|
|
|
iOffset = iX + iY * nBlockXSize;
|
|
switch( poBlock->GetDataType() )
|
|
{
|
|
case GDT_Byte:
|
|
dfValue = ((GByte *) poBlock->GetDataRef())[iOffset];
|
|
break;
|
|
case GDT_UInt16:
|
|
dfValue = ((GUInt16 *) poBlock->GetDataRef())[iOffset];
|
|
break;
|
|
case GDT_Int16:
|
|
dfValue = ((GInt16 *) poBlock->GetDataRef())[iOffset];
|
|
break;
|
|
case GDT_UInt32:
|
|
dfValue = ((GUInt32 *) poBlock->GetDataRef())[iOffset];
|
|
break;
|
|
case GDT_Int32:
|
|
dfValue = ((GInt32 *) poBlock->GetDataRef())[iOffset];
|
|
break;
|
|
case GDT_Float32:
|
|
dfValue = ((float *) poBlock->GetDataRef())[iOffset];
|
|
break;
|
|
case GDT_Float64:
|
|
dfValue = ((double *) poBlock->GetDataRef())[iOffset];
|
|
break;
|
|
case GDT_CInt16:
|
|
dfReal = ((GInt16 *) poBlock->GetDataRef())[iOffset*2];
|
|
dfImag = ((GInt16 *) poBlock->GetDataRef())[iOffset*2+1];
|
|
dfValue = sqrt(dfReal*dfReal + dfImag*dfImag);
|
|
break;
|
|
case GDT_CInt32:
|
|
dfReal = ((GInt32 *) poBlock->GetDataRef())[iOffset*2];
|
|
dfImag = ((GInt32 *) poBlock->GetDataRef())[iOffset*2+1];
|
|
dfValue = sqrt(dfReal*dfReal + dfImag*dfImag);
|
|
break;
|
|
case GDT_CFloat32:
|
|
dfReal = ((float *) poBlock->GetDataRef())[iOffset*2];
|
|
dfImag = ((float *) poBlock->GetDataRef())[iOffset*2+1];
|
|
dfValue = sqrt(dfReal*dfReal + dfImag*dfImag);
|
|
break;
|
|
case GDT_CFloat64:
|
|
dfReal = ((double *) poBlock->GetDataRef())[iOffset*2];
|
|
dfImag = ((double *) poBlock->GetDataRef())[iOffset*2+1];
|
|
dfValue = sqrt(dfReal*dfReal + dfImag*dfImag);
|
|
break;
|
|
default:
|
|
CPLAssert( FALSE );
|
|
}
|
|
|
|
if( bGotNoDataValue && dfValue == dfNoDataValue )
|
|
continue;
|
|
|
|
if( nActualSamples < nSamples )
|
|
pafSampleBuf[nActualSamples++] = (float) dfValue;
|
|
}
|
|
|
|
iRemainder = iX - iXValid;
|
|
}
|
|
|
|
poBlock->DropLock();
|
|
}
|
|
|
|
return nActualSamples;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALInitGCPs() */
|
|
/************************************************************************/
|
|
|
|
void CPL_STDCALL GDALInitGCPs( int nCount, GDAL_GCP * psGCP )
|
|
|
|
{
|
|
if( nCount > 0 )
|
|
{
|
|
VALIDATE_POINTER0( psGCP, "GDALInitGCPs" );
|
|
}
|
|
|
|
for( int iGCP = 0; iGCP < nCount; iGCP++ )
|
|
{
|
|
memset( psGCP, 0, sizeof(GDAL_GCP) );
|
|
psGCP->pszId = CPLStrdup("");
|
|
psGCP->pszInfo = CPLStrdup("");
|
|
psGCP++;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDeinitGCPs() */
|
|
/************************************************************************/
|
|
|
|
void CPL_STDCALL GDALDeinitGCPs( int nCount, GDAL_GCP * psGCP )
|
|
|
|
{
|
|
if ( nCount > 0 )
|
|
{
|
|
VALIDATE_POINTER0( psGCP, "GDALDeinitGCPs" );
|
|
}
|
|
|
|
for( int iGCP = 0; iGCP < nCount; iGCP++ )
|
|
{
|
|
CPLFree( psGCP->pszId );
|
|
CPLFree( psGCP->pszInfo );
|
|
psGCP++;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDuplicateGCPs() */
|
|
/************************************************************************/
|
|
|
|
GDAL_GCP * CPL_STDCALL
|
|
GDALDuplicateGCPs( int nCount, const GDAL_GCP *pasGCPList )
|
|
|
|
{
|
|
GDAL_GCP *pasReturn;
|
|
|
|
pasReturn = (GDAL_GCP *) CPLMalloc(sizeof(GDAL_GCP) * nCount);
|
|
GDALInitGCPs( nCount, pasReturn );
|
|
|
|
for( int iGCP = 0; iGCP < nCount; iGCP++ )
|
|
{
|
|
CPLFree( pasReturn[iGCP].pszId );
|
|
pasReturn[iGCP].pszId = CPLStrdup( pasGCPList[iGCP].pszId );
|
|
|
|
CPLFree( pasReturn[iGCP].pszInfo );
|
|
pasReturn[iGCP].pszInfo = CPLStrdup( pasGCPList[iGCP].pszInfo );
|
|
|
|
pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel;
|
|
pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine;
|
|
pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX;
|
|
pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY;
|
|
pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ;
|
|
}
|
|
|
|
return pasReturn;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALFindAssociatedFile() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Find file with alternate extension.
|
|
*
|
|
* Finds the file with the indicated extension, substituting it in place
|
|
* of the extension of the base filename. Generally used to search for
|
|
* associated files like world files .RPB files, etc. If necessary, the
|
|
* extension will be tried in both upper and lower case. If a sibling file
|
|
* list is available it will be used instead of doing VSIStatExL() calls to
|
|
* probe the file system.
|
|
*
|
|
* Note that the result is a dynamic CPLString so this method should not
|
|
* be used in a situation where there could be cross heap issues. It is
|
|
* generally imprudent for application built on GDAL to use this function
|
|
* unless they are sure they will always use the same runtime heap as GDAL.
|
|
*
|
|
* @param pszBaseFilename the filename relative to which to search.
|
|
* @param pszExt the target extension in either upper or lower case.
|
|
* @param papszSiblingFiles the list of files in the same directory as
|
|
* pszBaseFilename or NULL if they are not known.
|
|
* @param nFlags special options controlling search. None defined yet, just
|
|
* pass 0.
|
|
*
|
|
* @return an empty string if the target is not found, otherwise the target
|
|
* file with similar path style as the pszBaseFilename.
|
|
*/
|
|
|
|
CPLString GDALFindAssociatedFile( const char *pszBaseFilename,
|
|
const char *pszExt,
|
|
char **papszSiblingFiles,
|
|
int nFlags )
|
|
|
|
{
|
|
(void) nFlags;
|
|
|
|
CPLString osTarget = CPLResetExtension( pszBaseFilename, pszExt );
|
|
|
|
if( papszSiblingFiles == NULL )
|
|
{
|
|
VSIStatBufL sStatBuf;
|
|
|
|
if( VSIStatExL( osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 )
|
|
{
|
|
CPLString osAltExt = pszExt;
|
|
|
|
if( islower( pszExt[0] ) )
|
|
osAltExt.toupper();
|
|
else
|
|
osAltExt.tolower();
|
|
|
|
osTarget = CPLResetExtension( pszBaseFilename, osAltExt );
|
|
|
|
if( VSIStatExL( osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG ) != 0 )
|
|
return "";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int iSibling = CSLFindString( papszSiblingFiles,
|
|
CPLGetFilename(osTarget) );
|
|
if( iSibling < 0 )
|
|
return "";
|
|
|
|
osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
|
|
osTarget += papszSiblingFiles[iSibling];
|
|
}
|
|
|
|
return osTarget;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALLoadOziMapFile() */
|
|
/************************************************************************/
|
|
|
|
#define MAX_GCP 30
|
|
|
|
int CPL_STDCALL GDALLoadOziMapFile( const char *pszFilename,
|
|
double *padfGeoTransform, char **ppszWKT,
|
|
int *pnGCPCount, GDAL_GCP **ppasGCPs )
|
|
|
|
|
|
{
|
|
char **papszLines;
|
|
int iLine, nLines=0;
|
|
int nCoordinateCount = 0;
|
|
GDAL_GCP asGCPs[MAX_GCP];
|
|
|
|
VALIDATE_POINTER1( pszFilename, "GDALLoadOziMapFile", FALSE );
|
|
VALIDATE_POINTER1( padfGeoTransform, "GDALLoadOziMapFile", FALSE );
|
|
VALIDATE_POINTER1( pnGCPCount, "GDALLoadOziMapFile", FALSE );
|
|
VALIDATE_POINTER1( ppasGCPs, "GDALLoadOziMapFile", FALSE );
|
|
|
|
papszLines = CSLLoad2( pszFilename, 1000, 200, NULL );
|
|
|
|
if ( !papszLines )
|
|
return FALSE;
|
|
|
|
nLines = CSLCount( papszLines );
|
|
|
|
// Check the OziExplorer Map file signature
|
|
if ( nLines < 5
|
|
|| !EQUALN(papszLines[0], "OziExplorer Map Data File Version ", 34) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map format.",
|
|
pszFilename );
|
|
CSLDestroy( papszLines );
|
|
return FALSE;
|
|
}
|
|
|
|
OGRSpatialReference oSRS;
|
|
OGRErr eErr = OGRERR_NONE;
|
|
|
|
/* The Map Scale Factor has been introduced recently on the 6th line */
|
|
/* and is a trick that is used to just change that line without changing */
|
|
/* the rest of the MAP file but providing an imagery that is smaller or larger */
|
|
/* so we have to correct the pixel/line values read in the .MAP file so they */
|
|
/* match the actual imagery dimension. Well, this is a bad summary of what */
|
|
/* is explained at http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */
|
|
double dfMSF = 1;
|
|
|
|
for ( iLine = 5; iLine < nLines; iLine++ )
|
|
{
|
|
if ( EQUALN(papszLines[iLine], "MSF,", 4) )
|
|
{
|
|
dfMSF = CPLAtof(papszLines[iLine] + 4);
|
|
if (dfMSF <= 0.01) /* Suspicious values */
|
|
{
|
|
CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]);
|
|
dfMSF = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
eErr = oSRS.importFromOzi( papszLines );
|
|
if ( eErr == OGRERR_NONE )
|
|
{
|
|
if ( ppszWKT != NULL )
|
|
oSRS.exportToWkt( ppszWKT );
|
|
}
|
|
|
|
// Iterate all lines in the MAP-file
|
|
for ( iLine = 5; iLine < nLines; iLine++ )
|
|
{
|
|
char **papszTok = NULL;
|
|
|
|
papszTok = CSLTokenizeString2( papszLines[iLine], ",",
|
|
CSLT_ALLOWEMPTYTOKENS
|
|
| CSLT_STRIPLEADSPACES
|
|
| CSLT_STRIPENDSPACES );
|
|
|
|
if ( CSLCount(papszTok) < 12 )
|
|
{
|
|
CSLDestroy(papszTok);
|
|
continue;
|
|
}
|
|
|
|
if ( CSLCount(papszTok) >= 17
|
|
&& EQUALN(papszTok[0], "Point", 5)
|
|
&& !EQUAL(papszTok[2], "")
|
|
&& !EQUAL(papszTok[3], "")
|
|
&& nCoordinateCount < MAX_GCP )
|
|
{
|
|
int bReadOk = FALSE;
|
|
double dfLon = 0., dfLat = 0.;
|
|
|
|
if ( !EQUAL(papszTok[6], "")
|
|
&& !EQUAL(papszTok[7], "")
|
|
&& !EQUAL(papszTok[9], "")
|
|
&& !EQUAL(papszTok[10], "") )
|
|
{
|
|
// Set geographical coordinates of the pixels
|
|
dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0;
|
|
dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0;
|
|
if ( EQUAL(papszTok[11], "W") )
|
|
dfLon = -dfLon;
|
|
if ( EQUAL(papszTok[8], "S") )
|
|
dfLat = -dfLat;
|
|
|
|
// Transform from the geographical coordinates into projected
|
|
// coordinates.
|
|
if ( eErr == OGRERR_NONE )
|
|
{
|
|
OGRSpatialReference *poLatLong = NULL;
|
|
OGRCoordinateTransformation *poTransform = NULL;
|
|
|
|
poLatLong = oSRS.CloneGeogCS();
|
|
if ( poLatLong )
|
|
{
|
|
poTransform = OGRCreateCoordinateTransformation( poLatLong, &oSRS );
|
|
if ( poTransform )
|
|
{
|
|
bReadOk = poTransform->Transform( 1, &dfLon, &dfLat );
|
|
delete poTransform;
|
|
}
|
|
delete poLatLong;
|
|
}
|
|
}
|
|
}
|
|
else if ( !EQUAL(papszTok[14], "")
|
|
&& !EQUAL(papszTok[15], "") )
|
|
{
|
|
// Set cartesian coordinates of the pixels.
|
|
dfLon = CPLAtofM(papszTok[14]);
|
|
dfLat = CPLAtofM(papszTok[15]);
|
|
bReadOk = TRUE;
|
|
|
|
//if ( EQUAL(papszTok[16], "S") )
|
|
// dfLat = -dfLat;
|
|
}
|
|
|
|
if ( bReadOk )
|
|
{
|
|
GDALInitGCPs( 1, asGCPs + nCoordinateCount );
|
|
|
|
// Set pixel/line part
|
|
asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]) / dfMSF;
|
|
asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]) / dfMSF;
|
|
|
|
asGCPs[nCoordinateCount].dfGCPX = dfLon;
|
|
asGCPs[nCoordinateCount].dfGCPY = dfLat;
|
|
|
|
nCoordinateCount++;
|
|
}
|
|
}
|
|
|
|
CSLDestroy( papszTok );
|
|
}
|
|
|
|
CSLDestroy( papszLines );
|
|
|
|
if ( nCoordinateCount == 0 )
|
|
{
|
|
CPLDebug( "GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.",
|
|
pszFilename );
|
|
return FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to convert the GCPs into a geotransform definition, if */
|
|
/* possible. Otherwise we will need to use them as GCPs. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !GDALGCPsToGeoTransform( nCoordinateCount, asGCPs, padfGeoTransform,
|
|
CSLTestBoolean(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO")) ) )
|
|
{
|
|
if ( pnGCPCount && ppasGCPs )
|
|
{
|
|
CPLDebug( "GDAL",
|
|
"GDALLoadOziMapFile(%s) found file, wasn't able to derive a\n"
|
|
"first order geotransform. Using points as GCPs.",
|
|
pszFilename );
|
|
|
|
*ppasGCPs = (GDAL_GCP *)
|
|
CPLCalloc( sizeof(GDAL_GCP),nCoordinateCount );
|
|
memcpy( *ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount );
|
|
*pnGCPCount = nCoordinateCount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GDALDeinitGCPs( nCoordinateCount, asGCPs );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#undef MAX_GCP
|
|
|
|
/************************************************************************/
|
|
/* GDALReadOziMapFile() */
|
|
/************************************************************************/
|
|
|
|
int CPL_STDCALL GDALReadOziMapFile( const char * pszBaseFilename,
|
|
double *padfGeoTransform, char **ppszWKT,
|
|
int *pnGCPCount, GDAL_GCP **ppasGCPs )
|
|
|
|
|
|
{
|
|
const char *pszOzi;
|
|
FILE *fpOzi;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try lower case, then upper case. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszOzi = CPLResetExtension( pszBaseFilename, "map" );
|
|
|
|
fpOzi = VSIFOpen( pszOzi, "rt" );
|
|
|
|
if ( fpOzi == NULL && VSIIsCaseSensitiveFS(pszOzi) )
|
|
{
|
|
pszOzi = CPLResetExtension( pszBaseFilename, "MAP" );
|
|
fpOzi = VSIFOpen( pszOzi, "rt" );
|
|
}
|
|
|
|
if ( fpOzi == NULL )
|
|
return FALSE;
|
|
|
|
VSIFClose( fpOzi );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We found the file, now load and parse it. */
|
|
/* -------------------------------------------------------------------- */
|
|
return GDALLoadOziMapFile( pszOzi, padfGeoTransform, ppszWKT,
|
|
pnGCPCount, ppasGCPs );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALLoadTabFile() */
|
|
/* */
|
|
/* Helper function for translator implementators wanting */
|
|
/* support for MapInfo .tab-files. */
|
|
/************************************************************************/
|
|
|
|
#define MAX_GCP 256
|
|
|
|
int CPL_STDCALL GDALLoadTabFile( const char *pszFilename,
|
|
double *padfGeoTransform, char **ppszWKT,
|
|
int *pnGCPCount, GDAL_GCP **ppasGCPs )
|
|
|
|
|
|
{
|
|
char **papszLines;
|
|
char **papszTok=NULL;
|
|
int bTypeRasterFound = FALSE;
|
|
int bInsideTableDef = FALSE;
|
|
int iLine, numLines=0;
|
|
int nCoordinateCount = 0;
|
|
GDAL_GCP asGCPs[MAX_GCP];
|
|
|
|
papszLines = CSLLoad2( pszFilename, 1000, 200, NULL );
|
|
|
|
if ( !papszLines )
|
|
return FALSE;
|
|
|
|
numLines = CSLCount(papszLines);
|
|
|
|
// Iterate all lines in the TAB-file
|
|
for(iLine=0; iLine<numLines; iLine++)
|
|
{
|
|
CSLDestroy(papszTok);
|
|
papszTok = CSLTokenizeStringComplex(papszLines[iLine], " \t(),;",
|
|
TRUE, FALSE);
|
|
|
|
if (CSLCount(papszTok) < 2)
|
|
continue;
|
|
|
|
// Did we find table definition
|
|
if (EQUAL(papszTok[0], "Definition") && EQUAL(papszTok[1], "Table") )
|
|
{
|
|
bInsideTableDef = TRUE;
|
|
}
|
|
else if (bInsideTableDef && (EQUAL(papszTok[0], "Type")) )
|
|
{
|
|
// Only RASTER-type will be handled
|
|
if (EQUAL(papszTok[1], "RASTER"))
|
|
{
|
|
bTypeRasterFound = TRUE;
|
|
}
|
|
else
|
|
{
|
|
CSLDestroy(papszTok);
|
|
CSLDestroy(papszLines);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (bTypeRasterFound && bInsideTableDef
|
|
&& CSLCount(papszTok) > 4
|
|
&& EQUAL(papszTok[4], "Label")
|
|
&& nCoordinateCount < MAX_GCP )
|
|
{
|
|
GDALInitGCPs( 1, asGCPs + nCoordinateCount );
|
|
|
|
asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]);
|
|
asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]);
|
|
asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]);
|
|
asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]);
|
|
if( papszTok[5] != NULL )
|
|
{
|
|
CPLFree( asGCPs[nCoordinateCount].pszId );
|
|
asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]);
|
|
}
|
|
|
|
nCoordinateCount++;
|
|
}
|
|
else if( bTypeRasterFound && bInsideTableDef
|
|
&& EQUAL(papszTok[0],"CoordSys")
|
|
&& ppszWKT != NULL )
|
|
{
|
|
OGRSpatialReference oSRS;
|
|
|
|
if( oSRS.importFromMICoordSys( papszLines[iLine] ) == OGRERR_NONE )
|
|
oSRS.exportToWkt( ppszWKT );
|
|
}
|
|
else if( EQUAL(papszTok[0],"Units")
|
|
&& CSLCount(papszTok) > 1
|
|
&& EQUAL(papszTok[1],"degree") )
|
|
{
|
|
/*
|
|
** If we have units of "degree", but a projected coordinate
|
|
** system we need to convert it to geographic. See to01_02.TAB.
|
|
*/
|
|
if( ppszWKT != NULL && *ppszWKT != NULL
|
|
&& EQUALN(*ppszWKT,"PROJCS",6) )
|
|
{
|
|
OGRSpatialReference oSRS, oSRSGeogCS;
|
|
char *pszSrcWKT = *ppszWKT;
|
|
|
|
oSRS.importFromWkt( &pszSrcWKT );
|
|
oSRSGeogCS.CopyGeogCSFrom( &oSRS );
|
|
CPLFree( *ppszWKT );
|
|
oSRSGeogCS.exportToWkt( ppszWKT );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
CSLDestroy(papszTok);
|
|
CSLDestroy(papszLines);
|
|
|
|
if( nCoordinateCount == 0 )
|
|
{
|
|
CPLDebug( "GDAL", "GDALLoadTabFile(%s) did not get any GCPs.",
|
|
pszFilename );
|
|
return FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to convert the GCPs into a geotransform definition, if */
|
|
/* possible. Otherwise we will need to use them as GCPs. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !GDALGCPsToGeoTransform( nCoordinateCount, asGCPs, padfGeoTransform,
|
|
CSLTestBoolean(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO")) ) )
|
|
{
|
|
if (pnGCPCount && ppasGCPs)
|
|
{
|
|
CPLDebug( "GDAL",
|
|
"GDALLoadTabFile(%s) found file, wasn't able to derive a\n"
|
|
"first order geotransform. Using points as GCPs.",
|
|
pszFilename );
|
|
|
|
*ppasGCPs = (GDAL_GCP *)
|
|
CPLCalloc( sizeof(GDAL_GCP),nCoordinateCount );
|
|
memcpy( *ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount );
|
|
*pnGCPCount = nCoordinateCount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GDALDeinitGCPs( nCoordinateCount, asGCPs );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#undef MAX_GCP
|
|
|
|
/************************************************************************/
|
|
/* GDALReadTabFile() */
|
|
/* */
|
|
/* Helper function for translator implementators wanting */
|
|
/* support for MapInfo .tab-files. */
|
|
/************************************************************************/
|
|
|
|
int CPL_STDCALL GDALReadTabFile( const char * pszBaseFilename,
|
|
double *padfGeoTransform, char **ppszWKT,
|
|
int *pnGCPCount, GDAL_GCP **ppasGCPs )
|
|
|
|
|
|
{
|
|
return GDALReadTabFile2(pszBaseFilename, padfGeoTransform,
|
|
ppszWKT, pnGCPCount, ppasGCPs,
|
|
NULL, NULL);
|
|
}
|
|
|
|
|
|
int GDALReadTabFile2( const char * pszBaseFilename,
|
|
double *padfGeoTransform, char **ppszWKT,
|
|
int *pnGCPCount, GDAL_GCP **ppasGCPs,
|
|
char** papszSiblingFiles, char** ppszTabFileNameOut )
|
|
{
|
|
const char *pszTAB;
|
|
VSILFILE *fpTAB;
|
|
|
|
if (ppszTabFileNameOut)
|
|
*ppszTabFileNameOut = NULL;
|
|
|
|
if( !GDALCanFileAcceptSidecarFile(pszBaseFilename) )
|
|
return FALSE;
|
|
|
|
pszTAB = CPLResetExtension( pszBaseFilename, "tab" );
|
|
|
|
if (papszSiblingFiles)
|
|
{
|
|
int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTAB));
|
|
if (iSibling >= 0)
|
|
{
|
|
CPLString osTabFilename = pszBaseFilename;
|
|
osTabFilename.resize(strlen(pszBaseFilename) -
|
|
strlen(CPLGetFilename(pszBaseFilename)));
|
|
osTabFilename += papszSiblingFiles[iSibling];
|
|
if ( GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT,
|
|
pnGCPCount, ppasGCPs ) )
|
|
{
|
|
if (ppszTabFileNameOut)
|
|
*ppszTabFileNameOut = CPLStrdup(osTabFilename);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try lower case, then upper case. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
fpTAB = VSIFOpenL( pszTAB, "rt" );
|
|
|
|
if( fpTAB == NULL && VSIIsCaseSensitiveFS(pszTAB) )
|
|
{
|
|
pszTAB = CPLResetExtension( pszBaseFilename, "TAB" );
|
|
fpTAB = VSIFOpenL( pszTAB, "rt" );
|
|
}
|
|
|
|
if( fpTAB == NULL )
|
|
return FALSE;
|
|
|
|
VSIFCloseL( fpTAB );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We found the file, now load and parse it. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (GDALLoadTabFile( pszTAB, padfGeoTransform, ppszWKT,
|
|
pnGCPCount, ppasGCPs ) )
|
|
{
|
|
if (ppszTabFileNameOut)
|
|
*ppszTabFileNameOut = CPLStrdup(pszTAB);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALLoadWorldFile() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Read ESRI world file.
|
|
*
|
|
* This function reads an ESRI style world file, and formats a geotransform
|
|
* from its contents.
|
|
*
|
|
* The world file contains an affine transformation with the parameters
|
|
* in a different order than in a geotransform array.
|
|
*
|
|
* <ul>
|
|
* <li> geotransform[1] : width of pixel
|
|
* <li> geotransform[4] : rotational coefficient, zero for north up images.
|
|
* <li> geotransform[2] : rotational coefficient, zero for north up images.
|
|
* <li> geotransform[5] : height of pixel (but negative)
|
|
* <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel.
|
|
* <li> geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel.
|
|
* </ul>
|
|
*
|
|
* @param pszFilename the world file name.
|
|
* @param padfGeoTransform the six double array into which the
|
|
* geotransformation should be placed.
|
|
*
|
|
* @return TRUE on success or FALSE on failure.
|
|
*/
|
|
|
|
int CPL_STDCALL
|
|
GDALLoadWorldFile( const char *pszFilename, double *padfGeoTransform )
|
|
|
|
{
|
|
char **papszLines;
|
|
|
|
VALIDATE_POINTER1( pszFilename, "GDALLoadWorldFile", FALSE );
|
|
VALIDATE_POINTER1( padfGeoTransform, "GDALLoadWorldFile", FALSE );
|
|
|
|
papszLines = CSLLoad2( pszFilename, 100, 100, NULL );
|
|
|
|
if ( !papszLines )
|
|
return FALSE;
|
|
|
|
double world[6];
|
|
// reads the first 6 non-empty lines
|
|
int nLines = 0;
|
|
int nLinesCount = CSLCount(papszLines);
|
|
for( int i = 0; i < nLinesCount && nLines < 6; ++i )
|
|
{
|
|
CPLString line(papszLines[i]);
|
|
if( line.Trim().empty() )
|
|
continue;
|
|
|
|
world[nLines] = CPLAtofM(line);
|
|
++nLines;
|
|
}
|
|
|
|
if( nLines == 6
|
|
&& (world[0] != 0.0 || world[2] != 0.0)
|
|
&& (world[3] != 0.0 || world[1] != 0.0) )
|
|
{
|
|
padfGeoTransform[0] = world[4];
|
|
padfGeoTransform[1] = world[0];
|
|
padfGeoTransform[2] = world[2];
|
|
padfGeoTransform[3] = world[5];
|
|
padfGeoTransform[4] = world[1];
|
|
padfGeoTransform[5] = world[3];
|
|
|
|
// correct for center of pixel vs. top left of pixel
|
|
padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
|
|
padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
|
|
padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
|
|
padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
|
|
|
|
CSLDestroy(papszLines);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
CPLDebug( "GDAL",
|
|
"GDALLoadWorldFile(%s) found file, but it was corrupt.",
|
|
pszFilename );
|
|
CSLDestroy(papszLines);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALReadWorldFile() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Read ESRI world file.
|
|
*
|
|
* This function reads an ESRI style world file, and formats a geotransform
|
|
* from its contents. It does the same as GDALLoadWorldFile() function, but
|
|
* it will form the filename for the worldfile from the filename of the raster
|
|
* file referred and the suggested extension. If no extension is provided,
|
|
* the code will internally try the unix style and windows style world file
|
|
* extensions (eg. for .tif these would be .tfw and .tifw).
|
|
*
|
|
* The world file contains an affine transformation with the parameters
|
|
* in a different order than in a geotransform array.
|
|
*
|
|
* <ul>
|
|
* <li> geotransform[1] : width of pixel
|
|
* <li> geotransform[4] : rotational coefficient, zero for north up images.
|
|
* <li> geotransform[2] : rotational coefficient, zero for north up images.
|
|
* <li> geotransform[5] : height of pixel (but negative)
|
|
* <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel.
|
|
* <li> geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel.
|
|
* </ul>
|
|
*
|
|
* @param pszBaseFilename the target raster file.
|
|
* @param pszExtension the extension to use (ie. ".wld") or NULL to derive it
|
|
* from the pszBaseFilename
|
|
* @param padfGeoTransform the six double array into which the
|
|
* geotransformation should be placed.
|
|
*
|
|
* @return TRUE on success or FALSE on failure.
|
|
*/
|
|
|
|
int CPL_STDCALL
|
|
GDALReadWorldFile( const char *pszBaseFilename, const char *pszExtension,
|
|
double *padfGeoTransform )
|
|
|
|
{
|
|
return GDALReadWorldFile2(pszBaseFilename, pszExtension,
|
|
padfGeoTransform, NULL, NULL);
|
|
}
|
|
|
|
int GDALReadWorldFile2( const char *pszBaseFilename, const char *pszExtension,
|
|
double *padfGeoTransform, char** papszSiblingFiles,
|
|
char** ppszWorldFileNameOut )
|
|
{
|
|
const char *pszTFW;
|
|
char szExtUpper[32], szExtLower[32];
|
|
int i;
|
|
|
|
VALIDATE_POINTER1( pszBaseFilename, "GDALReadWorldFile", FALSE );
|
|
VALIDATE_POINTER1( padfGeoTransform, "GDALReadWorldFile", FALSE );
|
|
|
|
if (ppszWorldFileNameOut)
|
|
*ppszWorldFileNameOut = NULL;
|
|
|
|
if( !GDALCanFileAcceptSidecarFile(pszBaseFilename) )
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If we aren't given an extension, try both the unix and */
|
|
/* windows style extensions. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pszExtension == NULL )
|
|
{
|
|
char szDerivedExtension[100];
|
|
std::string oBaseExt = CPLGetExtension( pszBaseFilename );
|
|
|
|
if( oBaseExt.length() < 2 )
|
|
return FALSE;
|
|
|
|
// windows version - first + last + 'w'
|
|
szDerivedExtension[0] = oBaseExt[0];
|
|
szDerivedExtension[1] = oBaseExt[oBaseExt.length()-1];
|
|
szDerivedExtension[2] = 'w';
|
|
szDerivedExtension[3] = '\0';
|
|
|
|
if( GDALReadWorldFile2( pszBaseFilename, szDerivedExtension,
|
|
padfGeoTransform, papszSiblingFiles,
|
|
ppszWorldFileNameOut ) )
|
|
return TRUE;
|
|
|
|
// unix version - extension + 'w'
|
|
if( oBaseExt.length() > sizeof(szDerivedExtension)-2 )
|
|
return FALSE;
|
|
|
|
strcpy( szDerivedExtension, oBaseExt.c_str() );
|
|
strcat( szDerivedExtension, "w" );
|
|
return GDALReadWorldFile2( pszBaseFilename, szDerivedExtension,
|
|
padfGeoTransform, papszSiblingFiles,
|
|
ppszWorldFileNameOut );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Skip the leading period in the extension if there is one. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( *pszExtension == '.' )
|
|
pszExtension++;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Generate upper and lower case versions of the extension. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLStrlcpy( szExtUpper, pszExtension, sizeof(szExtUpper) );
|
|
CPLStrlcpy( szExtLower, pszExtension, sizeof(szExtLower) );
|
|
|
|
for( i = 0; szExtUpper[i] != '\0'; i++ )
|
|
{
|
|
szExtUpper[i] = (char) toupper(szExtUpper[i]);
|
|
szExtLower[i] = (char) tolower(szExtLower[i]);
|
|
}
|
|
|
|
VSIStatBufL sStatBuf;
|
|
int bGotTFW;
|
|
|
|
pszTFW = CPLResetExtension( pszBaseFilename, szExtLower );
|
|
|
|
if (papszSiblingFiles)
|
|
{
|
|
int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTFW));
|
|
if (iSibling >= 0)
|
|
{
|
|
CPLString osTFWFilename = pszBaseFilename;
|
|
osTFWFilename.resize(strlen(pszBaseFilename) -
|
|
strlen(CPLGetFilename(pszBaseFilename)));
|
|
osTFWFilename += papszSiblingFiles[iSibling];
|
|
if (GDALLoadWorldFile( osTFWFilename, padfGeoTransform ))
|
|
{
|
|
if (ppszWorldFileNameOut)
|
|
*ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try lower case, then upper case. */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
bGotTFW = VSIStatExL( pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0;
|
|
|
|
if( !bGotTFW && VSIIsCaseSensitiveFS(pszTFW) )
|
|
{
|
|
pszTFW = CPLResetExtension( pszBaseFilename, szExtUpper );
|
|
bGotTFW = VSIStatExL( pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0;
|
|
}
|
|
|
|
if( !bGotTFW )
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We found the file, now load and parse it. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (GDALLoadWorldFile( pszTFW, padfGeoTransform ))
|
|
{
|
|
if (ppszWorldFileNameOut)
|
|
*ppszWorldFileNameOut = CPLStrdup(pszTFW);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALWriteWorldFile() */
|
|
/* */
|
|
/* Helper function for translator implementators wanting */
|
|
/* support for ESRI world files. */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Write ESRI world file.
|
|
*
|
|
* This function writes an ESRI style world file from the passed geotransform.
|
|
*
|
|
* The world file contains an affine transformation with the parameters
|
|
* in a different order than in a geotransform array.
|
|
*
|
|
* <ul>
|
|
* <li> geotransform[1] : width of pixel
|
|
* <li> geotransform[4] : rotational coefficient, zero for north up images.
|
|
* <li> geotransform[2] : rotational coefficient, zero for north up images.
|
|
* <li> geotransform[5] : height of pixel (but negative)
|
|
* <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel.
|
|
* <li> geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel.
|
|
* </ul>
|
|
*
|
|
* @param pszBaseFilename the target raster file.
|
|
* @param pszExtension the extension to use (ie. ".wld"). Must not be NULL
|
|
* @param padfGeoTransform the six double array from which the
|
|
* geotransformation should be read.
|
|
*
|
|
* @return TRUE on success or FALSE on failure.
|
|
*/
|
|
|
|
int CPL_STDCALL
|
|
GDALWriteWorldFile( const char * pszBaseFilename, const char *pszExtension,
|
|
double *padfGeoTransform )
|
|
|
|
{
|
|
VALIDATE_POINTER1( pszBaseFilename, "GDALWriteWorldFile", FALSE );
|
|
VALIDATE_POINTER1( pszExtension, "GDALWriteWorldFile", FALSE );
|
|
VALIDATE_POINTER1( padfGeoTransform, "GDALWriteWorldFile", FALSE );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Prepare the text to write to the file. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLString osTFWText;
|
|
|
|
osTFWText.Printf( "%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n",
|
|
padfGeoTransform[1],
|
|
padfGeoTransform[4],
|
|
padfGeoTransform[2],
|
|
padfGeoTransform[5],
|
|
padfGeoTransform[0]
|
|
+ 0.5 * padfGeoTransform[1]
|
|
+ 0.5 * padfGeoTransform[2],
|
|
padfGeoTransform[3]
|
|
+ 0.5 * padfGeoTransform[4]
|
|
+ 0.5 * padfGeoTransform[5] );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Update extention, and write to disk. */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszTFW;
|
|
VSILFILE *fpTFW;
|
|
|
|
pszTFW = CPLResetExtension( pszBaseFilename, pszExtension );
|
|
fpTFW = VSIFOpenL( pszTFW, "wt" );
|
|
if( fpTFW == NULL )
|
|
return FALSE;
|
|
|
|
VSIFWriteL( (void *) osTFWText.c_str(), 1, osTFWText.size(), fpTFW );
|
|
VSIFCloseL( fpTFW );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALVersionInfo() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Get runtime version information.
|
|
*
|
|
* Available pszRequest values:
|
|
* <ul>
|
|
* <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string. ie. "1170"
|
|
* Note: starting with GDAL 1.10, this string will be longer than 4 characters.
|
|
* <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a string.
|
|
* ie. "20020416".
|
|
* <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "1.1.7"
|
|
* <li> "--version": Returns one line version message suitable for use in
|
|
* response to --version requests. ie. "GDAL 1.1.7, released 2002/04/16"
|
|
* <li> "LICENSE": Returns the content of the LICENSE.TXT file from the GDAL_DATA directory.
|
|
* Before GDAL 1.7.0, the returned string was leaking memory but this is now resolved.
|
|
* So the result should not been freed by the caller.
|
|
* <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines with
|
|
* information on build time options.
|
|
* </ul>
|
|
*
|
|
* @param pszRequest the type of version info desired, as listed above.
|
|
*
|
|
* @return an internal string containing the requested information.
|
|
*/
|
|
|
|
const char * CPL_STDCALL GDALVersionInfo( const char *pszRequest )
|
|
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try to capture as much build information as practical. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pszRequest != NULL && EQUAL(pszRequest,"BUILD_INFO") )
|
|
{
|
|
CPLString osBuildInfo;
|
|
|
|
#ifdef ESRI_BUILD
|
|
osBuildInfo += "ESRI_BUILD=YES\n";
|
|
#endif
|
|
#ifdef PAM_ENABLED
|
|
osBuildInfo += "PAM_ENABLED=YES\n";
|
|
#endif
|
|
#ifdef OGR_ENABLED
|
|
osBuildInfo += "OGR_ENABLED=YES\n";
|
|
#endif
|
|
|
|
CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
|
|
CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE );
|
|
return (char *) CPLGetTLS(CTLS_VERSIONINFO);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* LICENSE is a special case. We try to find and read the */
|
|
/* LICENSE.TXT file from the GDAL_DATA directory and return it */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pszRequest != NULL && EQUAL(pszRequest,"LICENSE") )
|
|
{
|
|
char* pszResultLicence = (char*) CPLGetTLS( CTLS_VERSIONINFO_LICENCE );
|
|
if( pszResultLicence != NULL )
|
|
{
|
|
return pszResultLicence;
|
|
}
|
|
|
|
const char *pszFilename = CPLFindFile( "etc", "LICENSE.TXT" );
|
|
VSILFILE *fp = NULL;
|
|
int nLength;
|
|
|
|
if( pszFilename != NULL )
|
|
fp = VSIFOpenL( pszFilename, "r" );
|
|
|
|
if( fp != NULL )
|
|
{
|
|
VSIFSeekL( fp, 0, SEEK_END );
|
|
nLength = (int) VSIFTellL( fp ) + 1;
|
|
VSIFSeekL( fp, SEEK_SET, 0 );
|
|
|
|
pszResultLicence = (char *) VSICalloc(1,nLength);
|
|
if (pszResultLicence)
|
|
VSIFReadL( pszResultLicence, 1, nLength-1, fp );
|
|
|
|
VSIFCloseL( fp );
|
|
}
|
|
|
|
if (!pszResultLicence)
|
|
{
|
|
pszResultLicence = CPLStrdup(
|
|
"GDAL/OGR is released under the MIT/X license.\n"
|
|
"The LICENSE.TXT distributed with GDAL/OGR should\n"
|
|
"contain additional details.\n" );
|
|
}
|
|
|
|
CPLSetTLS( CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE );
|
|
return pszResultLicence;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* All other strings are fairly small. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLString osVersionInfo;
|
|
|
|
if( pszRequest == NULL || EQUAL(pszRequest,"VERSION_NUM") )
|
|
osVersionInfo.Printf( "%d", GDAL_VERSION_NUM );
|
|
else if( EQUAL(pszRequest,"RELEASE_DATE") )
|
|
osVersionInfo.Printf( "%d", GDAL_RELEASE_DATE );
|
|
else if( EQUAL(pszRequest,"RELEASE_NAME") )
|
|
osVersionInfo.Printf( GDAL_RELEASE_NAME );
|
|
else // --version
|
|
osVersionInfo.Printf( "GDAL %s, released %d/%02d/%02d",
|
|
GDAL_RELEASE_NAME,
|
|
GDAL_RELEASE_DATE / 10000,
|
|
(GDAL_RELEASE_DATE % 10000) / 100,
|
|
GDAL_RELEASE_DATE % 100 );
|
|
|
|
CPLFree(CPLGetTLS(CTLS_VERSIONINFO)); // clear old value.
|
|
CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE );
|
|
return (char *) CPLGetTLS(CTLS_VERSIONINFO);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALCheckVersion() */
|
|
/************************************************************************/
|
|
|
|
/** Return TRUE if GDAL library version at runtime matches nVersionMajor.nVersionMinor.
|
|
|
|
The purpose of this method is to ensure that calling code will run with the GDAL
|
|
version it is compiled for. It is primarly intented for external plugins.
|
|
|
|
@param nVersionMajor Major version to be tested against
|
|
@param nVersionMinor Minor version to be tested against
|
|
@param pszCallingComponentName If not NULL, in case of version mismatch, the method
|
|
will issue a failure mentionning the name of
|
|
the calling component.
|
|
|
|
@return TRUE if GDAL library version at runtime matches nVersionMajor.nVersionMinor, FALSE otherwise.
|
|
*/
|
|
int CPL_STDCALL GDALCheckVersion( int nVersionMajor, int nVersionMinor,
|
|
const char* pszCallingComponentName)
|
|
{
|
|
if (nVersionMajor == GDAL_VERSION_MAJOR &&
|
|
nVersionMinor == GDAL_VERSION_MINOR)
|
|
return TRUE;
|
|
|
|
if (pszCallingComponentName)
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"%s was compiled against GDAL %d.%d but current library version is %d.%d\n",
|
|
pszCallingComponentName, nVersionMajor, nVersionMinor,
|
|
GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDecToDMS() */
|
|
/* */
|
|
/* Translate a decimal degrees value to a DMS string with */
|
|
/* hemisphere. */
|
|
/************************************************************************/
|
|
|
|
const char * CPL_STDCALL GDALDecToDMS( double dfAngle, const char * pszAxis,
|
|
int nPrecision )
|
|
|
|
{
|
|
return CPLDecToDMS( dfAngle, pszAxis, nPrecision );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALPackedDMSToDec() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
|
|
*
|
|
* See CPLPackedDMSToDec().
|
|
*/
|
|
|
|
double CPL_STDCALL GDALPackedDMSToDec( double dfPacked )
|
|
|
|
{
|
|
return CPLPackedDMSToDec( dfPacked );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDecToPackedDMS() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
|
|
*
|
|
* See CPLDecToPackedDMS().
|
|
*/
|
|
|
|
double CPL_STDCALL GDALDecToPackedDMS( double dfDec )
|
|
|
|
{
|
|
return CPLDecToPackedDMS( dfDec );
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGCPsToGeoTransform() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Generate Geotransform from GCPs.
|
|
*
|
|
* Given a set of GCPs perform first order fit as a geotransform.
|
|
*
|
|
* Due to imprecision in the calculations the fit algorithm will often
|
|
* return non-zero rotational coefficients even if given perfectly non-rotated
|
|
* inputs. A special case has been implemented for corner corner coordinates
|
|
* given in TL, TR, BR, BL order. So when using this to get a geotransform
|
|
* from 4 corner coordinates, pass them in this order.
|
|
*
|
|
* @param nGCPCount the number of GCPs being passed in.
|
|
* @param pasGCPs the list of GCP structures.
|
|
* @param padfGeoTransform the six double array in which the affine
|
|
* geotransformation will be returned.
|
|
* @param bApproxOK If FALSE the function will fail if the geotransform is not
|
|
* essentially an exact fit (within 0.25 pixel) for all GCPs.
|
|
*
|
|
* @return TRUE on success or FALSE if there aren't enough points to prepare a
|
|
* geotransform, the pointers are ill-determined or if bApproxOK is FALSE
|
|
* and the fit is poor.
|
|
*/
|
|
|
|
int CPL_STDCALL
|
|
GDALGCPsToGeoTransform( int nGCPCount, const GDAL_GCP *pasGCPs,
|
|
double *padfGeoTransform, int bApproxOK )
|
|
|
|
{
|
|
int i;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Recognise a few special cases. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nGCPCount < 2 )
|
|
return FALSE;
|
|
|
|
if( nGCPCount == 2 )
|
|
{
|
|
if( pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel
|
|
|| pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine )
|
|
return FALSE;
|
|
|
|
padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX)
|
|
/ (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
|
|
padfGeoTransform[2] = 0.0;
|
|
|
|
padfGeoTransform[4] = 0.0;
|
|
padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY)
|
|
/ (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
|
|
|
|
padfGeoTransform[0] = pasGCPs[0].dfGCPX
|
|
- pasGCPs[0].dfGCPPixel * padfGeoTransform[1]
|
|
- pasGCPs[0].dfGCPLine * padfGeoTransform[2];
|
|
|
|
padfGeoTransform[3] = pasGCPs[0].dfGCPY
|
|
- pasGCPs[0].dfGCPPixel * padfGeoTransform[4]
|
|
- pasGCPs[0].dfGCPLine * padfGeoTransform[5];
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Special case of 4 corner coordinates of a non-rotated */
|
|
/* image. The points must be in TL-TR-BR-BL order for now. */
|
|
/* This case helps avoid some imprecision in the general */
|
|
/* calcuations. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nGCPCount == 4
|
|
&& pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine
|
|
&& pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine
|
|
&& pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel
|
|
&& pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel
|
|
&& pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine
|
|
&& pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel
|
|
&& pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY
|
|
&& pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY
|
|
&& pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX
|
|
&& pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX
|
|
&& pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY
|
|
&& pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX )
|
|
{
|
|
padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX)
|
|
/ (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
|
|
padfGeoTransform[2] = 0.0;
|
|
padfGeoTransform[4] = 0.0;
|
|
padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY)
|
|
/ (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine);
|
|
|
|
padfGeoTransform[0] =
|
|
pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
|
|
padfGeoTransform[3] =
|
|
pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
|
|
return TRUE;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Compute source and destination ranges so we can normalize */
|
|
/* the values to make the least squares computation more stable. */
|
|
/* -------------------------------------------------------------------- */
|
|
double min_pixel = pasGCPs[0].dfGCPPixel;
|
|
double max_pixel = pasGCPs[0].dfGCPPixel;
|
|
double min_line = pasGCPs[0].dfGCPLine;
|
|
double max_line = pasGCPs[0].dfGCPLine;
|
|
double min_geox = pasGCPs[0].dfGCPX;
|
|
double max_geox = pasGCPs[0].dfGCPX;
|
|
double min_geoy = pasGCPs[0].dfGCPY;
|
|
double max_geoy = pasGCPs[0].dfGCPY;
|
|
|
|
for (i = 1; i < nGCPCount; ++i) {
|
|
min_pixel = MIN(min_pixel, pasGCPs[i].dfGCPPixel);
|
|
max_pixel = MAX(max_pixel, pasGCPs[i].dfGCPPixel);
|
|
min_line = MIN(min_line, pasGCPs[i].dfGCPLine);
|
|
max_line = MAX(max_line, pasGCPs[i].dfGCPLine);
|
|
min_geox = MIN(min_geox, pasGCPs[i].dfGCPX);
|
|
max_geox = MAX(max_geox, pasGCPs[i].dfGCPX);
|
|
min_geoy = MIN(min_geoy, pasGCPs[i].dfGCPY);
|
|
max_geoy = MAX(max_geoy, pasGCPs[i].dfGCPY);
|
|
}
|
|
|
|
double EPS = 1.0e-12;
|
|
|
|
if( ABS(max_pixel - min_pixel) < EPS
|
|
|| ABS(max_line - min_line) < EPS
|
|
|| ABS(max_geox - min_geox) < EPS
|
|
|| ABS(max_geoy - min_geoy) < EPS)
|
|
{
|
|
return FALSE; // degenerate in at least one dimension.
|
|
}
|
|
|
|
double pl_normalize[6], geo_normalize[6];
|
|
|
|
pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
|
|
pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
|
|
pl_normalize[2] = 0.0;
|
|
pl_normalize[3] = -min_line / (max_line - min_line);
|
|
pl_normalize[4] = 0.0;
|
|
pl_normalize[5] = 1.0 / (max_line - min_line);
|
|
|
|
geo_normalize[0] = -min_geox / (max_geox - min_geox);
|
|
geo_normalize[1] = 1.0 / (max_geox - min_geox);
|
|
geo_normalize[2] = 0.0;
|
|
geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
|
|
geo_normalize[4] = 0.0;
|
|
geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* In the general case, do a least squares error approximation by */
|
|
/* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
double sum_x = 0.0, sum_y = 0.0, sum_xy = 0.0, sum_xx = 0.0, sum_yy = 0.0;
|
|
double sum_Lon = 0.0, sum_Lonx = 0.0, sum_Lony = 0.0;
|
|
double sum_Lat = 0.0, sum_Latx = 0.0, sum_Laty = 0.0;
|
|
double divisor;
|
|
|
|
for (i = 0; i < nGCPCount; ++i) {
|
|
double pixel, line, geox, geoy;
|
|
|
|
GDALApplyGeoTransform(pl_normalize,
|
|
pasGCPs[i].dfGCPPixel,
|
|
pasGCPs[i].dfGCPLine,
|
|
&pixel, &line);
|
|
GDALApplyGeoTransform(geo_normalize,
|
|
pasGCPs[i].dfGCPX,
|
|
pasGCPs[i].dfGCPY,
|
|
&geox, &geoy);
|
|
|
|
sum_x += pixel;
|
|
sum_y += line;
|
|
sum_xy += pixel * line;
|
|
sum_xx += pixel * pixel;
|
|
sum_yy += line * line;
|
|
sum_Lon += geox;
|
|
sum_Lonx += geox * pixel;
|
|
sum_Lony += geox * line;
|
|
sum_Lat += geoy;
|
|
sum_Latx += geoy * pixel;
|
|
sum_Laty += geoy * line;
|
|
}
|
|
|
|
divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy)
|
|
+ 2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx
|
|
- sum_x * sum_x * sum_yy;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If the divisor is zero, there is no valid solution. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (divisor == 0.0)
|
|
return FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Compute top/left origin. */
|
|
/* -------------------------------------------------------------------- */
|
|
double gt_normalized[6];
|
|
gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy)
|
|
+ sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy)
|
|
+ sum_Lony * (sum_x * sum_xy - sum_y * sum_xx))
|
|
/ divisor;
|
|
|
|
gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy)
|
|
+ sum_Latx * (sum_y * sum_xy - sum_x * sum_yy)
|
|
+ sum_Laty * (sum_x * sum_xy - sum_y * sum_xx))
|
|
/ divisor;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Compute X related coefficients. */
|
|
/* -------------------------------------------------------------------- */
|
|
gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy)
|
|
+ sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y)
|
|
+ sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount))
|
|
/ divisor;
|
|
|
|
gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx)
|
|
+ sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy)
|
|
+ sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x))
|
|
/ divisor;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Compute Y related coefficients. */
|
|
/* -------------------------------------------------------------------- */
|
|
gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy)
|
|
+ sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y)
|
|
+ sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount))
|
|
/ divisor;
|
|
|
|
gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx)
|
|
+ sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy)
|
|
+ sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x))
|
|
/ divisor;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Compose the resulting transformation with the normalization */
|
|
/* geotransformations. */
|
|
/* -------------------------------------------------------------------- */
|
|
double gt1p2[6], inv_geo_normalize[6];
|
|
if( !GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
|
|
return FALSE;
|
|
|
|
GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
|
|
GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Now check if any of the input points fit this poorly. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !bApproxOK )
|
|
{
|
|
// FIXME? Not sure if it is the more accurate way of computing
|
|
// pixel size
|
|
double dfPixelSize = 0.5 * (ABS(padfGeoTransform[1])
|
|
+ ABS(padfGeoTransform[2])
|
|
+ ABS(padfGeoTransform[4])
|
|
+ ABS(padfGeoTransform[5]));
|
|
|
|
for( i = 0; i < nGCPCount; i++ )
|
|
{
|
|
double dfErrorX, dfErrorY;
|
|
|
|
dfErrorX =
|
|
(pasGCPs[i].dfGCPPixel * padfGeoTransform[1]
|
|
+ pasGCPs[i].dfGCPLine * padfGeoTransform[2]
|
|
+ padfGeoTransform[0])
|
|
- pasGCPs[i].dfGCPX;
|
|
dfErrorY =
|
|
(pasGCPs[i].dfGCPPixel * padfGeoTransform[4]
|
|
+ pasGCPs[i].dfGCPLine * padfGeoTransform[5]
|
|
+ padfGeoTransform[3])
|
|
- pasGCPs[i].dfGCPY;
|
|
|
|
if( ABS(dfErrorX) > 0.25 * dfPixelSize
|
|
|| ABS(dfErrorY) > 0.25 * dfPixelSize )
|
|
{
|
|
CPLDebug("GDAL", "dfErrorX/dfPixelSize = %.2f, dfErrorY/dfPixelSize = %.2f",
|
|
ABS(dfErrorX)/dfPixelSize, ABS(dfErrorY)/dfPixelSize);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALComposeGeoTransforms() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief Compose two geotransforms.
|
|
*
|
|
* The resulting geotransform is the equivelent to padfGT1 and then padfGT2
|
|
* being applied to a point.
|
|
*
|
|
* @param padfGT1 the first geotransform, six values.
|
|
* @param padfGT2 the second geotransform, six values.
|
|
* @param padfGTOut the output geotransform, six values, may safely be the same
|
|
* array as padfGT1 or padfGT2.
|
|
*/
|
|
|
|
void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
|
|
double *padfGTOut)
|
|
|
|
{
|
|
double gtwrk[6];
|
|
// We need to think of the geotransform in a more normal form to do
|
|
// the matrix multiple:
|
|
//
|
|
// __ __
|
|
// | gt[1] gt[2] gt[0] |
|
|
// | gt[4] gt[5] gt[3] |
|
|
// | 0.0 0.0 1.0 |
|
|
// -- --
|
|
//
|
|
// Then we can use normal matrix multiplication to produce the
|
|
// composed transformation. I don't actually reform the matrix
|
|
// explicitly which is why the following may seem kind of spagettish.
|
|
|
|
gtwrk[1] =
|
|
padfGT2[1] * padfGT1[1]
|
|
+ padfGT2[2] * padfGT1[4];
|
|
gtwrk[2] =
|
|
padfGT2[1] * padfGT1[2]
|
|
+ padfGT2[2] * padfGT1[5];
|
|
gtwrk[0] =
|
|
padfGT2[1] * padfGT1[0]
|
|
+ padfGT2[2] * padfGT1[3]
|
|
+ padfGT2[0] * 1.0;
|
|
|
|
gtwrk[4] =
|
|
padfGT2[4] * padfGT1[1]
|
|
+ padfGT2[5] * padfGT1[4];
|
|
gtwrk[5] =
|
|
padfGT2[4] * padfGT1[2]
|
|
+ padfGT2[5] * padfGT1[5];
|
|
gtwrk[3] =
|
|
padfGT2[4] * padfGT1[0]
|
|
+ padfGT2[5] * padfGT1[3]
|
|
+ padfGT2[3] * 1.0;
|
|
memcpy(padfGTOut, gtwrk, sizeof(double) * 6);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGeneralCmdLineProcessor() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \brief General utility option processing.
|
|
*
|
|
* This function is intended to provide a variety of generic commandline
|
|
* options for all GDAL commandline utilities. It takes care of the following
|
|
* commandline options:
|
|
*
|
|
* --version: report version of GDAL in use.
|
|
* --build: report build info about GDAL in use.
|
|
* --license: report GDAL license info.
|
|
* --formats: report all format drivers configured.
|
|
* --format [format]: report details of one format driver.
|
|
* --optfile filename: expand an option file into the argument list.
|
|
* --config key value: set system configuration option.
|
|
* --debug [on/off/value]: set debug level.
|
|
* --mempreload dir: preload directory contents into /vsimem
|
|
* --pause: Pause for user input (allows time to attach debugger)
|
|
* --locale [locale]: Install a locale using setlocale() (debugging)
|
|
* --help-general: report detailed help on general options.
|
|
*
|
|
* The argument array is replaced "in place" and should be freed with
|
|
* CSLDestroy() when no longer needed. The typical usage looks something
|
|
* like the following. Note that the formats should be registered so that
|
|
* the --formats and --format options will work properly.
|
|
*
|
|
* int main( int argc, char ** argv )
|
|
* {
|
|
* GDALAllRegister();
|
|
*
|
|
* argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
|
|
* if( argc < 1 )
|
|
* exit( -argc );
|
|
*
|
|
* @param nArgc number of values in the argument list.
|
|
* @param ppapszArgv pointer to the argument list array (will be updated in place).
|
|
* @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
|
|
* to determine which drivers should be displayed by --formats.
|
|
* If set to 0, GDAL_OF_RASTER is assumed.
|
|
*
|
|
* @return updated nArgc argument count. Return of 0 requests terminate
|
|
* without error, return of -1 requests exit with error code.
|
|
*/
|
|
|
|
int CPL_STDCALL
|
|
GDALGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOptions )
|
|
|
|
{
|
|
char **papszReturn = NULL;
|
|
int iArg;
|
|
char **papszArgv = *ppapszArgv;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Preserve the program name. */
|
|
/* -------------------------------------------------------------------- */
|
|
papszReturn = CSLAddString( papszReturn, papszArgv[0] );
|
|
|
|
/* ==================================================================== */
|
|
/* Loop over all arguments. */
|
|
/* ==================================================================== */
|
|
for( iArg = 1; iArg < nArgc; iArg++ )
|
|
{
|
|
/* -------------------------------------------------------------------- */
|
|
/* --version */
|
|
/* -------------------------------------------------------------------- */
|
|
if( EQUAL(papszArgv[iArg],"--version") )
|
|
{
|
|
printf( "%s\n", GDALVersionInfo( "--version" ) );
|
|
CSLDestroy( papszReturn );
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --build */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg],"--build") )
|
|
{
|
|
printf( "%s", GDALVersionInfo( "BUILD_INFO" ) );
|
|
CSLDestroy( papszReturn );
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --license */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg],"--license") )
|
|
{
|
|
printf( "%s\n", GDALVersionInfo( "LICENSE" ) );
|
|
CSLDestroy( papszReturn );
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --config */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg],"--config") )
|
|
{
|
|
if( iArg + 2 >= nArgc )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"--config option given without a key and value argument." );
|
|
CSLDestroy( papszReturn );
|
|
return -1;
|
|
}
|
|
|
|
CPLSetConfigOption( papszArgv[iArg+1], papszArgv[iArg+2] );
|
|
|
|
iArg += 2;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --mempreload */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg],"--mempreload") )
|
|
{
|
|
int i;
|
|
|
|
if( iArg + 1 >= nArgc )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"--mempreload option given without directory path.");
|
|
CSLDestroy( papszReturn );
|
|
return -1;
|
|
}
|
|
|
|
char **papszFiles = CPLReadDir( papszArgv[iArg+1] );
|
|
if( CSLCount(papszFiles) == 0 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"--mempreload given invalid or empty directory.");
|
|
CSLDestroy( papszReturn );
|
|
return -1;
|
|
}
|
|
|
|
for( i = 0; papszFiles[i] != NULL; i++ )
|
|
{
|
|
CPLString osOldPath, osNewPath;
|
|
VSIStatBufL sStatBuf;
|
|
|
|
if( EQUAL(papszFiles[i],".") || EQUAL(papszFiles[i],"..") )
|
|
continue;
|
|
|
|
osOldPath = CPLFormFilename( papszArgv[iArg+1],
|
|
papszFiles[i], NULL );
|
|
osNewPath.Printf( "/vsimem/%s", papszFiles[i] );
|
|
|
|
if( VSIStatL( osOldPath, &sStatBuf ) != 0
|
|
|| VSI_ISDIR( sStatBuf.st_mode ) )
|
|
{
|
|
CPLDebug( "VSI", "Skipping preload of %s.",
|
|
osOldPath.c_str() );
|
|
continue;
|
|
}
|
|
|
|
CPLDebug( "VSI", "Preloading %s to %s.",
|
|
osOldPath.c_str(), osNewPath.c_str() );
|
|
|
|
if( CPLCopyFile( osNewPath, osOldPath ) != 0 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Failed to copy %s to /vsimem",
|
|
osOldPath.c_str() );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
CSLDestroy( papszFiles );
|
|
iArg += 1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --debug */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg],"--debug") )
|
|
{
|
|
if( iArg + 1 >= nArgc )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"--debug option given without debug level." );
|
|
CSLDestroy( papszReturn );
|
|
return -1;
|
|
}
|
|
|
|
CPLSetConfigOption( "CPL_DEBUG", papszArgv[iArg+1] );
|
|
iArg += 1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --optfile */
|
|
/* */
|
|
/* Annoyingly the options inserted by --optfile will *not* be */
|
|
/* processed properly if they are general options. */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg],"--optfile") )
|
|
{
|
|
const char *pszLine;
|
|
FILE *fpOptFile;
|
|
|
|
if( iArg + 1 >= nArgc )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"--optfile option given without filename." );
|
|
CSLDestroy( papszReturn );
|
|
return -1;
|
|
}
|
|
|
|
fpOptFile = VSIFOpen( papszArgv[iArg+1], "rb" );
|
|
|
|
if( fpOptFile == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Unable to open optfile '%s'.\n%s",
|
|
papszArgv[iArg+1], VSIStrerror( errno ) );
|
|
CSLDestroy( papszReturn );
|
|
return -1;
|
|
}
|
|
|
|
while( (pszLine = CPLReadLine( fpOptFile )) != NULL )
|
|
{
|
|
char **papszTokens;
|
|
int i;
|
|
|
|
if( pszLine[0] == '#' || strlen(pszLine) == 0 )
|
|
continue;
|
|
|
|
papszTokens = CSLTokenizeString( pszLine );
|
|
for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++)
|
|
papszReturn = CSLAddString( papszReturn, papszTokens[i] );
|
|
CSLDestroy( papszTokens );
|
|
}
|
|
|
|
VSIFClose( fpOptFile );
|
|
|
|
iArg += 1;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --formats */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg], "--formats") )
|
|
{
|
|
int iDr;
|
|
|
|
if( nOptions == 0 )
|
|
nOptions = GDAL_OF_RASTER;
|
|
|
|
printf( "Supported Formats:\n" );
|
|
for( iDr = 0; iDr < GDALGetDriverCount(); iDr++ )
|
|
{
|
|
GDALDriverH hDriver = GDALGetDriver(iDr);
|
|
|
|
const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets, *pszKind;
|
|
char** papszMD = GDALGetMetadata( hDriver, NULL );
|
|
|
|
if( nOptions == GDAL_OF_RASTER &&
|
|
!CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) )
|
|
continue;
|
|
if( nOptions == GDAL_OF_VECTOR &&
|
|
!CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ) )
|
|
continue;
|
|
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_OPEN, FALSE ) )
|
|
pszRFlag = "r";
|
|
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATE, FALSE ) )
|
|
pszWFlag = "w+";
|
|
else if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATECOPY, FALSE ) )
|
|
pszWFlag = "w";
|
|
else
|
|
pszWFlag = "o";
|
|
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_VIRTUALIO, FALSE ) )
|
|
pszVirtualIO = "v";
|
|
else
|
|
pszVirtualIO = "";
|
|
|
|
if( CSLFetchBoolean( papszMD, GDAL_DMD_SUBDATASETS, FALSE ) )
|
|
pszSubdatasets = "s";
|
|
else
|
|
pszSubdatasets = "";
|
|
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) &&
|
|
CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ))
|
|
pszKind = "raster,vector";
|
|
else if( CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) )
|
|
pszKind = "raster";
|
|
else if( CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ) )
|
|
pszKind = "vector";
|
|
else
|
|
pszKind = "unknown kind";
|
|
|
|
printf( " %s -%s- (%s%s%s%s): %s\n",
|
|
GDALGetDriverShortName( hDriver ),
|
|
pszKind,
|
|
pszRFlag, pszWFlag, pszVirtualIO, pszSubdatasets,
|
|
GDALGetDriverLongName( hDriver ) );
|
|
}
|
|
|
|
CSLDestroy( papszReturn );
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --format */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg], "--format") )
|
|
{
|
|
GDALDriverH hDriver;
|
|
char **papszMD;
|
|
|
|
CSLDestroy( papszReturn );
|
|
|
|
if( iArg + 1 >= nArgc )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"--format option given without a format code." );
|
|
return -1;
|
|
}
|
|
|
|
hDriver = GDALGetDriverByName( papszArgv[iArg+1] );
|
|
if( hDriver == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"--format option given with format '%s', but that format not\n"
|
|
"recognised. Use the --formats option to get a list of available formats,\n"
|
|
"and use the short code (ie. GTiff or HFA) as the format identifier.\n",
|
|
papszArgv[iArg+1] );
|
|
return -1;
|
|
}
|
|
|
|
printf( "Format Details:\n" );
|
|
printf( " Short Name: %s\n", GDALGetDriverShortName( hDriver ) );
|
|
printf( " Long Name: %s\n", GDALGetDriverLongName( hDriver ) );
|
|
|
|
papszMD = GDALGetMetadata( hDriver, NULL );
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_RASTER, FALSE ) )
|
|
printf( " Supports: Raster\n" );
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_VECTOR, FALSE ) )
|
|
printf( " Supports: Vector\n" );
|
|
|
|
const char* pszExt = CSLFetchNameValue( papszMD, GDAL_DMD_EXTENSIONS );
|
|
if( pszExt != NULL )
|
|
printf( " Extension%s: %s\n", (strchr(pszExt, ' ') ? "s" : ""),
|
|
pszExt );
|
|
|
|
if( CSLFetchNameValue( papszMD, GDAL_DMD_MIMETYPE ) )
|
|
printf( " Mime Type: %s\n",
|
|
CSLFetchNameValue( papszMD, GDAL_DMD_MIMETYPE ) );
|
|
if( CSLFetchNameValue( papszMD, GDAL_DMD_HELPTOPIC ) )
|
|
printf( " Help Topic: %s\n",
|
|
CSLFetchNameValue( papszMD, GDAL_DMD_HELPTOPIC ) );
|
|
|
|
if( CSLFetchBoolean( papszMD, GDAL_DMD_SUBDATASETS, FALSE ) )
|
|
printf( " Supports: Subdatasets\n" );
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_OPEN, FALSE ) )
|
|
printf( " Supports: Open() - Open existing dataset.\n" );
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATE, FALSE ) )
|
|
printf( " Supports: Create() - Create writeable dataset.\n" );
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_CREATECOPY, FALSE ) )
|
|
printf( " Supports: CreateCopy() - Create dataset by copying another.\n" );
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_VIRTUALIO, FALSE ) )
|
|
printf( " Supports: Virtual IO - eg. /vsimem/\n" );
|
|
if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONDATATYPES ) )
|
|
printf( " Creation Datatypes: %s\n",
|
|
CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONDATATYPES ) );
|
|
if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONFIELDDATATYPES ) )
|
|
printf( " Creation Field Datatypes: %s\n",
|
|
CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONFIELDDATATYPES ) );
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_NOTNULL_FIELDS, FALSE ) )
|
|
printf( " Supports: Creating fields with NOT NULL constraint.\n" );
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_DEFAULT_FIELDS, FALSE ) )
|
|
printf( " Supports: Creating fields with DEFAULT values.\n" );
|
|
if( CSLFetchBoolean( papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, FALSE ) )
|
|
printf( " Supports: Creating geometry fields with NOT NULL constraint.\n" );
|
|
if( CSLFetchNameValue( papszMD, GDAL_DMD_CREATIONOPTIONLIST ) )
|
|
{
|
|
CPLXMLNode *psCOL =
|
|
CPLParseXMLString(
|
|
CSLFetchNameValue( papszMD,
|
|
GDAL_DMD_CREATIONOPTIONLIST ) );
|
|
char *pszFormattedXML =
|
|
CPLSerializeXMLTree( psCOL );
|
|
|
|
CPLDestroyXMLNode( psCOL );
|
|
|
|
printf( "\n%s\n", pszFormattedXML );
|
|
CPLFree( pszFormattedXML );
|
|
}
|
|
if( CSLFetchNameValue( papszMD, GDAL_DS_LAYER_CREATIONOPTIONLIST ) )
|
|
{
|
|
CPLXMLNode *psCOL =
|
|
CPLParseXMLString(
|
|
CSLFetchNameValue( papszMD,
|
|
GDAL_DS_LAYER_CREATIONOPTIONLIST ) );
|
|
char *pszFormattedXML =
|
|
CPLSerializeXMLTree( psCOL );
|
|
|
|
CPLDestroyXMLNode( psCOL );
|
|
|
|
printf( "\n%s\n", pszFormattedXML );
|
|
CPLFree( pszFormattedXML );
|
|
}
|
|
|
|
if( CSLFetchNameValue( papszMD, GDAL_DMD_CONNECTION_PREFIX ) )
|
|
printf( " Connection prefix: %s\n",
|
|
CSLFetchNameValue( papszMD, GDAL_DMD_CONNECTION_PREFIX ) );
|
|
|
|
if( CSLFetchNameValue( papszMD, GDAL_DMD_OPENOPTIONLIST ) )
|
|
{
|
|
CPLXMLNode *psCOL =
|
|
CPLParseXMLString(
|
|
CSLFetchNameValue( papszMD,
|
|
GDAL_DMD_OPENOPTIONLIST ) );
|
|
char *pszFormattedXML =
|
|
CPLSerializeXMLTree( psCOL );
|
|
|
|
CPLDestroyXMLNode( psCOL );
|
|
|
|
printf( "%s\n", pszFormattedXML );
|
|
CPLFree( pszFormattedXML );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --help-general */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg],"--help-general") )
|
|
{
|
|
printf( "Generic GDAL utility command options:\n" );
|
|
printf( " --version: report version of GDAL in use.\n" );
|
|
printf( " --license: report GDAL license info.\n" );
|
|
printf( " --formats: report all configured format drivers.\n" );
|
|
printf( " --format [format]: details of one format.\n" );
|
|
printf( " --optfile filename: expand an option file into the argument list.\n" );
|
|
printf( " --config key value: set system configuration option.\n" );
|
|
printf( " --debug [on/off/value]: set debug level.\n" );
|
|
printf( " --pause: wait for user input, time to attach debugger\n" );
|
|
printf( " --locale [locale]: install locale for debugging (ie. en_US.UTF-8)\n" );
|
|
printf( " --help-general: report detailed help on general options.\n" );
|
|
CSLDestroy( papszReturn );
|
|
return 0;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --locale */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg],"--locale") && iArg < nArgc-1 )
|
|
{
|
|
CPLsetlocale( LC_ALL, papszArgv[++iArg] );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* --pause */
|
|
/* -------------------------------------------------------------------- */
|
|
else if( EQUAL(papszArgv[iArg],"--pause") )
|
|
{
|
|
printf( "Hit <ENTER> to Continue.\n" );
|
|
CPLReadLine( stdin );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* carry through unrecognised options. */
|
|
/* -------------------------------------------------------------------- */
|
|
else
|
|
{
|
|
papszReturn = CSLAddString( papszReturn, papszArgv[iArg] );
|
|
}
|
|
}
|
|
|
|
*ppapszArgv = papszReturn;
|
|
|
|
return CSLCount( *ppapszArgv );
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* _FetchDblFromMD() */
|
|
/************************************************************************/
|
|
|
|
static int _FetchDblFromMD( char **papszMD, const char *pszKey,
|
|
double *padfTarget, int nCount, double dfDefault )
|
|
|
|
{
|
|
char szFullKey[200];
|
|
|
|
sprintf( szFullKey, "%s", pszKey );
|
|
|
|
const char *pszValue = CSLFetchNameValue( papszMD, szFullKey );
|
|
int i;
|
|
|
|
for( i = 0; i < nCount; i++ )
|
|
padfTarget[i] = dfDefault;
|
|
|
|
if( pszValue == NULL )
|
|
return FALSE;
|
|
|
|
if( nCount == 1 )
|
|
{
|
|
*padfTarget = CPLAtofM( pszValue );
|
|
return TRUE;
|
|
}
|
|
|
|
char **papszTokens = CSLTokenizeStringComplex( pszValue, " ,",
|
|
FALSE, FALSE );
|
|
|
|
if( CSLCount( papszTokens ) != nCount )
|
|
{
|
|
CSLDestroy( papszTokens );
|
|
return FALSE;
|
|
}
|
|
|
|
for( i = 0; i < nCount; i++ )
|
|
padfTarget[i] = CPLAtofM(papszTokens[i]);
|
|
|
|
CSLDestroy( papszTokens );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALExtractRPCInfo() */
|
|
/* */
|
|
/* Extract RPC info from metadata, and apply to an RPCInfo */
|
|
/* structure. The inverse of this function is RPCInfoToMD() in */
|
|
/* alg/gdal_rpc.cpp (should it be needed). */
|
|
/************************************************************************/
|
|
|
|
int CPL_STDCALL GDALExtractRPCInfo( char **papszMD, GDALRPCInfo *psRPC )
|
|
|
|
{
|
|
if( CSLFetchNameValue( papszMD, RPC_LINE_NUM_COEFF ) == NULL )
|
|
return FALSE;
|
|
|
|
if( CSLFetchNameValue( papszMD, RPC_LINE_NUM_COEFF ) == NULL
|
|
|| CSLFetchNameValue( papszMD, RPC_LINE_DEN_COEFF ) == NULL
|
|
|| CSLFetchNameValue( papszMD, RPC_SAMP_NUM_COEFF ) == NULL
|
|
|| CSLFetchNameValue( papszMD, RPC_SAMP_DEN_COEFF ) == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Some required RPC metadata missing in GDALExtractRPCInfo()");
|
|
return FALSE;
|
|
}
|
|
|
|
_FetchDblFromMD( papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0 );
|
|
_FetchDblFromMD( papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0 );
|
|
_FetchDblFromMD( papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0 );
|
|
_FetchDblFromMD( papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0 );
|
|
_FetchDblFromMD( papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0 );
|
|
_FetchDblFromMD( papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE),1, 1.0);
|
|
_FetchDblFromMD( papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0 );
|
|
_FetchDblFromMD( papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0 );
|
|
_FetchDblFromMD( papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0 );
|
|
_FetchDblFromMD( papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0 );
|
|
|
|
_FetchDblFromMD( papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF,
|
|
20, 0.0 );
|
|
_FetchDblFromMD( papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF,
|
|
20, 0.0 );
|
|
_FetchDblFromMD( papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF,
|
|
20, 0.0 );
|
|
_FetchDblFromMD( papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF,
|
|
20, 0.0 );
|
|
|
|
_FetchDblFromMD( papszMD, "MIN_LONG", &(psRPC->dfMIN_LONG), 1, -180.0 );
|
|
_FetchDblFromMD( papszMD, "MIN_LAT", &(psRPC->dfMIN_LAT), 1, -90.0 );
|
|
_FetchDblFromMD( papszMD, "MAX_LONG", &(psRPC->dfMAX_LONG), 1, 180.0 );
|
|
_FetchDblFromMD( papszMD, "MAX_LAT", &(psRPC->dfMAX_LAT), 1, 90.0 );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALFindAssociatedAuxFile() */
|
|
/************************************************************************/
|
|
|
|
GDALDataset *GDALFindAssociatedAuxFile( const char *pszBasename,
|
|
GDALAccess eAccess,
|
|
GDALDataset *poDependentDS )
|
|
|
|
{
|
|
const char *pszAuxSuffixLC = "aux";
|
|
const char *pszAuxSuffixUC = "AUX";
|
|
|
|
if( EQUAL(CPLGetExtension(pszBasename), pszAuxSuffixLC) )
|
|
return NULL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Don't even try to look for an .aux file if we don't have a */
|
|
/* path of any kind. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( strlen(pszBasename) == 0 )
|
|
return NULL;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* We didn't find that, so try and find a corresponding aux */
|
|
/* file. Check that we are the dependent file of the aux */
|
|
/* file, or if we aren't verify that the dependent file does */
|
|
/* not exist, likely mean it is us but some sort of renaming */
|
|
/* has occured. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLString osJustFile = CPLGetFilename(pszBasename); // without dir
|
|
CPLString osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixLC);
|
|
GDALDataset *poODS = NULL;
|
|
GByte abyHeader[32];
|
|
VSILFILE *fp;
|
|
|
|
fp = VSIFOpenL( osAuxFilename, "rb" );
|
|
|
|
|
|
if ( fp == NULL && VSIIsCaseSensitiveFS(osAuxFilename))
|
|
{
|
|
// Can't found file with lower case suffix. Try the upper case one.
|
|
osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixUC);
|
|
fp = VSIFOpenL( osAuxFilename, "rb" );
|
|
}
|
|
|
|
if( fp != NULL )
|
|
{
|
|
if( VSIFReadL( abyHeader, 1, 32, fp ) == 32 &&
|
|
EQUALN((char *) abyHeader,"EHFA_HEADER_TAG",15) )
|
|
{
|
|
/* Avoid causing failure in opening of main file from SWIG bindings */
|
|
/* when auxiliary file cannot be opened (#3269) */
|
|
CPLTurnFailureIntoWarning(TRUE);
|
|
if( poDependentDS != NULL && poDependentDS->GetShared() )
|
|
poODS = (GDALDataset *) GDALOpenShared( osAuxFilename, eAccess );
|
|
else
|
|
poODS = (GDALDataset *) GDALOpen( osAuxFilename, eAccess );
|
|
CPLTurnFailureIntoWarning(FALSE);
|
|
}
|
|
VSIFCloseL( fp );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try replacing extension with .aux */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poODS != NULL )
|
|
{
|
|
const char *pszDep
|
|
= poODS->GetMetadataItem( "HFA_DEPENDENT_FILE", "HFA" );
|
|
if( pszDep == NULL )
|
|
{
|
|
CPLDebug( "AUX",
|
|
"Found %s but it has no dependent file, ignoring.",
|
|
osAuxFilename.c_str() );
|
|
GDALClose( poODS );
|
|
poODS = NULL;
|
|
}
|
|
else if( !EQUAL(pszDep,osJustFile) )
|
|
{
|
|
VSIStatBufL sStatBuf;
|
|
|
|
if( VSIStatExL( pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
|
|
{
|
|
CPLDebug( "AUX", "%s is for file %s, not %s, ignoring.",
|
|
osAuxFilename.c_str(),
|
|
pszDep, osJustFile.c_str() );
|
|
GDALClose( poODS );
|
|
poODS = NULL;
|
|
}
|
|
else
|
|
{
|
|
CPLDebug( "AUX", "%s is for file %s, not %s, but since\n"
|
|
"%s does not exist, we will use .aux file as our own.",
|
|
osAuxFilename.c_str(),
|
|
pszDep, osJustFile.c_str(),
|
|
pszDep );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Confirm that the aux file matches the configuration of the */
|
|
/* dependent dataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poODS != NULL && poDependentDS != NULL
|
|
&& (poODS->GetRasterCount() != poDependentDS->GetRasterCount()
|
|
|| poODS->GetRasterXSize() != poDependentDS->GetRasterXSize()
|
|
|| poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()) )
|
|
{
|
|
CPLDebug( "AUX",
|
|
"Ignoring aux file %s as its raster configuration\n"
|
|
"(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
|
|
osAuxFilename.c_str(),
|
|
poODS->GetRasterXSize(),
|
|
poODS->GetRasterYSize(),
|
|
poODS->GetRasterCount(),
|
|
poDependentDS->GetRasterXSize(),
|
|
poDependentDS->GetRasterYSize(),
|
|
poDependentDS->GetRasterCount() );
|
|
|
|
GDALClose( poODS );
|
|
poODS = NULL;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Try appending .aux to the end of the filename. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poODS == NULL )
|
|
{
|
|
osAuxFilename = pszBasename;
|
|
osAuxFilename += ".";
|
|
osAuxFilename += pszAuxSuffixLC;
|
|
fp = VSIFOpenL( osAuxFilename, "rb" );
|
|
if ( fp == NULL && VSIIsCaseSensitiveFS(osAuxFilename) )
|
|
{
|
|
// Can't found file with lower case suffix. Try the upper case one.
|
|
osAuxFilename = pszBasename;
|
|
osAuxFilename += ".";
|
|
osAuxFilename += pszAuxSuffixUC;
|
|
fp = VSIFOpenL( osAuxFilename, "rb" );
|
|
}
|
|
|
|
if( fp != NULL )
|
|
{
|
|
if( VSIFReadL( abyHeader, 1, 32, fp ) == 32 &&
|
|
EQUALN((char *) abyHeader,"EHFA_HEADER_TAG",15) )
|
|
{
|
|
/* Avoid causing failure in opening of main file from SWIG bindings */
|
|
/* when auxiliary file cannot be opened (#3269) */
|
|
CPLTurnFailureIntoWarning(TRUE);
|
|
if( poDependentDS != NULL && poDependentDS->GetShared() )
|
|
poODS = (GDALDataset *) GDALOpenShared( osAuxFilename, eAccess );
|
|
else
|
|
poODS = (GDALDataset *) GDALOpen( osAuxFilename, eAccess );
|
|
CPLTurnFailureIntoWarning(FALSE);
|
|
}
|
|
VSIFCloseL( fp );
|
|
}
|
|
|
|
if( poODS != NULL )
|
|
{
|
|
const char *pszDep
|
|
= poODS->GetMetadataItem( "HFA_DEPENDENT_FILE", "HFA" );
|
|
if( pszDep == NULL )
|
|
{
|
|
CPLDebug( "AUX",
|
|
"Found %s but it has no dependent file, ignoring.",
|
|
osAuxFilename.c_str() );
|
|
GDALClose( poODS );
|
|
poODS = NULL;
|
|
}
|
|
else if( !EQUAL(pszDep,osJustFile) )
|
|
{
|
|
VSIStatBufL sStatBuf;
|
|
|
|
if( VSIStatExL( pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG ) == 0 )
|
|
{
|
|
CPLDebug( "AUX", "%s is for file %s, not %s, ignoring.",
|
|
osAuxFilename.c_str(),
|
|
pszDep, osJustFile.c_str() );
|
|
GDALClose( poODS );
|
|
poODS = NULL;
|
|
}
|
|
else
|
|
{
|
|
CPLDebug( "AUX", "%s is for file %s, not %s, but since\n"
|
|
"%s does not exist, we will use .aux file as our own.",
|
|
osAuxFilename.c_str(),
|
|
pszDep, osJustFile.c_str(),
|
|
pszDep );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Confirm that the aux file matches the configuration of the */
|
|
/* dependent dataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( poODS != NULL && poDependentDS != NULL
|
|
&& (poODS->GetRasterCount() != poDependentDS->GetRasterCount()
|
|
|| poODS->GetRasterXSize() != poDependentDS->GetRasterXSize()
|
|
|| poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()) )
|
|
{
|
|
CPLDebug( "AUX",
|
|
"Ignoring aux file %s as its raster configuration\n"
|
|
"(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
|
|
osAuxFilename.c_str(),
|
|
poODS->GetRasterXSize(),
|
|
poODS->GetRasterYSize(),
|
|
poODS->GetRasterCount(),
|
|
poDependentDS->GetRasterXSize(),
|
|
poDependentDS->GetRasterYSize(),
|
|
poDependentDS->GetRasterCount() );
|
|
|
|
GDALClose( poODS );
|
|
poODS = NULL;
|
|
}
|
|
|
|
return poODS;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* -------------------------------------------------------------------- */
|
|
/* The following stubs are present to ensure that older GDAL */
|
|
/* bridges don't fail with newer libraries. */
|
|
/* -------------------------------------------------------------------- */
|
|
/************************************************************************/
|
|
|
|
CPL_C_START
|
|
|
|
void * CPL_STDCALL GDALCreateProjDef( const char * )
|
|
{
|
|
CPLDebug( "GDAL", "GDALCreateProjDef no longer supported." );
|
|
return NULL;
|
|
}
|
|
|
|
CPLErr CPL_STDCALL GDALReprojectToLongLat( void *, double *, double * )
|
|
{
|
|
CPLDebug( "GDAL", "GDALReprojectToLatLong no longer supported." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
CPLErr CPL_STDCALL GDALReprojectFromLongLat( void *, double *, double * )
|
|
{
|
|
CPLDebug( "GDAL", "GDALReprojectFromLatLong no longer supported." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
void CPL_STDCALL GDALDestroyProjDef( void * )
|
|
|
|
{
|
|
CPLDebug( "GDAL", "GDALDestroyProjDef no longer supported." );
|
|
}
|
|
|
|
CPL_C_END
|
|
|
|
/************************************************************************/
|
|
/* Infrastructure to check that dataset characteristics are valid */
|
|
/************************************************************************/
|
|
|
|
CPL_C_START
|
|
|
|
/**
|
|
* \brief Return TRUE if the dataset dimensions are valid.
|
|
*
|
|
* @param nXSize raster width
|
|
* @param nYSize raster height
|
|
*
|
|
* @since GDAL 1.7.0
|
|
*/
|
|
int GDALCheckDatasetDimensions( int nXSize, int nYSize )
|
|
{
|
|
if (nXSize <= 0 || nYSize <= 0)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Invalid dataset dimensions : %d x %d", nXSize, nYSize);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* \brief Return TRUE if the band count is valid.
|
|
*
|
|
* If the configuration option GDAL_MAX_BAND_COUNT is defined,
|
|
* the band count will be compared to the maximum number of band allowed.
|
|
*
|
|
* @param nBands the band count
|
|
* @param bIsZeroAllowed TRUE if band count == 0 is allowed
|
|
*
|
|
* @since GDAL 1.7.0
|
|
*/
|
|
|
|
int GDALCheckBandCount( int nBands, int bIsZeroAllowed )
|
|
{
|
|
int nMaxBands = -1;
|
|
const char* pszMaxBandCount = CPLGetConfigOption("GDAL_MAX_BAND_COUNT", NULL);
|
|
if (pszMaxBandCount != NULL)
|
|
{
|
|
nMaxBands = atoi(pszMaxBandCount);
|
|
}
|
|
if (nBands < 0 || (!bIsZeroAllowed && nBands == 0) ||
|
|
(nMaxBands >= 0 && nBands > nMaxBands) )
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Invalid band count : %d", nBands);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
CPL_C_END
|
|
|
|
|
|
/************************************************************************/
|
|
/* GDALSerializeGCPListToXML() */
|
|
/************************************************************************/
|
|
|
|
void GDALSerializeGCPListToXML( CPLXMLNode* psParentNode,
|
|
GDAL_GCP* pasGCPList,
|
|
int nGCPCount,
|
|
const char* pszGCPProjection )
|
|
{
|
|
CPLString oFmt;
|
|
|
|
CPLXMLNode *psPamGCPList = CPLCreateXMLNode( psParentNode, CXT_Element,
|
|
"GCPList" );
|
|
|
|
CPLXMLNode* psLastChild = NULL;
|
|
|
|
if( pszGCPProjection != NULL
|
|
&& strlen(pszGCPProjection) > 0 )
|
|
{
|
|
CPLSetXMLValue( psPamGCPList, "#Projection",
|
|
pszGCPProjection );
|
|
psLastChild = psPamGCPList->psChild;
|
|
}
|
|
|
|
for( int iGCP = 0; iGCP < nGCPCount; iGCP++ )
|
|
{
|
|
CPLXMLNode *psXMLGCP;
|
|
GDAL_GCP *psGCP = pasGCPList + iGCP;
|
|
|
|
psXMLGCP = CPLCreateXMLNode( NULL, CXT_Element, "GCP" );
|
|
|
|
if( psLastChild == NULL )
|
|
psPamGCPList->psChild = psXMLGCP;
|
|
else
|
|
psLastChild->psNext = psXMLGCP;
|
|
psLastChild = psXMLGCP;
|
|
|
|
CPLSetXMLValue( psXMLGCP, "#Id", psGCP->pszId );
|
|
|
|
if( psGCP->pszInfo != NULL && strlen(psGCP->pszInfo) > 0 )
|
|
CPLSetXMLValue( psXMLGCP, "Info", psGCP->pszInfo );
|
|
|
|
CPLSetXMLValue( psXMLGCP, "#Pixel",
|
|
oFmt.Printf( "%.4f", psGCP->dfGCPPixel ) );
|
|
|
|
CPLSetXMLValue( psXMLGCP, "#Line",
|
|
oFmt.Printf( "%.4f", psGCP->dfGCPLine ) );
|
|
|
|
CPLSetXMLValue( psXMLGCP, "#X",
|
|
oFmt.Printf( "%.12E", psGCP->dfGCPX ) );
|
|
|
|
CPLSetXMLValue( psXMLGCP, "#Y",
|
|
oFmt.Printf( "%.12E", psGCP->dfGCPY ) );
|
|
|
|
/* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it back */
|
|
if( psGCP->dfGCPZ != 0.0 )
|
|
CPLSetXMLValue( psXMLGCP, "#Z",
|
|
oFmt.Printf( "%.12E", psGCP->dfGCPZ ) );
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDeserializeGCPListFromXML() */
|
|
/************************************************************************/
|
|
|
|
void GDALDeserializeGCPListFromXML( CPLXMLNode* psGCPList,
|
|
GDAL_GCP** ppasGCPList,
|
|
int* pnGCPCount,
|
|
char** ppszGCPProjection )
|
|
{
|
|
CPLXMLNode *psXMLGCP;
|
|
OGRSpatialReference oSRS;
|
|
|
|
if( ppszGCPProjection )
|
|
{
|
|
const char *pszRawProj = CPLGetXMLValue(psGCPList, "Projection", "");
|
|
|
|
if( strlen(pszRawProj) > 0
|
|
&& oSRS.SetFromUserInput( pszRawProj ) == OGRERR_NONE )
|
|
oSRS.exportToWkt( ppszGCPProjection );
|
|
else
|
|
*ppszGCPProjection = CPLStrdup("");
|
|
}
|
|
|
|
// Count GCPs.
|
|
int nGCPMax = 0;
|
|
|
|
for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL;
|
|
psXMLGCP = psXMLGCP->psNext )
|
|
nGCPMax++;
|
|
|
|
*ppasGCPList = (GDAL_GCP *) CPLCalloc(sizeof(GDAL_GCP),nGCPMax);
|
|
*pnGCPCount = 0;
|
|
|
|
for( psXMLGCP = psGCPList->psChild; psXMLGCP != NULL;
|
|
psXMLGCP = psXMLGCP->psNext )
|
|
{
|
|
GDAL_GCP *psGCP = *ppasGCPList + *pnGCPCount;
|
|
|
|
if( !EQUAL(psXMLGCP->pszValue,"GCP") ||
|
|
psXMLGCP->eType != CXT_Element )
|
|
continue;
|
|
|
|
GDALInitGCPs( 1, psGCP );
|
|
|
|
CPLFree( psGCP->pszId );
|
|
psGCP->pszId = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Id",""));
|
|
|
|
CPLFree( psGCP->pszInfo );
|
|
psGCP->pszInfo = CPLStrdup(CPLGetXMLValue(psXMLGCP,"Info",""));
|
|
|
|
psGCP->dfGCPPixel = CPLAtof(CPLGetXMLValue(psXMLGCP,"Pixel","0.0"));
|
|
psGCP->dfGCPLine = CPLAtof(CPLGetXMLValue(psXMLGCP,"Line","0.0"));
|
|
|
|
psGCP->dfGCPX = CPLAtof(CPLGetXMLValue(psXMLGCP,"X","0.0"));
|
|
psGCP->dfGCPY = CPLAtof(CPLGetXMLValue(psXMLGCP,"Y","0.0"));
|
|
const char* pszZ = CPLGetXMLValue(psXMLGCP,"Z",NULL);
|
|
if( pszZ == NULL )
|
|
{
|
|
/* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it back */
|
|
pszZ = CPLGetXMLValue(psXMLGCP,"GCPZ","0.0");
|
|
}
|
|
psGCP->dfGCPZ = CPLAtof(pszZ);
|
|
|
|
(*pnGCPCount) ++;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALSerializeOpenOptionsToXML() */
|
|
/************************************************************************/
|
|
|
|
void GDALSerializeOpenOptionsToXML( CPLXMLNode* psParentNode, char** papszOpenOptions)
|
|
{
|
|
if( papszOpenOptions != NULL )
|
|
{
|
|
CPLXMLNode* psOpenOptions = CPLCreateXMLNode( psParentNode, CXT_Element, "OpenOptions" );
|
|
CPLXMLNode* psLastChild = NULL;
|
|
|
|
for(char** papszIter = papszOpenOptions; *papszIter != NULL; papszIter ++ )
|
|
{
|
|
const char *pszRawValue;
|
|
char *pszKey = NULL;
|
|
CPLXMLNode *psOOI;
|
|
|
|
pszRawValue = CPLParseNameValue( *papszIter, &pszKey );
|
|
|
|
psOOI = CPLCreateXMLNode( NULL, CXT_Element, "OOI" );
|
|
if( psLastChild == NULL )
|
|
psOpenOptions->psChild = psOOI;
|
|
else
|
|
psLastChild->psNext = psOOI;
|
|
psLastChild = psOOI;
|
|
|
|
CPLSetXMLValue( psOOI, "#key", pszKey );
|
|
CPLCreateXMLNode( psOOI, CXT_Text, pszRawValue );
|
|
|
|
CPLFree( pszKey );
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDeserializeOpenOptionsFromXML() */
|
|
/************************************************************************/
|
|
|
|
char** GDALDeserializeOpenOptionsFromXML( CPLXMLNode* psParentNode )
|
|
{
|
|
char** papszOpenOptions = NULL;
|
|
CPLXMLNode* psOpenOptions = CPLGetXMLNode(psParentNode, "OpenOptions");
|
|
if( psOpenOptions != NULL )
|
|
{
|
|
CPLXMLNode* psOOI;
|
|
for( psOOI = psOpenOptions->psChild; psOOI != NULL;
|
|
psOOI = psOOI->psNext )
|
|
{
|
|
if( !EQUAL(psOOI->pszValue,"OOI")
|
|
|| psOOI->eType != CXT_Element
|
|
|| psOOI->psChild == NULL
|
|
|| psOOI->psChild->psNext == NULL
|
|
|| psOOI->psChild->eType != CXT_Attribute
|
|
|| psOOI->psChild->psChild == NULL )
|
|
continue;
|
|
|
|
char* pszName = psOOI->psChild->psChild->pszValue;
|
|
char* pszValue = psOOI->psChild->psNext->pszValue;
|
|
if( pszName != NULL && pszValue != NULL )
|
|
papszOpenOptions = CSLSetNameValue( papszOpenOptions, pszName, pszValue );
|
|
}
|
|
}
|
|
return papszOpenOptions;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterIOGetResampleAlg() */
|
|
/************************************************************************/
|
|
|
|
GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char* pszResampling)
|
|
{
|
|
GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
|
|
if( EQUALN(pszResampling, "NEAR", 4) )
|
|
eResampleAlg = GRIORA_NearestNeighbour;
|
|
else if( EQUAL(pszResampling, "BILINEAR") )
|
|
eResampleAlg = GRIORA_Bilinear;
|
|
else if( EQUAL(pszResampling, "CUBIC") )
|
|
eResampleAlg = GRIORA_Cubic;
|
|
else if( EQUAL(pszResampling, "CUBICSPLINE") )
|
|
eResampleAlg = GRIORA_CubicSpline;
|
|
else if( EQUAL(pszResampling, "LANCZOS") )
|
|
eResampleAlg = GRIORA_Lanczos;
|
|
else if( EQUAL(pszResampling, "AVERAGE") )
|
|
eResampleAlg = GRIORA_Average;
|
|
else if( EQUAL(pszResampling, "MODE") )
|
|
eResampleAlg = GRIORA_Mode;
|
|
else if( EQUAL(pszResampling, "GAUSS") )
|
|
eResampleAlg = GRIORA_Gauss;
|
|
else
|
|
CPLError(CE_Warning, CPLE_NotSupported,
|
|
"GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
|
|
return eResampleAlg;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALRasterIOExtraArgSetResampleAlg() */
|
|
/************************************************************************/
|
|
|
|
void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg* psExtraArg,
|
|
int nXSize, int nYSize,
|
|
int nBufXSize, int nBufYSize)
|
|
{
|
|
if( (nBufXSize != nXSize || nBufYSize != nYSize) &&
|
|
psExtraArg->eResampleAlg == GRIORA_NearestNeighbour )
|
|
{
|
|
const char* pszResampling = CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", NULL);
|
|
if( pszResampling != NULL )
|
|
{
|
|
psExtraArg->eResampleAlg = GDALRasterIOGetResampleAlg(pszResampling);
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALCanFileAcceptSidecarFile() */
|
|
/************************************************************************/
|
|
|
|
int GDALCanFileAcceptSidecarFile(const char* pszFilename)
|
|
{
|
|
if( strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?') )
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|