Skip to content

Commit

Permalink
Add QgsFeatureRenderer::legendKeys method to retrieve all legend
Browse files Browse the repository at this point in the history
keys for the renderer
  • Loading branch information
nyalldawson authored and github-actions[bot] committed Mar 8, 2023
1 parent 74c11ee commit d52b461
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 0 deletions.
20 changes: 20 additions & 0 deletions python/core/auto_generated/symbology/qgsrenderer.sip.in
Expand Up @@ -302,6 +302,15 @@ the UserStyle element of a SLD style document
virtual void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props = QVariantMap() ) const;
%Docstring
used from subclasses to create SLD Rule elements following SLD v1.1 specs
%End

QSet< QString > legendKeys() const;
%Docstring
Returns the set of all legend keys used by the renderer.

.. seealso:: :py:func:`legendSymbolItems`

.. versionadded:: 3.32
%End

virtual bool legendSymbolItemsCheckable() const;
Expand All @@ -317,6 +326,8 @@ Returns ``True`` if the legend symbology item with the specified ``key`` is chec

.. seealso:: :py:func:`checkLegendSymbolItem`

.. seealso:: :py:func:`legendKeys`

.. versionadded:: 2.5
%End

Expand All @@ -326,6 +337,8 @@ Sets whether the legend symbology item with the specified ``ley`` should be chec

.. seealso:: :py:func:`legendSymbolItemChecked`

.. seealso:: :py:func:`legendKeys`

.. versionadded:: 2.5
%End

Expand All @@ -336,6 +349,8 @@ Sets the symbol to be used for a legend symbol item.
:param key: rule key for legend symbol
:param symbol: new symbol for legend item. Ownership is transferred to renderer.

.. seealso:: :py:func:`legendKeys`

.. versionadded:: 2.14
%End

Expand All @@ -351,13 +366,18 @@ the features displayed using that key.
- ok: will be set to ``True`` if legend key was successfully converted to a filter expression


.. seealso:: :py:func:`legendKeys`


.. versionadded:: 3.26
%End

virtual QgsLegendSymbolList legendSymbolItems() const;
%Docstring
Returns a list of symbology items for the legend

.. seealso:: :py:func:`legendKeys`

.. versionadded:: 2.6
%End

Expand Down
13 changes: 13 additions & 0 deletions src/core/symbology/qgsrenderer.cpp
Expand Up @@ -330,6 +330,19 @@ QgsFeatureRenderer *QgsFeatureRenderer::loadSld( const QDomNode &node, Qgis::Geo
return r;
}

QSet<QString> QgsFeatureRenderer::legendKeys() const
{
// build up a list of unique legend keys
const QgsLegendSymbolList allLegendSymbols = legendSymbolItems();
QSet< QString > keys;
keys.reserve( allLegendSymbols.size() );
for ( const QgsLegendSymbolItem &symbol : allLegendSymbols )
{
keys.insert( symbol.ruleKey() );
}
return keys;
}

QDomElement QgsFeatureRenderer::writeSld( QDomDocument &doc, const QString &styleName, const QVariantMap &props ) const
{
QDomElement userStyleElem = doc.createElement( QStringLiteral( "UserStyle" ) );
Expand Down
19 changes: 19 additions & 0 deletions src/core/symbology/qgsrenderer.h
Expand Up @@ -339,6 +339,15 @@ class CORE_EXPORT QgsFeatureRenderer
( void ) props; // warning avoidance
}

/**
* Returns the set of all legend keys used by the renderer.
*
* \see legendSymbolItems()
*
* \since QGIS 3.32
*/
QSet< QString > legendKeys() const;

/**
* Returns TRUE if symbology items in legend are checkable.
*
Expand All @@ -350,6 +359,7 @@ class CORE_EXPORT QgsFeatureRenderer
* Returns TRUE if the legend symbology item with the specified \a key is checked.
*
* \see checkLegendSymbolItem()
* \see legendKeys()
*
* \since QGIS 2.5
*/
Expand All @@ -359,6 +369,7 @@ class CORE_EXPORT QgsFeatureRenderer
* Sets whether the legend symbology item with the specified \a ley should be checked.
*
* \see legendSymbolItemChecked()
* \see legendKeys()
*
* \since QGIS 2.5
*/
Expand All @@ -368,6 +379,9 @@ class CORE_EXPORT QgsFeatureRenderer
* Sets the symbol to be used for a legend symbol item.
* \param key rule key for legend symbol
* \param symbol new symbol for legend item. Ownership is transferred to renderer.
*
* \see legendKeys()
*
* \since QGIS 2.14
*/
virtual void setLegendSymbolItem( const QString &key, QgsSymbol *symbol SIP_TRANSFER );
Expand All @@ -382,12 +396,17 @@ class CORE_EXPORT QgsFeatureRenderer
*
* \returns QGIS expression string for matching features with the specified key
*
* \see legendKeys()
*
* \since QGIS 3.26
*/
virtual QString legendKeyToExpression( const QString &key, QgsVectorLayer *layer, bool &ok SIP_OUT ) const;

/**
* Returns a list of symbology items for the legend
*
* \see legendKeys()
*
* \since QGIS 2.6
*/
virtual QgsLegendSymbolList legendSymbolItems() const;
Expand Down
27 changes: 27 additions & 0 deletions tests/src/core/testqgsrulebasedrenderer.cpp
Expand Up @@ -1152,6 +1152,33 @@ class TestQgsRuleBasedRenderer: public QgsTest
QCOMPARE( counter->featureCount( "2" ), 1LL );
}

void testLegendKeys()
{
QgsRuleBasedRenderer::Rule *rootRule = new QgsRuleBasedRenderer::Rule( nullptr );
std::unique_ptr< QgsRuleBasedRenderer > renderer = std::make_unique< QgsRuleBasedRenderer >( rootRule );

QVERIFY( renderer->legendKeys().empty() );

QgsRuleBasedRenderer::Rule *rule2 = new QgsRuleBasedRenderer::Rule( nullptr, 0, 0, "\"field_name\" = 5" );
QgsRuleBasedRenderer::Rule *rule3 = new QgsRuleBasedRenderer::Rule( nullptr, 2000, 0, "\"field_name\" = 6" );
QgsRuleBasedRenderer::Rule *rule4 = new QgsRuleBasedRenderer::Rule( nullptr, 0, 1000, "\"field_name\" = 7" );
QgsRuleBasedRenderer::Rule *rule5 = new QgsRuleBasedRenderer::Rule( nullptr, 1000, 3000 );

rootRule->appendChild( rule2 );
rootRule->appendChild( rule3 );
rootRule->appendChild( rule4 );
rootRule->appendChild( rule5 );

QSet< QString > expected = QSet< QString >
{
rule2->ruleKey(),
rule3->ruleKey(),
rule4->ruleKey(),
rule5->ruleKey()
};
QCOMPARE( renderer->legendKeys(), expected );
}

void testLegendKeyToExpression()
{
QgsRuleBasedRenderer::Rule *rootRule = new QgsRuleBasedRenderer::Rule( nullptr );
Expand Down
2 changes: 2 additions & 0 deletions tests/src/python/test_qgscategorizedsymbolrenderer.py
Expand Up @@ -412,6 +412,8 @@ def testLegendKeysWhileCounting(self):
default_symbol.setColor(QColor(255, 255, 255))
renderer.addCategory(QgsRendererCategory('', default_symbol, 'default'))

self.assertEqual(renderer.legendKeys(), {'0', '1', '2', '3', '4'})

context = QgsRenderContext()
context.setRendererScale(0) # simulate counting
renderer.startRender(context, fields)
Expand Down
15 changes: 15 additions & 0 deletions tests/src/python/test_qgsgraduatedsymbolrenderer.py
Expand Up @@ -482,6 +482,21 @@ def testFilterNeedsGeometry(self):
renderer.setClassAttribute("value - $area")
self.assertTrue(renderer.filterNeedsGeometry())

def test_legend_keys(self):
renderer = QgsGraduatedSymbolRenderer()
renderer.setClassAttribute('field_name')

self.assertFalse(renderer.legendKeys())

symbol_a = createMarkerSymbol()
renderer.addClassRange(QgsRendererRange(1, 2, symbol_a, 'a'))
symbol_b = createMarkerSymbol()
renderer.addClassRange(QgsRendererRange(5, 6, symbol_b, 'b'))
symbol_c = createMarkerSymbol()
renderer.addClassRange(QgsRendererRange(15.5, 16.5, symbol_c, 'c', False))

self.assertEqual(renderer.legendKeys(), {'0', '1', '2'})

def test_legend_key_to_expression(self):
renderer = QgsGraduatedSymbolRenderer()
renderer.setClassAttribute('field_name')
Expand Down
10 changes: 10 additions & 0 deletions tests/src/python/test_qgsmergedfeaturerenderer.py
Expand Up @@ -47,6 +47,16 @@ def tearDown(self):
with open(report_file_path, 'a') as report_file:
report_file.write(self.report)

def test_legend_keys(self):
symbol1 = QgsFillSymbol()
symbol2 = QgsFillSymbol()
sub_renderer = QgsCategorizedSymbolRenderer('cat', [QgsRendererCategory('cat1', symbol1, 'cat1'),
QgsRendererCategory('cat2', symbol2, 'cat2')
])

renderer = QgsMergedFeatureRenderer(sub_renderer)
self.assertEqual(renderer.legendKeys(), {'0', '1'})

def testSinglePolys(self):
source = QgsVectorLayer(os.path.join(TEST_DATA_DIR, 'polys_overlapping.shp'))
self.assertTrue(source.isValid())
Expand Down
14 changes: 14 additions & 0 deletions tests/src/python/test_qgspointclusterrenderer.py
Expand Up @@ -45,6 +45,8 @@
QgsSymbolLayer,
QgsUnitTypes,
QgsVectorLayer,
QgsCategorizedSymbolRenderer,
QgsRendererCategory
)
from qgis.testing import start_app, unittest

Expand Down Expand Up @@ -122,6 +124,18 @@ def testSaveCreate(self):
c = QgsPointClusterRenderer.create(elem, QgsReadWriteContext())
self._checkProperties(c)

def test_legend_keys(self):
symbol1 = QgsMarkerSymbol()
symbol2 = QgsMarkerSymbol()
sub_renderer = QgsCategorizedSymbolRenderer('cat', [QgsRendererCategory('cat1', symbol1, 'cat1'),
QgsRendererCategory('cat2', symbol2, 'cat2')
])

renderer = QgsPointClusterRenderer()
renderer.setEmbeddedRenderer(sub_renderer)

self.assertEqual(renderer.legendKeys(), {'0', '1'})

def testConvert(self):
""" test renderer conversion """

Expand Down
12 changes: 12 additions & 0 deletions tests/src/python/test_qgspointdisplacementrenderer.py
Expand Up @@ -485,6 +485,18 @@ def testClusterConcentricLabelsDifferentSizesFarther(self):
self.assertTrue(res)
self._tearDown(layer)

def test_legend_keys(self):
symbol1 = QgsMarkerSymbol()
symbol2 = QgsMarkerSymbol()
sub_renderer = QgsCategorizedSymbolRenderer('cat', [QgsRendererCategory('cat1', symbol1, 'cat1'),
QgsRendererCategory('cat2', symbol2, 'cat2')
])

renderer = QgsPointDisplacementRenderer()
renderer.setEmbeddedRenderer(sub_renderer)

self.assertEqual(renderer.legendKeys(), {'0', '1'})

def test_legend_key_to_expression(self):
sym1 = QgsMarkerSymbol.createSimple({'color': '#fdbf6f', 'outline_color': 'black'})
sub_renderer = QgsSingleSymbolRenderer(sym1)
Expand Down
6 changes: 6 additions & 0 deletions tests/src/python/test_qgssinglesymbolrenderer.py
Expand Up @@ -84,6 +84,12 @@ def testUsedAttributes(self):

self.assertCountEqual(self.renderer.usedAttributes(ctx), {})

def test_legend_keys(self):
sym1 = QgsFillSymbol.createSimple({'color': '#fdbf6f', 'outline_color': 'black'})
renderer = QgsSingleSymbolRenderer(sym1)

self.assertEqual(renderer.legendKeys(), {'0'})

def test_legend_key_to_expression(self):
sym1 = QgsFillSymbol.createSimple({'color': '#fdbf6f', 'outline_color': 'black'})
renderer = QgsSingleSymbolRenderer(sym1)
Expand Down

0 comments on commit d52b461

Please sign in to comment.