Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsalgorithmextractbyexpression.cpp 3 : : --------------------- 4 : : begin : April 2017 5 : : copyright : (C) 2017 by Nyall Dawson 6 : : email : nyall dot dawson at gmail dot com 7 : : ***************************************************************************/ 8 : : 9 : : /*************************************************************************** 10 : : * * 11 : : * This program is free software; you can redistribute it and/or modify * 12 : : * it under the terms of the GNU General Public License as published by * 13 : : * the Free Software Foundation; either version 2 of the License, or * 14 : : * (at your option) any later version. * 15 : : * * 16 : : ***************************************************************************/ 17 : : 18 : : #include "qgsalgorithmextractbyexpression.h" 19 : : 20 : : ///@cond PRIVATE 21 : : 22 : 0 : QString QgsExtractByExpressionAlgorithm::name() const 23 : : { 24 : 0 : return QStringLiteral( "extractbyexpression" ); 25 : : } 26 : : 27 : 0 : QString QgsExtractByExpressionAlgorithm::displayName() const 28 : : { 29 : 0 : return QObject::tr( "Extract by expression" ); 30 : : } 31 : : 32 : 0 : QStringList QgsExtractByExpressionAlgorithm::tags() const 33 : : { 34 : 0 : return QObject::tr( "extract,filter,expression,field" ).split( ',' ); 35 : 0 : } 36 : : 37 : 0 : QString QgsExtractByExpressionAlgorithm::group() const 38 : : { 39 : 0 : return QObject::tr( "Vector selection" ); 40 : : } 41 : : 42 : 0 : QString QgsExtractByExpressionAlgorithm::groupId() const 43 : : { 44 : 0 : return QStringLiteral( "vectorselection" ); 45 : : } 46 : : 47 : 0 : void QgsExtractByExpressionAlgorithm::initAlgorithm( const QVariantMap & ) 48 : : { 49 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), 50 : 0 : QList< int >() << QgsProcessing::TypeVector ) ); 51 : 0 : addParameter( new QgsProcessingParameterExpression( QStringLiteral( "EXPRESSION" ), QObject::tr( "Expression" ), QVariant(), QStringLiteral( "INPUT" ) ) ); 52 : : 53 : 0 : addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Matching features" ) ) ); 54 : 0 : QgsProcessingParameterFeatureSink *failOutput = new QgsProcessingParameterFeatureSink( QStringLiteral( "FAIL_OUTPUT" ), QObject::tr( "Non-matching" ), 55 : 0 : QgsProcessing::TypeVectorAnyGeometry, QVariant(), true ); 56 : 0 : failOutput->setCreateByDefault( false ); 57 : 0 : addParameter( failOutput ); 58 : 0 : } 59 : : 60 : 0 : QString QgsExtractByExpressionAlgorithm::shortHelpString() const 61 : : { 62 : 0 : return QObject::tr( "This algorithm creates a new vector layer that only contains matching features from an input layer. " 63 : : "The criteria for adding features to the resulting layer is based on a QGIS expression.\n\n" 64 : : "For more information about expressions see the <a href =\"{qgisdocs}/user_manual/working_with_vector/expression.html\">user manual</a>" ); 65 : : } 66 : : 67 : 0 : QgsExtractByExpressionAlgorithm *QgsExtractByExpressionAlgorithm::createInstance() const 68 : : { 69 : 0 : return new QgsExtractByExpressionAlgorithm(); 70 : : } 71 : : 72 : 0 : QVariantMap QgsExtractByExpressionAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) 73 : : { 74 : 0 : std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); 75 : 0 : if ( !source ) 76 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); 77 : : 78 : 0 : QString expressionString = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context ); 79 : : 80 : 0 : QString matchingSinkId; 81 : 0 : std::unique_ptr< QgsFeatureSink > matchingSink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, matchingSinkId, source->fields(), 82 : 0 : source->wkbType(), source->sourceCrs() ) ); 83 : 0 : if ( !matchingSink ) 84 : 0 : throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); 85 : : 86 : 0 : QString nonMatchingSinkId; 87 : 0 : std::unique_ptr< QgsFeatureSink > nonMatchingSink( parameterAsSink( parameters, QStringLiteral( "FAIL_OUTPUT" ), context, nonMatchingSinkId, source->fields(), 88 : 0 : source->wkbType(), source->sourceCrs() ) ); 89 : : 90 : 0 : QgsExpression expression( expressionString ); 91 : 0 : if ( expression.hasParserError() ) 92 : : { 93 : 0 : throw QgsProcessingException( expression.parserErrorString() ); 94 : : } 95 : : 96 : 0 : QgsExpressionContext expressionContext = createExpressionContext( parameters, context, source.get() ); 97 : : 98 : 0 : long count = source->featureCount(); 99 : : 100 : 0 : double step = count > 0 ? 100.0 / count : 1; 101 : 0 : int current = 0; 102 : : 103 : 0 : if ( !nonMatchingSink ) 104 : : { 105 : : // not saving failing features - so only fetch good features 106 : 0 : QgsFeatureRequest req; 107 : 0 : req.setFilterExpression( expressionString ); 108 : 0 : req.setExpressionContext( expressionContext ); 109 : : 110 : 0 : QgsFeatureIterator it = source->getFeatures( req, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks ); 111 : 0 : QgsFeature f; 112 : 0 : while ( it.nextFeature( f ) ) 113 : : { 114 : 0 : if ( feedback->isCanceled() ) 115 : : { 116 : 0 : break; 117 : : } 118 : : 119 : 0 : matchingSink->addFeature( f, QgsFeatureSink::FastInsert ); 120 : : 121 : 0 : feedback->setProgress( current * step ); 122 : 0 : current++; 123 : : } 124 : 0 : } 125 : : else 126 : : { 127 : : // saving non-matching features, so we need EVERYTHING 128 : 0 : expressionContext.setFields( source->fields() ); 129 : 0 : expression.prepare( &expressionContext ); 130 : : 131 : 0 : QgsFeatureIterator it = source->getFeatures(); 132 : 0 : QgsFeature f; 133 : 0 : while ( it.nextFeature( f ) ) 134 : : { 135 : 0 : if ( feedback->isCanceled() ) 136 : : { 137 : 0 : break; 138 : : } 139 : : 140 : 0 : expressionContext.setFeature( f ); 141 : 0 : if ( expression.evaluate( &expressionContext ).toBool() ) 142 : : { 143 : 0 : matchingSink->addFeature( f, QgsFeatureSink::FastInsert ); 144 : 0 : } 145 : : else 146 : : { 147 : 0 : nonMatchingSink->addFeature( f, QgsFeatureSink::FastInsert ); 148 : : } 149 : : 150 : 0 : feedback->setProgress( current * step ); 151 : 0 : current++; 152 : : } 153 : 0 : } 154 : : 155 : : 156 : 0 : QVariantMap outputs; 157 : 0 : outputs.insert( QStringLiteral( "OUTPUT" ), matchingSinkId ); 158 : 0 : if ( nonMatchingSink ) 159 : 0 : outputs.insert( QStringLiteral( "FAIL_OUTPUT" ), nonMatchingSinkId ); 160 : 0 : return outputs; 161 : 0 : } 162 : : 163 : : ///@endcond 164 : :