Skip to content

Commit

Permalink
[processing] Always report errors if features cannot be written
Browse files Browse the repository at this point in the history
to a destination

Before we silently ignored these - now algorithms will automatically
push errors to the log if a feature cannot be written to a sink (no
changes to algorithms or special handling required)
  • Loading branch information
nyalldawson committed Feb 17, 2018
1 parent 84d2443 commit 1f3ee05
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 3 deletions.
1 change: 1 addition & 0 deletions python/core/processing/qgsprocessingutils.sip.in
Expand Up @@ -283,6 +283,7 @@ Returns an expression context scope suitable for this source.




/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
47 changes: 44 additions & 3 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -388,7 +388,7 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
destination = layer->id();

// this is a factory, so we need to return a proxy
std::unique_ptr< QgsProxyFeatureSink > sink( new QgsProxyFeatureSink( layer->dataProvider() ) );
std::unique_ptr< QgsProcessingFeatureSink > sink( new QgsProcessingFeatureSink( layer->dataProvider(), destination, context ) );
context.temporaryLayerStore()->addMapLayer( layer.release() );

return sink.release();
Expand All @@ -415,7 +415,7 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
throw QgsProcessingException( QObject::tr( "Could not create layer %1: %2" ).arg( destination, writer->errorMessage() ) );
}
destination = finalFileName;
return writer.release();
return new QgsProcessingFeatureSink( writer.release(), destination, context, true );
}
else
{
Expand All @@ -434,7 +434,7 @@ QgsFeatureSink *QgsProcessingUtils::createFeatureSink( QString &destination, Qgs
destination = layer->id();

context.temporaryLayerStore()->addMapLayer( layer.release() );
return exporter.release();
return new QgsProcessingFeatureSink( exporter.release(), destination, context, true );
}
}
return nullptr;
Expand Down Expand Up @@ -750,3 +750,44 @@ QgsExpressionContextScope *QgsProcessingFeatureSource::createExpressionContextSc
}
return expressionContextScope;
}


//
// QgsProcessingFeatureSink
//
QgsProcessingFeatureSink::QgsProcessingFeatureSink( QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink )
: QgsProxyFeatureSink( originalSink )
, mContext( context )
, mSinkName( sinkName )
, mOwnsSink( ownsOriginalSink )
{}

QgsProcessingFeatureSink::~QgsProcessingFeatureSink()
{
if ( mOwnsSink )
delete destinationSink();
}

bool QgsProcessingFeatureSink::addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags )
{
bool result = QgsProxyFeatureSink::addFeature( feature, flags );
if ( !result )
mContext.feedback()->reportError( QObject::tr( "Feature could not be written to %1" ).arg( mSinkName ) );
return result;
}

bool QgsProcessingFeatureSink::addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags )
{
bool result = QgsProxyFeatureSink::addFeatures( features, flags );
if ( !result )
mContext.feedback()->reportError( QObject::tr( "%1 feature(s) could not be written to %2" ).arg( features.count() ).arg( mSinkName ) );
return result;
}

bool QgsProcessingFeatureSink::addFeatures( QgsFeatureIterator &iterator, QgsFeatureSink::Flags flags )
{
bool result = !QgsProxyFeatureSink::addFeatures( iterator, flags );
if ( !result )
mContext.feedback()->reportError( QObject::tr( "Features could not be written to %2" ).arg( mSinkName ) );
return result;
}
42 changes: 42 additions & 0 deletions src/core/processing/qgsprocessingutils.h
Expand Up @@ -336,6 +336,48 @@ class CORE_EXPORT QgsProcessingFeatureSource : public QgsFeatureSource

};

#ifndef SIP_RUN

/**
* \class QgsProcessingFeatureSink
* \ingroup core
* QgsProxyFeatureSink subclass which reports feature addition errors to a QgsProcessingContext.
* \since QGIS 3.0
* \note Not available in Python bindings.
*/
class CORE_EXPORT QgsProcessingFeatureSink : public QgsProxyFeatureSink
{
public:


/**
* Constructor for QgsProcessingFeatureSink, accepting an original feature sink \a originalSink
* and processing \a context. Any added features are added to the \a originalSink, with feature
* writing errors being reports to \a context.
*
* The \a context must exist for the lifetime of this object.
*
* The \a sinkName is used to identify the destination sink when reporting errors.
*
* Ownership of \a originalSink is dictated by \a ownsOriginalSource. If \a ownsOriginalSink is false,
* ownership is not transferred, and callers must ensure that \a originalSink exists for the lifetime of this object.
* If \a ownsOriginalSink is true, then this object will take ownership of \a originalSink.
*/
QgsProcessingFeatureSink( QgsFeatureSink *originalSink, const QString &sinkName, QgsProcessingContext &context, bool ownsOriginalSink = false );
~QgsProcessingFeatureSink();
bool addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags = nullptr ) override;
bool addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags = nullptr ) override;
bool addFeatures( QgsFeatureIterator &iterator, QgsFeatureSink::Flags flags = nullptr ) override;

private:

QgsProcessingContext &mContext;
QString mSinkName;
bool mOwnsSink = false;

};
#endif

#endif // QGSPROCESSINGUTILS_H


0 comments on commit 1f3ee05

Please sign in to comment.