QgsLayerTreeModelLegendNode::ItemMetrics QgsDataDefinedSizeLegendNode::draw( const QgsLegendSettings &settings, QgsLayerTreeModelLegendNode::ItemContext *ctx ) { // setup temporary render context QgsRenderContext context; context.setScaleFactor( settings.dpi() / 25.4 ); context.setRendererScale( settings.mapScale() ); context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) ); context.setForceVectorOutput( true ); if ( ctx && ctx->painter ) { context.setPainter( ctx->painter ); ctx->painter->save(); ctx->painter->setRenderHint( QPainter::Antialiasing ); ctx->painter->translate( ctx->point ); ctx->painter->scale( 1 / context.scaleFactor(), 1 / context.scaleFactor() ); } QgsDataDefinedSizeLegend ddsLegend( *mSettings ); ddsLegend.setFont( settings.style( QgsLegendStyle::SymbolLabel ).font() ); ddsLegend.setTextColor( settings.fontColor() ); QSize contentSize; int labelXOffset; ddsLegend.drawCollapsedLegend( context, &contentSize, &labelXOffset ); if ( ctx && ctx->painter ) ctx->painter->restore(); ItemMetrics im; im.symbolSize = QSizeF( ( contentSize.width() - labelXOffset ) / context.scaleFactor(), contentSize.height() / context.scaleFactor() ); im.labelSize = QSizeF( labelXOffset / context.scaleFactor(), contentSize.height() / context.scaleFactor() ); return im; }
static QgsRenderContext _createRenderContext( double mupp, double dpi, double scale ) { QgsRenderContext context; context.setScaleFactor( dpi / 25.4 ); context.setRendererScale( scale ); context.setMapToPixel( QgsMapToPixel( mupp ) ); return context; }
inline QgsRenderContext * QgsSymbolV2LegendNode::createTemporaryRenderContext() const { double scale = 0.0; double mupp = 0.0; int dpi = 0; if ( model() ) model()->legendMapViewData( &mupp, &dpi, &scale ); bool validData = mupp != 0 && dpi != 0 && scale != 0; // setup temporary render context QScopedPointer<QgsRenderContext> context( new QgsRenderContext ); context->setScaleFactor( dpi / 25.4 ); context->setRendererScale( scale ); context->setMapToPixel( QgsMapToPixel( mupp ) ); return validData ? context.take() : 0; }
QgsRenderContext *QgsLayerTreeModelLegendNode::createTemporaryRenderContext() const { double scale = 0.0; double mupp = 0.0; int dpi = 0; if ( model() ) model()->legendMapViewData( &mupp, &dpi, &scale ); if ( qgsDoubleNear( mupp, 0.0 ) || dpi == 0 || qgsDoubleNear( scale, 0.0 ) ) return nullptr; // setup temporary render context std::unique_ptr<QgsRenderContext> context = qgis::make_unique<QgsRenderContext>( ); context->setScaleFactor( dpi / 25.4 ); context->setRendererScale( scale ); context->setMapToPixel( QgsMapToPixel( mupp ) ); return context.release(); }
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; }
QSizeF QgsSymbolV2LegendNode::drawSymbol( const QgsLegendSettings& settings, ItemContext* ctx, double itemHeight ) const { QgsSymbolV2* s = mItem.symbol(); if ( !s ) { return QSizeF(); } // setup temporary render context QgsRenderContext context; context.setScaleFactor( settings.dpi() / 25.4 ); context.setRendererScale( settings.mapScale() ); context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) ); context.setForceVectorOutput( true ); context.setPainter( ctx ? ctx->painter : 0 ); //Consider symbol size for point markers double height = settings.symbolSize().height(); double width = settings.symbolSize().width(); double size = 0; //Center small marker symbols double widthOffset = 0; double heightOffset = 0; if ( QgsMarkerSymbolV2* markerSymbol = dynamic_cast<QgsMarkerSymbolV2*>( s ) ) { // allow marker symbol to occupy bigger area if necessary size = markerSymbol->size() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context, s->outputUnit(), s->mapUnitScale() ) / context.scaleFactor(); height = size; width = size; if ( width < settings.symbolSize().width() ) { widthOffset = ( settings.symbolSize().width() - width ) / 2.0; } if ( height < settings.symbolSize().height() ) { heightOffset = ( settings.symbolSize().height() - height ) / 2.0; } } if ( ctx ) { double currentXPosition = ctx->point.x(); double currentYCoord = ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2; QPainter* p = ctx->painter; //setup painter scaling to dots so that raster symbology is drawn to scale double dotsPerMM = context.scaleFactor(); int opacity = 255; if ( QgsVectorLayer* vectorLayer = dynamic_cast<QgsVectorLayer*>( layerNode()->layer() ) ) opacity = 255 - ( 255 * vectorLayer->layerTransparency() / 100 ); p->save(); p->setRenderHint( QPainter::Antialiasing ); p->translate( currentXPosition + widthOffset, currentYCoord + heightOffset ); p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM ); if ( opacity != 255 && settings.useAdvancedEffects() ) { //semi transparent layer, so need to draw symbol to an image (to flatten it first) //create image which is same size as legend rect, in case symbol bleeds outside its alloted space QSize tempImageSize( width * dotsPerMM, height * dotsPerMM ); QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 ); tempImage.fill( Qt::transparent ); QPainter imagePainter( &tempImage ); context.setPainter( &imagePainter ); s->drawPreviewIcon( &imagePainter, tempImageSize, &context ); context.setPainter( ctx->painter ); //reduce opacity of image imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn ); imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) ); imagePainter.end(); //draw rendered symbol image p->drawImage( 0, 0, tempImage ); } else { s->drawPreviewIcon( p, QSize( width * dotsPerMM, height * dotsPerMM ), &context ); } p->restore(); } return QSizeF( qMax( width + 2 * widthOffset, ( double ) settings.symbolSize().width() ), qMax( height + 2 * heightOffset, ( double ) settings.symbolSize().height() ) ); }
QSizeF QgsSymbolLegendNode::drawSymbol( const QgsLegendSettings &settings, ItemContext *ctx, double itemHeight ) const { QgsSymbol *s = mItem.symbol(); if ( !s ) { return QSizeF(); } // setup temporary render context QgsRenderContext context; context.setScaleFactor( settings.dpi() / 25.4 ); context.setRendererScale( settings.mapScale() ); context.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * context.scaleFactor() ) ) ); context.setForceVectorOutput( true ); context.setPainter( ctx ? ctx->painter : nullptr ); if ( ctx && ctx->context ) { context.setExpressionContext( ctx->context->expressionContext() ); } else { // setup a minimal expression context QgsExpressionContext expContext; expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); context.setExpressionContext( expContext ); } //Consider symbol size for point markers double height = settings.symbolSize().height(); double width = settings.symbolSize().width(); //Center small marker symbols double widthOffset = 0; double heightOffset = 0; if ( QgsMarkerSymbol *markerSymbol = dynamic_cast<QgsMarkerSymbol *>( s ) ) { // allow marker symbol to occupy bigger area if necessary double size = markerSymbol->size( context ) / context.scaleFactor(); height = size; width = size; if ( width < settings.symbolSize().width() ) { widthOffset = ( settings.symbolSize().width() - width ) / 2.0; } if ( height < settings.symbolSize().height() ) { heightOffset = ( settings.symbolSize().height() - height ) / 2.0; } } if ( ctx && ctx->painter ) { double currentXPosition = ctx->point.x(); double currentYCoord = ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2; QPainter *p = ctx->painter; //setup painter scaling to dots so that raster symbology is drawn to scale double dotsPerMM = context.scaleFactor(); int opacity = 255; if ( QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layerNode()->layer() ) ) opacity = ( 255 * vectorLayer->opacity() ); p->save(); p->setRenderHint( QPainter::Antialiasing ); p->translate( currentXPosition + widthOffset, currentYCoord + heightOffset ); p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM ); if ( opacity != 255 && settings.useAdvancedEffects() ) { //semi transparent layer, so need to draw symbol to an image (to flatten it first) //create image which is same size as legend rect, in case symbol bleeds outside its alloted space QSize tempImageSize( width * dotsPerMM, height * dotsPerMM ); QImage tempImage = QImage( tempImageSize, QImage::Format_ARGB32 ); tempImage.fill( Qt::transparent ); QPainter imagePainter( &tempImage ); context.setPainter( &imagePainter ); s->drawPreviewIcon( &imagePainter, tempImageSize, &context ); context.setPainter( ctx->painter ); //reduce opacity of image imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn ); imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) ); imagePainter.end(); //draw rendered symbol image p->drawImage( 0, 0, tempImage ); } else { s->drawPreviewIcon( p, QSize( width * dotsPerMM, height * dotsPerMM ), &context ); } if ( !mTextOnSymbolLabel.isEmpty() ) { QFontMetricsF fm( mTextOnSymbolTextFormat.scaledFont( context ) ); qreal yBaselineVCenter = ( height * dotsPerMM + fm.ascent() - fm.descent() ) / 2; QgsTextRenderer::drawText( QPointF( width * dotsPerMM / 2, yBaselineVCenter ), 0, QgsTextRenderer::AlignCenter, QStringList() << mTextOnSymbolLabel, context, mTextOnSymbolTextFormat ); } p->restore(); } return QSizeF( std::max( width + 2 * widthOffset, static_cast< double >( settings.symbolSize().width() ) ), std::max( height + 2 * heightOffset, static_cast< double >( settings.symbolSize().height() ) ) ); }
QVariant QgsSymbolV2LegendNode::data( int role ) const { if ( role == Qt::DisplayRole ) { return mLabel; } else if ( role == Qt::EditRole ) { return mUserLabel.isEmpty() ? mItem.label() : mUserLabel; } else if ( role == Qt::DecorationRole ) { QSize iconSize( 16, 16 ); // TODO: configurable const int indentSize = 20; if ( mPixmap.isNull() ) { QPixmap pix; if ( mItem.symbol() ) { double scale = 0.0; double mupp = 0.0; int dpi = 0; if ( model() ) model()->legendMapViewData( &mupp, &dpi, &scale ); bool validData = mupp != 0 && dpi != 0 && scale != 0; // setup temporary render context QgsRenderContext context; context.setScaleFactor( dpi / 25.4 ); context.setRendererScale( scale ); context.setMapToPixel( QgsMapToPixel( mupp ) ); // hope it's ok to leave out other params pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mItem.symbol(), iconSize, validData ? &context : 0 ); } else { pix = QPixmap( iconSize ); pix.fill( Qt::transparent ); } if ( mItem.level() == 0 ) mPixmap = pix; else { // ident the symbol icon to make it look like a tree structure QPixmap pix2( pix.width() + mItem.level() * indentSize, pix.height() ); pix2.fill( Qt::transparent ); QPainter p( &pix2 ); p.drawPixmap( mItem.level() * indentSize, 0, pix ); p.end(); mPixmap = pix2; } } return mPixmap; } else if ( role == Qt::CheckStateRole ) { if ( !mItem.isCheckable() ) return QVariant(); QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( mLayerNode->layer() ); if ( !vlayer || !vlayer->rendererV2() ) return QVariant(); return vlayer->rendererV2()->legendSymbolItemChecked( mItem.ruleKey() ) ? Qt::Checked : Qt::Unchecked; } else if ( role == RuleKeyRole ) { return mItem.ruleKey(); } else if ( role == SymbolV2LegacyRuleKeyRole ) { return QVariant::fromValue<void*>( mItem.legacyRuleKey() ); } return QVariant(); }
QgsMapToPixel QgsMapToPixel::fromScale( double scale, QgsUnitTypes::DistanceUnit mapUnits, double dpi ) { double metersPerPixel = 25.4 / dpi / 1000.0; double mapUnitsPerPixel = metersPerPixel * QgsUnitTypes::fromUnitToUnitFactor( QgsUnitTypes::DistanceMeters, mapUnits ); return QgsMapToPixel( mapUnitsPerPixel * scale ); }