bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext &context, QSet<QString> &attributeNames ) { QgsDiagramLayerSettings &s2 = mSettings; const QgsMapSettings &mapSettings = mEngine->mapSettings(); if ( context.coordinateTransform().isValid() ) // this is context for layer rendering - use its CT as it includes correct datum transform s2.setCoordinateTransform( context.coordinateTransform() ); else { // otherwise fall back to creating our own CT - this one may not have the correct datum transform! Q_NOWARN_DEPRECATED_PUSH s2.setCoordinateTransform( QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs() ) ); Q_NOWARN_DEPRECATED_POP } s2.setRenderer( mDiagRenderer ); bool result = s2.prepare( context.expressionContext() ); //add attributes needed by the diagram renderer attributeNames.unite( s2.referencedFields( context.expressionContext() ) ); return result; }
void QgsCategorizedSymbolRendererV2::startRender( QgsRenderContext& context, const QgsFields& fields ) { mCounting = context.rendererScale() == 0.0; // make sure that the hash table is up to date rebuildHash(); // find out classification attribute index from name mAttrNum = fields.fieldNameIndex( mAttrName ); if ( mAttrNum == -1 ) { mExpression.reset( new QgsExpression( mAttrName ) ); mExpression->prepare( &context.expressionContext() ); } QgsCategoryList::iterator it = mCategories.begin(); for ( ; it != mCategories.end(); ++it ) { it->symbol()->startRender( context, &fields ); if ( mRotation.data() || mSizeScale.data() ) { QgsSymbolV2* tempSymbol = it->symbol()->clone(); tempSymbol->setRenderHints(( mRotation.data() ? QgsSymbolV2::DataDefinedRotation : 0 ) | ( mSizeScale.data() ? QgsSymbolV2::DataDefinedSizeScale : 0 ) ); tempSymbol->startRender( context, &fields ); mTempSymbols[ it->symbol()] = tempSymbol; } } return; }
QSizeF QgsTextDiagram::diagramSize( const QgsFeature& feature, const QgsRenderContext& c, const QgsDiagramSettings& s, const QgsDiagramInterpolationSettings& is ) { QgsExpressionContext expressionContext = c.expressionContext(); expressionContext.setFeature( feature ); if ( feature.fields() ) expressionContext.setFields( *feature.fields() ); QVariant attrVal; if ( is.classificationAttributeIsExpression ) { QgsExpression* expression = getExpression( is.classificationAttributeExpression, expressionContext ); attrVal = expression->evaluate( &expressionContext ); } else { attrVal = feature.attributes().at( is.classificationAttribute ); } bool ok = false; double val = attrVal.toDouble( &ok ); if ( !ok ) { return QSizeF(); //zero size if attribute is missing } return sizeForValue( val, s, is ); }
bool QgsVectorLayerLabelProvider::prepare( const QgsRenderContext &context, QSet<QString> &attributeNames ) { QgsPalLayerSettings &lyr = mSettings; const QgsMapSettings &mapSettings = mEngine->mapSettings(); QgsDebugMsgLevel( "PREPARE LAYER " + mLayerId, 4 ); if ( lyr.drawLabels ) { if ( lyr.fieldName.isEmpty() ) { return false; } if ( lyr.isExpression ) { QgsExpression exp( lyr.fieldName ); if ( exp.hasEvalError() ) { QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 ); return false; } } else { // If we aren't an expression, we check to see if we can find the column. if ( mFields.lookupField( lyr.fieldName ) == -1 ) { return false; } } } lyr.mCurFields = mFields; if ( lyr.drawLabels || lyr.obstacle ) { if ( lyr.drawLabels ) { // add field indices for label's text, from expression or field if ( lyr.isExpression ) { // prepare expression for use in QgsPalLayerSettings::registerFeature() QgsExpression *exp = lyr.getLabelExpression(); exp->prepare( &context.expressionContext() ); if ( exp->hasEvalError() ) { QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 ); } Q_FOREACH ( const QString &name, exp->referencedColumns() ) { QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 ); attributeNames.insert( name ); } } else { attributeNames.insert( lyr.fieldName ); } }
void Qgs25DRenderer::startRender( QgsRenderContext& context, const QgsFields& fields ) { QgsExpressionContextScope* scope = new QgsExpressionContextScope( "2.5D Renderer" ); scope->setVariable( "qgis_25d_height", mHeight.expressionOrField() ); scope->setVariable( "qgis_25d_angle", mAngle ); context.expressionContext().appendScope( scope ); mSymbol->startRender( context, &fields ); }
bool QgsVectorLayerFeatureCounter::run() { QgsLegendSymbolList symbolList = mRenderer->legendSymbolItems(); QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin(); for ( ; symbolIt != symbolList.constEnd(); ++symbolIt ) { mSymbolFeatureCountMap.insert( symbolIt->label(), 0 ); } // If there are no features to be counted, we can spare us the trouble if ( mFeatureCount > 0 ) { int featuresCounted = 0; // Renderer (rule based) may depend on context scale, with scale is ignored if 0 QgsRenderContext renderContext; renderContext.setRendererScale( 0 ); renderContext.setExpressionContext( mExpressionContext ); QgsFeatureRequest request; if ( !mRenderer->filterNeedsGeometry() ) request.setFlags( QgsFeatureRequest::NoGeometry ); request.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mSource->fields() ); QgsFeatureIterator fit = mSource->getFeatures( request ); // TODO: replace QgsInterruptionChecker with QgsFeedback // fit.setInterruptionChecker( mFeedback ); mRenderer->startRender( renderContext, mSource->fields() ); double progress = 0; QgsFeature f; while ( fit.nextFeature( f ) ) { renderContext.expressionContext().setFeature( f ); QSet<QString> featureKeyList = mRenderer->legendKeysForFeature( f, renderContext ); Q_FOREACH ( const QString &key, featureKeyList ) { mSymbolFeatureCountMap[key] += 1; } ++featuresCounted; double p = ( static_cast< double >( featuresCounted ) / mFeatureCount ) * 100; if ( p - progress > 1 ) { progress = p; setProgress( progress ); } if ( isCanceled() ) { mRenderer->stopRender( renderContext ); return false; } } mRenderer->stopRender( renderContext ); }
void QgsPointDisplacementRenderer::drawSymbols( const ClusteredGroup &group, QgsRenderContext &context, const QList<QPointF> &symbolPositions ) { QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin(); ClusteredGroup::const_iterator groupIt = group.constBegin(); for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd(); ++symbolPosIt, ++groupIt ) { context.expressionContext().setFeature( groupIt->feature ); groupIt->symbol()->startRender( context ); groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected ); groupIt->symbol()->stopRender( context ); } }
void test_willRenderFeature_symbolsForFeature() { // prepare features QgsVectorLayer* layer = new QgsVectorLayer( "point?field=fld:int", "x", "memory" ); int idx = layer->fieldNameIndex( "fld" ); QVERIFY( idx != -1 ); QgsFeature f1; f1.initAttributes( 1 ); f1.setAttribute( idx, QVariant( 2 ) ); QgsFeature f2; f2.initAttributes( 1 ); f2.setAttribute( idx, QVariant( 8 ) ); QgsFeature f3; f3.initAttributes( 1 ); f3.setAttribute( idx, QVariant( 100 ) ); // prepare renderer QgsSymbol* s1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ); QgsSymbol* s2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ); RRule* rootRule = new RRule( nullptr ); rootRule->appendChild( new RRule( s1, 0, 0, "fld >= 5 and fld <= 20" ) ); rootRule->appendChild( new RRule( s2, 0, 0, "fld <= 10" ) ); QgsRuleBasedRenderer r( rootRule ); QVERIFY( r.capabilities() & QgsFeatureRenderer::MoreSymbolsPerFeature ); QgsRenderContext ctx; // dummy render context ctx.expressionContext().setFields( layer->fields() ); r.startRender( ctx, layer->fields() ); // test willRenderFeature ctx.expressionContext().setFeature( f1 ); QVERIFY( r.willRenderFeature( f1, ctx ) ); ctx.expressionContext().setFeature( f2 ); QVERIFY( r.willRenderFeature( f2, ctx ) ); ctx.expressionContext().setFeature( f3 ); QVERIFY( !r.willRenderFeature( f3, ctx ) ); // test symbolsForFeature ctx.expressionContext().setFeature( f1 ); QgsSymbolList lst1 = r.symbolsForFeature( f1, ctx ); QVERIFY( lst1.count() == 1 ); ctx.expressionContext().setFeature( f2 ); QgsSymbolList lst2 = r.symbolsForFeature( f2, ctx ); QVERIFY( lst2.count() == 2 ); ctx.expressionContext().setFeature( f3 ); QgsSymbolList lst3 = r.symbolsForFeature( f3, ctx ); QVERIFY( lst3.isEmpty() ); r.stopRender( ctx ); delete layer; }
QVariant QgsGraduatedSymbolRenderer::valueForFeature( QgsFeature &feature, QgsRenderContext &context ) const { QgsAttributes attrs = feature.attributes(); QVariant value; if ( mAttrNum < 0 || mAttrNum >= attrs.count() ) { value = mExpression->evaluate( &context.expressionContext() ); } else { value = attrs.at( mAttrNum ); } return value; }
QSizeF QgsHistogramDiagram::diagramSize( const QgsFeature& feature, const QgsRenderContext& c, const QgsDiagramSettings& s, const QgsDiagramInterpolationSettings& is ) { QSizeF size; if ( feature.attributes().isEmpty() ) { return size; //zero size if no attributes } if ( qgsDoubleNear( is.upperValue, is.lowerValue ) ) return size; // invalid value range => zero size double maxValue = 0; QgsExpressionContext expressionContext = c.expressionContext(); expressionContext.setFeature( feature ); if ( !feature.fields().isEmpty() ) expressionContext.setFields( feature.fields() ); Q_FOREACH ( const QString& cat, s.categoryAttributes ) { QgsExpression* expression = getExpression( cat, expressionContext ); maxValue = qMax( expression->evaluate( &expressionContext ).toDouble(), maxValue ); } // Scale, if extension is smaller than the specified minimum if ( maxValue < s.minimumSize ) { maxValue = s.minimumSize; } switch ( s.diagramOrientation ) { case QgsDiagramSettings::Up: case QgsDiagramSettings::Down: mScaleFactor = (( is.upperSize.width() - is.lowerSize.height() ) / ( is.upperValue - is.lowerValue ) ); size.scale( s.barWidth * s.categoryAttributes.size(), maxValue * mScaleFactor, Qt::IgnoreAspectRatio ); break; case QgsDiagramSettings::Right: case QgsDiagramSettings::Left: mScaleFactor = (( is.upperSize.width() - is.lowerSize.width() ) / ( is.upperValue - is.lowerValue ) ); size.scale( maxValue * mScaleFactor, s.barWidth * s.categoryAttributes.size(), Qt::IgnoreAspectRatio ); break; } return size; }
QVariant QgsCategorizedSymbolRendererV2::valueForFeature( QgsFeature& feature, QgsRenderContext &context ) const { QgsAttributes attrs = feature.attributes(); QVariant value; if ( mAttrNum == -1 ) { Q_ASSERT( mExpression.data() ); value = mExpression->evaluate( &context.expressionContext() ); } else { value = attrs.value( mAttrNum ); } return value; }
void QgsVectorLayerDiagramProvider::drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const { #if 1 // XXX strk // features are pre-rotated but not scaled/translated, // so we only disable rotation here. Ideally, they'd be // also pre-scaled/translated, as suggested here: // https://issues.qgis.org/issues/11856 QgsMapToPixel xform = context.mapToPixel(); xform.setMapRotation( 0, 0, 0 ); #else const QgsMapToPixel &xform = context.mapToPixel(); #endif QgsDiagramLabelFeature *dlf = dynamic_cast<QgsDiagramLabelFeature *>( label->getFeaturePart()->feature() ); QgsFeature feature; feature.setFields( mFields ); feature.setValid( true ); feature.setId( label->getFeaturePart()->featureId() ); feature.setAttributes( dlf->attributes() ); context.expressionContext().setFeature( feature ); //calculate top-left point for diagram //first, calculate the centroid of the label (accounts for PAL creating //rotated labels when we do not want to draw the diagrams rotated) double centerX = 0; double centerY = 0; for ( int i = 0; i < 4; ++i ) { centerX += label->getX( i ); centerY += label->getY( i ); } QgsPointXY outPt( centerX / 4.0, centerY / 4.0 ); //then, calculate the top left point for the diagram with this center position QgsPointXY centerPt = xform.transform( outPt.x() - label->getWidth() / 2, outPt.y() - label->getHeight() / 2 ); mSettings.renderer()->renderDiagram( feature, context, centerPt.toQPointF(), mSettings.dataDefinedProperties() ); //insert into label search tree to manipulate position interactively mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, QString(), QFont(), true, false ); }
void QgsHeatmapRenderer::startRender( QgsRenderContext& context, const QgsFields& fields ) { Q_UNUSED( fields ); if ( !context.painter() ) { return; } // find out classification attribute index from name mWeightAttrNum = fields.lookupField( mWeightExpressionString ); if ( mWeightAttrNum == -1 ) { mWeightExpression.reset( new QgsExpression( mWeightExpressionString ) ); mWeightExpression->prepare( &context.expressionContext() ); } initializeValues( context ); return; }
void QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const { const QgsSymbol *s = mItem.symbol(); if ( !s ) { return; } QgsRenderContext ctx; ctx.setScaleFactor( settings.dpi() / 25.4 ); ctx.setRendererScale( settings.mapScale() ); ctx.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * ctx.scaleFactor() ) ) ); ctx.setForceVectorOutput( true ); // ensure that a minimal expression context is available QgsExpressionContext expContext = context.expressionContext(); expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); ctx.setExpressionContext( expContext ); const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), minimumIconSize(), 0, &ctx ); QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) ); int opacity = 255; if ( QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layerNode()->layer() ) ) opacity = ( 255 * vectorLayer->opacity() ); if ( opacity != 255 ) { QPainter painter; painter.begin( &img ); painter.setCompositionMode( QPainter::CompositionMode_DestinationIn ); painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) ); painter.end(); } QByteArray byteArray; QBuffer buffer( &byteArray ); img.save( &buffer, "PNG" ); const QString base64 = QString::fromLatin1( byteArray.toBase64().data() ); json[ "icon" ] = base64; }
void QgsGraduatedSymbolRenderer::startRender( QgsRenderContext &context, const QgsFields &fields ) { mCounting = context.rendererScale() == 0.0; // find out classification attribute index from name mAttrNum = fields.lookupField( mAttrName ); if ( mAttrNum == -1 ) { mExpression.reset( new QgsExpression( mAttrName ) ); mExpression->prepare( &context.expressionContext() ); } Q_FOREACH ( const QgsRendererRange &range, mRanges ) { if ( !range.symbol() ) continue; range.symbol()->startRender( context, fields ); } }
void QgsMapHitTest::runHitTestLayer( QgsVectorLayer* vl, SymbolV2Set& usedSymbols, QgsRenderContext& context ) { QgsFeatureRendererV2* r = vl->rendererV2(); bool moreSymbolsPerFeature = r->capabilities() & QgsFeatureRendererV2::MoreSymbolsPerFeature; r->startRender( context, vl->fields() ); QgsFeature f; QgsFeatureRequest request( context.extent() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); QgsFeatureIterator fi = vl->getFeatures( request ); while ( fi.nextFeature( f ) ) { context.expressionContext().setFeature( f ); if ( moreSymbolsPerFeature ) { Q_FOREACH ( QgsSymbolV2* s, r->originalSymbolsForFeature( f, context ) ) usedSymbols.insert( s ); } else usedSymbols.insert( r->originalSymbolForFeature( f, context ) ); } r->stopRender( context ); }
void QgsMapHitTest::run() { // TODO: do we need this temp image? QImage tmpImage( mSettings.outputSize(), mSettings.outputImageFormat() ); tmpImage.setDotsPerMeterX( mSettings.outputDpi() * 25.4 ); tmpImage.setDotsPerMeterY( mSettings.outputDpi() * 25.4 ); QPainter painter( &tmpImage ); QgsRenderContext context = QgsRenderContext::fromMapSettings( mSettings ); context.setPainter( &painter ); // we are not going to draw anything, but we still need a working painter Q_FOREACH ( const QString& layerID, mSettings.layers() ) { QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) ); if ( !vl || !vl->rendererV2() ) continue; if ( vl->hasScaleBasedVisibility() && ( mSettings.scale() < vl->minimumScale() || mSettings.scale() > vl->maximumScale() ) ) { mHitTest[vl] = SymbolV2Set(); // no symbols -> will not be shown continue; } if ( mSettings.hasCrsTransformEnabled() ) { context.setCoordinateTransform( mSettings.layerTransform( vl ) ); context.setExtent( mSettings.outputExtentToLayerExtent( vl, mSettings.visibleExtent() ) ); } context.expressionContext() << QgsExpressionContextUtils::layerScope( vl ); SymbolV2Set& usedSymbols = mHitTest[vl]; runHitTestLayer( vl, usedSymbols, context ); } painter.end(); }
void QgsAttributeTableFilterModel::generateListOfVisibleFeatures() { if ( !layer() ) return; bool filter = false; QgsRectangle rect = mCanvas->mapSettings().mapToLayerCoordinates( layer(), mCanvas->extent() ); QgsRenderContext renderContext; renderContext.expressionContext() << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope() << QgsExpressionContextUtils::layerScope( layer() ); QgsFeatureRendererV2* renderer = layer()->rendererV2(); mFilteredFeatures.clear(); if ( !renderer ) { QgsDebugMsg( "Cannot get renderer" ); return; } const QgsMapSettings& ms = mCanvas->mapSettings(); if ( layer()->hasScaleBasedVisibility() && ( layer()->minimumScale() > ms.scale() || layer()->maximumScale() <= ms.scale() ) ) { QgsDebugMsg( "Out of scale limits" ); } else { if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { // setup scale // mapRenderer()->renderContext()->scale is not automaticaly updated when // render extent changes (because it's scale is used to identify if changed // since last render) -> use local context renderContext.setExtent( ms.visibleExtent() ); renderContext.setMapToPixel( ms.mapToPixel() ); renderContext.setRendererScale( ms.scale() ); } filter = renderer && renderer->capabilities() & QgsFeatureRendererV2::Filter; } renderer->startRender( renderContext, layer()->fields() ); QgsFeatureRequest r( masterModel()->request() ); if ( !r.filterRect().isNull() ) { r.setFilterRect( r.filterRect().intersect( &rect ) ); } else { r.setFilterRect( rect ); } QgsFeatureIterator features = masterModel()->layerCache()->getFeatures( r ); QgsFeature f; while ( features.nextFeature( f ) ) { renderContext.expressionContext().setFeature( f ); if ( !filter || renderer->willRenderFeature( f, renderContext ) ) { mFilteredFeatures << f.id(); } #if 0 if ( t.elapsed() > 5000 ) { bool cancel = false; emit progress( i, cancel ); if ( cancel ) break; t.restart(); } #endif } features.close(); if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { renderer->stopRender( renderContext ); } }
void QgsHistogramDiagram::renderDiagram( const QgsFeature& feature, QgsRenderContext& c, const QgsDiagramSettings& s, QPointF position ) { QPainter* p = c.painter(); if ( !p ) { return; } QList<double> values; double maxValue = 0; QgsExpressionContext expressionContext = c.expressionContext(); expressionContext.setFeature( feature ); if ( !feature.fields().isEmpty() ) expressionContext.setFields( feature.fields() ); Q_FOREACH ( const QString& cat, s.categoryAttributes ) { QgsExpression* expression = getExpression( cat, expressionContext ); double currentVal = expression->evaluate( &expressionContext ).toDouble(); values.push_back( currentVal ); maxValue = qMax( currentVal, maxValue ); } double scaledMaxVal = sizePainterUnits( maxValue * mScaleFactor, s, c ); double currentOffset = 0; double scaledWidth = sizePainterUnits( s.barWidth, s, c ); double baseX = position.x(); double baseY = position.y(); mPen.setColor( s.penColor ); setPenWidth( mPen, s, c ); p->setPen( mPen ); QList<double>::const_iterator valIt = values.constBegin(); QList< QColor >::const_iterator colIt = s.categoryColors.constBegin(); for ( ; valIt != values.constEnd(); ++valIt, ++colIt ) { double length = sizePainterUnits( *valIt * mScaleFactor, s, c ); mCategoryBrush.setColor( *colIt ); p->setBrush( mCategoryBrush ); switch ( s.diagramOrientation ) { case QgsDiagramSettings::Up: p->drawRect( baseX + currentOffset, baseY, scaledWidth, length * -1 ); break; case QgsDiagramSettings::Down: p->drawRect( baseX + currentOffset, baseY - scaledMaxVal, scaledWidth, length ); break; case QgsDiagramSettings::Right: p->drawRect( baseX, baseY - currentOffset, length, scaledWidth * -1 ); break; case QgsDiagramSettings::Left: p->drawRect( baseX + scaledMaxVal, baseY - currentOffset, 0 - length, scaledWidth * -1 ); break; } currentOffset += scaledWidth; } }
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas, QgsGeometry* selectGeometry, bool doContains, bool doDifference, bool singleSelect ) { if ( selectGeometry->type() != QGis::Polygon ) { return; } QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( vlayer == nullptr ) { return; } // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. //QgsGeometry selectGeomTrans( *selectGeometry ); //if ( canvas->mapSettings().hasCrsTransformEnabled() ) //{ // try // { // QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() ); // selectGeomTrans.transform( ct ); // } // catch ( QgsCsException &cse ) // { // Q_UNUSED( cse ); // // catch exception for 'invalid' point and leave existing selection unchanged // QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) ); // LOG_INFO( "CRS Exception\nSelection extends beyond layer's coordinate system" ); // return; // } //} QgsGeometry selectGeomTrans; try{ selectGeomTrans = toLayerCoordinates( canvas, selectGeometry, vlayer ); } catch ( QgsCsException & ) { return; } QApplication::setOverrideCursor( Qt::WaitCursor ); QgsDebugMsg( "Selection layer: " + vlayer->name() ); QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() ); QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) ); QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); QgsFeatureRendererV2* r = vlayer->rendererV2(); if ( r ) r->startRender( context, vlayer->pendingFields() ); QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes(), vlayer->pendingFields() ); else request.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeatureIds newSelectedFeatures; QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { #if (VERSION_INT >= 21601) context.expressionContext().setFeature( f ); //taken from QGIS 2.16.1 // make sure to only use features that are visible if ( r && !r->willRenderFeature( f, context ) ) #else if ( r && !r->willRenderFeature( f ) ) #endif continue; QgsGeometry* g = f.geometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g->distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } if ( r ) r->stopRender( context ); QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); if ( doDifference ) { QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds(); QgsFeatureIds selectedFeatures; QgsFeatureIds deselectedFeatures; QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd(); while ( i != newSelectedFeatures.constBegin() ) { --i; if ( layerSelectedFeatures.contains( *i ) ) { deselectedFeatures.insert( *i ); } else { selectedFeatures.insert( *i ); } } vlayer->modifySelection( selectedFeatures, deselectedFeatures ); } else { SelectFeatures( vlayer, newSelectedFeatures ); // vlayer->setSelectedFeatures( newSelectedFeatures ); } QApplication::restoreOverrideCursor(); }
bool QgsHeatmapRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker ) { Q_UNUSED( layer ); Q_UNUSED( selected ); Q_UNUSED( drawVertexMarker ); if ( !context.painter() ) { return false; } if ( !feature.hasGeometry() || feature.geometry().type() != QgsWkbTypes::PointGeometry ) { //can only render point type return false; } double weight = 1.0; if ( !mWeightExpressionString.isEmpty() ) { QVariant value; if ( mWeightAttrNum == -1 ) { Q_ASSERT( mWeightExpression.data() ); value = mWeightExpression->evaluate( &context.expressionContext() ); } else { QgsAttributes attrs = feature.attributes(); value = attrs.value( mWeightAttrNum ); } bool ok = false; double evalWeight = value.toDouble( &ok ); if ( ok ) { weight = evalWeight; } } int width = context.painter()->device()->width() / mRenderQuality; int height = context.painter()->device()->height() / mRenderQuality; //transform geometry if required QgsGeometry geom = feature.geometry(); QgsCoordinateTransform xform = context.coordinateTransform(); if ( xform.isValid() ) { geom.transform( xform ); } //convert point to multipoint QgsMultiPoint multiPoint = convertToMultipoint( &geom ); //loop through all points in multipoint for ( QgsMultiPoint::const_iterator pointIt = multiPoint.constBegin(); pointIt != multiPoint.constEnd(); ++pointIt ) { QgsPoint pixel = context.mapToPixel().transform( *pointIt ); int pointX = pixel.x() / mRenderQuality; int pointY = pixel.y() / mRenderQuality; for ( int x = qMax( pointX - mRadiusPixels, 0 ); x < qMin( pointX + mRadiusPixels, width ); ++x ) { for ( int y = qMax( pointY - mRadiusPixels, 0 ); y < qMin( pointY + mRadiusPixels, height ); ++y ) { int index = y * width + x; if ( index >= mValues.count() ) { continue; } double distanceSquared = pow( pointX - x, 2.0 ) + pow( pointY - y, 2.0 ); if ( distanceSquared > mRadiusSquared ) { continue; } double score = weight * quarticKernel( sqrt( distanceSquared ), mRadiusPixels ); double value = mValues.at( index ) + score; if ( value > mCalculatedMaxValue ) { mCalculatedMaxValue = value; } mValues[ index ] = value; } } } mFeaturesRendered++; #if 0 //TODO - enable progressive rendering if ( mFeaturesRendered % 200 == 0 ) { renderImage( context ); } #endif return true; }
QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas *canvas, const QgsGeometry &selectGeometry, bool doContains, bool singleSelect ) { QgsFeatureIds newSelectedFeatures; if ( selectGeometry.type() != QgsWkbTypes::PolygonGeometry ) return newSelectedFeatures; QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( !vlayer ) return newSelectedFeatures; // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. QgsGeometry selectGeomTrans = selectGeometry; try { QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs(), QgsProject::instance() ); if ( !ct.isShortCircuited() && selectGeomTrans.type() == QgsWkbTypes::PolygonGeometry ) { // convert add more points to the edges of the rectangle // improve transformation result QgsPolygonXY poly( selectGeomTrans.asPolygon() ); if ( poly.size() == 1 && poly.at( 0 ).size() == 5 ) { const QgsPolylineXY &ringIn = poly.at( 0 ); QgsPolygonXY newpoly( 1 ); newpoly[0].resize( 41 ); QgsPolylineXY &ringOut = newpoly[0]; ringOut[ 0 ] = ringIn.at( 0 ); int i = 1; for ( int j = 1; j < 5; j++ ) { QgsVector v( ( ringIn.at( j ) - ringIn.at( j - 1 ) ) / 10.0 ); for ( int k = 0; k < 9; k++ ) { ringOut[ i ] = ringOut[ i - 1 ] + v; i++; } ringOut[ i++ ] = ringIn.at( j ); } selectGeomTrans = QgsGeometry::fromPolygonXY( newpoly ); } } selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsDebugMsg( QStringLiteral( "Caught CRS exception " ) ); QgisApp::instance()->messageBar()->pushMessage( QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system" ), Qgis::Warning, QgisApp::instance()->messageTimeout() ); return newSelectedFeatures; } QgsDebugMsgLevel( "Selection layer: " + vlayer->name(), 3 ); QgsDebugMsgLevel( "Selection polygon: " + selectGeomTrans.asWkt(), 3 ); QgsDebugMsgLevel( "doContains: " + QString( doContains ? "T" : "F" ), 3 ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); context.expressionContext() << QgsExpressionContextUtils::layerScope( vlayer ); std::unique_ptr< QgsFeatureRenderer > r; if ( vlayer->renderer() ) { r.reset( vlayer->renderer()->clone() ); r->startRender( context, vlayer->fields() ); } QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes( context ), vlayer->fields() ); else request.setNoAttributes(); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { context.expressionContext().setFeature( f ); // make sure to only use features that are visible if ( r && !r->willRenderFeature( f, context ) ) continue; QgsGeometry g = f.geometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g.distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } if ( r ) r->stopRender( context ); QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); return newSelectedFeatures; }
void QgsTextDiagram::renderDiagram( const QgsFeature& feature, QgsRenderContext& c, const QgsDiagramSettings& s, QPointF position ) { QPainter* p = c.painter(); if ( !p ) { return; } //convert from mm / map units to painter units QSizeF spu = sizePainterUnits( s.size, s, c ); double w = spu.width(); double h = spu.height(); double baseX = position.x(); double baseY = position.y() - h; QVector<QPointF> textPositions; //midpoints for text placement int nCategories = s.categoryAttributes.size(); for ( int i = 0; i < nCategories; ++i ) { if ( mOrientation == Horizontal ) { textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0, baseY + h / 2.0 ) ); } else //vertical { textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) ); } } mPen.setColor( s.penColor ); setPenWidth( mPen, s, c ); p->setPen( mPen ); mBrush.setColor( s.backgroundColor ); p->setBrush( mBrush ); //draw shapes and separator lines first if ( mShape == Circle ) { p->drawEllipse( baseX, baseY, w, h ); //draw separator lines QList<QPointF> intersect; //intersections between shape and separation lines QPointF center( baseX + w / 2.0, baseY + h / 2.0 ); double r1 = w / 2.0; double r2 = h / 2.0; for ( int i = 1; i < nCategories; ++i ) { if ( mOrientation == Horizontal ) { lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect ); } else //vertical { lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect ); } if ( intersect.size() > 1 ) { p->drawLine( intersect.at( 0 ), intersect.at( 1 ) ); } } } else if ( mShape == Rectangle ) { p->drawRect( QRectF( baseX, baseY, w, h ) ); for ( int i = 1; i < nCategories; ++i ) { if ( mOrientation == Horizontal ) { p->drawLine( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) ); } else { p->drawLine( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ) ); } } } else //triangle { QPolygonF triangle; triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY ); p->drawPolygon( triangle ); QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h ); QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY ); QPointF intersectionPoint1, intersectionPoint2; for ( int i = 1; i < nCategories; ++i ) { if ( mOrientation == Horizontal ) { QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY ); if ( baseX + w / nCategories * i < baseX + w / 2.0 ) { verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 ); } else { verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 ); } p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 ); } else //vertical { QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i ); horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 ); horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 ); p->drawLine( intersectionPoint1, intersectionPoint2 ); } } } //draw text QFont sFont = scaledFont( s, c ); QFontMetricsF fontMetrics( sFont ); p->setFont( sFont ); QgsExpressionContext expressionContext = c.expressionContext(); expressionContext.setFeature( feature ); if ( feature.fields() ) expressionContext.setFields( *feature.fields() ); for ( int i = 0; i < textPositions.size(); ++i ) { QgsExpression* expression = getExpression( s.categoryAttributes.at( i ), expressionContext ); QString val = expression->evaluate( &expressionContext ).toString(); //find out dimesions double textWidth = fontMetrics.width( val ); double textHeight = fontMetrics.height(); mPen.setColor( s.categoryColors.at( i ) ); p->setPen( mPen ); QPointF position = textPositions.at( i ); // Calculate vertical placement double xOffset = 0; switch ( s.labelPlacementMethod ) { case QgsDiagramSettings::Height: xOffset = textHeight / 2.0; break; case QgsDiagramSettings::XHeight: xOffset = fontMetrics.xHeight(); break; } p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val ); } }
QSet<QString> QgsSymbolLayer::usedAttributes( const QgsRenderContext &context ) const { QSet<QString> columns = mDataDefinedProperties.referencedFields( context.expressionContext() ); return columns; }
bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames ) { QgsDiagramLayerSettings& s2 = mSettings; const QgsMapSettings& mapSettings = mEngine->mapSettings(); s2.ct = nullptr; if ( mapSettings.hasCrsTransformEnabled() ) { if ( context.coordinateTransform() ) // this is context for layer rendering - use its CT as it includes correct datum transform s2.ct = context.coordinateTransform()->clone(); else // otherwise fall back to creating our own CT - this one may not have the correct datum transform! s2.ct = new QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs() ); } s2.xform = &mapSettings.mapToPixel(); s2.fields = mFields; s2.renderer = mDiagRenderer; const QgsDiagramRendererV2* diagRenderer = s2.renderer; //add attributes needed by the diagram renderer QList<QString> att = diagRenderer->diagramAttributes(); QList<QString>::const_iterator attIt = att.constBegin(); for ( ; attIt != att.constEnd(); ++attIt ) { QgsExpression* expression = diagRenderer->diagram()->getExpression( *attIt, context.expressionContext() ); QStringList columns = expression->referencedColumns(); QStringList::const_iterator columnsIterator = columns.constBegin(); for ( ; columnsIterator != columns.constEnd(); ++columnsIterator ) { if ( !attributeNames.contains( *columnsIterator ) ) attributeNames << *columnsIterator; } } const QgsLinearlyInterpolatedDiagramRenderer* linearlyInterpolatedDiagramRenderer = dynamic_cast<const QgsLinearlyInterpolatedDiagramRenderer*>( diagRenderer ); if ( linearlyInterpolatedDiagramRenderer ) { if ( linearlyInterpolatedDiagramRenderer->classificationAttributeIsExpression() ) { QgsExpression* expression = diagRenderer->diagram()->getExpression( linearlyInterpolatedDiagramRenderer->classificationAttributeExpression(), context.expressionContext() ); QStringList columns = expression->referencedColumns(); QStringList::const_iterator columnsIterator = columns.constBegin(); for ( ; columnsIterator != columns.constEnd(); ++columnsIterator ) { if ( !attributeNames.contains( *columnsIterator ) ) attributeNames << *columnsIterator; } } else { QString name = mFields.at( linearlyInterpolatedDiagramRenderer->classificationAttribute() ).name(); if ( !attributeNames.contains( name ) ) attributeNames << name; } } //and the ones needed for data defined diagram positions if ( mSettings.xPosColumn != -1 ) attributeNames << mFields.at( mSettings.xPosColumn ).name(); if ( mSettings.yPosColumn != -1 ) attributeNames << mFields.at( mSettings.yPosColumn ).name(); return true; }
void QgsDecorationCopyright::render( const QgsMapSettings &mapSettings, QgsRenderContext &context ) { Q_UNUSED( mapSettings ); if ( !enabled() ) return; context.painter()->save(); context.painter()->setRenderHint( QPainter::Antialiasing, true ); QString displayString = QgsExpression::replaceExpressionText( mLabelText, &context.expressionContext() ); QStringList displayStringList = displayString.split( QStringLiteral( "\n" ) ); QFontMetricsF fm( mTextFormat.scaledFont( context ) ); double textWidth = QgsTextRenderer::textWidth( context, mTextFormat, displayStringList, &fm ); double textHeight = QgsTextRenderer::textHeight( context, mTextFormat, displayStringList, QgsTextRenderer::Point, &fm ); QPaintDevice *device = context.painter()->device(); int deviceHeight = device->height() / device->devicePixelRatioF(); int deviceWidth = device->width() / device->devicePixelRatioF(); float xOffset( 0 ), yOffset( 0 ); // Set margin according to selected units switch ( mMarginUnit ) { case QgsUnitTypes::RenderMillimeters: { int pixelsInchX = context.painter()->device()->logicalDpiX(); int pixelsInchY = context.painter()->device()->logicalDpiY(); xOffset = pixelsInchX * INCHES_TO_MM * mMarginHorizontal; yOffset = pixelsInchY * INCHES_TO_MM * mMarginVertical; break; } case QgsUnitTypes::RenderPixels: { xOffset = mMarginHorizontal; yOffset = mMarginVertical; break; } case QgsUnitTypes::RenderPercentage: { xOffset = ( ( deviceWidth - textWidth ) / 100. ) * mMarginHorizontal; yOffset = ( ( deviceHeight - textHeight ) / 100. ) * mMarginVertical; break; } case QgsUnitTypes::RenderMapUnits: case QgsUnitTypes::RenderPoints: case QgsUnitTypes::RenderInches: case QgsUnitTypes::RenderUnknownUnit: case QgsUnitTypes::RenderMetersInMapUnits: break; } // Determine placement of label from form combo box QgsTextRenderer::HAlignment horizontalAlignment = QgsTextRenderer::AlignLeft; switch ( mPlacement ) { case BottomLeft: // Bottom Left, xOffset is set above yOffset = deviceHeight - yOffset; break; case TopLeft: // Top left, xOffset is set above yOffset = yOffset + textHeight; break; case TopRight: // Top Right yOffset = yOffset + textHeight; xOffset = deviceWidth - xOffset; horizontalAlignment = QgsTextRenderer::AlignRight; break; case BottomRight: // Bottom Right yOffset = deviceHeight - yOffset; xOffset = deviceWidth - xOffset; horizontalAlignment = QgsTextRenderer::AlignRight; break; default: QgsDebugMsg( QStringLiteral( "Unknown placement index of %1" ).arg( static_cast<int>( mPlacement ) ) ); } //Paint label to canvas QgsTextRenderer::drawText( QPointF( xOffset, yOffset ), 0.0, horizontalAlignment, displayStringList, context, mTextFormat ); context.painter()->restore(); }