Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[processing] Don't use vector layers directly as feature sources
Instead, parameters evaluate to QgsFeatureSource, which are
used for retrieving features, feature count, crs, wkb type,
etc.

This abstracts away the actual feature source, so that
algorithms may potentially operate from non-layer
feature sources.

It also helps remove the need for specialised QgsProcessingUtils
methods like getFeatures, featureCount, and createSpatialIndex.
Instead the standard API methods using QgsFeatureSources can
be used instead.
  • Loading branch information
nyalldawson committed Jun 5, 2017
1 parent 005a08e commit b6fb41d
Show file tree
Hide file tree
Showing 16 changed files with 256 additions and 123 deletions.
11 changes: 11 additions & 0 deletions python/core/processing/qgsprocessingalgorithm.sip
Expand Up @@ -326,6 +326,17 @@ class QgsProcessingAlgorithm
:rtype: QgsFeatureSink
%End

QgsFeatureSource *parameterAsSource( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const /Factory/;
%Docstring
Evaluates the parameter with matching ``definition`` to a feature source.

Sources will either be taken from ``context``'s active project, or loaded from external
sources and stored temporarily in the ``context``.

This function creates a new object and the caller takes responsibility for deleting the returned object.
:rtype: QgsFeatureSource
%End

QgsMapLayer *parameterAsLayer( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const;
%Docstring
Evaluates the parameter with matching ``name`` to a map layer.
Expand Down
11 changes: 11 additions & 0 deletions python/core/processing/qgsprocessingparameters.sip
Expand Up @@ -322,6 +322,17 @@ class QgsProcessingParameters
:rtype: QgsFeatureSink
%End

static QgsFeatureSource *parameterAsSource( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context ) /Factory/;
%Docstring
Evaluates the parameter with matching ``definition`` to a feature source.

Sources will either be taken from ``context``'s active project, or loaded from external
sources and stored temporarily in the ``context``.

This function creates a new object and the caller takes responsibility for deleting the returned object.
:rtype: QgsFeatureSource
%End

static QgsMapLayer *parameterAsLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context );
%Docstring
Evaluates the parameter with matching ``definition`` to a map layer.
Expand Down
28 changes: 1 addition & 27 deletions python/core/processing/qgsprocessingutils.sip
Expand Up @@ -86,33 +86,6 @@ class QgsProcessingUtils
:rtype: str
%End

static QgsFeatureIterator getFeatures( QgsVectorLayer *layer, const QgsProcessingContext &context, const QgsFeatureRequest &request = QgsFeatureRequest() );
%Docstring
Returns an iterator for the features in a ``layer``, respecting
the settings from the supplied ``context``.
An optional base ``request`` can be used to optimise the returned
iterator, eg by restricting the returned attributes or geometry.
:rtype: QgsFeatureIterator
%End

static long featureCount( QgsVectorLayer *layer, const QgsProcessingContext &context );
%Docstring
Returns an approximate feature count for a ``layer``, when
the settings from the supplied ``context`` are respected. E.g. if the
context is set to only use selected features, then calling this will
return the count of selected features in the layer.
:rtype: long
%End

static QgsSpatialIndex createSpatialIndex( QgsVectorLayer *layer, const QgsProcessingContext &context );
%Docstring
Creates a spatial index for a layer, when
the settings from the supplied ``context`` are respected. E.g. if the
context is set to only use selected features, then calling this will
return an index containing only selected features in the layer.
:rtype: QgsSpatialIndex
%End

static QList< QVariant > uniqueValues( QgsVectorLayer *layer, int fieldIndex, const QgsProcessingContext &context );
%Docstring
Returns a list of unique values contained in a single field in a ``layer``, when
Expand Down Expand Up @@ -163,6 +136,7 @@ class QgsProcessingUtils




/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
8 changes: 8 additions & 0 deletions python/core/qgsspatialindex.sip
Expand Up @@ -34,6 +34,14 @@ Constructor - creates R-tree
This is much faster approach than creating an empty index and then inserting features one by one.

.. versionadded:: 2.8
%End

explicit QgsSpatialIndex( const QgsFeatureSource &source );
%Docstring
Constructor - creates R-tree and bulk loads it with features from the source.
This is much faster approach than creating an empty index and then inserting features one by one.

.. versionadded:: 3.0
%End

QgsSpatialIndex( const QgsSpatialIndex &other );
Expand Down
1 change: 1 addition & 0 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -2070,6 +2070,7 @@ Set the extent
QgsVectorLayer( const QgsVectorLayer &rhs );
};


/************************************************************************
* This file has been generated automatically from *
* *
Expand Down
10 changes: 5 additions & 5 deletions python/plugins/processing/algs/qgis/Boundary.py
Expand Up @@ -70,9 +70,9 @@ def displayName(self):
return self.tr('Boundary')

def processAlgorithm(self, parameters, context, feedback):
layer = self.parameterAsLayer(parameters, self.INPUT_LAYER, context)
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)

input_wkb = layer.wkbType()
input_wkb = source.wkbType()
if QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.LineGeometry:
output_wkb = QgsWkbTypes.MultiPoint
elif QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.PolygonGeometry:
Expand All @@ -83,10 +83,10 @@ def processAlgorithm(self, parameters, context, feedback):
output_wkb = QgsWkbTypes.addM(output_wkb)

(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context, '',
layer.fields(), output_wkb, layer.crs())
source.fields(), output_wkb, source.crs())

features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / QgsProcessingUtils.featureCount(layer, context)
features = source.getFeatures()
total = 100.0 / source.featureCount()

for current, input_feature in enumerate(features):
if feedback.isCanceled():
Expand Down
20 changes: 10 additions & 10 deletions src/core/processing/qgsnativealgorithms.cpp
Expand Up @@ -78,19 +78,19 @@ QString QgsCentroidAlgorithm::shortHelpString() const

QVariantMap QgsCentroidAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const
{
QgsVectorLayer *layer = parameterAsVectorLayer( parameters, QStringLiteral( "INPUT" ), context );
if ( !layer )
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
return QVariantMap();

QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT_LAYER" ), context, layer->fields(), QgsWkbTypes::Point, layer->crs(), dest ) );
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT_LAYER" ), context, source->fields(), QgsWkbTypes::Point, source->sourceCrs(), dest ) );

long count = QgsProcessingUtils::featureCount( layer, context );
long count = source->featureCount();
if ( count <= 0 )
return QVariantMap();

QgsFeature f;
QgsFeatureIterator it = QgsProcessingUtils::getFeatures( layer, context );
QgsFeatureIterator it = source->getFeatures();

double step = 100.0 / count;
int current = 0;
Expand Down Expand Up @@ -151,12 +151,12 @@ QString QgsBufferAlgorithm::shortHelpString() const

QVariantMap QgsBufferAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const
{
QgsVectorLayer *layer = parameterAsVectorLayer( parameters, QStringLiteral( "INPUT" ), context );
if ( !layer )
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
return QVariantMap();

QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT_LAYER" ), context, layer->fields(), QgsWkbTypes::Polygon, layer->crs(), dest ) );
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT_LAYER" ), context, source->fields(), QgsWkbTypes::Polygon, source->sourceCrs(), dest ) );

// fixed parameters
//bool dissolve = QgsProcessingParameters::parameterAsBool( parameters, QStringLiteral( "DISSOLVE" ), context );
Expand All @@ -168,12 +168,12 @@ QVariantMap QgsBufferAlgorithm::processAlgorithm( const QVariantMap &parameters,
bool dynamicBuffer = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "DISTANCE" ) );
const QgsProcessingParameterDefinition *distanceParamDef = parameterDefinition( QStringLiteral( "DISTANCE" ) );

long count = QgsProcessingUtils::featureCount( layer, context );
long count = source->featureCount();
if ( count <= 0 )
return QVariantMap();

QgsFeature f;
QgsFeatureIterator it = QgsProcessingUtils::getFeatures( layer, context );
QgsFeatureIterator it = source->getFeatures();

double step = 100.0 / count;
int current = 0;
Expand Down
5 changes: 5 additions & 0 deletions src/core/processing/qgsprocessingalgorithm.cpp
Expand Up @@ -215,6 +215,11 @@ QgsFeatureSink *QgsProcessingAlgorithm::parameterAsSink( const QVariantMap &para
return QgsProcessingParameters::parameterAsSink( parameterDefinition( name ), parameters, fields, geometryType, crs, context, destinationIdentifier );
}

QgsFeatureSource *QgsProcessingAlgorithm::parameterAsSource( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const
{
return QgsProcessingParameters::parameterAsSource( parameterDefinition( name ), parameters, context );
}

QgsMapLayer *QgsProcessingAlgorithm::parameterAsLayer( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const
{
return QgsProcessingParameters::parameterAsLayer( parameterDefinition( name ), parameters, context );
Expand Down
10 changes: 10 additions & 0 deletions src/core/processing/qgsprocessingalgorithm.h
Expand Up @@ -316,6 +316,16 @@ class CORE_EXPORT QgsProcessingAlgorithm
const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs,
QString &destinationIdentifier SIP_OUT ) const SIP_FACTORY;

/**
* Evaluates the parameter with matching \a definition to a feature source.
*
* Sources will either be taken from \a context's active project, or loaded from external
* sources and stored temporarily in the \a context.
*
* This function creates a new object and the caller takes responsibility for deleting the returned object.
*/
QgsFeatureSource *parameterAsSource( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const SIP_FACTORY;

/**
* Evaluates the parameter with matching \a name to a map layer.
*
Expand Down
27 changes: 27 additions & 0 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -18,6 +18,7 @@
#include "qgsprocessingparameters.h"
#include "qgsprocessingcontext.h"
#include "qgsprocessingutils.h"
#include "qgsvectorlayerfeatureiterator.h"

bool QgsProcessingParameters::isDynamic( const QVariantMap &parameters, const QString &name )
{
Expand Down Expand Up @@ -247,6 +248,32 @@ QgsFeatureSink *QgsProcessingParameters::parameterAsSink( const QgsProcessingPar
return sink.release();
}

QgsFeatureSource *QgsProcessingParameters::parameterAsSource( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context )
{
if ( !definition )
return nullptr;

QString layerRef = parameterAsString( definition, parameters, context );
if ( layerRef.isEmpty() )
layerRef = definition->defaultValue().toString();

if ( layerRef.isEmpty() )
return nullptr;

QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( layerRef, context ) );
if ( !vl )
return nullptr;

if ( context.flags() & QgsProcessingContext::UseSelectionIfPresent && vl->selectedFeatureCount() > 0 )
{
return new QgsProcessingFeatureSource( new QgsVectorLayerSelectedFeatureSource( vl ), context, true );
}
else
{
return new QgsProcessingFeatureSource( vl, context );
}
}

QgsMapLayer *QgsProcessingParameters::parameterAsLayer( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context )
{
if ( !definition )
Expand Down
11 changes: 11 additions & 0 deletions src/core/processing/qgsprocessingparameters.h
Expand Up @@ -29,6 +29,7 @@ class QgsProcessingContext;
class QgsRasterLayer;
class QgsVectorLayer;
class QgsFeatureSink;
class QgsFeatureSource;

/**
* \class QgsProcessingFeatureSink
Expand Down Expand Up @@ -340,6 +341,16 @@ class CORE_EXPORT QgsProcessingParameters
const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context, QString &destinationIdentifier SIP_OUT ) SIP_FACTORY;

/**
* Evaluates the parameter with matching \a definition to a feature source.
*
* Sources will either be taken from \a context's active project, or loaded from external
* sources and stored temporarily in the \a context.
*
* This function creates a new object and the caller takes responsibility for deleting the returned object.
*/
static QgsFeatureSource *parameterAsSource( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context ) SIP_FACTORY;

/**
* Evaluates the parameter with matching \a definition to a map layer.
*
Expand Down
88 changes: 52 additions & 36 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -226,42 +226,6 @@ QString QgsProcessingUtils::normalizeLayerSource( const QString &source )
return normalized.trimmed();
}

QgsFeatureIterator QgsProcessingUtils::getFeatures( QgsVectorLayer *layer, const QgsProcessingContext &context, const QgsFeatureRequest &request )
{
bool useSelection = context.flags() & QgsProcessingContext::UseSelectionIfPresent && layer->selectedFeatureCount() > 0;

QgsFeatureRequest req( request );
req.setInvalidGeometryCheck( context.invalidGeometryCheck() );
req.setInvalidGeometryCallback( context.invalidGeometryCallback() );
if ( useSelection )
{
return layer->getSelectedFeatures( req );
}
else
{
return layer->getFeatures( req );
}
}

long QgsProcessingUtils::featureCount( QgsVectorLayer *layer, const QgsProcessingContext &context )
{
bool useSelection = context.flags() & QgsProcessingContext::UseSelectionIfPresent && layer->selectedFeatureCount() > 0;
if ( useSelection )
return layer->selectedFeatureCount();
else
return layer->featureCount();
}

QgsSpatialIndex QgsProcessingUtils::createSpatialIndex( QgsVectorLayer *layer, const QgsProcessingContext &context )
{
QgsFeatureRequest request;
request.setSubsetOfAttributes( QgsAttributeList() );
bool useSelection = context.flags() & QgsProcessingContext::UseSelectionIfPresent && layer->selectedFeatureCount() > 0;
if ( useSelection )
return QgsSpatialIndex( layer->getSelectedFeatures( request ) );
else
return QgsSpatialIndex( layer->getFeatures( request ) );
}

QList<QVariant> QgsProcessingUtils::uniqueValues( QgsVectorLayer *layer, int fieldIndex, const QgsProcessingContext &context )
{
Expand Down Expand Up @@ -434,4 +398,56 @@ QgsRectangle QgsProcessingUtils::combineLayerExtents( const QList<QgsMapLayer *>
}


//
// QgsProcessingFeatureSource
//

QgsProcessingFeatureSource::QgsProcessingFeatureSource( QgsFeatureSource *originalSource, const QgsProcessingContext &context, bool ownsOriginalSource )
: mSource( originalSource )
, mOwnsSource( ownsOriginalSource )
, mInvalidGeometryCheck( context.invalidGeometryCheck() )
, mInvalidGeometryCallback( context.invalidGeometryCallback() )
{}

QgsProcessingFeatureSource::~QgsProcessingFeatureSource()
{
if ( mOwnsSource )
delete mSource;
}

QgsFeatureIterator QgsProcessingFeatureSource::getFeatures( const QgsFeatureRequest &request ) const
{
QgsFeatureRequest req( request );
req.setInvalidGeometryCheck( mInvalidGeometryCheck );
req.setInvalidGeometryCallback( mInvalidGeometryCallback );
return mSource->getFeatures( req );
}

QgsCoordinateReferenceSystem QgsProcessingFeatureSource::sourceCrs() const
{
return mSource->sourceCrs();
}

QgsFields QgsProcessingFeatureSource::fields() const
{
return mSource->fields();
}

QgsWkbTypes::Type QgsProcessingFeatureSource::wkbType() const
{
return mSource->wkbType();
}

long QgsProcessingFeatureSource::featureCount() const
{
return mSource->featureCount();
}









0 comments on commit b6fb41d

Please sign in to comment.