LCOV - code coverage report
Current view: top level - core/symbology - qgscategorizedsymbolrenderer.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 728 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgscategorizedsymbolrenderer.cpp
       3                 :            :     ---------------------
       4                 :            :     begin                : November 2009
       5                 :            :     copyright            : (C) 2009 by Martin Dobias
       6                 :            :     email                : wonder dot sk at gmail dot com
       7                 :            :  ***************************************************************************
       8                 :            :  *                                                                         *
       9                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      10                 :            :  *   it under the terms of the GNU General Public License as published by  *
      11                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      12                 :            :  *   (at your option) any later version.                                   *
      13                 :            :  *                                                                         *
      14                 :            :  ***************************************************************************/
      15                 :            : #include <algorithm>
      16                 :            : 
      17                 :            : #include "qgscategorizedsymbolrenderer.h"
      18                 :            : 
      19                 :            : #include "qgsdatadefinedsizelegend.h"
      20                 :            : #include "qgssymbol.h"
      21                 :            : #include "qgssymbollayerutils.h"
      22                 :            : #include "qgscolorramp.h"
      23                 :            : #include "qgsgraduatedsymbolrenderer.h"
      24                 :            : #include "qgspointdisplacementrenderer.h"
      25                 :            : #include "qgsinvertedpolygonrenderer.h"
      26                 :            : #include "qgspainteffect.h"
      27                 :            : #include "qgspainteffectregistry.h"
      28                 :            : #include "qgssymbollayer.h"
      29                 :            : #include "qgsfeature.h"
      30                 :            : #include "qgsvectorlayer.h"
      31                 :            : #include "qgslogger.h"
      32                 :            : #include "qgsproperty.h"
      33                 :            : #include "qgsstyle.h"
      34                 :            : #include "qgsfieldformatter.h"
      35                 :            : #include "qgsfieldformatterregistry.h"
      36                 :            : #include "qgsapplication.h"
      37                 :            : #include "qgsexpressioncontextutils.h"
      38                 :            : #include "qgsstyleentityvisitor.h"
      39                 :            : #include "qgsembeddedsymbolrenderer.h"
      40                 :            : 
      41                 :            : #include <QDomDocument>
      42                 :            : #include <QDomElement>
      43                 :            : #include <QSettings> // for legend
      44                 :            : #include <QRegularExpression>
      45                 :            : 
      46                 :          0 : QgsRendererCategory::QgsRendererCategory( const QVariant &value, QgsSymbol *symbol, const QString &label, bool render )
      47                 :          0 :   : mValue( value )
      48                 :          0 :   , mSymbol( symbol )
      49                 :          0 :   , mLabel( label )
      50                 :          0 :   , mRender( render )
      51                 :            : {
      52                 :          0 : }
      53                 :            : 
      54                 :          0 : QgsRendererCategory::QgsRendererCategory( const QgsRendererCategory &cat )
      55                 :          0 :   : mValue( cat.mValue )
      56                 :          0 :   , mSymbol( cat.mSymbol ? cat.mSymbol->clone() : nullptr )
      57                 :          0 :   , mLabel( cat.mLabel )
      58                 :          0 :   , mRender( cat.mRender )
      59                 :            : {
      60                 :          0 : }
      61                 :            : 
      62                 :            : // copy+swap idion, the copy is done through the 'pass by value'
      63                 :          0 : QgsRendererCategory &QgsRendererCategory::operator=( QgsRendererCategory cat )
      64                 :            : {
      65                 :          0 :   swap( cat );
      66                 :          0 :   return *this;
      67                 :            : }
      68                 :            : 
      69                 :          0 : void QgsRendererCategory::swap( QgsRendererCategory &cat )
      70                 :            : {
      71                 :          0 :   std::swap( mValue, cat.mValue );
      72                 :          0 :   std::swap( mSymbol, cat.mSymbol );
      73                 :          0 :   std::swap( mLabel, cat.mLabel );
      74                 :          0 : }
      75                 :            : 
      76                 :          0 : QVariant QgsRendererCategory::value() const
      77                 :            : {
      78                 :          0 :   return mValue;
      79                 :            : }
      80                 :            : 
      81                 :          0 : QgsSymbol *QgsRendererCategory::symbol() const
      82                 :            : {
      83                 :          0 :   return mSymbol.get();
      84                 :            : }
      85                 :            : 
      86                 :          0 : QString QgsRendererCategory::label() const
      87                 :            : {
      88                 :          0 :   return mLabel;
      89                 :            : }
      90                 :            : 
      91                 :          0 : bool QgsRendererCategory::renderState() const
      92                 :            : {
      93                 :          0 :   return mRender;
      94                 :            : }
      95                 :            : 
      96                 :          0 : void QgsRendererCategory::setValue( const QVariant &value )
      97                 :            : {
      98                 :          0 :   mValue = value;
      99                 :          0 : }
     100                 :            : 
     101                 :          0 : void QgsRendererCategory::setSymbol( QgsSymbol *s )
     102                 :            : {
     103                 :          0 :   if ( mSymbol.get() != s ) mSymbol.reset( s );
     104                 :          0 : }
     105                 :            : 
     106                 :          0 : void QgsRendererCategory::setLabel( const QString &label )
     107                 :            : {
     108                 :          0 :   mLabel = label;
     109                 :          0 : }
     110                 :            : 
     111                 :          0 : void QgsRendererCategory::setRenderState( bool render )
     112                 :            : {
     113                 :          0 :   mRender = render;
     114                 :          0 : }
     115                 :            : 
     116                 :          0 : QString QgsRendererCategory::dump() const
     117                 :            : {
     118                 :          0 :   return QStringLiteral( "%1::%2::%3:%4\n" ).arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
     119                 :          0 : }
     120                 :            : 
     121                 :          0 : void QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
     122                 :            : {
     123                 :          0 :   if ( !mSymbol.get() || props.value( QStringLiteral( "attribute" ), QString() ).toString().isEmpty() )
     124                 :          0 :     return;
     125                 :            : 
     126                 :          0 :   QString attrName = props[ QStringLiteral( "attribute" )].toString();
     127                 :            : 
     128                 :          0 :   QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
     129                 :          0 :   element.appendChild( ruleElem );
     130                 :            : 
     131                 :          0 :   QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
     132                 :          0 :   nameElem.appendChild( doc.createTextNode( mLabel ) );
     133                 :          0 :   ruleElem.appendChild( nameElem );
     134                 :            : 
     135                 :          0 :   QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
     136                 :          0 :   QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
     137                 :          0 :   QString descrStr = QStringLiteral( "%1 is '%2'" ).arg( attrName, mValue.toString() );
     138                 :          0 :   titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
     139                 :          0 :   descrElem.appendChild( titleElem );
     140                 :          0 :   ruleElem.appendChild( descrElem );
     141                 :            : 
     142                 :            :   // create the ogc:Filter for the range
     143                 :          0 :   QString filterFunc;
     144                 :          0 :   if ( mValue.isNull() || mValue.toString().isEmpty() )
     145                 :            :   {
     146                 :          0 :     filterFunc = QStringLiteral( "%1 = '%2' or %1 is null" )
     147                 :          0 :                  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
     148                 :          0 :                        mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
     149                 :          0 :   }
     150                 :            :   else
     151                 :            :   {
     152                 :          0 :     filterFunc = QStringLiteral( "%1 = '%2'" )
     153                 :          0 :                  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
     154                 :          0 :                        mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
     155                 :            :   }
     156                 :            : 
     157                 :          0 :   QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
     158                 :            : 
     159                 :            :   // add the mix/max scale denoms if we got any from the callers
     160                 :          0 :   QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
     161                 :            : 
     162                 :          0 :   mSymbol->toSld( doc, ruleElem, props );
     163                 :          0 : }
     164                 :            : 
     165                 :            : ///////////////////
     166                 :            : 
     167                 :          0 : QgsCategorizedSymbolRenderer::QgsCategorizedSymbolRenderer( const QString &attrName, const QgsCategoryList &categories )
     168                 :          0 :   : QgsFeatureRenderer( QStringLiteral( "categorizedSymbol" ) )
     169                 :          0 :   , mAttrName( attrName )
     170                 :          0 : {
     171                 :            :   //important - we need a deep copy of the categories list, not a shared copy. This is required because
     172                 :            :   //QgsRendererCategory::symbol() is marked const, and so retrieving the symbol via this method does not
     173                 :            :   //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
     174                 :          0 :   for ( const QgsRendererCategory &cat : categories )
     175                 :            :   {
     176                 :          0 :     if ( !cat.symbol() )
     177                 :            :     {
     178                 :          0 :       QgsDebugMsg( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
     179                 :          0 :     }
     180                 :          0 :     mCategories << cat;
     181                 :            :   }
     182                 :          0 : }
     183                 :            : 
     184                 :          0 : QgsCategorizedSymbolRenderer::~QgsCategorizedSymbolRenderer() = default;
     185                 :            : 
     186                 :          0 : void QgsCategorizedSymbolRenderer::rebuildHash()
     187                 :            : {
     188                 :          0 :   mSymbolHash.clear();
     189                 :            : 
     190                 :          0 :   for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
     191                 :            :   {
     192                 :          0 :     const QVariant val = cat.value();
     193                 :          0 :     if ( val.type() == QVariant::List )
     194                 :            :     {
     195                 :          0 :       const QVariantList list = val.toList();
     196                 :          0 :       for ( const QVariant &v : list )
     197                 :            :       {
     198                 :          0 :         mSymbolHash.insert( v.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
     199                 :            :       }
     200                 :          0 :     }
     201                 :            :     else
     202                 :            :     {
     203                 :          0 :       mSymbolHash.insert( val.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
     204                 :            :     }
     205                 :          0 :   }
     206                 :          0 : }
     207                 :            : 
     208                 :          0 : QgsSymbol *QgsCategorizedSymbolRenderer::skipRender()
     209                 :            : {
     210                 :          0 :   return nullptr;
     211                 :            : }
     212                 :            : 
     213                 :          0 : QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value ) const
     214                 :            : {
     215                 :          0 :   bool found = false;
     216                 :          0 :   return symbolForValue( value, found );
     217                 :            : }
     218                 :            : 
     219                 :          0 : QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol ) const
     220                 :            : {
     221                 :          0 :   foundMatchingSymbol = false;
     222                 :            : 
     223                 :            :   // TODO: special case for int, double
     224                 :          0 :   QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( value.isNull() ? QString() : value.toString() );
     225                 :          0 :   if ( it == mSymbolHash.constEnd() )
     226                 :            :   {
     227                 :          0 :     if ( mSymbolHash.isEmpty() )
     228                 :            :     {
     229                 :          0 :       QgsDebugMsg( QStringLiteral( "there are no hashed symbols!!!" ) );
     230                 :          0 :     }
     231                 :            :     else
     232                 :            :     {
     233                 :          0 :       QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
     234                 :            :     }
     235                 :          0 :     return nullptr;
     236                 :            :   }
     237                 :            : 
     238                 :          0 :   foundMatchingSymbol = true;
     239                 :            : 
     240                 :          0 :   return *it;
     241                 :          0 : }
     242                 :            : 
     243                 :          0 : QgsSymbol *QgsCategorizedSymbolRenderer::symbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
     244                 :            : {
     245                 :          0 :   return originalSymbolForFeature( feature, context );
     246                 :            : }
     247                 :            : 
     248                 :          0 : QVariant QgsCategorizedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
     249                 :            : {
     250                 :          0 :   QgsAttributes attrs = feature.attributes();
     251                 :          0 :   QVariant value;
     252                 :          0 :   if ( mAttrNum == -1 )
     253                 :            :   {
     254                 :            :     Q_ASSERT( mExpression );
     255                 :            : 
     256                 :          0 :     value = mExpression->evaluate( &context.expressionContext() );
     257                 :          0 :   }
     258                 :            :   else
     259                 :            :   {
     260                 :          0 :     value = attrs.value( mAttrNum );
     261                 :            :   }
     262                 :            : 
     263                 :          0 :   return value;
     264                 :          0 : }
     265                 :            : 
     266                 :          0 : QgsSymbol *QgsCategorizedSymbolRenderer::originalSymbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
     267                 :            : {
     268                 :          0 :   QVariant value = valueForFeature( feature, context );
     269                 :            : 
     270                 :          0 :   bool foundCategory = false;
     271                 :            :   // find the right symbol for the category
     272                 :          0 :   QgsSymbol *symbol = symbolForValue( value, foundCategory );
     273                 :            : 
     274                 :          0 :   if ( !foundCategory )
     275                 :            :   {
     276                 :            :     // if no symbol found, use default symbol
     277                 :          0 :     return symbolForValue( QVariant( "" ), foundCategory );
     278                 :            :   }
     279                 :            : 
     280                 :          0 :   return symbol;
     281                 :          0 : }
     282                 :            : 
     283                 :            : 
     284                 :          0 : int QgsCategorizedSymbolRenderer::categoryIndexForValue( const QVariant &val )
     285                 :            : {
     286                 :          0 :   for ( int i = 0; i < mCategories.count(); i++ )
     287                 :            :   {
     288                 :          0 :     if ( mCategories[i].value() == val )
     289                 :          0 :       return i;
     290                 :          0 :   }
     291                 :          0 :   return -1;
     292                 :          0 : }
     293                 :            : 
     294                 :          0 : int QgsCategorizedSymbolRenderer::categoryIndexForLabel( const QString &val )
     295                 :            : {
     296                 :          0 :   int idx = -1;
     297                 :          0 :   for ( int i = 0; i < mCategories.count(); i++ )
     298                 :            :   {
     299                 :          0 :     if ( mCategories[i].label() == val )
     300                 :            :     {
     301                 :          0 :       if ( idx != -1 )
     302                 :          0 :         return -1;
     303                 :            :       else
     304                 :          0 :         idx = i;
     305                 :          0 :     }
     306                 :          0 :   }
     307                 :          0 :   return idx;
     308                 :          0 : }
     309                 :            : 
     310                 :          0 : bool QgsCategorizedSymbolRenderer::updateCategoryValue( int catIndex, const QVariant &value )
     311                 :            : {
     312                 :          0 :   if ( catIndex < 0 || catIndex >= mCategories.size() )
     313                 :          0 :     return false;
     314                 :          0 :   mCategories[catIndex].setValue( value );
     315                 :          0 :   return true;
     316                 :          0 : }
     317                 :            : 
     318                 :          0 : bool QgsCategorizedSymbolRenderer::updateCategorySymbol( int catIndex, QgsSymbol *symbol )
     319                 :            : {
     320                 :          0 :   if ( catIndex < 0 || catIndex >= mCategories.size() )
     321                 :          0 :     return false;
     322                 :          0 :   mCategories[catIndex].setSymbol( symbol );
     323                 :          0 :   return true;
     324                 :          0 : }
     325                 :            : 
     326                 :          0 : bool QgsCategorizedSymbolRenderer::updateCategoryLabel( int catIndex, const QString &label )
     327                 :            : {
     328                 :          0 :   if ( catIndex < 0 || catIndex >= mCategories.size() )
     329                 :          0 :     return false;
     330                 :          0 :   mCategories[catIndex].setLabel( label );
     331                 :          0 :   return true;
     332                 :          0 : }
     333                 :            : 
     334                 :          0 : bool QgsCategorizedSymbolRenderer::updateCategoryRenderState( int catIndex, bool render )
     335                 :            : {
     336                 :          0 :   if ( catIndex < 0 || catIndex >= mCategories.size() )
     337                 :          0 :     return false;
     338                 :          0 :   mCategories[catIndex].setRenderState( render );
     339                 :          0 :   return true;
     340                 :          0 : }
     341                 :            : 
     342                 :          0 : void QgsCategorizedSymbolRenderer::addCategory( const QgsRendererCategory &cat )
     343                 :            : {
     344                 :          0 :   if ( !cat.symbol() )
     345                 :            :   {
     346                 :          0 :     QgsDebugMsg( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
     347                 :          0 :     return;
     348                 :            :   }
     349                 :            : 
     350                 :          0 :   mCategories.append( cat );
     351                 :          0 : }
     352                 :            : 
     353                 :          0 : bool QgsCategorizedSymbolRenderer::deleteCategory( int catIndex )
     354                 :            : {
     355                 :          0 :   if ( catIndex < 0 || catIndex >= mCategories.size() )
     356                 :          0 :     return false;
     357                 :            : 
     358                 :          0 :   mCategories.removeAt( catIndex );
     359                 :          0 :   return true;
     360                 :          0 : }
     361                 :            : 
     362                 :          0 : void QgsCategorizedSymbolRenderer::deleteAllCategories()
     363                 :            : {
     364                 :          0 :   mCategories.clear();
     365                 :          0 : }
     366                 :            : 
     367                 :          0 : void QgsCategorizedSymbolRenderer::moveCategory( int from, int to )
     368                 :            : {
     369                 :          0 :   if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
     370                 :          0 :   mCategories.move( from, to );
     371                 :          0 : }
     372                 :            : 
     373                 :          0 : bool valueLessThan( const QgsRendererCategory &c1, const QgsRendererCategory &c2 )
     374                 :            : {
     375                 :          0 :   return qgsVariantLessThan( c1.value(), c2.value() );
     376                 :          0 : }
     377                 :          0 : bool valueGreaterThan( const QgsRendererCategory &c1, const QgsRendererCategory &c2 )
     378                 :            : {
     379                 :          0 :   return qgsVariantGreaterThan( c1.value(), c2.value() );
     380                 :          0 : }
     381                 :            : 
     382                 :          0 : void QgsCategorizedSymbolRenderer::sortByValue( Qt::SortOrder order )
     383                 :            : {
     384                 :          0 :   if ( order == Qt::AscendingOrder )
     385                 :            :   {
     386                 :          0 :     std::sort( mCategories.begin(), mCategories.end(), valueLessThan );
     387                 :          0 :   }
     388                 :            :   else
     389                 :            :   {
     390                 :          0 :     std::sort( mCategories.begin(), mCategories.end(), valueGreaterThan );
     391                 :            :   }
     392                 :          0 : }
     393                 :            : 
     394                 :          0 : bool labelLessThan( const QgsRendererCategory &c1, const QgsRendererCategory &c2 )
     395                 :            : {
     396                 :          0 :   return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
     397                 :          0 : }
     398                 :            : 
     399                 :          0 : bool labelGreaterThan( const QgsRendererCategory &c1, const QgsRendererCategory &c2 )
     400                 :            : {
     401                 :          0 :   return !labelLessThan( c1, c2 );
     402                 :            : }
     403                 :            : 
     404                 :          0 : void QgsCategorizedSymbolRenderer::sortByLabel( Qt::SortOrder order )
     405                 :            : {
     406                 :          0 :   if ( order == Qt::AscendingOrder )
     407                 :            :   {
     408                 :          0 :     std::sort( mCategories.begin(), mCategories.end(), labelLessThan );
     409                 :          0 :   }
     410                 :            :   else
     411                 :            :   {
     412                 :          0 :     std::sort( mCategories.begin(), mCategories.end(), labelGreaterThan );
     413                 :            :   }
     414                 :          0 : }
     415                 :            : 
     416                 :          0 : void QgsCategorizedSymbolRenderer::startRender( QgsRenderContext &context, const QgsFields &fields )
     417                 :            : {
     418                 :          0 :   QgsFeatureRenderer::startRender( context, fields );
     419                 :            : 
     420                 :          0 :   mCounting = context.rendererScale() == 0.0;
     421                 :            : 
     422                 :            :   // make sure that the hash table is up to date
     423                 :          0 :   rebuildHash();
     424                 :            : 
     425                 :            :   // find out classification attribute index from name
     426                 :          0 :   mAttrNum = fields.lookupField( mAttrName );
     427                 :          0 :   if ( mAttrNum == -1 )
     428                 :            :   {
     429                 :          0 :     mExpression.reset( new QgsExpression( mAttrName ) );
     430                 :          0 :     mExpression->prepare( &context.expressionContext() );
     431                 :          0 :   }
     432                 :            : 
     433                 :          0 :   for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
     434                 :            :   {
     435                 :          0 :     cat.symbol()->startRender( context, fields );
     436                 :            :   }
     437                 :          0 : }
     438                 :            : 
     439                 :          0 : void QgsCategorizedSymbolRenderer::stopRender( QgsRenderContext &context )
     440                 :            : {
     441                 :          0 :   QgsFeatureRenderer::stopRender( context );
     442                 :            : 
     443                 :          0 :   for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
     444                 :            :   {
     445                 :          0 :     cat.symbol()->stopRender( context );
     446                 :            :   }
     447                 :          0 :   mExpression.reset();
     448                 :          0 : }
     449                 :            : 
     450                 :          0 : QSet<QString> QgsCategorizedSymbolRenderer::usedAttributes( const QgsRenderContext &context ) const
     451                 :            : {
     452                 :          0 :   QSet<QString> attributes;
     453                 :          0 : 
     454                 :            :   // mAttrName can contain either attribute name or an expression.
     455                 :            :   // Sometimes it is not possible to distinguish between those two,
     456                 :            :   // e.g. "a - b" can be both a valid attribute name or expression.
     457                 :          0 :   // Since we do not have access to fields here, try both options.
     458                 :          0 :   attributes << mAttrName;
     459                 :            : 
     460                 :          0 :   QgsExpression testExpr( mAttrName );
     461                 :          0 :   if ( !testExpr.hasParserError() )
     462                 :          0 :     attributes.unite( testExpr.referencedColumns() );
     463                 :            : 
     464                 :          0 :   QgsCategoryList::const_iterator catIt = mCategories.constBegin();
     465                 :          0 :   for ( ; catIt != mCategories.constEnd(); ++catIt )
     466                 :            :   {
     467                 :          0 :     QgsSymbol *catSymbol = catIt->symbol();
     468                 :          0 :     if ( catSymbol )
     469                 :            :     {
     470                 :          0 :       attributes.unite( catSymbol->usedAttributes( context ) );
     471                 :          0 :     }
     472                 :          0 :   }
     473                 :          0 :   return attributes;
     474                 :          0 : }
     475                 :            : 
     476                 :          0 : bool QgsCategorizedSymbolRenderer::filterNeedsGeometry() const
     477                 :            : {
     478                 :          0 :   QgsExpression testExpr( mAttrName );
     479                 :          0 :   if ( !testExpr.hasParserError() )
     480                 :            :   {
     481                 :          0 :     QgsExpressionContext context;
     482                 :          0 :     context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); // unfortunately no layer access available!
     483                 :          0 :     testExpr.prepare( &context );
     484                 :          0 :     return testExpr.needsGeometry();
     485                 :          0 :   }
     486                 :          0 :   return false;
     487                 :          0 : }
     488                 :            : 
     489                 :          0 : QString QgsCategorizedSymbolRenderer::dump() const
     490                 :            : {
     491                 :          0 :   QString s = QStringLiteral( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
     492                 :          0 :   for ( int i = 0; i < mCategories.count(); i++ )
     493                 :          0 :     s += mCategories[i].dump();
     494                 :          0 :   return s;
     495                 :          0 : }
     496                 :            : 
     497                 :          0 : QgsCategorizedSymbolRenderer *QgsCategorizedSymbolRenderer::clone() const
     498                 :            : {
     499                 :          0 :   QgsCategorizedSymbolRenderer *r = new QgsCategorizedSymbolRenderer( mAttrName, mCategories );
     500                 :          0 :   if ( mSourceSymbol )
     501                 :          0 :     r->setSourceSymbol( mSourceSymbol->clone() );
     502                 :          0 :   if ( mSourceColorRamp )
     503                 :            :   {
     504                 :          0 :     r->setSourceColorRamp( mSourceColorRamp->clone() );
     505                 :          0 :   }
     506                 :          0 :   r->setUsingSymbolLevels( usingSymbolLevels() );
     507                 :          0 :   r->setDataDefinedSizeLegend( mDataDefinedSizeLegend ? new QgsDataDefinedSizeLegend( *mDataDefinedSizeLegend ) : nullptr );
     508                 :            : 
     509                 :          0 :   copyRendererData( r );
     510                 :          0 :   return r;
     511                 :          0 : }
     512                 :            : 
     513                 :          0 : void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
     514                 :            : {
     515                 :          0 :   QVariantMap newProps = props;
     516                 :          0 :   newProps[ QStringLiteral( "attribute" )] = mAttrName;
     517                 :            : 
     518                 :            :   // create a Rule for each range
     519                 :          0 :   for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
     520                 :            :   {
     521                 :          0 :     it->toSld( doc, element, newProps );
     522                 :          0 :   }
     523                 :          0 : }
     524                 :            : 
     525                 :          0 : QString QgsCategorizedSymbolRenderer::filter( const QgsFields &fields )
     526                 :            : {
     527                 :          0 :   int attrNum = fields.lookupField( mAttrName );
     528                 :          0 :   bool isExpression = ( attrNum == -1 );
     529                 :            : 
     530                 :          0 :   bool hasDefault = false;
     531                 :          0 :   bool defaultActive = false;
     532                 :          0 :   bool allActive = true;
     533                 :          0 :   bool noneActive = true;
     534                 :            : 
     535                 :            :   //we need to build lists of both inactive and active values, as either list may be required
     536                 :            :   //depending on whether the default category is active or not
     537                 :          0 :   QString activeValues;
     538                 :          0 :   QString inactiveValues;
     539                 :            : 
     540                 :          0 :   for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
     541                 :            :   {
     542                 :          0 :     if ( cat.value() == "" || cat.value().isNull() )
     543                 :            :     {
     544                 :          0 :       hasDefault = true;
     545                 :          0 :       defaultActive = cat.renderState();
     546                 :          0 :     }
     547                 :            : 
     548                 :          0 :     noneActive = noneActive && !cat.renderState();
     549                 :          0 :     allActive = allActive && cat.renderState();
     550                 :            : 
     551                 :          0 :     QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
     552                 :          0 :     const bool isList = cat.value().type() == QVariant::List;
     553                 :          0 :     QString value = QgsExpression::quotedValue( cat.value(), valType );
     554                 :            : 
     555                 :          0 :     if ( !cat.renderState() )
     556                 :            :     {
     557                 :          0 :       if ( cat.value() != "" )
     558                 :            :       {
     559                 :          0 :         if ( isList )
     560                 :            :         {
     561                 :          0 :           const QVariantList list = cat.value().toList();
     562                 :          0 :           for ( const QVariant &v : list )
     563                 :            :           {
     564                 :          0 :             if ( !inactiveValues.isEmpty() )
     565                 :          0 :               inactiveValues.append( ',' );
     566                 :            : 
     567                 :          0 :             inactiveValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
     568                 :            :           }
     569                 :          0 :         }
     570                 :            :         else
     571                 :            :         {
     572                 :          0 :           if ( !inactiveValues.isEmpty() )
     573                 :          0 :             inactiveValues.append( ',' );
     574                 :            : 
     575                 :          0 :           inactiveValues.append( value );
     576                 :            :         }
     577                 :          0 :       }
     578                 :          0 :     }
     579                 :            :     else
     580                 :            :     {
     581                 :          0 :       if ( cat.value() != "" )
     582                 :            :       {
     583                 :          0 :         if ( isList )
     584                 :            :         {
     585                 :          0 :           const QVariantList list = cat.value().toList();
     586                 :          0 :           for ( const QVariant &v : list )
     587                 :            :           {
     588                 :          0 :             if ( !activeValues.isEmpty() )
     589                 :          0 :               activeValues.append( ',' );
     590                 :            : 
     591                 :          0 :             activeValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
     592                 :            :           }
     593                 :          0 :         }
     594                 :            :         else
     595                 :            :         {
     596                 :          0 :           if ( !activeValues.isEmpty() )
     597                 :          0 :             activeValues.append( ',' );
     598                 :            : 
     599                 :          0 :           activeValues.append( value );
     600                 :            :         }
     601                 :          0 :       }
     602                 :            :     }
     603                 :          0 :   }
     604                 :            : 
     605                 :          0 :   QString attr = isExpression ? mAttrName : QStringLiteral( "\"%1\"" ).arg( mAttrName );
     606                 :            : 
     607                 :          0 :   if ( allActive && hasDefault )
     608                 :            :   {
     609                 :          0 :     return QString();
     610                 :            :   }
     611                 :          0 :   else if ( noneActive )
     612                 :            :   {
     613                 :          0 :     return QStringLiteral( "FALSE" );
     614                 :            :   }
     615                 :          0 :   else if ( defaultActive )
     616                 :            :   {
     617                 :          0 :     return QStringLiteral( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
     618                 :            :   }
     619                 :            :   else
     620                 :            :   {
     621                 :          0 :     return QStringLiteral( "(%1) IN (%2)" ).arg( attr, activeValues );
     622                 :            :   }
     623                 :          0 : }
     624                 :            : 
     625                 :          0 : QgsSymbolList QgsCategorizedSymbolRenderer::symbols( QgsRenderContext &context ) const
     626                 :            : {
     627                 :          0 :   Q_UNUSED( context )
     628                 :          0 :   QgsSymbolList lst;
     629                 :          0 :   lst.reserve( mCategories.count() );
     630                 :          0 :   for ( const QgsRendererCategory &cat : mCategories )
     631                 :            :   {
     632                 :          0 :     lst.append( cat.symbol() );
     633                 :            :   }
     634                 :          0 :   return lst;
     635                 :          0 : }
     636                 :            : 
     637                 :          0 : bool QgsCategorizedSymbolRenderer::accept( QgsStyleEntityVisitorInterface *visitor ) const
     638                 :            : {
     639                 :          0 :   for ( const QgsRendererCategory &cat : mCategories )
     640                 :            :   {
     641                 :          0 :     QgsStyleSymbolEntity entity( cat.symbol() );
     642                 :          0 :     if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, cat.value().toString(), cat.label() ) ) )
     643                 :          0 :       return false;
     644                 :          0 :   }
     645                 :            : 
     646                 :          0 :   if ( mSourceColorRamp )
     647                 :            :   {
     648                 :          0 :     QgsStyleColorRampEntity entity( mSourceColorRamp.get() );
     649                 :          0 :     if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
     650                 :          0 :       return false;
     651                 :          0 :   }
     652                 :            : 
     653                 :          0 :   return true;
     654                 :          0 : }
     655                 :            : 
     656                 :          0 : QgsFeatureRenderer *QgsCategorizedSymbolRenderer::create( QDomElement &element, const QgsReadWriteContext &context )
     657                 :            : {
     658                 :          0 :   QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
     659                 :          0 :   if ( symbolsElem.isNull() )
     660                 :          0 :     return nullptr;
     661                 :            : 
     662                 :          0 :   QDomElement catsElem = element.firstChildElement( QStringLiteral( "categories" ) );
     663                 :          0 :   if ( catsElem.isNull() )
     664                 :          0 :     return nullptr;
     665                 :            : 
     666                 :          0 :   QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
     667                 :          0 :   QgsCategoryList cats;
     668                 :            : 
     669                 :          0 :   QDomElement catElem = catsElem.firstChildElement();
     670                 :          0 :   while ( !catElem.isNull() )
     671                 :            :   {
     672                 :          0 :     if ( catElem.tagName() == QLatin1String( "category" ) )
     673                 :            :     {
     674                 :          0 :       QVariant value;
     675                 :          0 :       if ( catElem.hasAttribute( QStringLiteral( "value" ) ) )
     676                 :            :       {
     677                 :          0 :         value = QVariant( catElem.attribute( QStringLiteral( "value" ) ) );
     678                 :          0 :       }
     679                 :            :       else
     680                 :            :       {
     681                 :          0 :         QVariantList values;
     682                 :          0 :         QDomElement valElem = catElem.firstChildElement();
     683                 :          0 :         while ( !valElem.isNull() )
     684                 :            :         {
     685                 :          0 :           if ( valElem.tagName() == QLatin1String( "val" ) )
     686                 :            :           {
     687                 :          0 :             values << QVariant( valElem.attribute( QStringLiteral( "value" ) ) );
     688                 :          0 :           }
     689                 :          0 :           valElem = valElem.nextSiblingElement();
     690                 :            :         }
     691                 :          0 :         if ( !values.isEmpty() )
     692                 :          0 :           value = values;
     693                 :          0 :       }
     694                 :          0 :       QString symbolName = catElem.attribute( QStringLiteral( "symbol" ) );
     695                 :          0 :       QString label = catElem.attribute( QStringLiteral( "label" ) );
     696                 :          0 :       bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
     697                 :          0 :       if ( symbolMap.contains( symbolName ) )
     698                 :            :       {
     699                 :          0 :         QgsSymbol *symbol = symbolMap.take( symbolName );
     700                 :          0 :         cats.append( QgsRendererCategory( value, symbol, label, render ) );
     701                 :          0 :       }
     702                 :          0 :     }
     703                 :          0 :     catElem = catElem.nextSiblingElement();
     704                 :            :   }
     705                 :            : 
     706                 :          0 :   QString attrName = element.attribute( QStringLiteral( "attr" ) );
     707                 :            : 
     708                 :          0 :   QgsCategorizedSymbolRenderer *r = new QgsCategorizedSymbolRenderer( attrName, cats );
     709                 :            : 
     710                 :            :   // delete symbols if there are any more
     711                 :          0 :   QgsSymbolLayerUtils::clearSymbolMap( symbolMap );
     712                 :            : 
     713                 :            :   // try to load source symbol (optional)
     714                 :          0 :   QDomElement sourceSymbolElem = element.firstChildElement( QStringLiteral( "source-symbol" ) );
     715                 :          0 :   if ( !sourceSymbolElem.isNull() )
     716                 :            :   {
     717                 :          0 :     QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
     718                 :          0 :     if ( sourceSymbolMap.contains( QStringLiteral( "0" ) ) )
     719                 :            :     {
     720                 :          0 :       r->setSourceSymbol( sourceSymbolMap.take( QStringLiteral( "0" ) ) );
     721                 :          0 :     }
     722                 :          0 :     QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
     723                 :          0 :   }
     724                 :            : 
     725                 :            :   // try to load color ramp (optional)
     726                 :          0 :   QDomElement sourceColorRampElem = element.firstChildElement( QStringLiteral( "colorramp" ) );
     727                 :          0 :   if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
     728                 :            :   {
     729                 :          0 :     r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
     730                 :          0 :   }
     731                 :            : 
     732                 :          0 :   QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
     733                 :          0 :   if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
     734                 :            :   {
     735                 :          0 :     for ( const QgsRendererCategory &cat : r->mCategories )
     736                 :            :     {
     737                 :          0 :       convertSymbolRotation( cat.symbol(), rotationElem.attribute( QStringLiteral( "field" ) ) );
     738                 :            :     }
     739                 :          0 :     if ( r->mSourceSymbol )
     740                 :            :     {
     741                 :          0 :       convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
     742                 :          0 :     }
     743                 :          0 :   }
     744                 :            : 
     745                 :          0 :   QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
     746                 :          0 :   if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
     747                 :            :   {
     748                 :          0 :     for ( const QgsRendererCategory &cat : r->mCategories )
     749                 :            :     {
     750                 :          0 :       convertSymbolSizeScale( cat.symbol(),
     751                 :          0 :                               QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
     752                 :          0 :                               sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
     753                 :            :     }
     754                 :          0 :     if ( r->mSourceSymbol && r->mSourceSymbol->type() == QgsSymbol::Marker )
     755                 :            :     {
     756                 :          0 :       convertSymbolSizeScale( r->mSourceSymbol.get(),
     757                 :          0 :                               QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
     758                 :          0 :                               sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
     759                 :          0 :     }
     760                 :          0 :   }
     761                 :            : 
     762                 :          0 :   QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
     763                 :          0 :   if ( !ddsLegendSizeElem.isNull() )
     764                 :            :   {
     765                 :          0 :     r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
     766                 :          0 :   }
     767                 :            : 
     768                 :            :   // TODO: symbol levels
     769                 :          0 :   return r;
     770                 :          0 : }
     771                 :            : 
     772                 :          0 : QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
     773                 :            : {
     774                 :            :   // clazy:skip
     775                 :          0 :   QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
     776                 :          0 :   rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "categorizedSymbol" ) );
     777                 :          0 :   rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
     778                 :          0 :   rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
     779                 :          0 :   rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );
     780                 :            : 
     781                 :            :   // categories
     782                 :          0 :   if ( !mCategories.isEmpty() )
     783                 :            :   {
     784                 :          0 :     int i = 0;
     785                 :          0 :     QgsSymbolMap symbols;
     786                 :          0 :     QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
     787                 :          0 :     QgsCategoryList::const_iterator it = mCategories.constBegin();
     788                 :          0 :     for ( ; it != mCategories.constEnd(); ++it )
     789                 :            :     {
     790                 :          0 :       const QgsRendererCategory &cat = *it;
     791                 :          0 :       QString symbolName = QString::number( i );
     792                 :          0 :       symbols.insert( symbolName, cat.symbol() );
     793                 :            : 
     794                 :          0 :       QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
     795                 :          0 :       if ( cat.value().type() == QVariant::List )
     796                 :            :       {
     797                 :          0 :         const QVariantList list = cat.value().toList();
     798                 :          0 :         for ( const QVariant &v : list )
     799                 :            :         {
     800                 :          0 :           QDomElement valueElem = doc.createElement( QStringLiteral( "val" ) );
     801                 :          0 :           valueElem.setAttribute( "value", v.toString() );
     802                 :          0 :           catElem.appendChild( valueElem );
     803                 :          0 :         }
     804                 :          0 :       }
     805                 :            :       else
     806                 :            :       {
     807                 :          0 :         catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
     808                 :            :       }
     809                 :          0 :       catElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
     810                 :          0 :       catElem.setAttribute( QStringLiteral( "label" ), cat.label() );
     811                 :          0 :       catElem.setAttribute( QStringLiteral( "render" ), cat.renderState() ? "true" : "false" );
     812                 :          0 :       catsElem.appendChild( catElem );
     813                 :          0 :       i++;
     814                 :          0 :     }
     815                 :          0 :     rendererElem.appendChild( catsElem );
     816                 :            : 
     817                 :            :     // save symbols
     818                 :          0 :     QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
     819                 :          0 :     rendererElem.appendChild( symbolsElem );
     820                 :            : 
     821                 :          0 :   }
     822                 :            : 
     823                 :            :   // save source symbol
     824                 :          0 :   if ( mSourceSymbol )
     825                 :            :   {
     826                 :          0 :     QgsSymbolMap sourceSymbols;
     827                 :          0 :     sourceSymbols.insert( QStringLiteral( "0" ), mSourceSymbol.get() );
     828                 :          0 :     QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, QStringLiteral( "source-symbol" ), doc, context );
     829                 :          0 :     rendererElem.appendChild( sourceSymbolElem );
     830                 :          0 :   }
     831                 :            : 
     832                 :            :   // save source color ramp
     833                 :          0 :   if ( mSourceColorRamp )
     834                 :            :   {
     835                 :          0 :     QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
     836                 :          0 :     rendererElem.appendChild( colorRampElem );
     837                 :          0 :   }
     838                 :            : 
     839                 :          0 :   QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
     840                 :          0 :   rendererElem.appendChild( rotationElem );
     841                 :            : 
     842                 :          0 :   QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
     843                 :          0 :   rendererElem.appendChild( sizeScaleElem );
     844                 :            : 
     845                 :          0 :   if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect ) )
     846                 :          0 :     mPaintEffect->saveProperties( doc, rendererElem );
     847                 :            : 
     848                 :          0 :   if ( !mOrderBy.isEmpty() )
     849                 :            :   {
     850                 :          0 :     QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
     851                 :          0 :     mOrderBy.save( orderBy );
     852                 :          0 :     rendererElem.appendChild( orderBy );
     853                 :          0 :   }
     854                 :          0 :   rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
     855                 :            : 
     856                 :          0 :   if ( mDataDefinedSizeLegend )
     857                 :            :   {
     858                 :          0 :     QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
     859                 :          0 :     mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
     860                 :          0 :     rendererElem.appendChild( ddsLegendElem );
     861                 :          0 :   }
     862                 :            : 
     863                 :          0 :   return rendererElem;
     864                 :          0 : }
     865                 :            : 
     866                 :            : 
     867                 :          0 : QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
     868                 :            : {
     869                 :          0 :   QgsLegendSymbolList lst;
     870                 :          0 :   int i = 0;
     871                 :          0 :   for ( const QgsRendererCategory &cat : mCategories )
     872                 :            :   {
     873                 :          0 :     lst << QgsLegendSymbolItem( cat.symbol(), cat.label(), QString::number( i++ ), true );
     874                 :            :   }
     875                 :          0 :   return lst;
     876                 :          0 : }
     877                 :            : 
     878                 :          0 : QgsLegendSymbolList QgsCategorizedSymbolRenderer::legendSymbolItems() const
     879                 :            : {
     880                 :          0 :   if ( mDataDefinedSizeLegend && mSourceSymbol && mSourceSymbol->type() == QgsSymbol::Marker )
     881                 :            :   {
     882                 :            :     // check that all symbols that have the same size expression
     883                 :          0 :     QgsProperty ddSize;
     884                 :          0 :     for ( const QgsRendererCategory &category : mCategories )
     885                 :            :     {
     886                 :          0 :       const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( category.symbol() );
     887                 :          0 :       if ( ddSize )
     888                 :            :       {
     889                 :          0 :         QgsProperty sSize( symbol->dataDefinedSize() );
     890                 :          0 :         if ( sSize != ddSize )
     891                 :            :         {
     892                 :            :           // no common size expression
     893                 :          0 :           return baseLegendSymbolItems();
     894                 :            :         }
     895                 :          0 :       }
     896                 :            :       else
     897                 :            :       {
     898                 :          0 :         ddSize = symbol->dataDefinedSize();
     899                 :            :       }
     900                 :            :     }
     901                 :            : 
     902                 :          0 :     if ( ddSize && ddSize.isActive() )
     903                 :            :     {
     904                 :          0 :       QgsLegendSymbolList lst;
     905                 :            : 
     906                 :          0 :       QgsDataDefinedSizeLegend ddSizeLegend( *mDataDefinedSizeLegend );
     907                 :          0 :       ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
     908                 :          0 :       lst += ddSizeLegend.legendSymbolList();
     909                 :            : 
     910                 :          0 :       lst += baseLegendSymbolItems();
     911                 :          0 :       return lst;
     912                 :          0 :     }
     913                 :          0 :   }
     914                 :            : 
     915                 :          0 :   return baseLegendSymbolItems();
     916                 :          0 : }
     917                 :            : 
     918                 :          0 : QSet<QString> QgsCategorizedSymbolRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
     919                 :            : {
     920                 :          0 :   QString value = valueForFeature( feature, context ).toString();
     921                 :          0 :   int i = 0;
     922                 :            : 
     923                 :          0 :   for ( const QgsRendererCategory &cat : mCategories )
     924                 :            :   {
     925                 :          0 :     bool match = false;
     926                 :          0 :     if ( cat.value().type() == QVariant::List )
     927                 :            :     {
     928                 :          0 :       const QVariantList list = cat.value().toList();
     929                 :          0 :       for ( const QVariant &v : list )
     930                 :            :       {
     931                 :          0 :         if ( value == v )
     932                 :            :         {
     933                 :          0 :           match = true;
     934                 :          0 :           break;
     935                 :            :         }
     936                 :            :       }
     937                 :          0 :     }
     938                 :            :     else
     939                 :            :     {
     940                 :          0 :       match = value == cat.value();
     941                 :            :     }
     942                 :            : 
     943                 :          0 :     if ( match )
     944                 :            :     {
     945                 :          0 :       if ( cat.renderState() || mCounting )
     946                 :          0 :         return QSet< QString >() << QString::number( i );
     947                 :            :       else
     948                 :          0 :         return QSet< QString >();
     949                 :            :     }
     950                 :          0 :     i++;
     951                 :            :   }
     952                 :            : 
     953                 :          0 :   return QSet< QString >();
     954                 :          0 : }
     955                 :            : 
     956                 :          0 : QgsSymbol *QgsCategorizedSymbolRenderer::sourceSymbol()
     957                 :            : {
     958                 :          0 :   return mSourceSymbol.get();
     959                 :            : }
     960                 :            : 
     961                 :          0 : const QgsSymbol *QgsCategorizedSymbolRenderer::sourceSymbol() const
     962                 :            : {
     963                 :          0 :   return mSourceSymbol.get();
     964                 :            : }
     965                 :            : 
     966                 :          0 : void QgsCategorizedSymbolRenderer::setSourceSymbol( QgsSymbol *sym )
     967                 :            : {
     968                 :          0 :   mSourceSymbol.reset( sym );
     969                 :          0 : }
     970                 :            : 
     971                 :          0 : QgsColorRamp *QgsCategorizedSymbolRenderer::sourceColorRamp()
     972                 :            : {
     973                 :          0 :   return mSourceColorRamp.get();
     974                 :            : }
     975                 :            : 
     976                 :          0 : const QgsColorRamp *QgsCategorizedSymbolRenderer::sourceColorRamp() const
     977                 :            : {
     978                 :          0 :   return mSourceColorRamp.get();
     979                 :            : }
     980                 :            : 
     981                 :          0 : void QgsCategorizedSymbolRenderer::setSourceColorRamp( QgsColorRamp *ramp )
     982                 :            : {
     983                 :          0 :   mSourceColorRamp.reset( ramp );
     984                 :          0 : }
     985                 :            : 
     986                 :          0 : void QgsCategorizedSymbolRenderer::updateColorRamp( QgsColorRamp *ramp )
     987                 :            : {
     988                 :          0 :   setSourceColorRamp( ramp );
     989                 :          0 :   double num = mCategories.count() - 1;
     990                 :          0 :   double count = 0;
     991                 :            : 
     992                 :          0 :   QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
     993                 :          0 :   if ( randomRamp )
     994                 :            :   {
     995                 :            :     //ramp is a random colors ramp, so inform it of the total number of required colors
     996                 :            :     //this allows the ramp to pregenerate a set of visually distinctive colors
     997                 :          0 :     randomRamp->setTotalColorCount( mCategories.count() );
     998                 :          0 :   }
     999                 :            : 
    1000                 :          0 :   for ( const QgsRendererCategory &cat : mCategories )
    1001                 :            :   {
    1002                 :          0 :     double value = count / num;
    1003                 :          0 :     cat.symbol()->setColor( mSourceColorRamp->color( value ) );
    1004                 :          0 :     count += 1;
    1005                 :            :   }
    1006                 :          0 : }
    1007                 :            : 
    1008                 :          0 : void QgsCategorizedSymbolRenderer::updateSymbols( QgsSymbol *sym )
    1009                 :            : {
    1010                 :          0 :   int i = 0;
    1011                 :          0 :   for ( const QgsRendererCategory &cat : mCategories )
    1012                 :            :   {
    1013                 :          0 :     QgsSymbol *symbol = sym->clone();
    1014                 :          0 :     symbol->setColor( cat.symbol()->color() );
    1015                 :          0 :     updateCategorySymbol( i, symbol );
    1016                 :          0 :     ++i;
    1017                 :            :   }
    1018                 :          0 :   setSourceSymbol( sym->clone() );
    1019                 :          0 : }
    1020                 :            : 
    1021                 :          0 : bool QgsCategorizedSymbolRenderer::legendSymbolItemsCheckable() const
    1022                 :            : {
    1023                 :          0 :   return true;
    1024                 :            : }
    1025                 :            : 
    1026                 :          0 : bool QgsCategorizedSymbolRenderer::legendSymbolItemChecked( const QString &key )
    1027                 :            : {
    1028                 :            :   bool ok;
    1029                 :          0 :   int index = key.toInt( &ok );
    1030                 :          0 :   if ( ok && index >= 0 && index < mCategories.size() )
    1031                 :          0 :     return mCategories.at( index ).renderState();
    1032                 :            :   else
    1033                 :          0 :     return true;
    1034                 :          0 : }
    1035                 :            : 
    1036                 :          0 : void QgsCategorizedSymbolRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
    1037                 :            : {
    1038                 :            :   bool ok;
    1039                 :          0 :   int index = key.toInt( &ok );
    1040                 :          0 :   if ( ok )
    1041                 :          0 :     updateCategorySymbol( index, symbol );
    1042                 :            :   else
    1043                 :          0 :     delete symbol;
    1044                 :          0 : }
    1045                 :            : 
    1046                 :          0 : void QgsCategorizedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
    1047                 :            : {
    1048                 :            :   bool ok;
    1049                 :          0 :   int index = key.toInt( &ok );
    1050                 :          0 :   if ( ok )
    1051                 :          0 :     updateCategoryRenderState( index, state );
    1052                 :          0 : }
    1053                 :            : 
    1054                 :          0 : QgsCategorizedSymbolRenderer *QgsCategorizedSymbolRenderer::convertFromRenderer( const QgsFeatureRenderer *renderer, QgsVectorLayer *layer )
    1055                 :            : {
    1056                 :          0 :   std::unique_ptr< QgsCategorizedSymbolRenderer > r;
    1057                 :          0 :   if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
    1058                 :            :   {
    1059                 :          0 :     r.reset( static_cast<QgsCategorizedSymbolRenderer *>( renderer->clone() ) );
    1060                 :          0 :   }
    1061                 :          0 :   else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
    1062                 :            :   {
    1063                 :          0 :     const QgsGraduatedSymbolRenderer *graduatedSymbolRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
    1064                 :          0 :     if ( graduatedSymbolRenderer )
    1065                 :            :     {
    1066                 :          0 :       r.reset( new QgsCategorizedSymbolRenderer( QString(), QgsCategoryList() ) );
    1067                 :          0 :       if ( graduatedSymbolRenderer->sourceSymbol() )
    1068                 :          0 :         r->setSourceSymbol( graduatedSymbolRenderer->sourceSymbol()->clone() );
    1069                 :          0 :       if ( graduatedSymbolRenderer->sourceColorRamp() )
    1070                 :            :       {
    1071                 :          0 :         r->setSourceColorRamp( graduatedSymbolRenderer->sourceColorRamp()->clone() );
    1072                 :          0 :       }
    1073                 :          0 :       r->setClassAttribute( graduatedSymbolRenderer->classAttribute() );
    1074                 :          0 :     }
    1075                 :          0 :   }
    1076                 :          0 :   else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
    1077                 :            :   {
    1078                 :          0 :     const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
    1079                 :          0 :     if ( pointDistanceRenderer )
    1080                 :          0 :       r.reset( convertFromRenderer( pointDistanceRenderer->embeddedRenderer() ) );
    1081                 :          0 :   }
    1082                 :          0 :   else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
    1083                 :            :   {
    1084                 :          0 :     const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
    1085                 :          0 :     if ( invertedPolygonRenderer )
    1086                 :          0 :       r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
    1087                 :          0 :   }
    1088                 :          0 :   else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
    1089                 :            :   {
    1090                 :          0 :     const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
    1091                 :          0 :     QgsCategoryList categories;
    1092                 :          0 :     QgsFeatureRequest req;
    1093                 :          0 :     req.setFlags( QgsFeatureRequest::EmbeddedSymbols | QgsFeatureRequest::NoGeometry );
    1094                 :          0 :     req.setNoAttributes();
    1095                 :          0 :     QgsFeatureIterator it = layer->getFeatures( req );
    1096                 :          0 :     QgsFeature feature;
    1097                 :          0 :     while ( it.nextFeature( feature ) && categories.size() < 2000 )
    1098                 :            :     {
    1099                 :          0 :       if ( feature.embeddedSymbol() )
    1100                 :          0 :         categories.append( QgsRendererCategory( feature.id(), feature.embeddedSymbol()->clone(), QString::number( feature.id() ) ) );
    1101                 :            :     }
    1102                 :          0 :     categories.append( QgsRendererCategory( QVariant(), embeddedRenderer->defaultSymbol()->clone(), QString() ) );
    1103                 :          0 :     r.reset( new QgsCategorizedSymbolRenderer( QStringLiteral( "$id" ), categories ) );
    1104                 :          0 :   }
    1105                 :            : 
    1106                 :            :   // If not one of the specifically handled renderers, then just grab the symbol from the renderer
    1107                 :            :   // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
    1108                 :            : 
    1109                 :          0 :   if ( !r )
    1110                 :            :   {
    1111                 :          0 :     r = std::make_unique< QgsCategorizedSymbolRenderer >( QString(), QgsCategoryList() );
    1112                 :          0 :     QgsRenderContext context;
    1113                 :          0 :     QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
    1114                 :          0 :     if ( !symbols.isEmpty() )
    1115                 :            :     {
    1116                 :          0 :       r->setSourceSymbol( symbols.at( 0 )->clone() );
    1117                 :          0 :     }
    1118                 :          0 :   }
    1119                 :            : 
    1120                 :          0 :   r->setOrderBy( renderer->orderBy() );
    1121                 :          0 :   r->setOrderByEnabled( renderer->orderByEnabled() );
    1122                 :            : 
    1123                 :          0 :   return r.release();
    1124                 :          0 : }
    1125                 :            : 
    1126                 :          0 : void QgsCategorizedSymbolRenderer::setDataDefinedSizeLegend( QgsDataDefinedSizeLegend *settings )
    1127                 :            : {
    1128                 :          0 :   mDataDefinedSizeLegend.reset( settings );
    1129                 :          0 : }
    1130                 :            : 
    1131                 :          0 : QgsDataDefinedSizeLegend *QgsCategorizedSymbolRenderer::dataDefinedSizeLegend() const
    1132                 :            : {
    1133                 :          0 :   return mDataDefinedSizeLegend.get();
    1134                 :            : }
    1135                 :            : 
    1136                 :          0 : int QgsCategorizedSymbolRenderer::matchToSymbols( QgsStyle *style, const QgsSymbol::SymbolType type, QVariantList &unmatchedCategories, QStringList &unmatchedSymbols, const bool caseSensitive, const bool useTolerantMatch )
    1137                 :            : {
    1138                 :          0 :   if ( !style )
    1139                 :          0 :     return 0;
    1140                 :            : 
    1141                 :          0 :   int matched = 0;
    1142                 :          0 :   unmatchedSymbols = style->symbolNames();
    1143                 :          0 :   const QSet< QString > allSymbolNames = qgis::listToSet( unmatchedSymbols );
    1144                 :            : 
    1145                 :          0 :   const QRegularExpression tolerantMatchRe( QStringLiteral( "[^\\w\\d ]" ), QRegularExpression::UseUnicodePropertiesOption );
    1146                 :            : 
    1147                 :          0 :   for ( int catIdx = 0; catIdx < mCategories.count(); ++catIdx )
    1148                 :            :   {
    1149                 :          0 :     const QVariant value = mCategories.at( catIdx ).value();
    1150                 :          0 :     const QString val = value.toString().trimmed();
    1151                 :          0 :     std::unique_ptr< QgsSymbol > symbol( style->symbol( val ) );
    1152                 :            :     // case-sensitive match
    1153                 :          0 :     if ( symbol && symbol->type() == type )
    1154                 :            :     {
    1155                 :          0 :       matched++;
    1156                 :          0 :       unmatchedSymbols.removeAll( val );
    1157                 :          0 :       updateCategorySymbol( catIdx, symbol.release() );
    1158                 :          0 :       continue;
    1159                 :            :     }
    1160                 :            : 
    1161                 :          0 :     if ( !caseSensitive || useTolerantMatch )
    1162                 :            :     {
    1163                 :          0 :       QString testVal = val;
    1164                 :          0 :       if ( useTolerantMatch )
    1165                 :          0 :         testVal.replace( tolerantMatchRe, QString() );
    1166                 :            : 
    1167                 :          0 :       bool foundMatch = false;
    1168                 :          0 :       for ( const QString &name : allSymbolNames )
    1169                 :            :       {
    1170                 :          0 :         QString testName = name.trimmed();
    1171                 :          0 :         if ( useTolerantMatch )
    1172                 :          0 :           testName.replace( tolerantMatchRe, QString() );
    1173                 :            : 
    1174                 :          0 :         if ( testName == testVal || ( !caseSensitive && testName.trimmed().compare( testVal, Qt::CaseInsensitive ) == 0 ) )
    1175                 :            :         {
    1176                 :            :           // found a case-insensitive match
    1177                 :          0 :           std::unique_ptr< QgsSymbol > symbol( style->symbol( name ) );
    1178                 :          0 :           if ( symbol && symbol->type() == type )
    1179                 :            :           {
    1180                 :          0 :             matched++;
    1181                 :          0 :             unmatchedSymbols.removeAll( name );
    1182                 :          0 :             updateCategorySymbol( catIdx, symbol.release() );
    1183                 :          0 :             foundMatch = true;
    1184                 :          0 :             break;
    1185                 :            :           }
    1186                 :          0 :         }
    1187                 :          0 :       }
    1188                 :          0 :       if ( foundMatch )
    1189                 :          0 :         continue;
    1190                 :          0 :     }
    1191                 :            : 
    1192                 :          0 :     unmatchedCategories << value;
    1193                 :          0 :   }
    1194                 :            : 
    1195                 :          0 :   return matched;
    1196                 :          0 : }
    1197                 :            : 
    1198                 :          0 : QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QList<QVariant> &values, const QgsSymbol *symbol, QgsVectorLayer *layer, const QString &attributeName )
    1199                 :            : {
    1200                 :          0 :   QgsCategoryList cats;
    1201                 :          0 :   QVariantList vals = values;
    1202                 :            :   // sort the categories first
    1203                 :          0 :   QgsSymbolLayerUtils::sortVariantList( vals, Qt::AscendingOrder );
    1204                 :            : 
    1205                 :          0 :   if ( layer && !attributeName.isNull() )
    1206                 :            :   {
    1207                 :          0 :     const QgsFields fields = layer->fields();
    1208                 :          0 :     for ( const QVariant &value : vals )
    1209                 :            :     {
    1210                 :          0 :       QgsSymbol *newSymbol = symbol->clone();
    1211                 :          0 :       if ( !value.isNull() )
    1212                 :            :       {
    1213                 :          0 :         int fieldIdx = fields.lookupField( attributeName );
    1214                 :          0 :         QString categoryName = value.toString();
    1215                 :          0 :         if ( fieldIdx != -1 )
    1216                 :            :         {
    1217                 :          0 :           const QgsField field = fields.at( fieldIdx );
    1218                 :          0 :           const QgsEditorWidgetSetup setup = field.editorWidgetSetup();
    1219                 :          0 :           const QgsFieldFormatter *formatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
    1220                 :          0 :           categoryName = formatter->representValue( layer, fieldIdx, setup.config(), QVariant(), value );
    1221                 :          0 :         }
    1222                 :          0 :         cats.append( QgsRendererCategory( value, newSymbol,  categoryName, true ) );
    1223                 :          0 :       }
    1224                 :            :     }
    1225                 :          0 :   }
    1226                 :            : 
    1227                 :            :   // add null (default) value
    1228                 :          0 :   QgsSymbol *newSymbol = symbol->clone();
    1229                 :          0 :   cats.append( QgsRendererCategory( QVariant(), newSymbol, QString(), true ) );
    1230                 :            : 
    1231                 :          0 :   return cats;
    1232                 :          0 : }
    1233                 :            : 

Generated by: LCOV version 1.14