Skip to content
Permalink
Browse files

Port clip alg to c++

Rough benchtests reveal it's about 25% faster then the python
version
  • Loading branch information
nyalldawson committed Jun 12, 2017
1 parent d89b160 commit 36ce8d15bcaaf3a4639ae4a14a92ecf7af0528cc
Showing with 178 additions and 1 deletion.
  1. +155 −1 src/core/processing/qgsnativealgorithms.cpp
  2. +23 −0 src/core/processing/qgsnativealgorithms.h
@@ -22,6 +22,7 @@
#include "qgsprocessingutils.h"
#include "qgsvectorlayer.h"
#include "qgsgeometry.h"
#include "qgsgeometryengine.h"
#include "qgswkbtypes.h"

///@cond PRIVATE
@@ -47,7 +48,7 @@ QString QgsNativeAlgorithms::id() const

QString QgsNativeAlgorithms::name() const
{
return tr( "QGIS" );
return tr( "QGIS (native c++)" );
}

bool QgsNativeAlgorithms::supportsNonFileBasedOutput() const
@@ -60,6 +61,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsCentroidAlgorithm() );
addAlgorithm( new QgsBufferAlgorithm() );
addAlgorithm( new QgsDissolveAlgorithm() );
addAlgorithm( new QgsClipAlgorithm() );
}


@@ -360,4 +362,156 @@ QVariantMap QgsDissolveAlgorithm::processAlgorithm( const QVariantMap &parameter
return outputs;
}

QgsClipAlgorithm::QgsClipAlgorithm()
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "OVERLAY" ), QObject::tr( "Clip layer" ), QList< int >() << QgsProcessingParameterDefinition::TypeVectorPolygon ) );

addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Clipped" ) ) );
addOutput( new QgsProcessingOutputVectorLayer( QStringLiteral( "OUTPUT" ), QObject::tr( "Clipped" ) ) );
}

QString QgsClipAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm clips a vector layer using the polygons of an additional polygons layer. Only the parts of the features "
"in the input layer that falls within the polygons of the clipping layer will be added to the resulting layer.\n\n"
"The attributes of the features are not modified, although properties such as area or length of the features will "
"be modified by the clipping operation. If such properties are stored as attributes, those attributes will have to "
"be manually updated." );
}

QVariantMap QgsClipAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const
{
std::unique_ptr< QgsFeatureSource > featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !featureSource )
return QVariantMap();

std::unique_ptr< QgsFeatureSource > maskSource( parameterAsSource( parameters, QStringLiteral( "OVERLAY" ), context ) );
if ( !maskSource )
return QVariantMap();

QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, featureSource->fields(), QgsWkbTypes::multiType( featureSource->wkbType() ), featureSource->sourceCrs(), dest ) );

if ( !sink )
return QVariantMap();

// first build up a list of clip geometries
QList< QgsGeometry > clipGeoms;
QgsFeatureIterator it = maskSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QList< int >() ).setDestinationCrs( featureSource->sourceCrs() ) );
QgsFeature f;
while ( it.nextFeature( f ) )
{
if ( f.hasGeometry() )
clipGeoms << f.geometry();
}

if ( clipGeoms.isEmpty() )
return QVariantMap();

// are we clipping against a single feature? if so, we can show finer progress reports
bool singleClipFeature = false;
QgsGeometry combinedClipGeom;
if ( clipGeoms.length() > 1 )
{
combinedClipGeom = QgsGeometry::unaryUnion( clipGeoms );
singleClipFeature = false;
}
else
{
combinedClipGeom = clipGeoms.at( 0 );
singleClipFeature = true;
}

// use prepared geometries for faster intersection tests
std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( combinedClipGeom.geometry() ) );
engine->prepareGeometry();

QgsFeatureIds testedFeatureIds;

int i = -1;
Q_FOREACH ( const QgsGeometry &clipGeom, clipGeoms )
{
i++;
if ( feedback->isCanceled() )
{
break;
}

QgsFeatureIterator inputIt = featureSource->getFeatures( QgsFeatureRequest().setFilterRect( clipGeom.boundingBox() ) );
QgsFeatureList inputFeatures;
QgsFeature f;
while ( inputIt.nextFeature( f ) )
inputFeatures << f;

if ( inputFeatures.isEmpty() )
continue;

double step = 0;
if ( singleClipFeature )
step = 100.0 / inputFeatures.length();

int current = 0;
Q_FOREACH ( const QgsFeature &inputFeature, inputFeatures )
{
if ( feedback->isCanceled() )
{
break;
}

if ( !inputFeature.hasGeometry() )
continue;

if ( testedFeatureIds.contains( inputFeature.id() ) )
{
// don't retest a feature we have already checked
continue;
}
testedFeatureIds.insert( inputFeature.id() );

if ( !engine->intersects( *inputFeature.geometry().geometry() ) )
continue;

QgsGeometry newGeometry;
if ( !engine->contains( *inputFeature.geometry().geometry() ) )
{
QgsGeometry currentGeometry = inputFeature.geometry();
newGeometry = combinedClipGeom.intersection( currentGeometry );
if ( newGeometry.wkbType() == QgsWkbTypes::Unknown || QgsWkbTypes::flatType( newGeometry.geometry()->wkbType() ) == QgsWkbTypes::GeometryCollection )
{
QgsGeometry intCom = inputFeature.geometry().combine( newGeometry );
QgsGeometry intSym = inputFeature.geometry().symDifference( newGeometry );
newGeometry = intCom.difference( intSym );
}
}
else
{
// clip geometry totally contains feature geometry, so no need to perform intersection
newGeometry = inputFeature.geometry();
}

QgsFeature outputFeature;
outputFeature.setGeometry( newGeometry );
outputFeature.setAttributes( inputFeature.attributes() );
sink->addFeature( outputFeature );


if ( singleClipFeature )
feedback->setProgress( current * step );
}

if ( !singleClipFeature )
{
// coarse progress report for multiple clip geometries
feedback->setProgress( 100.0 * static_cast< double >( i ) / clipGeoms.length() );
}
}

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
}


///@endcond

@@ -112,6 +112,29 @@ class QgsDissolveAlgorithm : public QgsProcessingAlgorithm

};

/**
* Native clip algorithm.
*/
class QgsClipAlgorithm : public QgsProcessingAlgorithm
{

public:

QgsClipAlgorithm();

QString name() const override { return QStringLiteral( "clip" ); }
QString displayName() const override { return QObject::tr( "Clip" ); }
virtual QStringList tags() const override { return QObject::tr( "clip,intersect,intersection,mask" ).split( ',' ); }
QString group() const override { return QObject::tr( "Vector overlay tools" ); }
QString shortHelpString() const override;

protected:

virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const override;

};

///@endcond PRIVATE

#endif // QGSNATIVEALGORITHMS_H

0 comments on commit 36ce8d1

Please sign in to comment.
You can’t perform that action at this time.