Skip to content
Permalink
Browse files

[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 b6fb41d4ee6aab21862566554fbd8676788b7bb3
@@ -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.
@@ -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.
@@ -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
@@ -163,6 +136,7 @@ class QgsProcessingUtils




/************************************************************************
* This file has been generated automatically from *
* *
@@ -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 );
@@ -2070,6 +2070,7 @@ Set the extent
QgsVectorLayer( const QgsVectorLayer &rhs );
};


/************************************************************************
* This file has been generated automatically from *
* *
@@ -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:
@@ -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():
@@ -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;
@@ -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 );
@@ -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;
@@ -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 );
@@ -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.
*
@@ -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 )
{
@@ -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 )
@@ -29,6 +29,7 @@ class QgsProcessingContext;
class QgsRasterLayer;
class QgsVectorLayer;
class QgsFeatureSink;
class QgsFeatureSource;

/**
* \class QgsProcessingFeatureSink
@@ -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.
*
@@ -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 )
{
@@ -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.
You can’t perform that action at this time.