19#include <gdalwarper.h>
21#include <ogr_srs_api.h>
29static double ceil_with_tolerance(
double value )
31 if ( std::fabs( value - std::round( value ) ) < 1e-6 )
32 return std::round( value );
34 return std::ceil( value );
37static double floor_with_tolerance(
double value )
39 if ( std::fabs( value - std::round( value ) ) < 1e-6 )
40 return std::round( value );
42 return std::floor( value );
45static double fmod_with_tolerance(
double num,
double denom )
47 return num - floor_with_tolerance( num / denom ) * denom;
50static QgsRectangle transform_to_extent(
const double *geotransform,
double xSize,
double ySize )
52 QgsRectangle r( geotransform[0], geotransform[3], geotransform[0] + geotransform[1] * xSize, geotransform[3] + geotransform[5] * ySize );
57static int CPL_STDCALL _progress(
double dfComplete,
const char *pszMessage,
void *pProgressArg )
59 Q_UNUSED( pszMessage )
63 return handler->
progress( dfComplete );
68static CPLErr rescalePreWarpChunkProcessor(
void *pKern,
void *pArg )
70 GDALWarpKernel *kern = ( GDALWarpKernel * ) pKern;
71 const double cellsize = ( (
double * ) pArg )[0];
73 for (
int nBand = 0; nBand < kern->nBands; ++nBand )
75 float *bandData = (
float * ) kern->papabySrcImage[nBand];
76 for (
int nLine = 0; nLine < kern->nSrcYSize; ++nLine )
78 for (
int nPixel = 0; nPixel < kern->nSrcXSize; ++nPixel )
80 bandData[nPixel] /= cellsize;
82 bandData += kern->nSrcXSize;
88static CPLErr rescalePostWarpChunkProcessor(
void *pKern,
void *pArg )
90 GDALWarpKernel *kern = ( GDALWarpKernel * ) pKern;
91 const double cellsize = ( (
double * ) pArg )[1];
93 for (
int nBand = 0; nBand < kern->nBands; ++nBand )
95 float *bandData = (
float * ) kern->papabyDstImage[nBand];
96 for (
int nLine = 0; nLine < kern->nDstYSize; ++nLine )
98 for (
int nPixel = 0; nPixel < kern->nDstXSize; ++nPixel )
100 bandData[nPixel] *= cellsize;
102 bandData += kern->nDstXSize;
118 for (
int i = 0; i < 6; ++i )
148 if ( customCRSWkt.isEmpty() || customCRSWkt == rasterInfo.
crs() )
153 if ( !customCellSize.isValid() )
164 if ( customGridOffset.x() < 0 || customGridOffset.y() < 0 )
166 if ( !customCellSize.isValid() )
176 mGridOffsetX = fmod_with_tolerance( rasterInfo.
origin().x(), customCellSize.width() );
177 mGridOffsetY = fmod_with_tolerance( rasterInfo.
origin().y(), customCellSize.height() );
192 mCrsWkt = QStringLiteral(
"_error_" );
200 if ( !customCellSize.isValid() )
211 if ( customGridOffset.x() < 0 || customGridOffset.y() < 0 )
230 if (
mCrsWkt == QLatin1String(
"_error_" ) )
238 mErrorMessage = QObject::tr(
"Cell size must not be zero." );
245 double finalExtent[4] = { 0, 0, 0, 0 };
248 for (
int i = 0; i <
mRasters.count(); ++i )
258 mErrorMessage = QString(
"Failed to get suggested warp output.\n\n"
260 "Source WKT:\n%2\n\nDestination WKT:\n%3" )
267 if ( finalExtent[0] == 0 && finalExtent[1] == 0 && finalExtent[2] == 0 && finalExtent[3] == 0 )
278 if ( extent.
xMinimum() > finalExtent[0] )
280 if ( extent.
yMinimum() > finalExtent[1] )
282 if ( extent.
xMaximum() < finalExtent[2] )
284 if ( extent.
yMaximum() < finalExtent[3] )
300 if ( clipX0 > finalExtent[0] )
301 finalExtent[0] = clipX0;
302 if ( clipY0 > finalExtent[1] )
303 finalExtent[1] = clipY0;
304 if ( clipX1 < finalExtent[2] )
305 finalExtent[2] = clipX1;
306 if ( clipY1 < finalExtent[3] )
307 finalExtent[3] = clipY1;
316 const int xSize = floor_with_tolerance( ( finalExtent[2] - originX ) /
mCellSizeX );
317 const int ySize = floor_with_tolerance( ( finalExtent[3] - originY ) /
mCellSizeY );
319 if ( xSize <= 0 || ySize <= 0 )
321 mErrorMessage = QObject::tr(
"No common intersecting area." );
361 const auto constMRasters =
mRasters;
362 for (
const Item &r : constMRasters )
373 qDebug(
"---ALIGN------------------" );
374 qDebug(
"wkt %s",
mCrsWkt.toLatin1().constData() );
376 qDebug(
"transform" );
381 qDebug(
"extent %s", e.
toString().toLatin1().constData() );
387 double bestCellArea = INFINITY;
397 const auto constMRasters =
mRasters;
398 for (
const Item &raster : constMRasters )
403 const double cellArea = cs.width() * cs.height();
404 if ( cellArea < bestCellArea )
406 bestCellArea = cellArea;
418 GDALDriverH hDriver = GDALGetDriverByName(
"GTiff" );
421 mErrorMessage = QStringLiteral(
"GDALGetDriverByName(GTiff) failed." );
435 const int bandCount = GDALGetRasterCount( hSrcDS.get() );
436 const GDALDataType eDT = GDALGetRasterDataType( GDALGetRasterBand( hSrcDS.get(), 1 ) );
447 GDALSetProjection( hDstDS.get(),
mCrsWkt.toLatin1().constData() );
451 GDALColorTableH hCT = GDALGetRasterColorTable( GDALGetRasterBand( hSrcDS.get(), 1 ) );
453 GDALSetRasterColorTable( GDALGetRasterBand( hDstDS.get(), 1 ), hCT );
459 psWarpOptions->hSrcDS = hSrcDS.get();
460 psWarpOptions->hDstDS = hDstDS.get();
462 psWarpOptions->nBandCount = GDALGetRasterCount( hSrcDS.get() );
463 psWarpOptions->panSrcBands = (
int * ) CPLMalloc(
sizeof(
int ) * psWarpOptions->nBandCount );
464 psWarpOptions->panDstBands = (
int * ) CPLMalloc(
sizeof(
int ) * psWarpOptions->nBandCount );
465 for (
int i = 0; i < psWarpOptions->nBandCount; ++i )
467 psWarpOptions->panSrcBands[i] = i + 1;
468 psWarpOptions->panDstBands[i] = i + 1;
471 psWarpOptions->eResampleAlg =
static_cast<GDALResampleAlg
>( raster.
resampleMethod );
474 psWarpOptions->pfnProgress = _progress;
475 psWarpOptions->pProgressArg =
this;
478 psWarpOptions->pTransformerArg = GDALCreateGenImgProjTransformer( hSrcDS.get(), GDALGetProjectionRef( hSrcDS.get() ), hDstDS.get(), GDALGetProjectionRef( hDstDS.get() ), FALSE, 0.0, 1 );
479 psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
481 double rescaleArg[2];
486 psWarpOptions->pfnPreWarpChunkProcessor = rescalePreWarpChunkProcessor;
487 psWarpOptions->pfnPostWarpChunkProcessor = rescalePostWarpChunkProcessor;
488 psWarpOptions->pPreWarpProcessorArg = rescaleArg;
489 psWarpOptions->pPostWarpProcessorArg = rescaleArg;
491 psWarpOptions->eWorkingDataType = GDT_Float32;
495 GDALWarpOperation oOperation;
496 oOperation.Initialize( psWarpOptions.get() );
499 GDALDestroyGenImgProjTransformer( psWarpOptions->pTransformerArg );
509 void *hTransformArg = GDALCreateGenImgProjTransformer( info.
mDataset.get(), info.
mCrsWkt.toLatin1().constData(),
nullptr, destWkt.toLatin1().constData(), FALSE, 0, 1 );
510 if ( !hTransformArg )
514 double adfDstGeoTransform[6];
516 int nPixels = 0, nLines = 0;
518 eErr = GDALSuggestedWarpOutput2( info.
mDataset.get(), GDALGenImgProjTransform, hTransformArg, adfDstGeoTransform, &nPixels, &nLines, extents, 0 );
519 GDALDestroyGenImgProjTransformer( hTransformArg );
521 if ( eErr != CE_None )
524 const QSizeF cs( std::fabs( adfDstGeoTransform[1] ), std::fabs( adfDstGeoTransform[5] ) );
527 *rect =
QgsRectangle( extents[0], extents[1], extents[2], extents[3] );
531 *
gridOffset = QPointF( fmod_with_tolerance( adfDstGeoTransform[0], cs.width() ), fmod_with_tolerance( adfDstGeoTransform[3], cs.height() ) );
541 mDataset.reset( GDALOpen( layerpath.toUtf8().constData(), GA_ReadOnly ) );
551 mCrsWkt = QString::fromLatin1( GDALGetProjectionRef(
mDataset.get() ) );
578 qDebug(
"---RASTER INFO------------------" );
579 qDebug(
"wkt %s",
mCrsWkt.toLatin1().constData() );
584 qDebug(
"extent %s", r.
toString().toLatin1().constData() );
586 qDebug(
"transform" );
593 GDALRasterBandH hBand = GDALGetRasterBand(
mDataset.get(), 1 );
599 float *pafScanline = (
float * ) CPLMalloc(
sizeof(
float ) );
600 const CPLErr err = GDALRasterIO( hBand, GF_Read, px, py, 1, 1, pafScanline, 1, 1, GDT_Float32, 0, 0 );
601 const double value = err == CE_None ? pafScanline[0] : std::numeric_limits<double>::quiet_NaN();
602 CPLFree( pafScanline );
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
Takes one or more raster layers and warps (resamples) them to a common grid.
void dump() const
write contents of the object to standard error stream - for debugging
int mYSize
Computed raster grid height.
bool setParametersFromRaster(const RasterInfo &rasterInfo, const QString &customCRSWkt=QString(), QSizeF customCellSize=QSizeF(), QPointF customGridOffset=QPointF(-1, -1))
Set destination CRS, cell size and grid offset from a raster file.
QSizeF cellSize() const
Gets output cell size.
QgsRectangle clipExtent() const
Gets clipping extent (region of interest).
QPointF gridOffset() const
double mGridOffsetX
Destination grid offset - expected to be in interval <0,cellsize).
QgsRectangle alignedRasterExtent() const
Returns the expected extent of the resulting aligned raster.
bool run()
Run the alignment process.
double mClipExtent[4]
Optional clip extent: sets "requested area" which be extended to fit the raster grid.
List mRasters
List of rasters to be aligned (with their output files and other options).
bool createAndWarp(const Item &raster)
Internal function for processing of one raster (1. create output, 2. do the alignment).
QString mCrsWkt
Destination CRS - stored in well-known text (WKT) format.
void setClipExtent(double xmin, double ymin, double xmax, double ymax)
Configure clipping extent (region of interest).
int mXSize
Computed raster grid width.
int suggestedReferenceLayer() const
Returns the index of the layer which has smallest cell size (returns -1 on error).
bool checkInputParameters()
Determine destination extent from the input rasters and calculate derived values.
double mCellSizeX
Destination cell size.
double mGeoTransform[6]
Computed geo-transform.
QgsAlignRasterData::RasterItem Item
static bool suggestedWarpOutput(const RasterInfo &info, const QString &destWkt, QSizeF *cellSize=nullptr, QPointF *gridOffset=nullptr, QgsRectangle *rect=nullptr)
Determine suggested output of raster warp to a different CRS. Returns true on success.
QSize alignedRasterSize() const
Returns the expected size of the resulting aligned raster.
QString mErrorMessage
Last error message from run().
Represents a coordinate reference system (CRS).
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
A rectangle specified with double values.
Q_INVOKABLE QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
std::unique_ptr< GDALWarpOptions, GDALWarpOptionsDeleter > warp_options_unique_ptr
Scoped GDAL warp options.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
Qgis::GdalResampleAlgorithm resampleMethod
resampling method to be used
bool rescaleValues
rescaling of values according to the change of pixel size
double srcCellSizeInDestCRS
used for rescaling of values (if necessary)
QString inputFilename
filename of the source raster
QString outputFilename
filename of the newly created aligned raster (will be overwritten if exists already)
Helper struct to be sub-classed for progress reporting.
virtual bool progress(double complete)=0
Method to be overridden for progress reporting.
Utility class for gathering information about rasters.
QString crs() const
Returns the CRS in WKT format.
int mYSize
raster grid size Y
gdal::dataset_unique_ptr mDataset
handle to open GDAL dataset
void dump() const
Write contents of the object to standard error stream - for debugging.
RasterInfo(const QString &layerpath)
Construct raster info with a path to a raster file.
double mGeoTransform[6]
geotransform coefficients
QgsRectangle extent() const
Returns the extent of the raster.
QPointF gridOffset() const
Returns the grid offset.
QSizeF cellSize() const
Returns the cell size in map units.
double identify(double mx, double my)
Gets raster value at the given coordinates (from the first band).
int mXSize
raster grid size X
QPointF origin() const
Returns the origin of the raster.
int mBandCnt
number of raster's bands
QString mCrsWkt
CRS stored in WKT format.