mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
[FEATURE] Background saving of raster layers
Use the task manager framework to handle saving of raster layers in the background
This commit is contained in:
parent
25d9936df8
commit
110828feab
@ -298,6 +298,7 @@
|
||||
%Include raster/qgsrasterdataprovider.sip
|
||||
%Include raster/qgsrasterdrawer.sip
|
||||
%Include raster/qgsrasterfilewriter.sip
|
||||
%Include raster/qgsrasterfilewritertask.sip
|
||||
%Include raster/qgsrasterhistogram.sip
|
||||
%Include raster/qgsrasteridentifyresult.sip
|
||||
%Include raster/qgsrasterinterface.sip
|
||||
|
@ -49,7 +49,9 @@ class QgsRasterFileWriter
|
||||
@param crs crs to reproject to
|
||||
@param p dialog to show progress in */
|
||||
WriterError writeRaster( const QgsRasterPipe* pipe, int nCols, int nRows, const QgsRectangle& outputExtent,
|
||||
const QgsCoordinateReferenceSystem& crs, QProgressDialog* p = 0 );
|
||||
const QgsCoordinateReferenceSystem& crs, QgsRasterBlockFeedback *feedback = nullptr );
|
||||
|
||||
QString outputUrl() const;
|
||||
|
||||
void setOutputFormat( const QString& format );
|
||||
QString outputFormat() const;
|
||||
|
68
python/core/raster/qgsrasterfilewritertask.sip
Normal file
68
python/core/raster/qgsrasterfilewritertask.sip
Normal file
@ -0,0 +1,68 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/raster/qgsrasterfilewritertask.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsRasterFileWriterTask : QgsTask
|
||||
{
|
||||
%Docstring
|
||||
QgsTask task which performs a QgsRasterFileWriter layer saving operation as a background
|
||||
task. This can be used to save a raster layer out to a file without blocking the
|
||||
QGIS interface.
|
||||
\see QgsVectorFileWriterTask
|
||||
.. versionadded:: 3.0
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsrasterfilewritertask.h"
|
||||
%End
|
||||
|
||||
public:
|
||||
|
||||
QgsRasterFileWriterTask( const QgsRasterFileWriter &writer, QgsRasterPipe *pipe /Transfer/,
|
||||
int columns, int rows,
|
||||
const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs );
|
||||
%Docstring
|
||||
Constructor for QgsRasterFileWriterTask. Takes a source writer,
|
||||
columns, rows, outputExtent and destination crs.
|
||||
Ownership of the pipe is transferred to the writer task, and will
|
||||
be deleted when the task completes.
|
||||
%End
|
||||
|
||||
virtual void cancel();
|
||||
|
||||
signals:
|
||||
|
||||
void writeComplete( const QString &outputUrl );
|
||||
%Docstring
|
||||
Emitted when writing the layer is successfully completed. The outputUrl
|
||||
parameter indicates the file path for the written file(s).
|
||||
%End
|
||||
|
||||
void errorOccurred( int error );
|
||||
%Docstring
|
||||
Emitted when an error occurs which prevented the file being written (or if
|
||||
the task is canceled). The writing error will be reported.
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool run();
|
||||
virtual void finished( bool result );
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/raster/qgsrasterfilewritertask.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
|
||||
************************************************************************/
|
@ -219,6 +219,7 @@
|
||||
#include "qgsquerybuilder.h"
|
||||
#include "qgsrastercalcdialog.h"
|
||||
#include "qgsrasterfilewriter.h"
|
||||
#include "qgsrasterfilewritertask.h"
|
||||
#include "qgsrasteriterator.h"
|
||||
#include "qgsrasterlayer.h"
|
||||
#include "qgsrasterlayerproperties.h"
|
||||
@ -6453,13 +6454,6 @@ void QgisApp::saveAsRasterFile()
|
||||
fileWriter.setMaxTileHeight( d.maximumTileSizeY() );
|
||||
}
|
||||
|
||||
QProgressDialog pd( QString(), tr( "Abort..." ), 0, 0 );
|
||||
// Show the dialo immediately because cloning pipe can take some time (WCS)
|
||||
pd.setLabelText( tr( "Reading raster" ) );
|
||||
pd.setWindowTitle( tr( "Saving raster" ) );
|
||||
pd.show();
|
||||
pd.setWindowModality( Qt::WindowModal );
|
||||
|
||||
// TODO: show error dialogs
|
||||
// TODO: this code should go somewhere else, but probably not into QgsRasterFileWriter
|
||||
// clone pipe/provider is not really necessary, ready for threads
|
||||
@ -6525,35 +6519,51 @@ void QgisApp::saveAsRasterFile()
|
||||
fileWriter.setPyramidsFormat( d.pyramidsFormat() );
|
||||
fileWriter.setPyramidsConfigOptions( d.pyramidsConfigOptions() );
|
||||
|
||||
QgsRasterFileWriter::WriterError err = fileWriter.writeRaster( pipe.get(), d.nColumns(), d.nRows(), d.outputRectangle(), d.outputCrs(), &pd );
|
||||
if ( err != QgsRasterFileWriter::NoError )
|
||||
{
|
||||
QMessageBox::warning( this, tr( "Error" ),
|
||||
tr( "Cannot write raster error code: %1" ).arg( err ),
|
||||
QMessageBox::Ok );
|
||||
bool tileMode = d.tileMode();
|
||||
bool addToCanvas = d.addToCanvas();
|
||||
QPointer< QgsRasterLayer > rlWeakPointer( rasterLayer );
|
||||
|
||||
}
|
||||
else
|
||||
QgsRasterFileWriterTask *writerTask = new QgsRasterFileWriterTask( fileWriter, pipe.release(), d.nColumns(), d.nRows(),
|
||||
d.outputRectangle(), d.outputCrs() );
|
||||
|
||||
// when writer is successful:
|
||||
|
||||
connect( writerTask, &QgsRasterFileWriterTask::writeComplete, this, [this, tileMode, addToCanvas, rlWeakPointer ]( const QString & newFilename )
|
||||
{
|
||||
QString fileName( d.outputFileName() );
|
||||
if ( d.tileMode() )
|
||||
QString fileName = newFilename;
|
||||
if ( tileMode )
|
||||
{
|
||||
QFileInfo outputInfo( fileName );
|
||||
fileName = QStringLiteral( "%1/%2.vrt" ).arg( fileName, outputInfo.fileName() );
|
||||
}
|
||||
|
||||
if ( d.addToCanvas() )
|
||||
if ( addToCanvas )
|
||||
{
|
||||
addRasterLayers( QStringList( fileName ) );
|
||||
}
|
||||
if ( rlWeakPointer )
|
||||
emit layerSavedAs( rlWeakPointer, fileName );
|
||||
|
||||
emit layerSavedAs( rasterLayer, fileName );
|
||||
messageBar()->pushMessage( tr( "Saving done" ),
|
||||
tr( "Export to raster file has been completed" ),
|
||||
QgsMessageBar::INFO, messageTimeout() );
|
||||
}
|
||||
} );
|
||||
|
||||
// when an error occurs:
|
||||
connect( writerTask, &QgsRasterFileWriterTask::errorOccurred, this, [ = ]( int error )
|
||||
{
|
||||
if ( error != QgsRasterFileWriter::WriteCanceled )
|
||||
{
|
||||
QMessageBox::warning( this, tr( "Error" ),
|
||||
tr( "Cannot write raster error code: %1" ).arg( error ),
|
||||
QMessageBox::Ok );
|
||||
}
|
||||
} );
|
||||
|
||||
QgsApplication::taskManager()->addTask( writerTask );
|
||||
}
|
||||
|
||||
|
||||
void QgisApp::saveAsFile()
|
||||
{
|
||||
QgsMapLayer *layer = activeLayer();
|
||||
|
2
src/core/CMakeLists.txt
Normal file → Executable file
2
src/core/CMakeLists.txt
Normal file → Executable file
@ -336,6 +336,7 @@ SET(QGIS_CORE_SRCS
|
||||
raster/qgsrasterblock.cpp
|
||||
raster/qgsrasterchecker.cpp
|
||||
raster/qgsrasterdataprovider.cpp
|
||||
raster/qgsrasterfilewritertask.cpp
|
||||
raster/qgsrasteridentifyresult.cpp
|
||||
raster/qgsrasterinterface.cpp
|
||||
raster/qgsrasteriterator.cpp
|
||||
@ -597,6 +598,7 @@ SET(QGIS_CORE_MOC_HDRS
|
||||
processing/qgsprocessingfeedback.h
|
||||
processing/qgsprocessingregistry.h
|
||||
|
||||
raster/qgsrasterfilewritertask.h
|
||||
raster/qgsrasterlayer.h
|
||||
raster/qgsrasterdataprovider.h
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
|
||||
QgsVectorFileWriterTask::QgsVectorFileWriterTask( QgsVectorLayer *layer, const QString &fileName, const QgsVectorFileWriter::SaveVectorOptions &options )
|
||||
: QgsTask( tr( "Saving %1 " ).arg( fileName ), QgsTask::CanCancel )
|
||||
: QgsTask( tr( "Saving %1" ).arg( fileName ), QgsTask::CanCancel )
|
||||
, mLayer( layer )
|
||||
, mDestFileName( fileName )
|
||||
, mOptions( options )
|
||||
|
@ -30,6 +30,7 @@
|
||||
* task. This can be used to save a vector layer out to a file without blocking the
|
||||
* QGIS interface.
|
||||
* \since QGIS 3.0
|
||||
* \see QgsRasterFileWriterTask
|
||||
*/
|
||||
class CORE_EXPORT QgsVectorFileWriterTask : public QgsTask
|
||||
{
|
||||
|
@ -51,7 +51,6 @@ QgsRasterFileWriter::QgsRasterFileWriter( const QString &outputUrl )
|
||||
, mMaxTileHeight( 500 )
|
||||
, mBuildPyramidsFlag( QgsRaster::PyramidsFlagNo )
|
||||
, mPyramidsFormat( QgsRaster::PyramidsGTiff )
|
||||
, mProgressDialog( nullptr )
|
||||
, mPipe( nullptr )
|
||||
, mInput( nullptr )
|
||||
{
|
||||
@ -67,7 +66,6 @@ QgsRasterFileWriter::QgsRasterFileWriter()
|
||||
, mMaxTileHeight( 500 )
|
||||
, mBuildPyramidsFlag( QgsRaster::PyramidsFlagNo )
|
||||
, mPyramidsFormat( QgsRaster::PyramidsGTiff )
|
||||
, mProgressDialog( nullptr )
|
||||
, mPipe( nullptr )
|
||||
, mInput( nullptr )
|
||||
{
|
||||
@ -75,7 +73,7 @@ QgsRasterFileWriter::QgsRasterFileWriter()
|
||||
}
|
||||
|
||||
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( const QgsRasterPipe *pipe, int nCols, int nRows, const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs, QProgressDialog *progressDialog )
|
||||
const QgsCoordinateReferenceSystem &crs, QgsRasterBlockFeedback *feedback )
|
||||
{
|
||||
QgsDebugMsgLevel( "Entered", 4 );
|
||||
|
||||
@ -114,7 +112,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( const QgsRast
|
||||
QgsDebugMsgLevel( QString( "srcInput = %1" ).arg( typeid( srcInput ).name() ), 4 );
|
||||
#endif
|
||||
|
||||
mProgressDialog = progressDialog;
|
||||
mFeedback = feedback;
|
||||
|
||||
QgsRasterIterator iter( pipe->last() );
|
||||
|
||||
@ -135,20 +133,18 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( const QgsRast
|
||||
|
||||
if ( mMode == Image )
|
||||
{
|
||||
WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, crs, progressDialog );
|
||||
mProgressDialog = nullptr;
|
||||
WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, crs, feedback );
|
||||
return e;
|
||||
}
|
||||
else
|
||||
{
|
||||
mProgressDialog = nullptr;
|
||||
WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, crs, progressDialog );
|
||||
WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, crs, feedback );
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe *pipe, QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs, QProgressDialog *progressDialog )
|
||||
const QgsCoordinateReferenceSystem &crs, QgsRasterBlockFeedback *feedback )
|
||||
{
|
||||
QgsDebugMsgLevel( "Entered", 4 );
|
||||
if ( !iter )
|
||||
@ -291,7 +287,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const Qgs
|
||||
// initOutput() returns 0 in tile mode!
|
||||
destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList );
|
||||
|
||||
WriterError error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
|
||||
WriterError error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, feedback );
|
||||
|
||||
if ( error == NoDataConflict )
|
||||
{
|
||||
@ -319,7 +315,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const Qgs
|
||||
|
||||
// Try again
|
||||
destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList );
|
||||
error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
|
||||
error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, feedback );
|
||||
}
|
||||
|
||||
if ( destProvider )
|
||||
@ -328,17 +324,16 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const Qgs
|
||||
return error;
|
||||
}
|
||||
|
||||
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
|
||||
const QgsRasterPipe *pipe,
|
||||
QgsRasterIterator *iter,
|
||||
int nCols, int nRows,
|
||||
const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs,
|
||||
Qgis::DataType destDataType,
|
||||
const QList<bool> &destHasNoDataValueList,
|
||||
const QList<double> &destNoDataValueList,
|
||||
QgsRasterDataProvider *destProvider,
|
||||
QProgressDialog *progressDialog )
|
||||
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe *pipe,
|
||||
QgsRasterIterator *iter,
|
||||
int nCols, int nRows,
|
||||
const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs,
|
||||
Qgis::DataType destDataType,
|
||||
const QList<bool> &destHasNoDataValueList,
|
||||
const QList<double> &destNoDataValueList,
|
||||
QgsRasterDataProvider *destProvider,
|
||||
QgsRasterBlockFeedback *feedback )
|
||||
{
|
||||
Q_UNUSED( pipe );
|
||||
Q_UNUSED( destHasNoDataValueList );
|
||||
@ -369,14 +364,11 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
|
||||
|
||||
int nParts = 0;
|
||||
int fileIndex = 0;
|
||||
if ( progressDialog )
|
||||
if ( feedback )
|
||||
{
|
||||
int nPartsX = nCols / iter->maximumTileWidth() + 1;
|
||||
int nPartsY = nRows / iter->maximumTileHeight() + 1;
|
||||
nParts = nPartsX * nPartsY;
|
||||
progressDialog->setMaximum( nParts );
|
||||
progressDialog->show();
|
||||
progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
|
||||
}
|
||||
|
||||
// hmm why is there a for(;;) here ..
|
||||
@ -411,12 +403,10 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
|
||||
// TODO: verify if NoDataConflict happened, to do that we need the whole pipe or nuller interface
|
||||
}
|
||||
|
||||
if ( progressDialog && fileIndex < ( nParts - 1 ) )
|
||||
if ( feedback && fileIndex < ( nParts - 1 ) )
|
||||
{
|
||||
progressDialog->setValue( fileIndex + 1 );
|
||||
progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
|
||||
if ( progressDialog->wasCanceled() )
|
||||
feedback->setProgress( 100.0 * fileIndex / static_cast< double >( nParts ) );
|
||||
if ( feedback->isCanceled() )
|
||||
{
|
||||
for ( int i = 0; i < nBands; ++i )
|
||||
{
|
||||
@ -479,11 +469,11 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
|
||||
}
|
||||
|
||||
QgsDebugMsgLevel( "Done", 4 );
|
||||
return NoError;
|
||||
return ( feedback && feedback->isCanceled() ) ? WriteCanceled : NoError;
|
||||
}
|
||||
|
||||
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs, QProgressDialog *progressDialog )
|
||||
const QgsCoordinateReferenceSystem &crs, QgsRasterBlockFeedback *feedback )
|
||||
{
|
||||
QgsDebugMsgLevel( "Entered", 4 );
|
||||
if ( !iter )
|
||||
@ -520,17 +510,14 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRaste
|
||||
|
||||
destProvider = initOutput( nCols, nRows, crs, geoTransform, 4, Qgis::Byte );
|
||||
|
||||
iter->startRasterRead( 1, nCols, nRows, outputExtent );
|
||||
iter->startRasterRead( 1, nCols, nRows, outputExtent, feedback );
|
||||
|
||||
int nParts = 0;
|
||||
if ( progressDialog )
|
||||
if ( feedback )
|
||||
{
|
||||
int nPartsX = nCols / iter->maximumTileWidth() + 1;
|
||||
int nPartsY = nRows / iter->maximumTileHeight() + 1;
|
||||
nParts = nPartsX * nPartsY;
|
||||
progressDialog->setMaximum( nParts );
|
||||
progressDialog->show();
|
||||
progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
|
||||
}
|
||||
|
||||
QgsRasterBlock *inputBlock = nullptr;
|
||||
@ -541,12 +528,10 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRaste
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( progressDialog && fileIndex < ( nParts - 1 ) )
|
||||
if ( feedback && fileIndex < ( nParts - 1 ) )
|
||||
{
|
||||
progressDialog->setValue( fileIndex + 1 );
|
||||
progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
|
||||
QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
|
||||
if ( progressDialog->wasCanceled() )
|
||||
feedback->setProgress( 100.0 * fileIndex / static_cast< double >( nParts ) );
|
||||
if ( feedback->isCanceled() )
|
||||
{
|
||||
delete inputBlock;
|
||||
break;
|
||||
@ -626,9 +611,9 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRaste
|
||||
qgsFree( blueData );
|
||||
qgsFree( alphaData );
|
||||
|
||||
if ( progressDialog )
|
||||
if ( feedback )
|
||||
{
|
||||
progressDialog->setValue( progressDialog->maximum() );
|
||||
feedback->setProgress( 100.0 );
|
||||
}
|
||||
|
||||
if ( mTiledMode )
|
||||
@ -647,7 +632,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRaste
|
||||
buildPyramids( mOutputUrl );
|
||||
}
|
||||
}
|
||||
return NoError;
|
||||
return ( feedback && feedback->isCanceled() ) ? WriteCanceled : NoError;
|
||||
}
|
||||
|
||||
void QgsRasterFileWriter::addToVRT( const QString &filename, int band, int xSize, int ySize, int xOffset, int yOffset )
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
#include "qgsraster.h"
|
||||
|
||||
class QProgressDialog;
|
||||
class QgsRasterBlockFeedback;
|
||||
class QgsRasterIterator;
|
||||
class QgsRasterPipe;
|
||||
class QgsRectangle;
|
||||
@ -39,8 +39,8 @@ class CORE_EXPORT QgsRasterFileWriter
|
||||
public:
|
||||
enum Mode
|
||||
{
|
||||
Raw = 0, // Raw data
|
||||
Image = 1 // Rendered image
|
||||
Raw = 0, //!< Raw data
|
||||
Image = 1 //!< Rendered image
|
||||
};
|
||||
enum WriterError
|
||||
{
|
||||
@ -49,8 +49,8 @@ class CORE_EXPORT QgsRasterFileWriter
|
||||
DestProviderError = 2,
|
||||
CreateDatasourceError = 3,
|
||||
WriteError = 4,
|
||||
// Internal error if a value used for 'no data' was found in input
|
||||
NoDataConflict = 5
|
||||
NoDataConflict = 5, //!< Internal error if a value used for 'no data' was found in input
|
||||
WriteCanceled = 6, //!< Writing was manually canceled
|
||||
};
|
||||
|
||||
QgsRasterFileWriter( const QString &outputUrl );
|
||||
@ -73,9 +73,16 @@ class CORE_EXPORT QgsRasterFileWriter
|
||||
\param nRows number of output rows (or -1 to automatically calculate row number to have square pixels)
|
||||
\param outputExtent extent to output
|
||||
\param crs crs to reproject to
|
||||
\param p dialog to show progress in */
|
||||
\param feedback optional feedback object for progress reports
|
||||
*/
|
||||
WriterError writeRaster( const QgsRasterPipe *pipe, int nCols, int nRows, const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs, QProgressDialog *p = nullptr );
|
||||
const QgsCoordinateReferenceSystem &crs, QgsRasterBlockFeedback *feedback = nullptr );
|
||||
|
||||
/**
|
||||
* Returns the output URL for the raster.
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
QString outputUrl() const { return mOutputUrl; }
|
||||
|
||||
void setOutputFormat( const QString &format ) { mOutputFormat = format; }
|
||||
QString outputFormat() const { return mOutputFormat; }
|
||||
@ -113,7 +120,7 @@ class CORE_EXPORT QgsRasterFileWriter
|
||||
private:
|
||||
QgsRasterFileWriter(); //forbidden
|
||||
WriterError writeDataRaster( const QgsRasterPipe *pipe, QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs, QProgressDialog *progressDialog = nullptr );
|
||||
const QgsCoordinateReferenceSystem &crs, QgsRasterBlockFeedback *feedback = nullptr );
|
||||
|
||||
// Helper method used by previous one
|
||||
WriterError writeDataRaster( const QgsRasterPipe *pipe,
|
||||
@ -125,10 +132,11 @@ class CORE_EXPORT QgsRasterFileWriter
|
||||
const QList<bool> &destHasNoDataValueList,
|
||||
const QList<double> &destNoDataValueList,
|
||||
QgsRasterDataProvider *destProvider,
|
||||
QProgressDialog *progressDialog );
|
||||
QgsRasterBlockFeedback *feedback = nullptr );
|
||||
|
||||
WriterError writeImageRaster( QgsRasterIterator *iter, int nCols, int nRows, const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs, QProgressDialog *progressDialog = nullptr );
|
||||
const QgsCoordinateReferenceSystem &crs,
|
||||
QgsRasterBlockFeedback *feedback = nullptr );
|
||||
|
||||
/** \brief Initialize vrt member variables
|
||||
* \param xSize width of vrt
|
||||
@ -194,7 +202,7 @@ class CORE_EXPORT QgsRasterFileWriter
|
||||
QDomDocument mVRTDocument;
|
||||
QList<QDomElement> mVRTBands;
|
||||
|
||||
QProgressDialog *mProgressDialog = nullptr;
|
||||
QgsRasterBlockFeedback *mFeedback = nullptr;
|
||||
|
||||
const QgsRasterPipe *mPipe = nullptr;
|
||||
const QgsRasterInterface *mInput = nullptr;
|
||||
|
61
src/core/raster/qgsrasterfilewritertask.cpp
Normal file
61
src/core/raster/qgsrasterfilewritertask.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
/***************************************************************************
|
||||
qgsrasterfilewritertask.cpp
|
||||
---------------------------
|
||||
begin : Apr 2017
|
||||
copyright : (C) 2017 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsrasterfilewritertask.h"
|
||||
#include "qgsrasterinterface.h"
|
||||
|
||||
QgsRasterFileWriterTask::QgsRasterFileWriterTask( const QgsRasterFileWriter &writer, QgsRasterPipe *pipe,
|
||||
int columns, int rows,
|
||||
const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs )
|
||||
: QgsTask( tr( "Saving %1" ).arg( writer.outputUrl() ), QgsTask::CanCancel )
|
||||
, mWriter( writer )
|
||||
, mRows( rows )
|
||||
, mColumns( columns )
|
||||
, mExtent( outputExtent )
|
||||
, mCrs( crs )
|
||||
, mPipe( pipe )
|
||||
, mFeedback( new QgsRasterBlockFeedback() )
|
||||
{}
|
||||
|
||||
void QgsRasterFileWriterTask::cancel()
|
||||
{
|
||||
mFeedback->cancel();
|
||||
QgsTask::cancel();
|
||||
}
|
||||
|
||||
bool QgsRasterFileWriterTask::run()
|
||||
{
|
||||
if ( !mPipe )
|
||||
return false;
|
||||
|
||||
connect( mFeedback.get(), &QgsRasterBlockFeedback::progressChanged, this, &QgsRasterFileWriterTask::setProgress );
|
||||
|
||||
mError = mWriter.writeRaster( mPipe.get(), mColumns, mRows, mExtent, mCrs, mFeedback.get() );
|
||||
|
||||
return mError == QgsRasterFileWriter::NoError;
|
||||
}
|
||||
|
||||
void QgsRasterFileWriterTask::finished( bool result )
|
||||
{
|
||||
if ( result )
|
||||
emit writeComplete( mWriter.outputUrl() );
|
||||
else
|
||||
emit errorOccurred( mError );
|
||||
}
|
||||
|
||||
|
91
src/core/raster/qgsrasterfilewritertask.h
Normal file
91
src/core/raster/qgsrasterfilewritertask.h
Normal file
@ -0,0 +1,91 @@
|
||||
/***************************************************************************
|
||||
qgsrasterfilewritertask.h
|
||||
-------------------------
|
||||
begin : April 2017
|
||||
copyright : (C) 2017 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSRASTERFILEWRITERTASK_H
|
||||
#define QGSRASTERFILEWRITERTASK_H
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgstaskmanager.h"
|
||||
#include "qgsrasterfilewriter.h"
|
||||
#include "qgsrasterinterface.h"
|
||||
#include "qgsrasterpipe.h"
|
||||
|
||||
/**
|
||||
* \class QgsRasterFileWriterTask
|
||||
* \ingroup core
|
||||
* QgsTask task which performs a QgsRasterFileWriter layer saving operation as a background
|
||||
* task. This can be used to save a raster layer out to a file without blocking the
|
||||
* QGIS interface.
|
||||
* \see QgsVectorFileWriterTask
|
||||
* \since QGIS 3.0
|
||||
*/
|
||||
class CORE_EXPORT QgsRasterFileWriterTask : public QgsTask
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsRasterFileWriterTask. Takes a source \a writer,
|
||||
* \a columns, \a rows, \a outputExtent and destination \a crs.
|
||||
* Ownership of the \a pipe is transferred to the writer task, and will
|
||||
* be deleted when the task completes.
|
||||
*/
|
||||
QgsRasterFileWriterTask( const QgsRasterFileWriter &writer, QgsRasterPipe *pipe SIP_TRANSFER,
|
||||
int columns, int rows,
|
||||
const QgsRectangle &outputExtent,
|
||||
const QgsCoordinateReferenceSystem &crs );
|
||||
|
||||
virtual void cancel() override;
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
* Emitted when writing the layer is successfully completed. The \a outputUrl
|
||||
* parameter indicates the file path for the written file(s).
|
||||
*/
|
||||
void writeComplete( const QString &outputUrl );
|
||||
|
||||
/**
|
||||
* Emitted when an error occurs which prevented the file being written (or if
|
||||
* the task is canceled). The writing \a error will be reported.
|
||||
*/
|
||||
void errorOccurred( int error );
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool run() override;
|
||||
virtual void finished( bool result ) override;
|
||||
|
||||
private:
|
||||
|
||||
QgsRasterFileWriter mWriter;
|
||||
int mRows = 0;
|
||||
int mColumns = 0;
|
||||
QgsRectangle mExtent;
|
||||
QgsCoordinateReferenceSystem mCrs;
|
||||
std::unique_ptr< QgsRasterPipe > mPipe;
|
||||
|
||||
QString mDestFileName;
|
||||
|
||||
std::unique_ptr< QgsRasterBlockFeedback > mFeedback;
|
||||
|
||||
QgsRasterFileWriter::WriterError mError = QgsRasterFileWriter::NoError;
|
||||
|
||||
};
|
||||
|
||||
#endif //QGSRASTERFILEWRITERTASK_H
|
1
tests/src/python/CMakeLists.txt
Normal file → Executable file
1
tests/src/python/CMakeLists.txt
Normal file → Executable file
@ -97,6 +97,7 @@ ADD_PYTHON_TEST(PyQgsPointDisplacementRenderer test_qgspointdisplacementrenderer
|
||||
ADD_PYTHON_TEST(PyQgsProjectionSelectionWidgets test_qgsprojectionselectionwidgets.py)
|
||||
ADD_PYTHON_TEST(PyQgsRangeWidgets test_qgsrangewidgets.py)
|
||||
ADD_PYTHON_TEST(PyQgsRasterFileWriter test_qgsrasterfilewriter.py)
|
||||
ADD_PYTHON_TEST(PyQgsRasterFileWriterTask test_qgsrasterfilewritertask.py)
|
||||
ADD_PYTHON_TEST(PyQgsRasterLayer test_qgsrasterlayer.py)
|
||||
ADD_PYTHON_TEST(PyQgsRasterColorRampShader test_qgsrastercolorrampshader.py)
|
||||
ADD_PYTHON_TEST(PyQgsRectangle test_qgsrectangle.py)
|
||||
|
104
tests/src/python/test_qgsrasterfilewritertask.py
Executable file
104
tests/src/python/test_qgsrasterfilewritertask.py
Executable file
@ -0,0 +1,104 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsRasterFileWriterTask.
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
__author__ = 'Nyall Dawson'
|
||||
__date__ = '12/02/2017'
|
||||
__copyright__ = 'Copyright 2017, The QGIS Project'
|
||||
# This will get replaced with a git SHA1 when you do a git archive
|
||||
__revision__ = '$Format:%H$'
|
||||
|
||||
import qgis # NOQA
|
||||
import os
|
||||
|
||||
from qgis.core import (
|
||||
QgsApplication,
|
||||
QgsRasterLayer,
|
||||
QgsRasterPipe,
|
||||
QgsRasterFileWriter,
|
||||
QgsRasterFileWriterTask
|
||||
)
|
||||
from qgis.PyQt.QtCore import QCoreApplication, QDir
|
||||
from qgis.testing import start_app, unittest
|
||||
from utilities import unitTestDataPath
|
||||
|
||||
start_app()
|
||||
|
||||
|
||||
def create_temp_filename(base_file):
|
||||
return os.path.join(str(QDir.tempPath()), base_file)
|
||||
|
||||
|
||||
class TestQgsRasterFileWriterTask(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.success = False
|
||||
self.fail = False
|
||||
|
||||
def onSuccess(self):
|
||||
self.success = True
|
||||
|
||||
def onFail(self):
|
||||
self.fail = True
|
||||
|
||||
def testSuccess(self):
|
||||
"""test successfully writing a layer"""
|
||||
path = os.path.join(unitTestDataPath(), 'raster', 'with_color_table.tif')
|
||||
raster_layer = QgsRasterLayer(path, "test")
|
||||
self.assertTrue(raster_layer.isValid())
|
||||
|
||||
pipe = QgsRasterPipe()
|
||||
self.assertTrue(pipe.set(raster_layer.dataProvider().clone()))
|
||||
|
||||
tmp = create_temp_filename('success.tif')
|
||||
writer = QgsRasterFileWriter(tmp)
|
||||
|
||||
task = QgsRasterFileWriterTask(writer, pipe, 100, 100, raster_layer.extent(), raster_layer.crs())
|
||||
|
||||
task.writeComplete.connect(self.onSuccess)
|
||||
task.errorOccurred.connect(self.onFail)
|
||||
|
||||
QgsApplication.taskManager().addTask(task)
|
||||
while not self.success and not self.fail:
|
||||
QCoreApplication.processEvents()
|
||||
|
||||
self.assertTrue(self.success)
|
||||
self.assertFalse(self.fail)
|
||||
self.assertTrue(os.path.exists(tmp))
|
||||
|
||||
def testLayerRemovalBeforeRun(self):
|
||||
"""test behavior when layer is removed before task begins"""
|
||||
path = os.path.join(unitTestDataPath(), 'raster', 'with_color_table.tif')
|
||||
raster_layer = QgsRasterLayer(path, "test")
|
||||
self.assertTrue(raster_layer.isValid())
|
||||
|
||||
pipe = QgsRasterPipe()
|
||||
self.assertTrue(pipe.set(raster_layer.dataProvider().clone()))
|
||||
|
||||
tmp = create_temp_filename('remove_layer.tif')
|
||||
writer = QgsRasterFileWriter(tmp)
|
||||
|
||||
task = QgsRasterFileWriterTask(writer, pipe, 100, 100, raster_layer.extent(), raster_layer.crs())
|
||||
|
||||
task.writeComplete.connect(self.onSuccess)
|
||||
task.errorOccurred.connect(self.onFail)
|
||||
|
||||
# remove layer
|
||||
raster_layer = None
|
||||
|
||||
QgsApplication.taskManager().addTask(task)
|
||||
while not self.success and not self.fail:
|
||||
QCoreApplication.processEvents()
|
||||
|
||||
# in this case will still get a positive result - since the pipe is cloned before the task
|
||||
# begins the task is no longer dependent on the original layer
|
||||
self.assertTrue(self.success)
|
||||
self.assertFalse(self.fail)
|
||||
self.assertTrue(os.path.exists(tmp))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user