[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:
Nyall Dawson 2017-04-06 15:17:26 +10:00
parent 25d9936df8
commit 110828feab
13 changed files with 413 additions and 79 deletions

View File

@ -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

View File

@ -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;

View 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 *
************************************************************************/

View File

@ -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
View 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

View File

@ -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 )

View File

@ -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
{

View File

@ -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 )

View File

@ -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;

View 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 );
}

View 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
View 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)

View 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()