mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 06:06:00 -06:00
1549 lines
60 KiB
C++
1549 lines
60 KiB
C++
/******************************************************************************
|
|
* $Id: gdalwarper.cpp 29207 2015-05-18 17:23:45Z mloskot $
|
|
*
|
|
* Project: High Performance Image Reprojector
|
|
* Purpose: Implementation of high level convenience APIs for warper.
|
|
* Author: Frank Warmerdam, warmerdam@pobox.com
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2003, Frank Warmerdam <warmerdam@pobox.com>
|
|
* Copyright (c) 2008-2012, 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 "gdalwarper.h"
|
|
#include "cpl_string.h"
|
|
#include "cpl_minixml.h"
|
|
#include "ogr_api.h"
|
|
#include "gdal_priv.h"
|
|
|
|
CPL_CVSID("$Id: gdalwarper.cpp 29207 2015-05-18 17:23:45Z mloskot $");
|
|
|
|
/************************************************************************/
|
|
/* GDALReprojectImage() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Reproject image.
|
|
*
|
|
* This is a convenience function utilizing the GDALWarpOperation class to
|
|
* reproject an image from a source to a destination. In particular, this
|
|
* function takes care of establishing the transformation function to
|
|
* implement the reprojection, and will default a variety of other
|
|
* warp options.
|
|
*
|
|
* No metadata, projection info, or color tables are transferred
|
|
* to the output file.
|
|
*
|
|
* Starting with GDAL 2.0, nodata values set on destination dataset are taken
|
|
* into account.
|
|
*
|
|
* @param hSrcDS the source image file.
|
|
* @param pszSrcWKT the source projection. If NULL the source projection
|
|
* is read from from hSrcDS.
|
|
* @param hDstDS the destination image file.
|
|
* @param pszDstWKT the destination projection. If NULL the destination
|
|
* projection will be read from hDstDS.
|
|
* @param eResampleAlg the type of resampling to use.
|
|
* @param dfWarpMemoryLimit the amount of memory (in bytes) that the warp
|
|
* API is allowed to use for caching. This is in addition to the memory
|
|
* already allocated to the GDAL caching (as per GDALSetCacheMax()). May be
|
|
* 0.0 to use default memory settings.
|
|
* @param dfMaxError maximum error measured in input pixels that is allowed
|
|
* in approximating the transformation (0.0 for exact calculations).
|
|
* @param pfnProgress a GDALProgressFunc() compatible callback function for
|
|
* reporting progress or NULL.
|
|
* @param pProgressArg argument to be passed to pfnProgress. May be NULL.
|
|
* @param psOptions warp options, normally NULL.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr CPL_STDCALL
|
|
GDALReprojectImage( GDALDatasetH hSrcDS, const char *pszSrcWKT,
|
|
GDALDatasetH hDstDS, const char *pszDstWKT,
|
|
GDALResampleAlg eResampleAlg,
|
|
CPL_UNUSED double dfWarpMemoryLimit,
|
|
double dfMaxError,
|
|
GDALProgressFunc pfnProgress, void *pProgressArg,
|
|
GDALWarpOptions *psOptions )
|
|
|
|
{
|
|
GDALWarpOptions *psWOptions;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Setup a reprojection based transformer. */
|
|
/* -------------------------------------------------------------------- */
|
|
void *hTransformArg;
|
|
|
|
hTransformArg =
|
|
GDALCreateGenImgProjTransformer( hSrcDS, pszSrcWKT, hDstDS, pszDstWKT,
|
|
TRUE, 1000.0, 0 );
|
|
|
|
if( hTransformArg == NULL )
|
|
return CE_Failure;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create a copy of the user provided options, or a defaulted */
|
|
/* options structure. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( psOptions == NULL )
|
|
psWOptions = GDALCreateWarpOptions();
|
|
else
|
|
psWOptions = GDALCloneWarpOptions( psOptions );
|
|
|
|
psWOptions->eResampleAlg = eResampleAlg;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set transform. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( dfMaxError > 0.0 )
|
|
{
|
|
psWOptions->pTransformerArg =
|
|
GDALCreateApproxTransformer( GDALGenImgProjTransform,
|
|
hTransformArg, dfMaxError );
|
|
|
|
psWOptions->pfnTransformer = GDALApproxTransform;
|
|
}
|
|
else
|
|
{
|
|
psWOptions->pfnTransformer = GDALGenImgProjTransform;
|
|
psWOptions->pTransformerArg = hTransformArg;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set file and band mapping. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iBand;
|
|
|
|
psWOptions->hSrcDS = hSrcDS;
|
|
psWOptions->hDstDS = hDstDS;
|
|
|
|
if( psWOptions->nBandCount == 0 )
|
|
{
|
|
psWOptions->nBandCount = MIN(GDALGetRasterCount(hSrcDS),
|
|
GDALGetRasterCount(hDstDS));
|
|
|
|
psWOptions->panSrcBands = (int *)
|
|
CPLMalloc(sizeof(int) * psWOptions->nBandCount);
|
|
psWOptions->panDstBands = (int *)
|
|
CPLMalloc(sizeof(int) * psWOptions->nBandCount);
|
|
|
|
for( iBand = 0; iBand < psWOptions->nBandCount; iBand++ )
|
|
{
|
|
psWOptions->panSrcBands[iBand] = iBand+1;
|
|
psWOptions->panDstBands[iBand] = iBand+1;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set source nodata values if the source dataset seems to have */
|
|
/* any. Same for target nodata values */
|
|
/* -------------------------------------------------------------------- */
|
|
for( iBand = 0; iBand < psWOptions->nBandCount; iBand++ )
|
|
{
|
|
GDALRasterBandH hBand = GDALGetRasterBand( hSrcDS, iBand+1 );
|
|
int bGotNoData = FALSE;
|
|
double dfNoDataValue;
|
|
|
|
if (GDALGetRasterColorInterpretation(hBand) == GCI_AlphaBand)
|
|
{
|
|
psWOptions->nSrcAlphaBand = iBand + 1;
|
|
}
|
|
|
|
dfNoDataValue = GDALGetRasterNoDataValue( hBand, &bGotNoData );
|
|
if( bGotNoData )
|
|
{
|
|
if( psWOptions->padfSrcNoDataReal == NULL )
|
|
{
|
|
int ii;
|
|
|
|
psWOptions->padfSrcNoDataReal = (double *)
|
|
CPLMalloc(sizeof(double) * psWOptions->nBandCount);
|
|
psWOptions->padfSrcNoDataImag = (double *)
|
|
CPLMalloc(sizeof(double) * psWOptions->nBandCount);
|
|
|
|
for( ii = 0; ii < psWOptions->nBandCount; ii++ )
|
|
{
|
|
psWOptions->padfSrcNoDataReal[ii] = -1.1e20;
|
|
psWOptions->padfSrcNoDataImag[ii] = 0.0;
|
|
}
|
|
}
|
|
|
|
psWOptions->padfSrcNoDataReal[iBand] = dfNoDataValue;
|
|
}
|
|
|
|
// Deal with target band
|
|
hBand = GDALGetRasterBand( hDstDS, iBand+1 );
|
|
if (hBand && GDALGetRasterColorInterpretation(hBand) == GCI_AlphaBand)
|
|
{
|
|
psWOptions->nDstAlphaBand = iBand + 1;
|
|
}
|
|
|
|
dfNoDataValue = GDALGetRasterNoDataValue( hBand, &bGotNoData );
|
|
if( bGotNoData )
|
|
{
|
|
if( psWOptions->padfDstNoDataReal == NULL )
|
|
{
|
|
int ii;
|
|
|
|
psWOptions->padfDstNoDataReal = (double *)
|
|
CPLMalloc(sizeof(double) * psWOptions->nBandCount);
|
|
psWOptions->padfDstNoDataImag = (double *)
|
|
CPLMalloc(sizeof(double) * psWOptions->nBandCount);
|
|
|
|
for( ii = 0; ii < psWOptions->nBandCount; ii++ )
|
|
{
|
|
psWOptions->padfDstNoDataReal[ii] = -1.1e20;
|
|
psWOptions->padfDstNoDataImag[ii] = 0.0;
|
|
}
|
|
}
|
|
|
|
psWOptions->padfDstNoDataReal[iBand] = dfNoDataValue;
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set the progress function. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( pfnProgress != NULL )
|
|
{
|
|
psWOptions->pfnProgress = pfnProgress;
|
|
psWOptions->pProgressArg = pProgressArg;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create a warp options based on the options. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALWarpOperation oWarper;
|
|
CPLErr eErr;
|
|
|
|
eErr = oWarper.Initialize( psWOptions );
|
|
|
|
if( eErr == CE_None )
|
|
eErr = oWarper.ChunkAndWarpImage( 0, 0,
|
|
GDALGetRasterXSize(hDstDS),
|
|
GDALGetRasterYSize(hDstDS) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cleanup. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALDestroyGenImgProjTransformer( hTransformArg );
|
|
|
|
if( dfMaxError > 0.0 )
|
|
GDALDestroyApproxTransformer( psWOptions->pTransformerArg );
|
|
|
|
GDALDestroyWarpOptions( psWOptions );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALCreateAndReprojectImage() */
|
|
/* */
|
|
/* This is a "quicky" reprojection API. */
|
|
/************************************************************************/
|
|
|
|
CPLErr CPL_STDCALL GDALCreateAndReprojectImage(
|
|
GDALDatasetH hSrcDS, const char *pszSrcWKT,
|
|
const char *pszDstFilename, const char *pszDstWKT,
|
|
GDALDriverH hDstDriver, char **papszCreateOptions,
|
|
GDALResampleAlg eResampleAlg, double dfWarpMemoryLimit, double dfMaxError,
|
|
GDALProgressFunc pfnProgress, void *pProgressArg,
|
|
GDALWarpOptions *psOptions )
|
|
|
|
{
|
|
VALIDATE_POINTER1( hSrcDS, "GDALCreateAndReprojectImage", CE_Failure );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Default a few parameters. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( hDstDriver == NULL )
|
|
{
|
|
hDstDriver = GDALGetDriverByName( "GTiff" );
|
|
if (hDstDriver == NULL)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"GDALCreateAndReprojectImage needs GTiff driver");
|
|
return CE_Failure;
|
|
}
|
|
}
|
|
|
|
if( pszSrcWKT == NULL )
|
|
pszSrcWKT = GDALGetProjectionRef( hSrcDS );
|
|
|
|
if( pszDstWKT == NULL )
|
|
pszDstWKT = pszSrcWKT;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create a transformation object from the source to */
|
|
/* destination coordinate system. */
|
|
/* -------------------------------------------------------------------- */
|
|
void *hTransformArg;
|
|
|
|
hTransformArg =
|
|
GDALCreateGenImgProjTransformer( hSrcDS, pszSrcWKT, NULL, pszDstWKT,
|
|
TRUE, 1000.0, 0 );
|
|
|
|
if( hTransformArg == NULL )
|
|
return CE_Failure;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Get approximate output definition. */
|
|
/* -------------------------------------------------------------------- */
|
|
double adfDstGeoTransform[6];
|
|
int nPixels, nLines;
|
|
|
|
if( GDALSuggestedWarpOutput( hSrcDS,
|
|
GDALGenImgProjTransform, hTransformArg,
|
|
adfDstGeoTransform, &nPixels, &nLines )
|
|
!= CE_None )
|
|
return CE_Failure;
|
|
|
|
GDALDestroyGenImgProjTransformer( hTransformArg );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create the output file. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALDatasetH hDstDS;
|
|
|
|
hDstDS = GDALCreate( hDstDriver, pszDstFilename, nPixels, nLines,
|
|
GDALGetRasterCount(hSrcDS),
|
|
GDALGetRasterDataType(GDALGetRasterBand(hSrcDS,1)),
|
|
papszCreateOptions );
|
|
|
|
if( hDstDS == NULL )
|
|
return CE_Failure;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write out the projection definition. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALSetProjection( hDstDS, pszDstWKT );
|
|
GDALSetGeoTransform( hDstDS, adfDstGeoTransform );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Perform the reprojection. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLErr eErr ;
|
|
|
|
eErr =
|
|
GDALReprojectImage( hSrcDS, pszSrcWKT, hDstDS, pszDstWKT,
|
|
eResampleAlg, dfWarpMemoryLimit, dfMaxError,
|
|
pfnProgress, pProgressArg, psOptions );
|
|
|
|
GDALClose( hDstDS );
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALWarpNoDataMasker() */
|
|
/* */
|
|
/* GDALMaskFunc for establishing a validity mask for a source */
|
|
/* band based on a provided NODATA value. */
|
|
/************************************************************************/
|
|
|
|
CPLErr
|
|
GDALWarpNoDataMasker( void *pMaskFuncArg, int nBandCount, GDALDataType eType,
|
|
int /* nXOff */, int /* nYOff */, int nXSize, int nYSize,
|
|
GByte **ppImageData,
|
|
int bMaskIsFloat, void *pValidityMask, int* pbOutAllValid )
|
|
|
|
{
|
|
double *padfNoData = (double *) pMaskFuncArg;
|
|
GUInt32 *panValidityMask = (GUInt32 *) pValidityMask;
|
|
|
|
*pbOutAllValid = FALSE;
|
|
|
|
if( nBandCount != 1 || bMaskIsFloat )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Invalid nBandCount or bMaskIsFloat argument in SourceNoDataMask" );
|
|
return CE_Failure;
|
|
}
|
|
|
|
switch( eType )
|
|
{
|
|
case GDT_Byte:
|
|
{
|
|
int nNoData = (int) padfNoData[0];
|
|
GByte *pabyData = (GByte *) *ppImageData;
|
|
int iOffset;
|
|
|
|
// nothing to do if value is out of range.
|
|
if( padfNoData[0] < 0.0 || padfNoData[0] > 255.000001
|
|
|| padfNoData[1] != 0.0 )
|
|
{
|
|
*pbOutAllValid = TRUE;
|
|
return CE_None;
|
|
}
|
|
|
|
int bAllValid = TRUE;
|
|
for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- )
|
|
{
|
|
if( pabyData[iOffset] == nNoData )
|
|
{
|
|
bAllValid = FALSE;
|
|
panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f));
|
|
}
|
|
}
|
|
*pbOutAllValid = bAllValid;
|
|
}
|
|
break;
|
|
|
|
case GDT_Int16:
|
|
{
|
|
int nNoData = (int) padfNoData[0];
|
|
GInt16 *panData = (GInt16 *) *ppImageData;
|
|
int iOffset;
|
|
|
|
// nothing to do if value is out of range.
|
|
if( padfNoData[0] < -32768 || padfNoData[0] > 32767
|
|
|| padfNoData[1] != 0.0 )
|
|
{
|
|
*pbOutAllValid = TRUE;
|
|
return CE_None;
|
|
}
|
|
|
|
int bAllValid = TRUE;
|
|
for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- )
|
|
{
|
|
if( panData[iOffset] == nNoData )
|
|
{
|
|
bAllValid = FALSE;
|
|
panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f));
|
|
}
|
|
}
|
|
*pbOutAllValid = bAllValid;
|
|
}
|
|
break;
|
|
|
|
case GDT_UInt16:
|
|
{
|
|
int nNoData = (int) padfNoData[0];
|
|
GUInt16 *panData = (GUInt16 *) *ppImageData;
|
|
int iOffset;
|
|
|
|
// nothing to do if value is out of range.
|
|
if( padfNoData[0] < 0 || padfNoData[0] > 65535
|
|
|| padfNoData[1] != 0.0 )
|
|
{
|
|
*pbOutAllValid = TRUE;
|
|
return CE_None;
|
|
}
|
|
|
|
int bAllValid = TRUE;
|
|
for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- )
|
|
{
|
|
if( panData[iOffset] == nNoData )
|
|
{
|
|
bAllValid = FALSE;
|
|
panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f));
|
|
}
|
|
}
|
|
*pbOutAllValid = bAllValid;
|
|
}
|
|
break;
|
|
|
|
case GDT_Float32:
|
|
{
|
|
float fNoData = (float) padfNoData[0];
|
|
float *pafData = (float *) *ppImageData;
|
|
int iOffset;
|
|
int bIsNoDataNan = CPLIsNan(fNoData);
|
|
|
|
// nothing to do if value is out of range.
|
|
if( padfNoData[1] != 0.0 )
|
|
{
|
|
*pbOutAllValid = TRUE;
|
|
return CE_None;
|
|
}
|
|
|
|
int bAllValid = TRUE;
|
|
for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- )
|
|
{
|
|
float fVal = pafData[iOffset];
|
|
if( (bIsNoDataNan && CPLIsNan(fVal)) || (!bIsNoDataNan && ARE_REAL_EQUAL(fVal, fNoData)) )
|
|
{
|
|
bAllValid = FALSE;
|
|
panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f));
|
|
}
|
|
}
|
|
*pbOutAllValid = bAllValid;
|
|
}
|
|
break;
|
|
|
|
case GDT_Float64:
|
|
{
|
|
double dfNoData = padfNoData[0];
|
|
double *padfData = (double *) *ppImageData;
|
|
int iOffset;
|
|
int bIsNoDataNan = CPLIsNan(dfNoData);
|
|
|
|
// nothing to do if value is out of range.
|
|
if( padfNoData[1] != 0.0 )
|
|
{
|
|
*pbOutAllValid = TRUE;
|
|
return CE_None;
|
|
}
|
|
|
|
int bAllValid = TRUE;
|
|
for( iOffset = nXSize*nYSize-1; iOffset >= 0; iOffset-- )
|
|
{
|
|
double dfVal = padfData[iOffset];
|
|
if( (bIsNoDataNan && CPLIsNan(dfVal)) || (!bIsNoDataNan && ARE_REAL_EQUAL(dfVal, dfNoData)) )
|
|
{
|
|
bAllValid = FALSE;
|
|
panValidityMask[iOffset>>5] &= ~(0x01 << (iOffset & 0x1f));
|
|
}
|
|
}
|
|
*pbOutAllValid = bAllValid;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
double *padfWrk;
|
|
int iLine, iPixel;
|
|
int nWordSize = GDALGetDataTypeSize(eType)/8;
|
|
|
|
int bIsNoDataRealNan = CPLIsNan(padfNoData[0]);
|
|
int bIsNoDataImagNan = CPLIsNan(padfNoData[1]);
|
|
|
|
padfWrk = (double *) CPLMalloc(nXSize * sizeof(double) * 2);
|
|
int bAllValid = TRUE;
|
|
for( iLine = 0; iLine < nYSize; iLine++ )
|
|
{
|
|
GDALCopyWords( ((GByte *) *ppImageData)+nWordSize*iLine*nXSize,
|
|
eType, nWordSize,
|
|
padfWrk, GDT_CFloat64, 16, nXSize );
|
|
|
|
for( iPixel = 0; iPixel < nXSize; iPixel++ )
|
|
{
|
|
if( ((bIsNoDataRealNan && CPLIsNan(padfWrk[iPixel*2])) ||
|
|
(!bIsNoDataRealNan && ARE_REAL_EQUAL(padfWrk[iPixel*2], padfNoData[0])))
|
|
&& ((bIsNoDataImagNan && CPLIsNan(padfWrk[iPixel*2+1])) ||
|
|
(!bIsNoDataImagNan && ARE_REAL_EQUAL(padfWrk[iPixel*2+1], padfNoData[1]))) )
|
|
{
|
|
int iOffset = iPixel + iLine * nXSize;
|
|
|
|
bAllValid = FALSE;
|
|
panValidityMask[iOffset>>5] &=
|
|
~(0x01 << (iOffset & 0x1f));
|
|
}
|
|
}
|
|
|
|
}
|
|
*pbOutAllValid = bAllValid;
|
|
|
|
CPLFree( padfWrk );
|
|
}
|
|
break;
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALWarpSrcAlphaMasker() */
|
|
/* */
|
|
/* GDALMaskFunc for reading source simple 8bit alpha mask */
|
|
/* information and building a floating point density mask from */
|
|
/* it. */
|
|
/************************************************************************/
|
|
|
|
CPLErr
|
|
GDALWarpSrcAlphaMasker( void *pMaskFuncArg,
|
|
CPL_UNUSED int nBandCount,
|
|
CPL_UNUSED GDALDataType eType,
|
|
int nXOff, int nYOff, int nXSize, int nYSize,
|
|
GByte ** /*ppImageData */,
|
|
int bMaskIsFloat, void *pValidityMask,
|
|
int* pbOutAllOpaque )
|
|
|
|
{
|
|
GDALWarpOptions *psWO = (GDALWarpOptions *) pMaskFuncArg;
|
|
float *pafMask = (float *) pValidityMask;
|
|
*pbOutAllOpaque = FALSE;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do some minimal checking. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !bMaskIsFloat )
|
|
{
|
|
CPLAssert( FALSE );
|
|
return CE_Failure;
|
|
}
|
|
|
|
if( psWO == NULL || psWO->nSrcAlphaBand < 1 )
|
|
{
|
|
CPLAssert( FALSE );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read the alpha band. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLErr eErr;
|
|
GDALRasterBandH hAlphaBand = GDALGetRasterBand( psWO->hSrcDS,
|
|
psWO->nSrcAlphaBand );
|
|
if (hAlphaBand == NULL)
|
|
return CE_Failure;
|
|
|
|
eErr = GDALRasterIO( hAlphaBand, GF_Read, nXOff, nYOff, nXSize, nYSize,
|
|
pafMask, nXSize, nYSize, GDT_Float32, 0, 0 );
|
|
|
|
if( eErr != CE_None )
|
|
return eErr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Rescale from 0-255 to 0.0-1.0. */
|
|
/* -------------------------------------------------------------------- */
|
|
int bOutAllOpaque = TRUE;
|
|
for( int iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- )
|
|
{ // (1/255)
|
|
pafMask[iPixel] = (float)( pafMask[iPixel] * 0.00392157 );
|
|
if( pafMask[iPixel] >= 1.0F )
|
|
pafMask[iPixel] = 1.0F;
|
|
else
|
|
bOutAllOpaque = FALSE;
|
|
}
|
|
*pbOutAllOpaque = bOutAllOpaque;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALWarpSrcMaskMasker() */
|
|
/* */
|
|
/* GDALMaskFunc for reading source simple 8bit validity mask */
|
|
/* information and building a one bit validity mask. */
|
|
/************************************************************************/
|
|
|
|
CPLErr
|
|
GDALWarpSrcMaskMasker( void *pMaskFuncArg,
|
|
CPL_UNUSED int nBandCount,
|
|
CPL_UNUSED GDALDataType eType,
|
|
int nXOff, int nYOff, int nXSize, int nYSize,
|
|
GByte ** /*ppImageData */,
|
|
int bMaskIsFloat, void *pValidityMask )
|
|
|
|
{
|
|
GDALWarpOptions *psWO = (GDALWarpOptions *) pMaskFuncArg;
|
|
GUInt32 *panMask = (GUInt32 *) pValidityMask;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do some minimal checking. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( bMaskIsFloat )
|
|
{
|
|
CPLAssert( FALSE );
|
|
return CE_Failure;
|
|
}
|
|
|
|
if( psWO == NULL )
|
|
{
|
|
CPLAssert( FALSE );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Allocate a temporary buffer to read mask byte data into. */
|
|
/* -------------------------------------------------------------------- */
|
|
GByte *pabySrcMask;
|
|
|
|
pabySrcMask = (GByte *) VSIMalloc2(nXSize,nYSize);
|
|
if( pabySrcMask == NULL )
|
|
{
|
|
CPLError( CE_Failure, CPLE_OutOfMemory,
|
|
"Failed to allocate pabySrcMask (%dx%d) in GDALWarpSrcMaskMasker()",
|
|
nXSize, nYSize );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Fetch our mask band. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALRasterBandH hSrcBand, hMaskBand = NULL;
|
|
|
|
hSrcBand = GDALGetRasterBand( psWO->hSrcDS, psWO->panSrcBands[0] );
|
|
if( hSrcBand != NULL )
|
|
hMaskBand = GDALGetMaskBand( hSrcBand );
|
|
|
|
if( hMaskBand == NULL )
|
|
{
|
|
CPLAssert( FALSE );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read the mask band. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLErr eErr;
|
|
|
|
eErr = GDALRasterIO( hMaskBand, GF_Read, nXOff, nYOff, nXSize, nYSize,
|
|
pabySrcMask, nXSize, nYSize, GDT_Byte, 0, 0 );
|
|
|
|
if( eErr != CE_None )
|
|
{
|
|
CPLFree( pabySrcMask );
|
|
return eErr;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Pack into 1 bit per pixel for validity. */
|
|
/* -------------------------------------------------------------------- */
|
|
for( int iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- )
|
|
{
|
|
if( pabySrcMask[iPixel] == 0 )
|
|
panMask[iPixel>>5] &= ~(0x01 << (iPixel & 0x1f));
|
|
}
|
|
|
|
CPLFree( pabySrcMask );
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALWarpDstAlphaMasker() */
|
|
/* */
|
|
/* GDALMaskFunc for reading or writing the destination simple */
|
|
/* 8bit alpha mask information and building a floating point */
|
|
/* density mask from it. Note, writing is distinguished */
|
|
/* negative bandcount. */
|
|
/************************************************************************/
|
|
|
|
CPLErr
|
|
GDALWarpDstAlphaMasker( void *pMaskFuncArg, int nBandCount,
|
|
CPL_UNUSED GDALDataType eType,
|
|
int nXOff, int nYOff, int nXSize, int nYSize,
|
|
GByte ** /*ppImageData */,
|
|
int bMaskIsFloat, void *pValidityMask )
|
|
{
|
|
GDALWarpOptions *psWO = (GDALWarpOptions *) pMaskFuncArg;
|
|
float *pafMask = (float *) pValidityMask;
|
|
int iPixel;
|
|
CPLErr eErr;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Do some minimal checking. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( !bMaskIsFloat )
|
|
{
|
|
CPLAssert( FALSE );
|
|
return CE_Failure;
|
|
}
|
|
|
|
if( psWO == NULL || psWO->nDstAlphaBand < 1 )
|
|
{
|
|
CPLAssert( FALSE );
|
|
return CE_Failure;
|
|
}
|
|
|
|
GDALRasterBandH hAlphaBand =
|
|
GDALGetRasterBand( psWO->hDstDS, psWO->nDstAlphaBand );
|
|
if (hAlphaBand == NULL)
|
|
return CE_Failure;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Read alpha case. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( nBandCount >= 0 )
|
|
{
|
|
const char *pszInitDest =
|
|
CSLFetchNameValue( psWO->papszWarpOptions, "INIT_DEST" );
|
|
|
|
// Special logic for destinations being initialized on the fly.
|
|
if( pszInitDest != NULL )
|
|
{
|
|
for( iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- )
|
|
pafMask[iPixel] = 0.0;
|
|
return CE_None;
|
|
}
|
|
|
|
// Read data.
|
|
eErr = GDALRasterIO( hAlphaBand, GF_Read, nXOff, nYOff, nXSize, nYSize,
|
|
pafMask, nXSize, nYSize, GDT_Float32, 0, 0 );
|
|
|
|
if( eErr != CE_None )
|
|
return eErr;
|
|
|
|
// rescale.
|
|
for( iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- )
|
|
{
|
|
pafMask[iPixel] = (float) (pafMask[iPixel] * 0.00392157);
|
|
pafMask[iPixel] = MIN( 1.0F, pafMask[iPixel] );
|
|
}
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Write alpha case. */
|
|
/* -------------------------------------------------------------------- */
|
|
else
|
|
{
|
|
for( iPixel = nXSize * nYSize - 1; iPixel >= 0; iPixel-- )
|
|
pafMask[iPixel] = (float)(int) ( pafMask[iPixel] * 255.1 );
|
|
|
|
// Write data.
|
|
|
|
/* The VRT warper will pass destination sizes that may exceed */
|
|
/* the size of the raster for the partial blocks at the right */
|
|
/* and bottom of the band. So let's adjust the size */
|
|
int nDstXSize = nXSize;
|
|
if (nXOff + nXSize > GDALGetRasterBandXSize(hAlphaBand))
|
|
nDstXSize = GDALGetRasterBandXSize(hAlphaBand) - nXOff;
|
|
int nDstYSize = nYSize;
|
|
if (nYOff + nYSize > GDALGetRasterBandYSize(hAlphaBand))
|
|
nDstYSize = GDALGetRasterBandYSize(hAlphaBand) - nYOff;
|
|
|
|
eErr = GDALRasterIO( hAlphaBand, GF_Write,
|
|
nXOff, nYOff, nDstXSize, nDstYSize,
|
|
pafMask, nDstXSize, nDstYSize, GDT_Float32,
|
|
0, sizeof(float) * nXSize );
|
|
return eErr;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ==================================================================== */
|
|
/* GDALWarpOptions */
|
|
/* ==================================================================== */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* \var char **GDALWarpOptions::papszWarpOptions;
|
|
*
|
|
* A string list of additional options controlling the warp operation in
|
|
* name=value format. A suitable string list can be prepared with
|
|
* CSLSetNameValue().
|
|
*
|
|
* The following values are currently supported:
|
|
*
|
|
* - INIT_DEST=[value] or INIT_DEST=NO_DATA: This option forces the
|
|
* destination image to be initialized to the indicated value (for all bands)
|
|
* or indicates that it should be initialized to the NO_DATA value in
|
|
* padfDstNoDataReal/padfDstNoDataImag. If this value isn't set the
|
|
* destination image will be read and overlayed.
|
|
*
|
|
* - WRITE_FLUSH=YES/NO: This option forces a flush to disk of data after
|
|
* each chunk is processed. In some cases this helps ensure a serial
|
|
* writing of the output data otherwise a block of data may be written to disk
|
|
* each time a block of data is read for the input buffer resulting in alot
|
|
* of extra seeking around the disk, and reduced IO throughput. The default
|
|
* at this time is NO.
|
|
*
|
|
* - SKIP_NOSOURCE=YES/NO: Skip all processing for chunks for which there
|
|
* is no corresponding input data. This will disable initializing the
|
|
* destination (INIT_DEST) and all other processing, and so should be used
|
|
* careful. Mostly useful to short circuit a lot of extra work in mosaicing
|
|
* situations.
|
|
*
|
|
* - UNIFIED_SRC_NODATA=YES/[NO]: By default nodata masking values considered
|
|
* independently for each band. However, sometimes it is desired to treat all
|
|
* bands as nodata if and only if, all bands match the corresponding nodata
|
|
* values. To get this behavior set this option to YES.
|
|
*
|
|
* Normally when computing the source raster data to
|
|
* load to generate a particular output area, the warper samples transforms
|
|
* 21 points along each edge of the destination region back onto the source
|
|
* file, and uses this to compute a bounding window on the source image that
|
|
* is sufficient. Depending on the transformation in effect, the source
|
|
* window may be a bit too small, or even missing large areas. Problem
|
|
* situations are those where the transformation is very non-linear or
|
|
* "inside out". Examples are transforming from WGS84 to Polar Steregraphic
|
|
* for areas around the pole, or transformations where some of the image is
|
|
* untransformable. The following options provide some additional control
|
|
* to deal with errors in computing the source window:
|
|
*
|
|
* - SAMPLE_GRID=YES/NO: Setting this option to YES will force the sampling to
|
|
* include internal points as well as edge points which can be important if
|
|
* the transformation is esoteric inside out, or if large sections of the
|
|
* destination image are not transformable into the source coordinate system.
|
|
*
|
|
* - SAMPLE_STEPS: Modifies the density of the sampling grid. The default
|
|
* number of steps is 21. Increasing this can increase the computational
|
|
* cost, but improves the accuracy with which the source region is computed.
|
|
*
|
|
* - SOURCE_EXTRA: This is a number of extra pixels added around the source
|
|
* window for a given request, and by default it is 1 to take care of rounding
|
|
* error. Setting this larger will incease the amount of data that needs to
|
|
* be read, but can avoid missing source data.
|
|
*
|
|
* - CUTLINE: This may contain the WKT geometry for a cutline. It will
|
|
* be converted into a geometry by GDALWarpOperation::Initialize() and assigned
|
|
* to the GDALWarpOptions hCutline field. The coordinates must be expressed
|
|
* in source pixel/line coordinates. Note: this is different from the assumptions
|
|
* made for the -cutline option of the gdalwarp utility !
|
|
*
|
|
* - CUTLINE_BLEND_DIST: This may be set with a distance in pixels which
|
|
* will be assigned to the dfCutlineBlendDist field in the GDALWarpOptions.
|
|
*
|
|
* - CUTLINE_ALL_TOUCHED: This defaults to FALSE, but may be set to TRUE
|
|
* to enable ALL_TOUCHEd mode when rasterizing cutline polygons. This is
|
|
* useful to ensure that that all pixels overlapping the cutline polygon
|
|
* will be selected, not just those whose center point falls within the
|
|
* polygon.
|
|
*
|
|
* - OPTIMIZE_SIZE: This defaults to FALSE, but may be set to TRUE when
|
|
* outputing typically to a compressed dataset (GeoTIFF with COMPRESSED creation
|
|
* option set for example) for achieving a smaller file size. This is achieved
|
|
* by writing at once data aligned on full blocks of the target dataset, which
|
|
* avoids partial writes of compressed blocks and lost space when they are rewritten
|
|
* at the end of the file. However sticking to target block size may cause major
|
|
* processing slowdown for some particular reprojections.
|
|
*
|
|
* - NUM_THREADS: (GDAL >= 1.10) Can be set to a numeric value or ALL_CPUS to
|
|
* set the number of threads to use to parallelize the computation part of the
|
|
* warping. If not set, computation will be done in a single thread.
|
|
*
|
|
* - STREAMABLE_OUTPUT: (GDAL >= 2.0) This defaults to FALSE, but may be set to TRUE when
|
|
* outputing typically to a streamed file. The gdalwarp utility automatically
|
|
* sets this option when outputing to /vsistdout/ or a named pipe (on Unix).
|
|
* This option has performance impacts for some reprojections.
|
|
* Note: band interleaved output is not currently supported by the warping algorithm in
|
|
* a streamable compabible way.
|
|
*
|
|
* - SRC_COORD_PRECISION: (GDAL >= 2.0). Advanced setting. This defaults to 0, to indicate that
|
|
* no rounding of computing source image coordinates corresponding to the target
|
|
* image must be done. If greater than 0 (and typically below 1), this value,
|
|
* expressed in pixel, will be used to round computed source image coordinates. The purpose
|
|
* of this option is to make the results of warping with the approximated transformer
|
|
* more reproducible and not sensitive to changes in warping memory size. To achieve
|
|
* that, SRC_COORD_PRECISION must be at least 10 times greater than the error
|
|
* threshold. The higher the SRC_COORD_PRECISION/error_threshold ratio, the higher
|
|
* the performance will be, since exact reprojections must statistically be
|
|
* done with a frequency of 4*error_threshold/SRC_COORD_PRECISION.
|
|
*/
|
|
|
|
/************************************************************************/
|
|
/* GDALCreateWarpOptions() */
|
|
/************************************************************************/
|
|
|
|
GDALWarpOptions * CPL_STDCALL GDALCreateWarpOptions()
|
|
|
|
{
|
|
GDALWarpOptions *psOptions;
|
|
|
|
psOptions = (GDALWarpOptions *) CPLCalloc(sizeof(GDALWarpOptions),1);
|
|
|
|
psOptions->nBandCount = 0;
|
|
psOptions->eResampleAlg = GRA_NearestNeighbour;
|
|
psOptions->pfnProgress = GDALDummyProgress;
|
|
psOptions->eWorkingDataType = GDT_Unknown;
|
|
|
|
return psOptions;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDestroyWarpOptions() */
|
|
/************************************************************************/
|
|
|
|
void CPL_STDCALL GDALDestroyWarpOptions( GDALWarpOptions *psOptions )
|
|
|
|
{
|
|
if( psOptions == NULL )
|
|
return;
|
|
|
|
CSLDestroy( psOptions->papszWarpOptions );
|
|
CPLFree( psOptions->panSrcBands );
|
|
CPLFree( psOptions->panDstBands );
|
|
CPLFree( psOptions->padfSrcNoDataReal );
|
|
CPLFree( psOptions->padfSrcNoDataImag );
|
|
CPLFree( psOptions->padfDstNoDataReal );
|
|
CPLFree( psOptions->padfDstNoDataImag );
|
|
CPLFree( psOptions->papfnSrcPerBandValidityMaskFunc );
|
|
CPLFree( psOptions->papSrcPerBandValidityMaskFuncArg );
|
|
|
|
if( psOptions->hCutline != NULL )
|
|
OGR_G_DestroyGeometry( (OGRGeometryH) psOptions->hCutline );
|
|
|
|
CPLFree( psOptions );
|
|
}
|
|
|
|
|
|
#define COPY_MEM(target,type,count) \
|
|
do { if( (psSrcOptions->target) != NULL && (count) != 0 ) \
|
|
{ \
|
|
(psDstOptions->target) = (type *) CPLMalloc(sizeof(type)*(count)); \
|
|
memcpy( (psDstOptions->target), (psSrcOptions->target), \
|
|
sizeof(type) * (count) ); \
|
|
} \
|
|
else \
|
|
(psDstOptions->target) = NULL; } while(0)
|
|
|
|
/************************************************************************/
|
|
/* GDALCloneWarpOptions() */
|
|
/************************************************************************/
|
|
|
|
GDALWarpOptions * CPL_STDCALL
|
|
GDALCloneWarpOptions( const GDALWarpOptions *psSrcOptions )
|
|
|
|
{
|
|
GDALWarpOptions *psDstOptions = GDALCreateWarpOptions();
|
|
|
|
memcpy( psDstOptions, psSrcOptions, sizeof(GDALWarpOptions) );
|
|
|
|
if( psSrcOptions->papszWarpOptions != NULL )
|
|
psDstOptions->papszWarpOptions =
|
|
CSLDuplicate( psSrcOptions->papszWarpOptions );
|
|
|
|
COPY_MEM( panSrcBands, int, psSrcOptions->nBandCount );
|
|
COPY_MEM( panDstBands, int, psSrcOptions->nBandCount );
|
|
COPY_MEM( padfSrcNoDataReal, double, psSrcOptions->nBandCount );
|
|
COPY_MEM( padfSrcNoDataImag, double, psSrcOptions->nBandCount );
|
|
COPY_MEM( padfDstNoDataReal, double, psSrcOptions->nBandCount );
|
|
COPY_MEM( padfDstNoDataImag, double, psSrcOptions->nBandCount );
|
|
COPY_MEM( papfnSrcPerBandValidityMaskFunc, GDALMaskFunc,
|
|
psSrcOptions->nBandCount );
|
|
psDstOptions->papSrcPerBandValidityMaskFuncArg = NULL;
|
|
|
|
if( psSrcOptions->hCutline != NULL )
|
|
psDstOptions->hCutline =
|
|
OGR_G_Clone( (OGRGeometryH) psSrcOptions->hCutline );
|
|
psDstOptions->dfCutlineBlendDist = psSrcOptions->dfCutlineBlendDist;
|
|
|
|
return psDstOptions;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALSerializeWarpOptions() */
|
|
/************************************************************************/
|
|
|
|
CPLXMLNode * CPL_STDCALL
|
|
GDALSerializeWarpOptions( const GDALWarpOptions *psWO )
|
|
|
|
{
|
|
CPLXMLNode *psTree;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create root. */
|
|
/* -------------------------------------------------------------------- */
|
|
psTree = CPLCreateXMLNode( NULL, CXT_Element, "GDALWarpOptions" );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* WarpMemoryLimit */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLCreateXMLElementAndValue(
|
|
psTree, "WarpMemoryLimit",
|
|
CPLString().Printf("%g", psWO->dfWarpMemoryLimit ) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* ResampleAlg */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszAlgName;
|
|
|
|
if( psWO->eResampleAlg == GRA_NearestNeighbour )
|
|
pszAlgName = "NearestNeighbour";
|
|
else if( psWO->eResampleAlg == GRA_Bilinear )
|
|
pszAlgName = "Bilinear";
|
|
else if( psWO->eResampleAlg == GRA_Cubic )
|
|
pszAlgName = "Cubic";
|
|
else if( psWO->eResampleAlg == GRA_CubicSpline )
|
|
pszAlgName = "CubicSpline";
|
|
else if( psWO->eResampleAlg == GRA_Lanczos )
|
|
pszAlgName = "Lanczos";
|
|
else if( psWO->eResampleAlg == GRA_Average )
|
|
pszAlgName = "Average";
|
|
else if( psWO->eResampleAlg == GRA_Mode )
|
|
pszAlgName = "Mode";
|
|
else if( psWO->eResampleAlg == GRA_Max )
|
|
pszAlgName = "Maximum";
|
|
else if( psWO->eResampleAlg == GRA_Min )
|
|
pszAlgName = "Minimum";
|
|
else if( psWO->eResampleAlg == GRA_Med )
|
|
pszAlgName = "Median";
|
|
else if( psWO->eResampleAlg == GRA_Q1 )
|
|
pszAlgName = "Quartile1";
|
|
else if( psWO->eResampleAlg == GRA_Q3 )
|
|
pszAlgName = "Quartile3";
|
|
else
|
|
pszAlgName = "Unknown";
|
|
|
|
CPLCreateXMLElementAndValue(
|
|
psTree, "ResampleAlg", pszAlgName );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Working Data Type */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
CPLCreateXMLElementAndValue(
|
|
psTree, "WorkingDataType",
|
|
GDALGetDataTypeName( psWO->eWorkingDataType ) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Name/value warp options. */
|
|
/* -------------------------------------------------------------------- */
|
|
int iWO;
|
|
|
|
for( iWO = 0; psWO->papszWarpOptions != NULL
|
|
&& psWO->papszWarpOptions[iWO] != NULL; iWO++ )
|
|
{
|
|
char *pszName = NULL;
|
|
const char *pszValue =
|
|
CPLParseNameValue( psWO->papszWarpOptions[iWO], &pszName );
|
|
|
|
/* EXTRA_ELTS is an internal detail that we will recover */
|
|
/* no need to serialize it */
|
|
if( !EQUAL(pszName, "EXTRA_ELTS") )
|
|
{
|
|
CPLXMLNode *psOption =
|
|
CPLCreateXMLElementAndValue(
|
|
psTree, "Option", pszValue );
|
|
|
|
CPLCreateXMLNode(
|
|
CPLCreateXMLNode( psOption, CXT_Attribute, "name" ),
|
|
CXT_Text, pszName );
|
|
}
|
|
|
|
CPLFree(pszName);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Source and Destination Data Source */
|
|
/* -------------------------------------------------------------------- */
|
|
if( psWO->hSrcDS != NULL )
|
|
{
|
|
CPLCreateXMLElementAndValue(
|
|
psTree, "SourceDataset",
|
|
GDALGetDescription( psWO->hSrcDS ) );
|
|
|
|
char** papszOpenOptions = ((GDALDataset*)psWO->hSrcDS)->GetOpenOptions();
|
|
GDALSerializeOpenOptionsToXML(psTree, papszOpenOptions);
|
|
}
|
|
|
|
if( psWO->hDstDS != NULL && strlen(GDALGetDescription(psWO->hDstDS)) != 0 )
|
|
{
|
|
CPLCreateXMLElementAndValue(
|
|
psTree, "DestinationDataset",
|
|
GDALGetDescription( psWO->hDstDS ) );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Serialize transformer. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( psWO->pfnTransformer != NULL )
|
|
{
|
|
CPLXMLNode *psTransformerContainer;
|
|
CPLXMLNode *psTransformerTree;
|
|
|
|
psTransformerContainer =
|
|
CPLCreateXMLNode( psTree, CXT_Element, "Transformer" );
|
|
|
|
psTransformerTree =
|
|
GDALSerializeTransformer( psWO->pfnTransformer,
|
|
psWO->pTransformerArg );
|
|
|
|
if( psTransformerTree != NULL )
|
|
CPLAddXMLChild( psTransformerContainer, psTransformerTree );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Band count and lists. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psBandList = NULL;
|
|
int i;
|
|
|
|
if( psWO->nBandCount != 0 )
|
|
psBandList = CPLCreateXMLNode( psTree, CXT_Element, "BandList" );
|
|
|
|
for( i = 0; i < psWO->nBandCount; i++ )
|
|
{
|
|
CPLXMLNode *psBand;
|
|
|
|
psBand = CPLCreateXMLNode( psBandList, CXT_Element, "BandMapping" );
|
|
if( psWO->panSrcBands != NULL )
|
|
CPLCreateXMLNode(
|
|
CPLCreateXMLNode( psBand, CXT_Attribute, "src" ),
|
|
CXT_Text, CPLString().Printf( "%d", psWO->panSrcBands[i] ) );
|
|
if( psWO->panDstBands != NULL )
|
|
CPLCreateXMLNode(
|
|
CPLCreateXMLNode( psBand, CXT_Attribute, "dst" ),
|
|
CXT_Text, CPLString().Printf( "%d", psWO->panDstBands[i] ) );
|
|
|
|
if( psWO->padfSrcNoDataReal != NULL )
|
|
{
|
|
if (CPLIsNan(psWO->padfSrcNoDataReal[i]))
|
|
CPLCreateXMLElementAndValue(psBand, "SrcNoDataReal", "nan");
|
|
else
|
|
CPLCreateXMLElementAndValue(
|
|
psBand, "SrcNoDataReal",
|
|
CPLString().Printf( "%.16g", psWO->padfSrcNoDataReal[i] ) );
|
|
}
|
|
|
|
if( psWO->padfSrcNoDataImag != NULL )
|
|
{
|
|
if (CPLIsNan(psWO->padfSrcNoDataImag[i]))
|
|
CPLCreateXMLElementAndValue(psBand, "SrcNoDataImag", "nan");
|
|
else
|
|
CPLCreateXMLElementAndValue(
|
|
psBand, "SrcNoDataImag",
|
|
CPLString().Printf( "%.16g", psWO->padfSrcNoDataImag[i] ) );
|
|
}
|
|
|
|
if( psWO->padfDstNoDataReal != NULL )
|
|
{
|
|
if (CPLIsNan(psWO->padfDstNoDataReal[i]))
|
|
CPLCreateXMLElementAndValue(psBand, "DstNoDataReal", "nan");
|
|
else
|
|
CPLCreateXMLElementAndValue(
|
|
psBand, "DstNoDataReal",
|
|
CPLString().Printf( "%.16g", psWO->padfDstNoDataReal[i] ) );
|
|
}
|
|
|
|
if( psWO->padfDstNoDataImag != NULL )
|
|
{
|
|
if (CPLIsNan(psWO->padfDstNoDataImag[i]))
|
|
CPLCreateXMLElementAndValue(psBand, "DstNoDataImag", "nan");
|
|
else
|
|
CPLCreateXMLElementAndValue(
|
|
psBand, "DstNoDataImag",
|
|
CPLString().Printf( "%.16g", psWO->padfDstNoDataImag[i] ) );
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Alpha bands. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( psWO->nSrcAlphaBand > 0 )
|
|
CPLCreateXMLElementAndValue(
|
|
psTree, "SrcAlphaBand",
|
|
CPLString().Printf( "%d", psWO->nSrcAlphaBand ) );
|
|
|
|
if( psWO->nDstAlphaBand > 0 )
|
|
CPLCreateXMLElementAndValue(
|
|
psTree, "DstAlphaBand",
|
|
CPLString().Printf( "%d", psWO->nDstAlphaBand ) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cutline. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( psWO->hCutline != NULL )
|
|
{
|
|
char *pszWKT = NULL;
|
|
if( OGR_G_ExportToWkt( (OGRGeometryH) psWO->hCutline, &pszWKT )
|
|
== OGRERR_NONE )
|
|
{
|
|
CPLCreateXMLElementAndValue( psTree, "Cutline", pszWKT );
|
|
CPLFree( pszWKT );
|
|
}
|
|
}
|
|
|
|
if( psWO->dfCutlineBlendDist != 0.0 )
|
|
CPLCreateXMLElementAndValue(
|
|
psTree, "CutlineBlendDist",
|
|
CPLString().Printf( "%.5g", psWO->dfCutlineBlendDist ) );
|
|
|
|
return psTree;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALDeserializeWarpOptions() */
|
|
/************************************************************************/
|
|
|
|
GDALWarpOptions * CPL_STDCALL GDALDeserializeWarpOptions( CPLXMLNode *psTree )
|
|
|
|
{
|
|
CPLErrorReset();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Verify this is the right kind of object. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( psTree == NULL || psTree->eType != CXT_Element
|
|
|| !EQUAL(psTree->pszValue,"GDALWarpOptions") )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Wrong node, unable to deserialize GDALWarpOptions." );
|
|
return NULL;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create pre-initialized warp options. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALWarpOptions *psWO = GDALCreateWarpOptions();
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Warp memory limit. */
|
|
/* -------------------------------------------------------------------- */
|
|
psWO->dfWarpMemoryLimit =
|
|
CPLAtof(CPLGetXMLValue(psTree,"WarpMemoryLimit","0.0"));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* resample algorithm */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszValue =
|
|
CPLGetXMLValue(psTree,"ResampleAlg","Default");
|
|
|
|
if( EQUAL(pszValue,"NearestNeighbour") )
|
|
psWO->eResampleAlg = GRA_NearestNeighbour;
|
|
else if( EQUAL(pszValue,"Bilinear") )
|
|
psWO->eResampleAlg = GRA_Bilinear;
|
|
else if( EQUAL(pszValue,"Cubic") )
|
|
psWO->eResampleAlg = GRA_Cubic;
|
|
else if( EQUAL(pszValue,"CubicSpline") )
|
|
psWO->eResampleAlg = GRA_CubicSpline;
|
|
else if( EQUAL(pszValue,"Lanczos") )
|
|
psWO->eResampleAlg = GRA_Lanczos;
|
|
else if( EQUAL(pszValue,"Average") )
|
|
psWO->eResampleAlg = GRA_Average;
|
|
else if( EQUAL(pszValue,"Mode") )
|
|
psWO->eResampleAlg = GRA_Mode;
|
|
else if( EQUAL(pszValue,"Maximum") )
|
|
psWO->eResampleAlg = GRA_Max;
|
|
else if( EQUAL(pszValue,"Minimum") )
|
|
psWO->eResampleAlg = GRA_Min;
|
|
else if( EQUAL(pszValue,"Median") )
|
|
psWO->eResampleAlg = GRA_Med;
|
|
else if( EQUAL(pszValue,"Quartile1") )
|
|
psWO->eResampleAlg = GRA_Q1;
|
|
else if( EQUAL(pszValue,"Quartile3") )
|
|
psWO->eResampleAlg = GRA_Q3;
|
|
else if( EQUAL(pszValue,"Default") )
|
|
/* leave as is */;
|
|
else
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Unrecognise ResampleAlg value '%s'.",
|
|
pszValue );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Working data type. */
|
|
/* -------------------------------------------------------------------- */
|
|
psWO->eWorkingDataType =
|
|
GDALGetDataTypeByName(
|
|
CPLGetXMLValue(psTree,"WorkingDataType","Unknown"));
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Name/value warp options. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psItem;
|
|
|
|
for( psItem = psTree->psChild; psItem != NULL; psItem = psItem->psNext )
|
|
{
|
|
if( psItem->eType == CXT_Element
|
|
&& EQUAL(psItem->pszValue,"Option") )
|
|
{
|
|
const char *pszName = CPLGetXMLValue(psItem, "Name", NULL );
|
|
const char *pszValue = CPLGetXMLValue(psItem, "", NULL );
|
|
|
|
if( pszName != NULL && pszValue != NULL )
|
|
{
|
|
psWO->papszWarpOptions =
|
|
CSLSetNameValue( psWO->papszWarpOptions,
|
|
pszName, pszValue );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Source Dataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszValue = CPLGetXMLValue(psTree,"SourceDataset",NULL);
|
|
|
|
if( pszValue != NULL )
|
|
{
|
|
char** papszOpenOptions = GDALDeserializeOpenOptionsFromXML(psTree);
|
|
psWO->hSrcDS = GDALOpenEx(
|
|
pszValue, GDAL_OF_SHARED | GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, NULL,
|
|
(const char* const* )papszOpenOptions, NULL );
|
|
CSLDestroy(papszOpenOptions);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Destination Dataset. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszValue = CPLGetXMLValue(psTree,"DestinationDataset",NULL);
|
|
|
|
if( pszValue != NULL )
|
|
psWO->hDstDS = GDALOpenShared( pszValue, GA_Update );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* First, count band mappings so we can establish the bandcount. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psBandTree = CPLGetXMLNode( psTree, "BandList" );
|
|
CPLXMLNode *psBand = NULL;
|
|
|
|
psWO->nBandCount = 0;
|
|
|
|
if (psBandTree)
|
|
psBand = psBandTree->psChild;
|
|
else
|
|
psBand = NULL;
|
|
|
|
for( ; psBand != NULL; psBand = psBand->psNext )
|
|
{
|
|
if( psBand->eType != CXT_Element
|
|
|| !EQUAL(psBand->pszValue,"BandMapping") )
|
|
continue;
|
|
|
|
psWO->nBandCount++;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* Now actually process each bandmapping. */
|
|
/* ==================================================================== */
|
|
int iBand = 0;
|
|
|
|
if (psBandTree)
|
|
psBand = psBandTree->psChild;
|
|
else
|
|
psBand = NULL;
|
|
|
|
for( ; psBand != NULL; psBand = psBand->psNext )
|
|
{
|
|
if( psBand->eType != CXT_Element
|
|
|| !EQUAL(psBand->pszValue,"BandMapping") )
|
|
continue;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Source band */
|
|
/* -------------------------------------------------------------------- */
|
|
if( psWO->panSrcBands == NULL )
|
|
psWO->panSrcBands = (int *)CPLMalloc(sizeof(int)*psWO->nBandCount);
|
|
|
|
pszValue = CPLGetXMLValue(psBand,"src",NULL);
|
|
if( pszValue == NULL )
|
|
psWO->panSrcBands[iBand] = iBand+1;
|
|
else
|
|
psWO->panSrcBands[iBand] = atoi(pszValue);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Destination band. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszValue = CPLGetXMLValue(psBand,"dst",NULL);
|
|
if( pszValue != NULL )
|
|
{
|
|
if( psWO->panDstBands == NULL )
|
|
psWO->panDstBands =
|
|
(int *) CPLMalloc(sizeof(int)*psWO->nBandCount);
|
|
|
|
psWO->panDstBands[iBand] = atoi(pszValue);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Source nodata. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszValue = CPLGetXMLValue(psBand,"SrcNoDataReal",NULL);
|
|
if( pszValue != NULL )
|
|
{
|
|
if( psWO->padfSrcNoDataReal == NULL )
|
|
psWO->padfSrcNoDataReal =
|
|
(double *) CPLCalloc(sizeof(double),psWO->nBandCount);
|
|
|
|
psWO->padfSrcNoDataReal[iBand] = CPLAtof(pszValue);
|
|
}
|
|
|
|
pszValue = CPLGetXMLValue(psBand,"SrcNoDataImag",NULL);
|
|
if( pszValue != NULL )
|
|
{
|
|
if( psWO->padfSrcNoDataImag == NULL )
|
|
psWO->padfSrcNoDataImag =
|
|
(double *) CPLCalloc(sizeof(double),psWO->nBandCount);
|
|
|
|
psWO->padfSrcNoDataImag[iBand] = CPLAtof(pszValue);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Destination nodata. */
|
|
/* -------------------------------------------------------------------- */
|
|
pszValue = CPLGetXMLValue(psBand,"DstNoDataReal",NULL);
|
|
if( pszValue != NULL )
|
|
{
|
|
if( psWO->padfDstNoDataReal == NULL )
|
|
psWO->padfDstNoDataReal =
|
|
(double *) CPLCalloc(sizeof(double),psWO->nBandCount);
|
|
|
|
psWO->padfDstNoDataReal[iBand] = CPLAtof(pszValue);
|
|
}
|
|
|
|
pszValue = CPLGetXMLValue(psBand,"DstNoDataImag",NULL);
|
|
if( pszValue != NULL )
|
|
{
|
|
if( psWO->padfDstNoDataImag == NULL )
|
|
psWO->padfDstNoDataImag =
|
|
(double *) CPLCalloc(sizeof(double),psWO->nBandCount);
|
|
|
|
psWO->padfDstNoDataImag[iBand] = CPLAtof(pszValue);
|
|
}
|
|
|
|
iBand++;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Alpha bands. */
|
|
/* -------------------------------------------------------------------- */
|
|
psWO->nSrcAlphaBand =
|
|
atoi( CPLGetXMLValue( psTree, "SrcAlphaBand", "0" ) );
|
|
psWO->nDstAlphaBand =
|
|
atoi( CPLGetXMLValue( psTree, "DstAlphaBand", "0" ) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Cutline. */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszWKT = CPLGetXMLValue( psTree, "Cutline", NULL );
|
|
if( pszWKT )
|
|
{
|
|
OGR_G_CreateFromWkt( (char **) &pszWKT, NULL,
|
|
(OGRGeometryH *) (&psWO->hCutline) );
|
|
}
|
|
|
|
psWO->dfCutlineBlendDist =
|
|
CPLAtof( CPLGetXMLValue( psTree, "CutlineBlendDist", "0" ) );
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Transformation. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLXMLNode *psTransformer = CPLGetXMLNode( psTree, "Transformer" );
|
|
|
|
if( psTransformer != NULL && psTransformer->psChild != NULL )
|
|
{
|
|
GDALDeserializeTransformer( psTransformer->psChild,
|
|
&(psWO->pfnTransformer),
|
|
&(psWO->pTransformerArg) );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* If any error has occured, cleanup else return success. */
|
|
/* -------------------------------------------------------------------- */
|
|
if( CPLGetLastErrorNo() != CE_None )
|
|
{
|
|
if ( psWO->pTransformerArg )
|
|
{
|
|
GDALDestroyTransformer( psWO->pTransformerArg );
|
|
psWO->pTransformerArg = NULL;
|
|
}
|
|
if( psWO->hSrcDS != NULL )
|
|
{
|
|
GDALClose( psWO->hSrcDS );
|
|
psWO->hSrcDS = NULL;
|
|
}
|
|
if( psWO->hDstDS != NULL )
|
|
{
|
|
GDALClose( psWO->hDstDS );
|
|
psWO->hDstDS = NULL;
|
|
}
|
|
GDALDestroyWarpOptions( psWO );
|
|
return NULL;
|
|
}
|
|
else
|
|
return psWO;
|
|
}
|