Merge pull request #50200 from troopa81/fix_legend_geomgenerator

Fix legend icon geom generator is involved
This commit is contained in:
Julien Cabieces 2022-09-27 17:05:23 +02:00 committed by GitHub
commit 2d1e3fb387
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 149 additions and 37 deletions

View File

@ -125,7 +125,7 @@ Returns the symbol's type.
%End
QgsSymbolLayerList symbolLayers();
QgsSymbolLayerList symbolLayers() const;
%Docstring
Returns the list of symbol layers contained in the symbol.
@ -780,7 +780,6 @@ Render editing vertex marker at specified point
QgsSymbol( const QgsSymbol & );
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -971,7 +971,7 @@ Returns -1 if the ``renderer`` is not animated.
.. versionadded:: 3.26
%End
static QgsSymbol *restrictedSizeSymbol( const QgsSymbol *s, double minSize, double maxSize, QgsRenderContext *context, double &width, double &height );
static QgsSymbol *restrictedSizeSymbol( const QgsSymbol *s, double minSize, double maxSize, QgsRenderContext *context, double &width, double &height, bool *ok = 0 );
%Docstring
Creates a new symbol with size restricted to min/max size if original size is out of min/max range
@ -980,9 +980,13 @@ Creates a new symbol with size restricted to min/max size if original size is ou
:param maxSize: the maximum size in mm
:param context: the render context
:param width: expected width, can be changed by the function
:param height: expected height, can be changed by this function
:param height: expected height, can be changed by the function
:param ok: if not None, ok is set to false if it's not possible to compute a restricted symbol (if geometry generators
are involved for instance)
:return: 0 if size is within minSize/maxSize range. New symbol if size was out of min/max range. Caller takes ownership
:return: None if size is within minSize/maxSize range or if it's not possible to compute a
restricted size symbol. New symbol if size was out of min/max range.
Caller takes ownership
%End
static QgsStringMap evaluatePropertiesMap( const QMap<QString, QgsProperty> &propertiesMap, const QgsExpressionContext &context );
@ -997,8 +1001,6 @@ Evaluates a map of properties using the given ``context`` and returns a variant
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -37,6 +37,7 @@
#include "qgsmarkersymbol.h"
#include "qgsvariantutils.h"
#include "qgslayertreelayer.h"
#include "qgsgeometrygeneratorsymbollayer.h"
#include <QBuffer>
@ -343,25 +344,29 @@ QSize QgsSymbolLegendNode::minimumIconSize( QgsRenderContext *context ) const
const int iconSize = QgsLayerTreeModel::scaleIconSize( 16 );
const int largeIconSize = QgsLayerTreeModel::scaleIconSize( 512 );
QSize minSz( iconSize, iconSize );
if ( mItem.symbol() && mItem.symbol()->type() == Qgis::SymbolType::Marker )
if ( mItem.symbol() && ( mItem.symbol()->type() == Qgis::SymbolType::Marker
|| mItem.symbol()->type() == Qgis::SymbolType::Line ) )
{
int maxSize = largeIconSize;
// unusued width, height variables
double width = 0.0;
double height = 0.0;
const std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context, width, height ) );
bool ok;
std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context, width, height, &ok ) );
if ( !ok && context )
{
// It's not possible to get a restricted size symbol, so we restrict
// pixmap target size to be sure it would fit MAXIMUM_SIZE
maxSize = static_cast<int>( std::round( MAXIMUM_SIZE * context->scaleFactor() ) );
}
const QSize size( mItem.symbol()->type() == Qgis::SymbolType::Marker ? maxSize : minSz.width(),
maxSize );
minSz = QgsImageOperation::nonTransparentImageRect(
QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), QSize( largeIconSize, largeIconSize ), 0,
context ).toImage(),
minSz,
true ).size();
}
else if ( mItem.symbol() && mItem.symbol()->type() == Qgis::SymbolType::Line )
{
double width = 0.0;
double height = 0.0;
const std::unique_ptr<QgsSymbol> symbol( QgsSymbolLayerUtils::restrictedSizeSymbol( mItem.symbol(), MINIMUM_SIZE, MAXIMUM_SIZE, context, width, height ) );
minSz = QgsImageOperation::nonTransparentImageRect(
QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), QSize( minSz.width(), largeIconSize ), 0,
QgsSymbolLayerUtils::symbolPreviewPixmap( symbol ? symbol.get() : mItem.symbol(), size, 0,
context ).toImage(),
minSz,
true ).size();
@ -1564,4 +1569,3 @@ void QgsVectorLabelLegendNode::textWidthHeight( double &width, double &height, Q
height = QgsTextRenderer::textHeight( ctx, textFormat, 'A', true );
width = QgsTextRenderer::textWidth( ctx, textFormat, textLines, &fm );
}

View File

@ -160,7 +160,7 @@ class CORE_EXPORT QgsSymbol
* \see symbolLayerCount
* \since QGIS 2.7
*/
QgsSymbolLayerList symbolLayers() { return mLayers; }
QgsSymbolLayerList symbolLayers() const { return mLayers; }
#ifndef SIP_RUN
@ -856,4 +856,3 @@ class CORE_EXPORT QgsSymbol
};
#endif

View File

@ -4983,18 +4983,34 @@ double QgsSymbolLayerUtils::rendererFrameRate( const QgsFeatureRenderer *rendere
return visitor.refreshRate;
}
QgsSymbol *QgsSymbolLayerUtils::restrictedSizeSymbol( const QgsSymbol *s, double minSize, double maxSize, QgsRenderContext *context, double &width, double &height )
QgsSymbol *QgsSymbolLayerUtils::restrictedSizeSymbol( const QgsSymbol *s, double minSize, double maxSize, QgsRenderContext *context, double &width, double &height, bool *ok )
{
if ( !s || !context )
{
return 0;
return nullptr;
}
if ( ok )
*ok = true;
double size;
const QgsMarkerSymbol *markerSymbol = dynamic_cast<const QgsMarkerSymbol *>( s );
const QgsLineSymbol *lineSymbol = dynamic_cast<const QgsLineSymbol *>( s );
if ( markerSymbol )
{
const QgsSymbolLayerList sls = s->symbolLayers();
for ( const QgsSymbolLayer *sl : std::as_const( sls ) )
{
// geometry generators involved, there is no way to get a restricted size symbol
if ( sl->type() != Qgis::SymbolType::Marker )
{
if ( ok )
*ok = false;
return nullptr;
}
}
size = markerSymbol->size( *context );
}
else if ( lineSymbol )
@ -5003,7 +5019,10 @@ QgsSymbol *QgsSymbolLayerUtils::restrictedSizeSymbol( const QgsSymbol *s, double
}
else
{
return 0; //not size restriction implemented for other symbol types
if ( ok )
*ok = false;
return nullptr; //not size restriction implemented for other symbol types
}
size /= context->scaleFactor();
@ -5018,7 +5037,8 @@ QgsSymbol *QgsSymbolLayerUtils::restrictedSizeSymbol( const QgsSymbol *s, double
}
else
{
return 0;
// no need to restricted size symbol
return nullptr;
}
if ( markerSymbol )
@ -5038,7 +5058,8 @@ QgsSymbol *QgsSymbolLayerUtils::restrictedSizeSymbol( const QgsSymbol *s, double
height = size;
return ls;
}
return 0;
return nullptr;
}
QgsStringMap QgsSymbolLayerUtils::evaluatePropertiesMap( const QMap<QString, QgsProperty> &propertiesMap, const QgsExpressionContext &context )
@ -5051,4 +5072,3 @@ QgsStringMap QgsSymbolLayerUtils::evaluatePropertiesMap( const QMap<QString, Qgs
}
return properties;
}

View File

@ -881,10 +881,14 @@ class CORE_EXPORT QgsSymbolLayerUtils
* \param maxSize the maximum size in mm
* \param context the render context
* \param width expected width, can be changed by the function
* \param height expected height, can be changed by this function
* \return 0 if size is within minSize/maxSize range. New symbol if size was out of min/max range. Caller takes ownership
* \param height expected height, can be changed by the function
* \param ok if not nullptr, ok is set to false if it's not possible to compute a restricted symbol (if geometry generators
* are involved for instance)
* \return nullptr if size is within minSize/maxSize range or if it's not possible to compute a
* restricted size symbol. New symbol if size was out of min/max range.
* Caller takes ownership
*/
static QgsSymbol *restrictedSizeSymbol( const QgsSymbol *s, double minSize, double maxSize, QgsRenderContext *context, double &width, double &height );
static QgsSymbol *restrictedSizeSymbol( const QgsSymbol *s, double minSize, double maxSize, QgsRenderContext *context, double &width, double &height, bool *ok = nullptr );
/**
* Evaluates a map of properties using the given \a context and returns a variant map with evaluated expressions from the properties.
@ -925,5 +929,3 @@ class QPolygonF;
QList<QPolygonF> offsetLine( QPolygonF polyline, double dist, QgsWkbTypes::GeometryType geometryType ) SIP_SKIP;
#endif

View File

@ -31,6 +31,9 @@
#include "qgslegendsettings.h"
#include "qgsmarkersymbol.h"
#include "qgsannotationlayer.h"
#include "qgsgeometrygeneratorsymbollayer.h"
#include "qgsfillsymbol.h"
#include "qgsfillsymbollayer.h"
#include <QSignalSpy>
class TestQgsLayerTree : public QObject
@ -46,7 +49,10 @@ class TestQgsLayerTree : public QObject
void testCheckStateHiearchical();
void testCheckStateMutuallyExclusive();
void testCheckStateMutuallyExclusiveEdgeCases();
void testRestrictedSymbolSize_data();
void testRestrictedSymbolSize();
void testRestrictedSymbolSizeWithGeometryGenerator_data();
void testRestrictedSymbolSizeWithGeometryGenerator();
void testShowHideAllSymbolNodes();
void testFindLegendNode();
void testLegendSymbolCategorized();
@ -304,10 +310,26 @@ void TestQgsLayerTree::testCheckStateMutuallyExclusiveEdgeCases()
delete root3;
}
void TestQgsLayerTree::testRestrictedSymbolSize_data()
{
QTest::addColumn<double>( "maxSize" );
QTest::addColumn<int>( "expectedSize" );
// QTest::newRow( "smaller than max" ) << 15. << 52;
QTest::newRow( "bigger than max" ) << 10. << 40;
}
void TestQgsLayerTree::testRestrictedSymbolSize()
{
QFETCH( double, maxSize );
QFETCH( int, expectedSize );
// to force re-read of max/min size in QgsSymbolLegendNode constructor
QgsSymbolLegendNode::MINIMUM_SIZE = -1;
QgsSymbolLegendNode::MAXIMUM_SIZE = -1;
QgsSettings settings;
settings.setValue( "/qgis/legendsymbolMaximumSize", 15.0 );
settings.setValue( "/qgis/legendsymbolMaximumSize", maxSize );
//new memory layer
QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
@ -337,7 +359,71 @@ void TestQgsLayerTree::testRestrictedSymbolSize()
const QList<QgsLayerTreeModelLegendNode *> nodes = m->layerLegendNodes( n );
const QSize minimumSize = static_cast< QgsSymbolLegendNode *>( nodes.at( 0 ) )->minimumIconSize();
QCOMPARE( minimumSize.width(), 52 );
QCOMPARE( minimumSize.width(), expectedSize );
//cleanup
delete m;
delete root;
}
void TestQgsLayerTree::testRestrictedSymbolSizeWithGeometryGenerator_data()
{
QTest::addColumn<double>( "maxSize" );
QTest::addColumn<int>( "expectedSize" );
QTest::newRow( "smaller than max" ) << 15. << 42;
QTest::newRow( "bigger than max" ) << 10. << 38;
}
void TestQgsLayerTree::testRestrictedSymbolSizeWithGeometryGenerator()
{
QFETCH( double, maxSize );
QFETCH( int, expectedSize );
// to force re-read of max/min size in QgsSymbolLegendNode constructor
QgsSymbolLegendNode::MINIMUM_SIZE = -1;
QgsSymbolLegendNode::MAXIMUM_SIZE = -1;
QgsSettings settings;
settings.setValue( "/qgis/legendsymbolMaximumSize", maxSize );
//new memory layer
QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "Point?field=col1:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
QVERIFY( vl->isValid() );
QgsProject project;
project.addMapLayer( vl );
//create a categorized renderer with geometry generator for layer
QVariantMap ggProps;
ggProps.insert( QStringLiteral( "SymbolType" ), QStringLiteral( "Fill" ) );
ggProps.insert( QStringLiteral( "geometryModifier" ), QStringLiteral( "buffer( $geometry, 200 )" ) );
QgsSymbolLayer *ggSymbolLayer = QgsGeometryGeneratorSymbolLayer::create( ggProps );
QgsSymbolLayerList fillSymbolLayerList;
fillSymbolLayerList << new QgsSimpleFillSymbolLayer();
ggSymbolLayer->setSubSymbol( new QgsFillSymbol( fillSymbolLayerList ) );
QgsSymbolLayerList slList;
slList << ggSymbolLayer;
QgsMarkerSymbol *symbol = new QgsMarkerSymbol( slList );
QgsCategorizedSymbolRenderer *renderer = new QgsCategorizedSymbolRenderer();
renderer->setClassAttribute( QStringLiteral( "col1" ) );
renderer->setSourceSymbol( symbol->clone() );
renderer->addCategory( QgsRendererCategory( "a", symbol->clone(), QStringLiteral( "a" ) ) );
renderer->addCategory( QgsRendererCategory( "b", symbol->clone(), QStringLiteral( "b" ) ) );
vl->setRenderer( renderer );
//create legend with symbology nodes for categorized renderer
QgsLayerTree *root = new QgsLayerTree();
QgsLayerTreeLayer *n = new QgsLayerTreeLayer( vl );
root->addChildNode( n );
QgsLayerTreeModel *m = new QgsLayerTreeModel( root, nullptr );
m->setLegendMapViewData( 10, 96, 10 );
const QList<QgsLayerTreeModelLegendNode *> nodes = m->layerLegendNodes( n );
const QSize minimumSize = static_cast< QgsSymbolLegendNode *>( nodes.at( 0 ) )->minimumIconSize();
QCOMPARE( minimumSize.width(), expectedSize );
//cleanup
delete m;