Skip to content

Commit

Permalink
[processing] Add easy method to retrieve layers from context
Browse files Browse the repository at this point in the history
Allows python algorithms to call

layer = context.getMapLayer(other_alg_results['OUTPUT'] )
  • Loading branch information
nyalldawson committed Feb 16, 2018
1 parent ceee370 commit 14787ff
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 46 deletions.
14 changes: 14 additions & 0 deletions python/core/processing/qgsprocessingcontext.sip.in
Expand Up @@ -273,6 +273,18 @@ stored in this context. This includes settings like any layers loaded in the tem
and layersToLoadOnCompletion().
This is only safe to call when both this context and the other ``context`` share the same
thread() affinity, and that thread is the current thread.
%End

QgsMapLayer *getMapLayer( const QString &identifier );
%Docstring
Returns a map layer from the context with a matching ``identifier``.
This method considers layer IDs, names and sources when matching
the ``identifier`` (see :py:func:`QgsProcessingUtils.mapLayerFromString()`
for details).

Ownership is not transferred and remains with the context.

.. seealso:: :py:func:`takeResultLayer`
%End

QgsMapLayer *takeResultLayer( const QString &id ) /TransferBack/;
Expand All @@ -281,6 +293,8 @@ Takes the result map layer with matching ``id`` from the context and
transfers ownership of it back to the caller. This method can be used
to remove temporary layers which are not required for further processing
from a context.

.. seealso:: :py:func:`getMapLayer`
%End

private:
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -101,6 +101,7 @@ SET(QGIS_CORE_SRCS

processing/qgsprocessingalgorithm.cpp
processing/qgsprocessingalgrunnertask.cpp
processing/qgsprocessingcontext.cpp
processing/qgsprocessingfeedback.cpp
processing/qgsprocessingoutputs.cpp
processing/qgsprocessingparameters.cpp
Expand Down
73 changes: 73 additions & 0 deletions src/core/processing/qgsprocessingcontext.cpp
@@ -0,0 +1,73 @@
/***************************************************************************
qgsprocessingcontext.cpp
----------------------
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. *
* *
***************************************************************************/

#include "qgsprocessingcontext.h"
#include "qgsprocessingutils.h"

void QgsProcessingContext::setInvalidGeometryCheck( QgsFeatureRequest::InvalidGeometryCheck check )
{
mInvalidGeometryCheck = check;

switch ( mInvalidGeometryCheck )
{
case QgsFeatureRequest::GeometryAbortOnInvalid:
{
auto callback = []( const QgsFeature & feature )
{
throw QgsProcessingException( QObject::tr( "Feature (%1) has invalid geometry. Please fix the geometry or change the Processing setting to the \"Ignore invalid input features\" option." ).arg( feature.id() ) );
};
mInvalidGeometryCallback = callback;
break;
}

case QgsFeatureRequest::GeometrySkipInvalid:
{
auto callback = [ = ]( const QgsFeature & feature )
{
if ( mFeedback )
mFeedback->reportError( QObject::tr( "Feature (%1) has invalid geometry and has been skipped. Please fix the geometry or change the Processing setting to the \"Ignore invalid input features\" option." ).arg( feature.id() ) );
};
mInvalidGeometryCallback = callback;
break;
}

default:
break;
}
}

void QgsProcessingContext::takeResultsFrom( QgsProcessingContext &context )
{
QMap< QString, LayerDetails > loadOnCompletion = context.layersToLoadOnCompletion();
QMap< QString, LayerDetails >::const_iterator llIt = loadOnCompletion.constBegin();
for ( ; llIt != loadOnCompletion.constEnd(); ++llIt )
{
mLayersToLoadOnCompletion.insert( llIt.key(), llIt.value() );
}
context.setLayersToLoadOnCompletion( QMap< QString, LayerDetails >() );
tempLayerStore.transferLayersFromStore( context.temporaryLayerStore() );
}

QgsMapLayer *QgsProcessingContext::getMapLayer( const QString &identifier )
{
return QgsProcessingUtils::mapLayerFromString( identifier, *this, false );
}

QgsMapLayer *QgsProcessingContext::takeResultLayer( const QString &id )
{
return tempLayerStore.takeMapLayer( tempLayerStore.mapLayer( id ) );
}
63 changes: 17 additions & 46 deletions src/core/processing/qgsprocessingcontext.h
Expand Up @@ -220,37 +220,7 @@ class CORE_EXPORT QgsProcessingContext
* reset the invalidGeometryCallback() to a default implementation.
* \see invalidGeometryCheck()
*/
void setInvalidGeometryCheck( QgsFeatureRequest::InvalidGeometryCheck check )
{
mInvalidGeometryCheck = check;

switch ( mInvalidGeometryCheck )
{
case QgsFeatureRequest::GeometryAbortOnInvalid:
{
auto callback = []( const QgsFeature & feature )
{
throw QgsProcessingException( QObject::tr( "Feature (%1) has invalid geometry. Please fix the geometry or change the Processing setting to the \"Ignore invalid input features\" option." ).arg( feature.id() ) );
};
mInvalidGeometryCallback = callback;
break;
}

case QgsFeatureRequest::GeometrySkipInvalid:
{
auto callback = [ = ]( const QgsFeature & feature )
{
if ( mFeedback )
mFeedback->reportError( QObject::tr( "Feature (%1) has invalid geometry and has been skipped. Please fix the geometry or change the Processing setting to the \"Ignore invalid input features\" option." ).arg( feature.id() ) );
};
mInvalidGeometryCallback = callback;
break;
}

default:
break;
}
}
void setInvalidGeometryCheck( QgsFeatureRequest::InvalidGeometryCheck check );

/**
* Sets a callback function to use when encountering an invalid geometry and
Expand Down Expand Up @@ -374,28 +344,29 @@ class CORE_EXPORT QgsProcessingContext
* This is only safe to call when both this context and the other \a context share the same
* thread() affinity, and that thread is the current thread.
*/
void takeResultsFrom( QgsProcessingContext &context )
{
QMap< QString, LayerDetails > loadOnCompletion = context.layersToLoadOnCompletion();
QMap< QString, LayerDetails >::const_iterator llIt = loadOnCompletion.constBegin();
for ( ; llIt != loadOnCompletion.constEnd(); ++llIt )
{
mLayersToLoadOnCompletion.insert( llIt.key(), llIt.value() );
}
context.setLayersToLoadOnCompletion( QMap< QString, LayerDetails >() );
tempLayerStore.transferLayersFromStore( context.temporaryLayerStore() );
}
void takeResultsFrom( QgsProcessingContext &context );

/**
* Returns a map layer from the context with a matching \a identifier.
* This method considers layer IDs, names and sources when matching
* the \a identifier (see QgsProcessingUtils::mapLayerFromString()
* for details).
*
* Ownership is not transferred and remains with the context.
*
* \see takeResultLayer()
*/
QgsMapLayer *getMapLayer( const QString &identifier );

/**
* Takes the result map layer with matching \a id from the context and
* transfers ownership of it back to the caller. This method can be used
* to remove temporary layers which are not required for further processing
* from a context.
*
* \see getMapLayer()
*/
QgsMapLayer *takeResultLayer( const QString &id ) SIP_TRANSFERBACK
{
return tempLayerStore.takeMapLayer( tempLayerStore.mapLayer( id ) );
}
QgsMapLayer *takeResultLayer( const QString &id ) SIP_TRANSFERBACK;

private:

Expand Down
18 changes: 18 additions & 0 deletions tests/src/analysis/testqgsprocessing.cpp
Expand Up @@ -881,36 +881,53 @@ void TestQgsProcessing::mapLayerFromString()

// no project set yet
QVERIFY( ! QgsProcessingUtils::mapLayerFromString( QString(), c ) );
QVERIFY( !c.getMapLayer( QString() ) );
QVERIFY( ! QgsProcessingUtils::mapLayerFromString( QStringLiteral( "v1" ), c ) );
QVERIFY( !c.getMapLayer( QStringLiteral( "v1" ) ) );

c.setProject( &p );

// layers from current project
QVERIFY( ! QgsProcessingUtils::mapLayerFromString( QString(), c ) );
QVERIFY( !c.getMapLayer( QString() ) );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( raster1, c ), r1 );
QCOMPARE( c.getMapLayer( raster1 ), r1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( raster2, c ), r2 );
QCOMPARE( c.getMapLayer( raster2 ), r2 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "R1", c ), r1 );
QCOMPARE( c.getMapLayer( "R1" ), r1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "ar2", c ), r2 );
QCOMPARE( c.getMapLayer( "ar2" ), r2 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "V4", c ), v1 );
QCOMPARE( c.getMapLayer( "V4" ), v1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "v1", c ), v2 );
QCOMPARE( c.getMapLayer( "v1" ), v2 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( r1->id(), c ), r1 );
QCOMPARE( c.getMapLayer( r1->id() ), r1 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( v1->id(), c ), v1 );
QCOMPARE( c.getMapLayer( v1->id() ), v1 );

// check that layers in context temporary store are used
QgsVectorLayer *v5 = new QgsVectorLayer( "Polygon", "V5", "memory" );
QgsVectorLayer *v6 = new QgsVectorLayer( "Point", "v6", "memory" );
c.temporaryLayerStore()->addMapLayers( QList<QgsMapLayer *>() << v5 << v6 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "V5", c ), v5 );
QCOMPARE( c.getMapLayer( "V5" ), v5 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( "v6", c ), v6 );
QCOMPARE( c.getMapLayer( "v6" ), v6 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( v5->id(), c ), v5 );
QCOMPARE( c.getMapLayer( v5->id() ), v5 );
QCOMPARE( QgsProcessingUtils::mapLayerFromString( v6->id(), c ), v6 );
QCOMPARE( c.getMapLayer( v6->id() ), v6 );
QVERIFY( ! QgsProcessingUtils::mapLayerFromString( "aaaaa", c ) );
QVERIFY( !c.getMapLayer( "aaaa" ) );

// if specified, check that layers can be loaded
QVERIFY( ! QgsProcessingUtils::mapLayerFromString( "aaaaa", c ) );
QString newRaster = testDataDir + "requires_warped_vrt.tif";
// don't allow loading
QVERIFY( ! QgsProcessingUtils::mapLayerFromString( newRaster, c, false ) );
QVERIFY( !c.getMapLayer( newRaster ) );
// allow loading
QgsMapLayer *loadedLayer = QgsProcessingUtils::mapLayerFromString( newRaster, c, true );
QVERIFY( loadedLayer->isValid() );
Expand All @@ -920,6 +937,7 @@ void TestQgsProcessing::mapLayerFromString()

// since it's now in temporary store, should be accessible even if we deny loading new layers
QCOMPARE( QgsProcessingUtils::mapLayerFromString( newRaster, c, false ), loadedLayer );
QCOMPARE( c.getMapLayer( newRaster ), loadedLayer );
}

void TestQgsProcessing::algorithm()
Expand Down

0 comments on commit 14787ff

Please sign in to comment.