QVariantMap QgsExtractByExpressionAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !source ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); QString expressionString = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context ); QString matchingSinkId; std::unique_ptr< QgsFeatureSink > matchingSink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, matchingSinkId, source->fields(), source->wkbType(), source->sourceCrs() ) ); if ( !matchingSink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QString nonMatchingSinkId; std::unique_ptr< QgsFeatureSink > nonMatchingSink( parameterAsSink( parameters, QStringLiteral( "FAIL_OUTPUT" ), context, nonMatchingSinkId, source->fields(), source->wkbType(), source->sourceCrs() ) ); QgsExpression expression( expressionString ); if ( expression.hasParserError() ) { throw QgsProcessingException( expression.parserErrorString() ); } QgsExpressionContext expressionContext = createExpressionContext( parameters, context, source.get() ); long count = source->featureCount(); double step = count > 0 ? 100.0 / count : 1; int current = 0; if ( !nonMatchingSink ) { // not saving failing features - so only fetch good features QgsFeatureRequest req; req.setFilterExpression( expressionString ); req.setExpressionContext( expressionContext ); QgsFeatureIterator it = source->getFeatures( req, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks ); QgsFeature f; while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) { break; } matchingSink->addFeature( f, QgsFeatureSink::FastInsert ); feedback->setProgress( current * step ); current++; } } else { // saving non-matching features, so we need EVERYTHING expressionContext.setFields( source->fields() ); expression.prepare( &expressionContext ); QgsFeatureIterator it = source->getFeatures(); QgsFeature f; while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) { break; } expressionContext.setFeature( f ); if ( expression.evaluate( &expressionContext ).toBool() ) { matchingSink->addFeature( f, QgsFeatureSink::FastInsert ); } else { nonMatchingSink->addFeature( f, QgsFeatureSink::FastInsert ); } feedback->setProgress( current * step ); current++; } } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), matchingSinkId ); if ( nonMatchingSink ) outputs.insert( QStringLiteral( "FAIL_OUTPUT" ), nonMatchingSinkId ); return outputs; }
bool QgsVectorLayerRenderer::render() { if ( mGeometryType == QGis::NoGeometry || mGeometryType == QGis::UnknownGeometry ) return true; if ( !mRendererV2 ) { mErrors.append( QObject::tr( "No renderer for drawing." ) ); return false; } bool usingEffect = false; if ( mRendererV2->paintEffect() && mRendererV2->paintEffect()->enabled() ) { usingEffect = true; mRendererV2->paintEffect()->begin( mContext ); } // Per feature blending mode if ( mContext.useAdvancedEffects() && mFeatureBlendMode != QPainter::CompositionMode_SourceOver ) { // set the painter to the feature blend mode, so that features drawn // on this layer will interact and blend with each other mContext.painter()->setCompositionMode( mFeatureBlendMode ); } mRendererV2->startRender( mContext, mFields ); QString rendererFilter = mRendererV2->filter(); QgsRectangle requestExtent = mContext.extent(); mRendererV2->modifyRequestExtent( requestExtent, mContext ); QgsFeatureRequest featureRequest = QgsFeatureRequest() .setFilterRect( requestExtent ) .setSubsetOfAttributes( mAttrNames, mFields ); if ( !rendererFilter.isEmpty() ) { featureRequest.setFilterExpression( rendererFilter ); featureRequest.setExpressionContext( mContext.expressionContext() ); } // enable the simplification of the geometries (Using the current map2pixel context) before send it to renderer engine. if ( mSimplifyGeometry ) { double map2pixelTol = mSimplifyMethod.threshold(); bool validTransform = true; const QgsMapToPixel& mtp = mContext.mapToPixel(); map2pixelTol *= mtp.mapUnitsPerPixel(); const QgsCoordinateTransform* ct = mContext.coordinateTransform(); // resize the tolerance using the change of size of an 1-BBOX from the source CoordinateSystem to the target CoordinateSystem if ( ct && !(( QgsCoordinateTransform* )ct )->isShortCircuited() ) { try { QgsPoint center = mContext.extent().center(); double rectSize = ct->sourceCrs().geographicFlag() ? 0.0008983 /* ~100/(40075014/360=111319.4833) */ : 100; QgsRectangle sourceRect = QgsRectangle( center.x(), center.y(), center.x() + rectSize, center.y() + rectSize ); QgsRectangle targetRect = ct->transform( sourceRect ); QgsDebugMsg( QString( "Simplify - SourceTransformRect=%1" ).arg( sourceRect.toString( 16 ) ) ); QgsDebugMsg( QString( "Simplify - TargetTransformRect=%1" ).arg( targetRect.toString( 16 ) ) ); if ( !sourceRect.isEmpty() && sourceRect.isFinite() && !targetRect.isEmpty() && targetRect.isFinite() ) { QgsPoint minimumSrcPoint( sourceRect.xMinimum(), sourceRect.yMinimum() ); QgsPoint maximumSrcPoint( sourceRect.xMaximum(), sourceRect.yMaximum() ); QgsPoint minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() ); QgsPoint maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() ); double sourceHypothenuse = sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) ); double targetHypothenuse = sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) ); QgsDebugMsg( QString( "Simplify - SourceHypothenuse=%1" ).arg( sourceHypothenuse ) ); QgsDebugMsg( QString( "Simplify - TargetHypothenuse=%1" ).arg( targetHypothenuse ) ); if ( targetHypothenuse != 0 ) map2pixelTol *= ( sourceHypothenuse / targetHypothenuse ); } } catch ( QgsCsException &cse ) { QgsMessageLog::logMessage( QObject::tr( "Simplify transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) ); validTransform = false; } } if ( validTransform ) { QgsSimplifyMethod simplifyMethod; simplifyMethod.setMethodType( QgsSimplifyMethod::OptimizeForRendering ); simplifyMethod.setTolerance( map2pixelTol ); simplifyMethod.setForceLocalOptimization( mSimplifyMethod.forceLocalOptimization() ); featureRequest.setSimplifyMethod( simplifyMethod ); QgsVectorSimplifyMethod vectorMethod = mSimplifyMethod; mContext.setVectorSimplifyMethod( vectorMethod ); } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } QgsFeatureIterator fit = mSource->getFeatures( featureRequest ); if (( mRendererV2->capabilities() & QgsFeatureRendererV2::SymbolLevels ) && mRendererV2->usingSymbolLevels() ) drawRendererV2Levels( fit ); else drawRendererV2( fit ); if ( usingEffect ) { mRendererV2->paintEffect()->end( mContext ); } //apply layer transparency for vector layers if ( mContext.useAdvancedEffects() && mLayerTransparency != 0 ) { // a layer transparency has been set, so update the alpha for the flattened layer // by combining it with the layer transparency QColor transparentFillColor = QColor( 0, 0, 0, 255 - ( 255 * mLayerTransparency / 100 ) ); // use destination in composition mode to merge source's alpha with destination mContext.painter()->setCompositionMode( QPainter::CompositionMode_DestinationIn ); mContext.painter()->fillRect( 0, 0, mContext.painter()->device()->width(), mContext.painter()->device()->height(), transparentFillColor ); } return true; }
int QgsLayoutAtlas::updateFeatures() { mCurrentFeatureNo = -1; if ( !mCoverageLayer ) { return 0; } QgsExpressionContext expressionContext = createExpressionContext(); QString error; updateFilenameExpression( error ); // select all features with all attributes QgsFeatureRequest req; req.setExpressionContext( expressionContext ); mFilterParserError.clear(); if ( mFilterFeatures && !mFilterExpression.isEmpty() ) { QgsExpression filterExpression( mFilterExpression ); if ( filterExpression.hasParserError() ) { mFilterParserError = filterExpression.parserErrorString(); return 0; } //filter good to go req.setFilterExpression( mFilterExpression ); } QgsFeatureIterator fit = mCoverageLayer->getFeatures( req ); std::unique_ptr<QgsExpression> nameExpression; if ( !mPageNameExpression.isEmpty() ) { nameExpression = qgis::make_unique< QgsExpression >( mPageNameExpression ); if ( nameExpression->hasParserError() ) { nameExpression.reset( nullptr ); } else { nameExpression->prepare( &expressionContext ); } } // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process // We thus store the feature ids for future extraction QgsFeature feat; mFeatureIds.clear(); mFeatureKeys.clear(); std::unique_ptr<QgsExpression> sortExpression; if ( mSortFeatures && !mSortExpression.isEmpty() ) { sortExpression = qgis::make_unique< QgsExpression >( mSortExpression ); if ( sortExpression->hasParserError() ) { sortExpression.reset( nullptr ); } else { sortExpression->prepare( &expressionContext ); } } while ( fit.nextFeature( feat ) ) { expressionContext.setFeature( feat ); QString pageName; if ( nameExpression ) { QVariant result = nameExpression->evaluate( &expressionContext ); if ( nameExpression->hasEvalError() ) { QgsMessageLog::logMessage( tr( "Atlas name eval error: %1" ).arg( nameExpression->evalErrorString() ), tr( "Layout" ) ); } pageName = result.toString(); } mFeatureIds.push_back( qMakePair( feat.id(), pageName ) ); if ( sortExpression ) { QVariant result = sortExpression->evaluate( &expressionContext ); if ( sortExpression->hasEvalError() ) { QgsMessageLog::logMessage( tr( "Atlas sort eval error: %1" ).arg( sortExpression->evalErrorString() ), tr( "Layout" ) ); } mFeatureKeys.insert( feat.id(), result ); } } // sort features, if asked for if ( !mFeatureKeys.isEmpty() ) { AtlasFeatureSorter sorter( mFeatureKeys, mSortAscending ); std::sort( mFeatureIds.begin(), mFeatureIds.end(), sorter ); // clazy:exclude=detaching-member } emit numberFeaturesChanged( mFeatureIds.size() ); return mFeatureIds.size(); }
QVariant QgsAggregateCalculator::calculate( QgsAggregateCalculator::Aggregate aggregate, const QString& fieldOrExpression, QgsExpressionContext* context, bool* ok ) const { if ( ok ) *ok = false; if ( !mLayer ) return QVariant(); QScopedPointer<QgsExpression> expression; QScopedPointer<QgsExpressionContext> defaultContext; if ( !context ) { defaultContext.reset( createContext() ); context = defaultContext.data(); } int attrNum = mLayer->fieldNameIndex( fieldOrExpression ); if ( attrNum == -1 ) { Q_ASSERT( context ); context->setFields( mLayer->fields() ); // try to use expression expression.reset( new QgsExpression( fieldOrExpression ) ); if ( expression->hasParserError() || !expression->prepare( context ) ) { return QVariant(); } } QStringList lst; if ( expression.isNull() ) lst.append( fieldOrExpression ); else lst = expression->referencedColumns(); QgsFeatureRequest request = QgsFeatureRequest() .setFlags(( expression.data() && expression->needsGeometry() ) ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( lst, mLayer->fields() ); if ( !mFilterExpression.isEmpty() ) request.setFilterExpression( mFilterExpression ); if ( context ) request.setExpressionContext( *context ); //determine result type QVariant::Type resultType = QVariant::Double; if ( attrNum == -1 ) { // evaluate first feature, check result type QgsFeatureRequest testRequest( request ); testRequest.setLimit( 1 ); QgsFeature f; QgsFeatureIterator fit = mLayer->getFeatures( testRequest ); if ( !fit.nextFeature( f ) ) { //no matching features if ( ok ) *ok = true; return QVariant(); } if ( context ) context->setFeature( f ); QVariant v = expression->evaluate( context ); resultType = v.type(); } else { resultType = mLayer->fields().at( attrNum ).type(); } QgsFeatureIterator fit = mLayer->getFeatures( request ); return calculate( aggregate, fit, resultType, attrNum, expression.data(), mDelimiter, context, ok ); }