Skip to content

Commit

Permalink
[API] Allow categorized renderer category values to be lists of values
Browse files Browse the repository at this point in the history
When a category is a list of values, that category will apply to
any of the values from the list
  • Loading branch information
nyalldawson committed Jan 8, 2019
1 parent f3e7e1b commit d32714c
Show file tree
Hide file tree
Showing 4 changed files with 308 additions and 13 deletions.
Expand Up @@ -29,6 +29,8 @@ Constructor for QgsRendererCategory.
%Docstring
Constructor for a new QgsRendererCategory, with the specified ``value`` and ``symbol``.

If ``value`` is a list, then the category will match any of the values from this list.

The ownership of ``symbol`` is transferred to the category.

The ``label`` argument specifies the label used for this category in legends and the layer tree.
Expand All @@ -42,6 +44,8 @@ The ``render`` argument indicates whether the category should initially be rende
%Docstring
Returns the value corresponding to this category.

If the returned value is a list, then the category will match any of the values from this list.

.. seealso:: :py:func:`setValue`
%End

Expand All @@ -64,6 +68,8 @@ legends and the layer tree.
%Docstring
Sets the ``value`` corresponding to this category.

If ``value`` is a list, then the category will match any of the values from this list.

.. seealso:: :py:func:`value`
%End

Expand Down Expand Up @@ -179,8 +185,38 @@ Returns index of category with specified label (-1 if not found or not unique)
%End

bool updateCategoryValue( int catIndex, const QVariant &value );
%Docstring
Changes the value for the category with the specified index.

If ``value`` is a list, then the category will match any of the values from this list.

.. seealso:: :py:func:`updateCategorySymbol`

.. seealso:: :py:func:`updateCategoryLabel`
%End

bool updateCategorySymbol( int catIndex, QgsSymbol *symbol /Transfer/ );
%Docstring
Changes the ``symbol`` for the category with the specified index.

Ownership of ``symbol`` is transferred to the renderer.

.. seealso:: :py:func:`updateCategoryValue`

.. seealso:: :py:func:`updateCategoryLabel`
%End

bool updateCategoryLabel( int catIndex, const QString &label );
%Docstring
Changes the ``label`` for the category with the specified index.

A category's label is used to represent the category within
legends and the layer tree.

.. seealso:: :py:func:`updateCategoryValue`

.. seealso:: :py:func:`updateCategoryLabel`
%End

bool updateCategoryRenderState( int catIndex, bool render );
%Docstring
Expand Down
115 changes: 103 additions & 12 deletions src/core/symbology/qgscategorizedsymbolrenderer.cpp
Expand Up @@ -179,10 +179,22 @@ void QgsCategorizedSymbolRenderer::rebuildHash()
{
mSymbolHash.clear();

for ( int i = 0; i < mCategories.size(); ++i )
for ( const QgsRendererCategory &cat : qgis::as_const( mCategories ) )
{
const QgsRendererCategory &cat = mCategories.at( i );
mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
const QVariant val = cat.value();
QString valAsString;
if ( val.type() == QVariant::List )
{
const QVariantList list = val.toList();
for ( const QVariant &v : list )
{
mSymbolHash.insert( v.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
}
}
else
{
mSymbolHash.insert( val.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
}
}
}

Expand Down Expand Up @@ -530,26 +542,55 @@ QString QgsCategorizedSymbolRenderer::filter( const QgsFields &fields )
allActive = allActive && cat.renderState();

QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
const bool isList = cat.value().type() == QVariant::List;
QString value = QgsExpression::quotedValue( cat.value(), valType );

if ( !cat.renderState() )
{
if ( cat.value() != "" )
{
if ( !inactiveValues.isEmpty() )
inactiveValues.append( ',' );
if ( isList )
{
const QVariantList list = cat.value().toList();
for ( const QVariant &v : list )
{
if ( !inactiveValues.isEmpty() )
inactiveValues.append( ',' );

inactiveValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
}
}
else
{
if ( !inactiveValues.isEmpty() )
inactiveValues.append( ',' );

inactiveValues.append( value );
inactiveValues.append( value );
}
}
}
else
{
if ( cat.value() != "" )
{
if ( !activeValues.isEmpty() )
activeValues.append( ',' );
if ( isList )
{
const QVariantList list = cat.value().toList();
for ( const QVariant &v : list )
{
if ( !activeValues.isEmpty() )
activeValues.append( ',' );

activeValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
}
}
else
{
if ( !activeValues.isEmpty() )
activeValues.append( ',' );

activeValues.append( value );
activeValues.append( value );
}
}
}
}
Expand Down Expand Up @@ -604,7 +645,26 @@ QgsFeatureRenderer *QgsCategorizedSymbolRenderer::create( QDomElement &element,
{
if ( catElem.tagName() == QLatin1String( "category" ) )
{
QVariant value = QVariant( catElem.attribute( QStringLiteral( "value" ) ) );
QVariant value;
if ( catElem.hasAttribute( QStringLiteral( "value" ) ) )
{
value = QVariant( catElem.attribute( QStringLiteral( "value" ) ) );
}
else
{
QVariantList values;
QDomElement valElem = catElem.firstChildElement();
while ( !valElem.isNull() )
{
if ( valElem.tagName() == QLatin1String( "val" ) )
{
values << QVariant( valElem.attribute( QStringLiteral( "value" ) ) );
}
valElem = valElem.nextSiblingElement();
}
if ( !values.isEmpty() )
value = values;
}
QString symbolName = catElem.attribute( QStringLiteral( "symbol" ) );
QString label = catElem.attribute( QStringLiteral( "label" ) );
bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
Expand Down Expand Up @@ -706,7 +766,20 @@ QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsRead
symbols.insert( symbolName, cat.symbol() );

QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
if ( cat.value().type() == QVariant::List )
{
const QVariantList list = cat.value().toList();
for ( const QVariant &v : list )
{
QDomElement valueElem = doc.createElement( QStringLiteral( "val" ) );
valueElem.setAttribute( "value", v.toString() );
catElem.appendChild( valueElem );
}
}
else
{
catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
}
catElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
catElem.setAttribute( QStringLiteral( "label" ), cat.label() );
catElem.setAttribute( QStringLiteral( "render" ), cat.renderState() ? "true" : "false" );
Expand Down Expand Up @@ -823,7 +896,25 @@ QSet<QString> QgsCategorizedSymbolRenderer::legendKeysForFeature( const QgsFeatu

for ( const QgsRendererCategory &cat : mCategories )
{
if ( value == cat.value() )
bool match = false;
if ( cat.value().type() == QVariant::List )
{
const QVariantList list = cat.value().toList();
for ( const QVariant &v : list )
{
if ( value == v )
{
match = true;
break;
}
}
}
else
{
match = value == cat.value();
}

if ( match )
{
if ( cat.renderState() || mCounting )
return QSet< QString >() << QString::number( i );
Expand Down
35 changes: 35 additions & 0 deletions src/core/symbology/qgscategorizedsymbolrenderer.h
Expand Up @@ -44,6 +44,8 @@ class CORE_EXPORT QgsRendererCategory
/**
* Constructor for a new QgsRendererCategory, with the specified \a value and \a symbol.
*
* If \a value is a list, then the category will match any of the values from this list.
*
* The ownership of \a symbol is transferred to the category.
*
* The \a label argument specifies the label used for this category in legends and the layer tree.
Expand All @@ -57,6 +59,9 @@ class CORE_EXPORT QgsRendererCategory

/**
* Returns the value corresponding to this category.
*
* If the returned value is a list, then the category will match any of the values from this list.
*
* \see setValue()
*/
QVariant value() const;
Expand All @@ -76,6 +81,9 @@ class CORE_EXPORT QgsRendererCategory

/**
* Sets the \a value corresponding to this category.
*
* If \a value is a list, then the category will match any of the values from this list.
*
* \see value()
*/
void setValue( const QVariant &value );
Expand Down Expand Up @@ -175,8 +183,35 @@ class CORE_EXPORT QgsCategorizedSymbolRenderer : public QgsFeatureRenderer
*/
int categoryIndexForLabel( const QString &val );

/**
* Changes the value for the category with the specified index.
*
* If \a value is a list, then the category will match any of the values from this list.
*
* \see updateCategorySymbol()
* \see updateCategoryLabel()
*/
bool updateCategoryValue( int catIndex, const QVariant &value );

/**
* Changes the \a symbol for the category with the specified index.
*
* Ownership of \a symbol is transferred to the renderer.
*
* \see updateCategoryValue()
* \see updateCategoryLabel()
*/
bool updateCategorySymbol( int catIndex, QgsSymbol *symbol SIP_TRANSFER );

/**
* Changes the \a label for the category with the specified index.
*
* A category's label is used to represent the category within
* legends and the layer tree.
*
* \see updateCategoryValue()
* \see updateCategoryLabel()
*/
bool updateCategoryLabel( int catIndex, const QString &label );

//! \since QGIS 2.5
Expand Down

0 comments on commit d32714c

Please sign in to comment.