LCOV - code coverage report
Current view: top level - core/symbology - qgsrulebasedrenderer.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 922 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :     qgsrulebasedrenderer.cpp - Rule-based renderer (symbology)
       3                 :            :     ---------------------
       4                 :            :     begin                : May 2010
       5                 :            :     copyright            : (C) 2010 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                 :            : 
      16                 :            : #include "qgsrulebasedrenderer.h"
      17                 :            : #include "qgssymbollayer.h"
      18                 :            : #include "qgsexpression.h"
      19                 :            : #include "qgssymbollayerutils.h"
      20                 :            : #include "qgsrendercontext.h"
      21                 :            : #include "qgsvectorlayer.h"
      22                 :            : #include "qgslogger.h"
      23                 :            : #include "qgsogcutils.h"
      24                 :            : #include "qgssinglesymbolrenderer.h"
      25                 :            : #include "qgspointdisplacementrenderer.h"
      26                 :            : #include "qgsinvertedpolygonrenderer.h"
      27                 :            : #include "qgspainteffect.h"
      28                 :            : #include "qgspainteffectregistry.h"
      29                 :            : #include "qgsproperty.h"
      30                 :            : #include "qgsstyleentityvisitor.h"
      31                 :            : #include "qgsembeddedsymbolrenderer.h"
      32                 :            : #include <QSet>
      33                 :            : 
      34                 :            : #include <QDomDocument>
      35                 :            : #include <QDomElement>
      36                 :            : #include <QUuid>
      37                 :            : 
      38                 :            : 
      39                 :          0 : QgsRuleBasedRenderer::Rule::Rule( QgsSymbol *symbol, int scaleMinDenom, int scaleMaxDenom, const QString &filterExp, const QString &label, const QString &description, bool elseRule )
      40                 :          0 :   : mParent( nullptr )
      41                 :          0 :   , mSymbol( symbol )
      42                 :          0 :   , mMaximumScale( scaleMinDenom )
      43                 :          0 :   , mMinimumScale( scaleMaxDenom )
      44                 :          0 :   , mFilterExp( filterExp )
      45                 :          0 :   , mLabel( label )
      46                 :          0 :   , mDescription( description )
      47                 :          0 :   , mElseRule( elseRule )
      48                 :            : {
      49                 :          0 :   if ( mElseRule )
      50                 :          0 :     mFilterExp = QStringLiteral( "ELSE" );
      51                 :            : 
      52                 :          0 :   mRuleKey = QUuid::createUuid().toString();
      53                 :          0 :   initFilter();
      54                 :          0 : }
      55                 :            : 
      56                 :          0 : QgsRuleBasedRenderer::Rule::~Rule()
      57                 :            : {
      58                 :          0 :   qDeleteAll( mChildren );
      59                 :            :   // do NOT delete parent
      60                 :          0 : }
      61                 :            : 
      62                 :          0 : void QgsRuleBasedRenderer::Rule::initFilter()
      63                 :            : {
      64                 :          0 :   if ( mFilterExp.trimmed().compare( QLatin1String( "ELSE" ), Qt::CaseInsensitive ) == 0 )
      65                 :            :   {
      66                 :          0 :     mElseRule = true;
      67                 :          0 :     mFilter.reset();
      68                 :          0 :   }
      69                 :          0 :   else if ( mFilterExp.trimmed().isEmpty() )
      70                 :            :   {
      71                 :          0 :     mElseRule = false;
      72                 :          0 :     mFilter.reset();
      73                 :          0 :   }
      74                 :            :   else
      75                 :            :   {
      76                 :          0 :     mElseRule = false;
      77                 :          0 :     mFilter = std::make_unique< QgsExpression >( mFilterExp );
      78                 :            :   }
      79                 :          0 : }
      80                 :            : 
      81                 :          0 : void QgsRuleBasedRenderer::Rule::appendChild( Rule *rule )
      82                 :            : {
      83                 :          0 :   mChildren.append( rule );
      84                 :          0 :   rule->mParent = this;
      85                 :          0 :   updateElseRules();
      86                 :          0 : }
      87                 :            : 
      88                 :          0 : void QgsRuleBasedRenderer::Rule::insertChild( int i, Rule *rule )
      89                 :            : {
      90                 :          0 :   mChildren.insert( i, rule );
      91                 :          0 :   rule->mParent = this;
      92                 :          0 :   updateElseRules();
      93                 :          0 : }
      94                 :            : 
      95                 :          0 : void QgsRuleBasedRenderer::Rule::removeChild( Rule *rule )
      96                 :            : {
      97                 :          0 :   mChildren.removeAll( rule );
      98                 :          0 :   delete rule;
      99                 :          0 :   updateElseRules();
     100                 :          0 : }
     101                 :            : 
     102                 :          0 : void QgsRuleBasedRenderer::Rule::removeChildAt( int i )
     103                 :            : {
     104                 :          0 :   delete mChildren.takeAt( i );
     105                 :          0 :   updateElseRules();
     106                 :          0 : }
     107                 :            : 
     108                 :          0 : QgsRuleBasedRenderer::Rule  *QgsRuleBasedRenderer::Rule::takeChild( Rule *rule )
     109                 :            : {
     110                 :          0 :   mChildren.removeAll( rule );
     111                 :          0 :   rule->mParent = nullptr;
     112                 :          0 :   updateElseRules();
     113                 :          0 :   return rule;
     114                 :            : }
     115                 :            : 
     116                 :          0 : QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::takeChildAt( int i )
     117                 :            : {
     118                 :          0 :   Rule *rule = mChildren.takeAt( i );
     119                 :          0 :   rule->mParent = nullptr;
     120                 :          0 :   updateElseRules();
     121                 :          0 :   return rule;
     122                 :            : }
     123                 :            : 
     124                 :          0 : QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::findRuleByKey( const QString &key )
     125                 :            : {
     126                 :            :   // we could use a hash / map for search if this will be slow...
     127                 :            : 
     128                 :          0 :   if ( key == mRuleKey )
     129                 :          0 :     return this;
     130                 :            : 
     131                 :          0 :   const auto constMChildren = mChildren;
     132                 :          0 :   for ( Rule *rule : constMChildren )
     133                 :            :   {
     134                 :          0 :     Rule *r = rule->findRuleByKey( key );
     135                 :          0 :     if ( r )
     136                 :          0 :       return r;
     137                 :            :   }
     138                 :          0 :   return nullptr;
     139                 :          0 : }
     140                 :            : 
     141                 :          0 : void QgsRuleBasedRenderer::Rule::updateElseRules()
     142                 :            : {
     143                 :          0 :   mElseRules.clear();
     144                 :          0 :   const auto constMChildren = mChildren;
     145                 :          0 :   for ( Rule *rule : constMChildren )
     146                 :            :   {
     147                 :          0 :     if ( rule->isElse() )
     148                 :          0 :       mElseRules << rule;
     149                 :            :   }
     150                 :          0 : }
     151                 :            : 
     152                 :          0 : void QgsRuleBasedRenderer::Rule::setIsElse( bool iselse )
     153                 :            : {
     154                 :          0 :   mFilterExp = QStringLiteral( "ELSE" );
     155                 :          0 :   mElseRule = iselse;
     156                 :          0 :   mFilter.reset();
     157                 :          0 : }
     158                 :            : 
     159                 :          0 : bool QgsRuleBasedRenderer::Rule::accept( QgsStyleEntityVisitorInterface *visitor ) const
     160                 :            : {
     161                 :            :   // NOTE: if visitEnter returns false it means "don't visit the rule", not "abort all further visitations"
     162                 :          0 :   if ( mParent && !visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::SymbolRule, mRuleKey, mLabel ) ) )
     163                 :          0 :     return true;
     164                 :            : 
     165                 :          0 :   if ( mSymbol )
     166                 :            :   {
     167                 :          0 :     QgsStyleSymbolEntity entity( mSymbol.get() );
     168                 :          0 :     if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
     169                 :          0 :       return false;
     170                 :          0 :   }
     171                 :            : 
     172                 :          0 :   if ( !mChildren.empty() )
     173                 :            :   {
     174                 :          0 :     for ( const Rule *rule : mChildren )
     175                 :            :     {
     176                 :            : 
     177                 :          0 :       if ( !rule->accept( visitor ) )
     178                 :          0 :         return false;
     179                 :            :     }
     180                 :          0 :   }
     181                 :            : 
     182                 :          0 :   if ( mParent && !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::SymbolRule, mRuleKey, mLabel ) ) )
     183                 :          0 :     return false;
     184                 :            : 
     185                 :          0 :   return true;
     186                 :          0 : }
     187                 :            : 
     188                 :          0 : QString QgsRuleBasedRenderer::Rule::dump( int indent ) const
     189                 :            : {
     190                 :          0 :   QString off;
     191                 :          0 :   off.fill( QChar( ' ' ), indent );
     192                 :          0 :   QString symbolDump = ( mSymbol ? mSymbol->dump() : QStringLiteral( "[]" ) );
     193                 :          0 :   QString msg = off + QStringLiteral( "RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
     194                 :          0 :                 .arg( mLabel ).arg( mMaximumScale ).arg( mMinimumScale )
     195                 :          0 :                 .arg( mFilterExp, symbolDump );
     196                 :            : 
     197                 :          0 :   QStringList lst;
     198                 :          0 :   const auto constMChildren = mChildren;
     199                 :          0 :   for ( Rule *rule : constMChildren )
     200                 :            :   {
     201                 :          0 :     lst.append( rule->dump( indent + 2 ) );
     202                 :            :   }
     203                 :          0 :   msg += lst.join( QLatin1Char( '\n' ) );
     204                 :          0 :   return msg;
     205                 :          0 : }
     206                 :            : 
     207                 :          0 : QSet<QString> QgsRuleBasedRenderer::Rule::usedAttributes( const QgsRenderContext &context ) const
     208                 :            : {
     209                 :            :   // attributes needed by this rule
     210                 :          0 :   QSet<QString> attrs;
     211                 :          0 :   if ( mFilter )
     212                 :          0 :     attrs.unite( mFilter->referencedColumns() );
     213                 :          0 :   if ( mSymbol )
     214                 :          0 :     attrs.unite( mSymbol->usedAttributes( context ) );
     215                 :            : 
     216                 :            :   // attributes needed by child rules
     217                 :          0 :   const auto constMChildren = mChildren;
     218                 :          0 :   for ( Rule *rule : constMChildren )
     219                 :            :   {
     220                 :          0 :     attrs.unite( rule->usedAttributes( context ) );
     221                 :            :   }
     222                 :          0 :   return attrs;
     223                 :          0 : }
     224                 :            : 
     225                 :          0 : bool QgsRuleBasedRenderer::Rule::needsGeometry() const
     226                 :            : {
     227                 :          0 :   if ( mFilter && mFilter->needsGeometry() )
     228                 :          0 :     return true;
     229                 :            : 
     230                 :          0 :   const auto constMChildren = mChildren;
     231                 :          0 :   for ( Rule *rule : constMChildren )
     232                 :            :   {
     233                 :          0 :     if ( rule->needsGeometry() )
     234                 :          0 :       return true;
     235                 :            :   }
     236                 :            : 
     237                 :          0 :   return false;
     238                 :          0 : }
     239                 :            : 
     240                 :          0 : QgsSymbolList QgsRuleBasedRenderer::Rule::symbols( const QgsRenderContext &context ) const
     241                 :            : {
     242                 :          0 :   QgsSymbolList lst;
     243                 :          0 :   if ( mSymbol )
     244                 :          0 :     lst.append( mSymbol.get() );
     245                 :            : 
     246                 :          0 :   const auto constMChildren = mChildren;
     247                 :          0 :   for ( Rule *rule : constMChildren )
     248                 :            :   {
     249                 :          0 :     lst += rule->symbols( context );
     250                 :            :   }
     251                 :          0 :   return lst;
     252                 :          0 : }
     253                 :            : 
     254                 :          0 : void QgsRuleBasedRenderer::Rule::setSymbol( QgsSymbol *sym )
     255                 :            : {
     256                 :          0 :   mSymbol.reset( sym );
     257                 :          0 : }
     258                 :            : 
     259                 :          0 : void QgsRuleBasedRenderer::Rule::setFilterExpression( const QString &filterExp )
     260                 :            : {
     261                 :          0 :   mFilterExp = filterExp;
     262                 :          0 :   initFilter();
     263                 :          0 : }
     264                 :            : 
     265                 :          0 : QgsLegendSymbolList QgsRuleBasedRenderer::Rule::legendSymbolItems( int currentLevel ) const
     266                 :            : {
     267                 :          0 :   QgsLegendSymbolList lst;
     268                 :          0 :   if ( currentLevel != -1 ) // root rule should not be shown
     269                 :            :   {
     270                 :          0 :     lst << QgsLegendSymbolItem( mSymbol.get(), mLabel, mRuleKey, true, mMaximumScale, mMinimumScale, currentLevel, mParent ? mParent->mRuleKey : QString() );
     271                 :          0 :   }
     272                 :            : 
     273                 :          0 :   for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
     274                 :            :   {
     275                 :          0 :     Rule *rule = *it;
     276                 :          0 :     lst << rule->legendSymbolItems( currentLevel + 1 );
     277                 :          0 :   }
     278                 :          0 :   return lst;
     279                 :          0 : }
     280                 :            : 
     281                 :            : 
     282                 :          0 : bool QgsRuleBasedRenderer::Rule::isFilterOK( const QgsFeature &f, QgsRenderContext *context ) const
     283                 :            : {
     284                 :          0 :   if ( ! mFilter || mElseRule || ! context )
     285                 :          0 :     return true;
     286                 :            : 
     287                 :          0 :   context->expressionContext().setFeature( f );
     288                 :          0 :   QVariant res = mFilter->evaluate( &context->expressionContext() );
     289                 :          0 :   return res.toBool();
     290                 :          0 : }
     291                 :            : 
     292                 :          0 : bool QgsRuleBasedRenderer::Rule::isScaleOK( double scale ) const
     293                 :            : {
     294                 :          0 :   if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
     295                 :          0 :     return true;
     296                 :          0 :   if ( qgsDoubleNear( mMaximumScale, 0.0 ) && qgsDoubleNear( mMinimumScale, 0.0 ) )
     297                 :          0 :     return true;
     298                 :          0 :   if ( !qgsDoubleNear( mMaximumScale, 0.0 ) && mMaximumScale > scale )
     299                 :          0 :     return false;
     300                 :          0 :   if ( !qgsDoubleNear( mMinimumScale, 0.0 ) && mMinimumScale < scale )
     301                 :          0 :     return false;
     302                 :          0 :   return true;
     303                 :          0 : }
     304                 :            : 
     305                 :          0 : QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::clone() const
     306                 :            : {
     307                 :          0 :   QgsSymbol *sym = mSymbol ? mSymbol->clone() : nullptr;
     308                 :          0 :   Rule *newrule = new Rule( sym, mMaximumScale, mMinimumScale, mFilterExp, mLabel, mDescription );
     309                 :          0 :   newrule->setActive( mIsActive );
     310                 :            :   // clone children
     311                 :          0 :   const auto constMChildren = mChildren;
     312                 :          0 :   for ( Rule *rule : constMChildren )
     313                 :          0 :     newrule->appendChild( rule->clone() );
     314                 :          0 :   return newrule;
     315                 :          0 : }
     316                 :            : 
     317                 :          0 : QDomElement QgsRuleBasedRenderer::Rule::save( QDomDocument &doc, QgsSymbolMap &symbolMap ) const
     318                 :            : {
     319                 :          0 :   QDomElement ruleElem = doc.createElement( QStringLiteral( "rule" ) );
     320                 :            : 
     321                 :          0 :   if ( mSymbol )
     322                 :            :   {
     323                 :          0 :     int symbolIndex = symbolMap.size();
     324                 :          0 :     symbolMap[QString::number( symbolIndex )] = mSymbol.get();
     325                 :          0 :     ruleElem.setAttribute( QStringLiteral( "symbol" ), symbolIndex );
     326                 :          0 :   }
     327                 :          0 :   if ( !mFilterExp.isEmpty() )
     328                 :          0 :     ruleElem.setAttribute( QStringLiteral( "filter" ), mFilterExp );
     329                 :          0 :   if ( mMaximumScale != 0 )
     330                 :          0 :     ruleElem.setAttribute( QStringLiteral( "scalemindenom" ), mMaximumScale );
     331                 :          0 :   if ( mMinimumScale != 0 )
     332                 :          0 :     ruleElem.setAttribute( QStringLiteral( "scalemaxdenom" ), mMinimumScale );
     333                 :          0 :   if ( !mLabel.isEmpty() )
     334                 :          0 :     ruleElem.setAttribute( QStringLiteral( "label" ), mLabel );
     335                 :          0 :   if ( !mDescription.isEmpty() )
     336                 :          0 :     ruleElem.setAttribute( QStringLiteral( "description" ), mDescription );
     337                 :          0 :   if ( !mIsActive )
     338                 :          0 :     ruleElem.setAttribute( QStringLiteral( "checkstate" ), 0 );
     339                 :          0 :   ruleElem.setAttribute( QStringLiteral( "key" ), mRuleKey );
     340                 :            : 
     341                 :          0 :   const auto constMChildren = mChildren;
     342                 :          0 :   for ( Rule *rule : constMChildren )
     343                 :            :   {
     344                 :          0 :     ruleElem.appendChild( rule->save( doc, symbolMap ) );
     345                 :            :   }
     346                 :          0 :   return ruleElem;
     347                 :          0 : }
     348                 :            : 
     349                 :          0 : void QgsRuleBasedRenderer::Rule::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
     350                 :            : {
     351                 :            :   // do not convert this rule if there are no symbols
     352                 :          0 :   QgsRenderContext context;
     353                 :          0 :   if ( symbols( context ).isEmpty() )
     354                 :          0 :     return;
     355                 :            : 
     356                 :          0 :   if ( !mFilterExp.isEmpty() )
     357                 :            :   {
     358                 :          0 :     QString filter = props.value( QStringLiteral( "filter" ), QString() ).toString();
     359                 :          0 :     if ( !filter.isEmpty() )
     360                 :          0 :       filter += QLatin1String( " AND " );
     361                 :          0 :     filter += mFilterExp;
     362                 :          0 :     props[ QStringLiteral( "filter" )] = filter;
     363                 :          0 :   }
     364                 :            : 
     365                 :          0 :   QgsSymbolLayerUtils::mergeScaleDependencies( mMaximumScale, mMinimumScale, props );
     366                 :            : 
     367                 :          0 :   if ( mSymbol )
     368                 :            :   {
     369                 :          0 :     QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
     370                 :          0 :     element.appendChild( ruleElem );
     371                 :            : 
     372                 :            :     //XXX: <se:Name> is the rule identifier, but our the Rule objects
     373                 :            :     // have no properties could be used as identifier. Use the label.
     374                 :          0 :     QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
     375                 :          0 :     nameElem.appendChild( doc.createTextNode( mLabel ) );
     376                 :          0 :     ruleElem.appendChild( nameElem );
     377                 :            : 
     378                 :          0 :     if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
     379                 :            :     {
     380                 :          0 :       QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
     381                 :          0 :       if ( !mLabel.isEmpty() )
     382                 :            :       {
     383                 :          0 :         QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
     384                 :          0 :         titleElem.appendChild( doc.createTextNode( mLabel ) );
     385                 :          0 :         descrElem.appendChild( titleElem );
     386                 :          0 :       }
     387                 :          0 :       if ( !mDescription.isEmpty() )
     388                 :            :       {
     389                 :          0 :         QDomElement abstractElem = doc.createElement( QStringLiteral( "se:Abstract" ) );
     390                 :          0 :         abstractElem.appendChild( doc.createTextNode( mDescription ) );
     391                 :          0 :         descrElem.appendChild( abstractElem );
     392                 :          0 :       }
     393                 :          0 :       ruleElem.appendChild( descrElem );
     394                 :          0 :     }
     395                 :            : 
     396                 :          0 :     if ( !props.value( QStringLiteral( "filter" ), QString() ).toString().isEmpty() )
     397                 :            :     {
     398                 :          0 :       QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( QStringLiteral( "filter" ), QString() ).toString() );
     399                 :          0 :     }
     400                 :            : 
     401                 :          0 :     QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
     402                 :            : 
     403                 :          0 :     mSymbol->toSld( doc, ruleElem, props );
     404                 :          0 :   }
     405                 :            : 
     406                 :            :   // loop into children rule list
     407                 :          0 :   const auto constMChildren = mChildren;
     408                 :          0 :   for ( Rule *rule : constMChildren )
     409                 :            :   {
     410                 :          0 :     rule->toSld( doc, element, props );
     411                 :            :   }
     412                 :          0 : }
     413                 :            : 
     414                 :          0 : bool QgsRuleBasedRenderer::Rule::startRender( QgsRenderContext &context, const QgsFields &fields, QString &filter )
     415                 :            : {
     416                 :          0 :   mActiveChildren.clear();
     417                 :            : 
     418                 :          0 :   if ( ! mIsActive )
     419                 :          0 :     return false;
     420                 :            : 
     421                 :            :   // filter out rules which are not compatible with this scale
     422                 :          0 :   if ( !isScaleOK( context.rendererScale() ) )
     423                 :          0 :     return false;
     424                 :            : 
     425                 :            :   // init this rule
     426                 :          0 :   if ( mFilter )
     427                 :          0 :     mFilter->prepare( &context.expressionContext() );
     428                 :          0 :   if ( mSymbol )
     429                 :          0 :     mSymbol->startRender( context, fields );
     430                 :            : 
     431                 :            :   // init children
     432                 :            :   // build temporary list of active rules (usable with this scale)
     433                 :          0 :   QStringList subfilters;
     434                 :          0 :   const auto constMChildren = mChildren;
     435                 :          0 :   for ( Rule *rule : constMChildren )
     436                 :            :   {
     437                 :          0 :     QString subfilter;
     438                 :          0 :     if ( rule->startRender( context, fields, subfilter ) )
     439                 :            :     {
     440                 :            :       // only add those which are active with current scale
     441                 :          0 :       mActiveChildren.append( rule );
     442                 :          0 :       subfilters.append( subfilter );
     443                 :          0 :     }
     444                 :          0 :   }
     445                 :            : 
     446                 :            :   // subfilters (on the same level) are joined with OR
     447                 :            :   // Finally they are joined with their parent (this) with AND
     448                 :          0 :   QString sf;
     449                 :            :   // If there are subfilters present (and it's not a single empty one), group them and join them with OR
     450                 :          0 :   if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
     451                 :            :   {
     452                 :          0 :     if ( subfilters.contains( QStringLiteral( "TRUE" ) ) )
     453                 :            :     {
     454                 :          0 :       sf = QStringLiteral( "TRUE" );
     455                 :          0 :     }
     456                 :            :     else
     457                 :            :     {
     458                 :            :       // test for a common case -- all subfilters can be combined into a single "field in (...)" expression
     459                 :          0 :       if ( QgsExpression::attemptReduceToInClause( subfilters, sf ) )
     460                 :            :       {
     461                 :            :         // success! we can use a simple "field IN (...)" list!
     462                 :          0 :       }
     463                 :            :       // If we have more than 50 rules (to stay on the safe side) make a binary tree or SQLITE will fail,
     464                 :            :       // see: https://github.com/qgis/QGIS/issues/27269
     465                 :          0 :       else if ( subfilters.count() > 50 )
     466                 :            :       {
     467                 :          0 :         std::function<QString( const QStringList & )>bt = [ &bt ]( const QStringList & subf )
     468                 :            :         {
     469                 :          0 :           if ( subf.count( ) == 1 )
     470                 :            :           {
     471                 :          0 :             return subf.at( 0 );
     472                 :            :           }
     473                 :          0 :           else if ( subf.count( ) == 2 )
     474                 :            :           {
     475                 :          0 :             return subf.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
     476                 :            :           }
     477                 :            :           else
     478                 :            :           {
     479                 :          0 :             int midpos = static_cast<int>( subf.length() / 2 );
     480                 :          0 :             return QStringLiteral( "(%1) OR (%2)" ).arg( bt( subf.mid( 0, midpos ) ), bt( subf.mid( midpos ) ) );
     481                 :            :           }
     482                 :          0 :         };
     483                 :          0 :         sf = bt( subfilters );
     484                 :          0 :       }
     485                 :            :       else
     486                 :            :       {
     487                 :          0 :         sf = subfilters.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
     488                 :            :       }
     489                 :            :     }
     490                 :          0 :   }
     491                 :            : 
     492                 :            :   // Now join the subfilters with their parent (this) based on if
     493                 :            :   // * The parent is an else rule
     494                 :            :   // * The existence of parent filter and subfilters
     495                 :            : 
     496                 :            :   // No filter expression: ELSE rule or catchall rule
     497                 :          0 :   if ( !mFilter )
     498                 :            :   {
     499                 :          0 :     if ( mSymbol || sf.isEmpty() )
     500                 :          0 :       filter = QStringLiteral( "TRUE" );
     501                 :            :     else
     502                 :          0 :       filter = sf;
     503                 :          0 :   }
     504                 :          0 :   else if ( mSymbol )
     505                 :          0 :     filter = mFilterExp;
     506                 :          0 :   else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
     507                 :          0 :     filter = QStringLiteral( "(%1) AND (%2)" ).arg( mFilterExp, sf );
     508                 :          0 :   else if ( !mFilterExp.trimmed().isEmpty() )
     509                 :          0 :     filter = mFilterExp;
     510                 :          0 :   else if ( sf.isEmpty() )
     511                 :          0 :     filter = QStringLiteral( "TRUE" );
     512                 :            :   else
     513                 :          0 :     filter = sf;
     514                 :            : 
     515                 :          0 :   filter = filter.trimmed();
     516                 :            : 
     517                 :          0 :   return true;
     518                 :          0 : }
     519                 :            : 
     520                 :          0 : QSet<int> QgsRuleBasedRenderer::Rule::collectZLevels()
     521                 :            : {
     522                 :          0 :   QSet<int> symbolZLevelsSet;
     523                 :            : 
     524                 :            :   // process this rule
     525                 :          0 :   if ( mSymbol )
     526                 :            :   {
     527                 :            :     // find out which Z-levels are used
     528                 :          0 :     for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
     529                 :            :     {
     530                 :          0 :       symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
     531                 :          0 :     }
     532                 :          0 :   }
     533                 :            : 
     534                 :            :   // process children
     535                 :          0 :   QList<Rule *>::iterator it;
     536                 :          0 :   for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
     537                 :            :   {
     538                 :          0 :     Rule *rule = *it;
     539                 :          0 :     symbolZLevelsSet.unite( rule->collectZLevels() );
     540                 :          0 :   }
     541                 :          0 :   return symbolZLevelsSet;
     542                 :          0 : }
     543                 :            : 
     544                 :          0 : void QgsRuleBasedRenderer::Rule::setNormZLevels( const QMap<int, int> &zLevelsToNormLevels )
     545                 :            : {
     546                 :          0 :   if ( mSymbol )
     547                 :            :   {
     548                 :          0 :     for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
     549                 :            :     {
     550                 :          0 :       int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
     551                 :          0 :       mSymbolNormZLevels.insert( normLevel );
     552                 :          0 :     }
     553                 :          0 :   }
     554                 :            : 
     555                 :            :   // prepare list of normalized levels for each rule
     556                 :          0 :   const auto constMActiveChildren = mActiveChildren;
     557                 :          0 :   for ( Rule *rule : constMActiveChildren )
     558                 :            :   {
     559                 :          0 :     rule->setNormZLevels( zLevelsToNormLevels );
     560                 :            :   }
     561                 :          0 : }
     562                 :          0 : 
     563                 :            : 
     564                 :          0 : QgsRuleBasedRenderer::Rule::RenderResult QgsRuleBasedRenderer::Rule::renderFeature( QgsRuleBasedRenderer::FeatureToRender &featToRender, QgsRenderContext &context, QgsRuleBasedRenderer::RenderQueue &renderQueue )
     565                 :            : {
     566                 :          0 :   if ( !isFilterOK( featToRender.feat, &context ) )
     567                 :          0 :     return Filtered;
     568                 :            : 
     569                 :          0 :   bool rendered = false;
     570                 :            : 
     571                 :            :   // create job for this feature and this symbol, add to list of jobs
     572                 :          0 :   if ( mSymbol && mIsActive )
     573                 :            :   {
     574                 :            :     // add job to the queue: each symbol's zLevel must be added
     575                 :          0 :     const auto constMSymbolNormZLevels = mSymbolNormZLevels;
     576                 :          0 :     for ( int normZLevel : constMSymbolNormZLevels )
     577                 :            :     {
     578                 :            :       //QgsDebugMsg(QString("add job at level %1").arg(normZLevel));
     579                 :          0 :       renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol.get() ) );
     580                 :          0 :       rendered = true;
     581                 :            :     }
     582                 :          0 :   }
     583                 :            : 
     584                 :          0 :   bool willrendersomething = false;
     585                 :            : 
     586                 :            :   // process children
     587                 :          0 :   const auto constMChildren = mChildren;
     588                 :          0 :   for ( Rule *rule : constMChildren )
     589                 :            :   {
     590                 :            :     // Don't process else rules yet
     591                 :          0 :     if ( !rule->isElse() )
     592                 :            :     {
     593                 :          0 :       RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
     594                 :            :       // consider inactive items as "rendered" so the else rule will ignore them
     595                 :          0 :       willrendersomething |= ( res == Rendered || res == Inactive );
     596                 :          0 :       rendered |= ( res == Rendered );
     597                 :          0 :     }
     598                 :            :   }
     599                 :            : 
     600                 :            :   // If none of the rules passed then we jump into the else rules and process them.
     601                 :          0 :   if ( !willrendersomething )
     602                 :            :   {
     603                 :          0 :     const auto constMElseRules = mElseRules;
     604                 :          0 :     for ( Rule *rule : constMElseRules )
     605                 :            :     {
     606                 :          0 :       rendered |= rule->renderFeature( featToRender, context, renderQueue ) == Rendered;
     607                 :            :     }
     608                 :          0 :   }
     609                 :          0 :   if ( !mIsActive || ( mSymbol && !rendered ) )
     610                 :          0 :     return Inactive;
     611                 :          0 :   else if ( rendered )
     612                 :          0 :     return Rendered;
     613                 :            :   else
     614                 :          0 :     return Filtered;
     615                 :          0 : }
     616                 :            : 
     617                 :          0 : bool QgsRuleBasedRenderer::Rule::willRenderFeature( const QgsFeature &feature, QgsRenderContext *context )
     618                 :            : {
     619                 :          0 :   if ( !isFilterOK( feature, context ) )
     620                 :          0 :     return false;
     621                 :            : 
     622                 :          0 :   if ( mSymbol )
     623                 :          0 :     return true;
     624                 :            : 
     625                 :          0 :   const auto constMActiveChildren = mActiveChildren;
     626                 :          0 :   for ( Rule *rule : constMActiveChildren )
     627                 :            :   {
     628                 :          0 :     if ( rule->isElse() )
     629                 :            :     {
     630                 :          0 :       if ( rule->children().isEmpty() )
     631                 :            :       {
     632                 :          0 :         RuleList lst = rulesForFeature( feature, context, false );
     633                 :          0 :         lst.removeOne( rule );
     634                 :            : 
     635                 :          0 :         if ( lst.empty() )
     636                 :            :         {
     637                 :          0 :           return true;
     638                 :            :         }
     639                 :          0 :       }
     640                 :            :       else
     641                 :            :       {
     642                 :          0 :         return rule->willRenderFeature( feature, context );
     643                 :            :       }
     644                 :          0 :     }
     645                 :          0 :     else if ( rule->willRenderFeature( feature, context ) )
     646                 :            :     {
     647                 :          0 :       return true;
     648                 :            :     }
     649                 :            :   }
     650                 :          0 :   return false;
     651                 :          0 : }
     652                 :            : 
     653                 :          0 : QgsSymbolList QgsRuleBasedRenderer::Rule::symbolsForFeature( const QgsFeature &feature, QgsRenderContext *context )
     654                 :            : {
     655                 :          0 :   QgsSymbolList lst;
     656                 :          0 :   if ( !isFilterOK( feature, context ) )
     657                 :          0 :     return lst;
     658                 :          0 :   if ( mSymbol )
     659                 :          0 :     lst.append( mSymbol.get() );
     660                 :            : 
     661                 :          0 :   const auto constMActiveChildren = mActiveChildren;
     662                 :          0 :   for ( Rule *rule : constMActiveChildren )
     663                 :            :   {
     664                 :          0 :     lst += rule->symbolsForFeature( feature, context );
     665                 :            :   }
     666                 :          0 :   return lst;
     667                 :          0 : }
     668                 :            : 
     669                 :          0 : QSet<QString> QgsRuleBasedRenderer::Rule::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext *context )
     670                 :            : {
     671                 :          0 :   QSet< QString> lst;
     672                 :          0 :   if ( !isFilterOK( feature, context ) )
     673                 :          0 :     return lst;
     674                 :          0 :   lst.insert( mRuleKey );
     675                 :            : 
     676                 :          0 :   const auto constMActiveChildren = mActiveChildren;
     677                 :          0 :   for ( Rule *rule : constMActiveChildren )
     678                 :            :   {
     679                 :          0 :     bool validKey = false;
     680                 :          0 :     if ( rule->isElse() )
     681                 :            :     {
     682                 :          0 :       RuleList lst = rulesForFeature( feature, context, false );
     683                 :          0 :       lst.removeOne( rule );
     684                 :            : 
     685                 :          0 :       if ( lst.empty() )
     686                 :            :       {
     687                 :          0 :         validKey = true;
     688                 :          0 :       }
     689                 :          0 :     }
     690                 :          0 :     else if ( !rule->isElse( ) && rule->willRenderFeature( feature, context ) )
     691                 :            :     {
     692                 :          0 :       validKey = true;
     693                 :          0 :     }
     694                 :            : 
     695                 :          0 :     if ( validKey )
     696                 :            :     {
     697                 :          0 :       lst.unite( rule->legendKeysForFeature( feature, context ) );
     698                 :          0 :     }
     699                 :            :   }
     700                 :          0 :   return lst;
     701                 :          0 : }
     702                 :            : 
     703                 :          0 : QgsRuleBasedRenderer::RuleList QgsRuleBasedRenderer::Rule::rulesForFeature( const QgsFeature &feature, QgsRenderContext *context, bool onlyActive )
     704                 :            : {
     705                 :          0 :   RuleList lst;
     706                 :          0 :   if ( ! isFilterOK( feature, context ) || ( context && ! isScaleOK( context->rendererScale() ) ) )
     707                 :          0 :     return lst;
     708                 :            : 
     709                 :          0 :   if ( mSymbol )
     710                 :          0 :     lst.append( this );
     711                 :            : 
     712                 :          0 :   RuleList listChildren = children();
     713                 :          0 :   if ( onlyActive )
     714                 :          0 :     listChildren = mActiveChildren;
     715                 :            : 
     716                 :          0 :   const auto constListChildren = listChildren;
     717                 :          0 :   for ( Rule *rule : constListChildren )
     718                 :            :   {
     719                 :          0 :     lst += rule->rulesForFeature( feature, context, onlyActive );
     720                 :            :   }
     721                 :          0 :   return lst;
     722                 :          0 : }
     723                 :            : 
     724                 :          0 : void QgsRuleBasedRenderer::Rule::stopRender( QgsRenderContext &context )
     725                 :            : {
     726                 :          0 :   if ( mSymbol )
     727                 :          0 :     mSymbol->stopRender( context );
     728                 :            : 
     729                 :          0 :   const auto constMActiveChildren = mActiveChildren;
     730                 :          0 :   for ( Rule *rule : constMActiveChildren )
     731                 :            :   {
     732                 :          0 :     rule->stopRender( context );
     733                 :            :   }
     734                 :            : 
     735                 :          0 :   mActiveChildren.clear();
     736                 :          0 :   mSymbolNormZLevels.clear();
     737                 :          0 : }
     738                 :            : 
     739                 :          0 : QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::create( QDomElement &ruleElem, QgsSymbolMap &symbolMap )
     740                 :            : {
     741                 :          0 :   QString symbolIdx = ruleElem.attribute( QStringLiteral( "symbol" ) );
     742                 :          0 :   QgsSymbol *symbol = nullptr;
     743                 :          0 :   if ( !symbolIdx.isEmpty() )
     744                 :            :   {
     745                 :          0 :     if ( symbolMap.contains( symbolIdx ) )
     746                 :            :     {
     747                 :          0 :       symbol = symbolMap.take( symbolIdx );
     748                 :          0 :     }
     749                 :            :     else
     750                 :            :     {
     751                 :          0 :       QgsDebugMsg( "symbol for rule " + symbolIdx + " not found!" );
     752                 :            :     }
     753                 :          0 :   }
     754                 :            : 
     755                 :          0 :   QString filterExp = ruleElem.attribute( QStringLiteral( "filter" ) );
     756                 :          0 :   QString label = ruleElem.attribute( QStringLiteral( "label" ) );
     757                 :          0 :   QString description = ruleElem.attribute( QStringLiteral( "description" ) );
     758                 :          0 :   int scaleMinDenom = ruleElem.attribute( QStringLiteral( "scalemindenom" ), QStringLiteral( "0" ) ).toInt();
     759                 :          0 :   int scaleMaxDenom = ruleElem.attribute( QStringLiteral( "scalemaxdenom" ), QStringLiteral( "0" ) ).toInt();
     760                 :          0 :   QString ruleKey = ruleElem.attribute( QStringLiteral( "key" ) );
     761                 :          0 :   Rule *rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
     762                 :            : 
     763                 :          0 :   if ( !ruleKey.isEmpty() )
     764                 :          0 :     rule->mRuleKey = ruleKey;
     765                 :            : 
     766                 :          0 :   rule->setActive( ruleElem.attribute( QStringLiteral( "checkstate" ), QStringLiteral( "1" ) ).toInt() );
     767                 :            : 
     768                 :          0 :   QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
     769                 :          0 :   while ( !childRuleElem.isNull() )
     770                 :            :   {
     771                 :          0 :     Rule *childRule = create( childRuleElem, symbolMap );
     772                 :          0 :     if ( childRule )
     773                 :            :     {
     774                 :          0 :       rule->appendChild( childRule );
     775                 :          0 :     }
     776                 :            :     else
     777                 :            :     {
     778                 :          0 :       QgsDebugMsg( QStringLiteral( "failed to init a child rule!" ) );
     779                 :            :     }
     780                 :          0 :     childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
     781                 :            :   }
     782                 :            : 
     783                 :          0 :   return rule;
     784                 :          0 : }
     785                 :            : 
     786                 :          0 : QgsRuleBasedRenderer::RuleList QgsRuleBasedRenderer::Rule::descendants() const
     787                 :            : {
     788                 :          0 :   RuleList l;
     789                 :          0 :   for ( QgsRuleBasedRenderer::Rule *c : mChildren )
     790                 :            :   {
     791                 :          0 :     l += c;
     792                 :          0 :     l += c->descendants();
     793                 :            :   }
     794                 :          0 :   return l;
     795                 :          0 : }
     796                 :            : 
     797                 :          0 : QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::createFromSld( QDomElement &ruleElem, QgsWkbTypes::GeometryType geomType )
     798                 :            : {
     799                 :          0 :   if ( ruleElem.localName() != QLatin1String( "Rule" ) )
     800                 :            :   {
     801                 :          0 :     QgsDebugMsg( QStringLiteral( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
     802                 :          0 :     return nullptr;
     803                 :            :   }
     804                 :            : 
     805                 :          0 :   QString label, description, filterExp;
     806                 :          0 :   int scaleMinDenom = 0, scaleMaxDenom = 0;
     807                 :          0 :   QgsSymbolLayerList layers;
     808                 :            : 
     809                 :            :   // retrieve the Rule element child nodes
     810                 :          0 :   QDomElement childElem = ruleElem.firstChildElement();
     811                 :          0 :   while ( !childElem.isNull() )
     812                 :            :   {
     813                 :          0 :     if ( childElem.localName() == QLatin1String( "Name" ) )
     814                 :            :     {
     815                 :            :       // <se:Name> tag contains the rule identifier,
     816                 :            :       // so prefer title tag for the label property value
     817                 :          0 :       if ( label.isEmpty() )
     818                 :          0 :         label = childElem.firstChild().nodeValue();
     819                 :          0 :     }
     820                 :          0 :     else if ( childElem.localName() == QLatin1String( "Description" ) )
     821                 :            :     {
     822                 :            :       // <se:Description> can contains a title and an abstract
     823                 :          0 :       QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
     824                 :          0 :       if ( !titleElem.isNull() )
     825                 :            :       {
     826                 :          0 :         label = titleElem.firstChild().nodeValue();
     827                 :          0 :       }
     828                 :            : 
     829                 :          0 :       QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
     830                 :          0 :       if ( !abstractElem.isNull() )
     831                 :            :       {
     832                 :          0 :         description = abstractElem.firstChild().nodeValue();
     833                 :          0 :       }
     834                 :          0 :     }
     835                 :          0 :     else if ( childElem.localName() == QLatin1String( "Abstract" ) )
     836                 :            :     {
     837                 :            :       // <sld:Abstract> (v1.0)
     838                 :          0 :       description = childElem.firstChild().nodeValue();
     839                 :          0 :     }
     840                 :          0 :     else if ( childElem.localName() == QLatin1String( "Title" ) )
     841                 :            :     {
     842                 :            :       // <sld:Title> (v1.0)
     843                 :          0 :       label = childElem.firstChild().nodeValue();
     844                 :          0 :     }
     845                 :          0 :     else if ( childElem.localName() == QLatin1String( "Filter" ) )
     846                 :            :     {
     847                 :          0 :       QgsExpression *filter = QgsOgcUtils::expressionFromOgcFilter( childElem );
     848                 :          0 :       if ( filter )
     849                 :            :       {
     850                 :          0 :         if ( filter->hasParserError() )
     851                 :            :         {
     852                 :          0 :           QgsDebugMsg( "parser error: " + filter->parserErrorString() );
     853                 :          0 :         }
     854                 :            :         else
     855                 :            :         {
     856                 :          0 :           filterExp = filter->expression();
     857                 :            :         }
     858                 :          0 :         delete filter;
     859                 :          0 :       }
     860                 :          0 :     }
     861                 :          0 :     else if ( childElem.localName() == QLatin1String( "MinScaleDenominator" ) )
     862                 :            :     {
     863                 :            :       bool ok;
     864                 :          0 :       int v = childElem.firstChild().nodeValue().toInt( &ok );
     865                 :          0 :       if ( ok )
     866                 :          0 :         scaleMinDenom = v;
     867                 :          0 :     }
     868                 :          0 :     else if ( childElem.localName() == QLatin1String( "MaxScaleDenominator" ) )
     869                 :            :     {
     870                 :            :       bool ok;
     871                 :          0 :       int v = childElem.firstChild().nodeValue().toInt( &ok );
     872                 :          0 :       if ( ok )
     873                 :          0 :         scaleMaxDenom = v;
     874                 :          0 :     }
     875                 :          0 :     else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
     876                 :            :     {
     877                 :            :       // create symbol layers for this symbolizer
     878                 :          0 :       QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
     879                 :          0 :     }
     880                 :            : 
     881                 :          0 :     childElem = childElem.nextSiblingElement();
     882                 :            :   }
     883                 :            : 
     884                 :            :   // now create the symbol
     885                 :          0 :   QgsSymbol *symbol = nullptr;
     886                 :          0 :   if ( !layers.isEmpty() )
     887                 :            :   {
     888                 :          0 :     switch ( geomType )
     889                 :            :     {
     890                 :            :       case QgsWkbTypes::LineGeometry:
     891                 :          0 :         symbol = new QgsLineSymbol( layers );
     892                 :          0 :         break;
     893                 :            : 
     894                 :            :       case QgsWkbTypes::PolygonGeometry:
     895                 :          0 :         symbol = new QgsFillSymbol( layers );
     896                 :          0 :         break;
     897                 :            : 
     898                 :            :       case QgsWkbTypes::PointGeometry:
     899                 :          0 :         symbol = new QgsMarkerSymbol( layers );
     900                 :          0 :         break;
     901                 :            : 
     902                 :            :       default:
     903                 :          0 :         QgsDebugMsg( QStringLiteral( "invalid geometry type: found %1" ).arg( geomType ) );
     904                 :          0 :         return nullptr;
     905                 :            :     }
     906                 :          0 :   }
     907                 :            : 
     908                 :            :   // and then create and return the new rule
     909                 :          0 :   return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
     910                 :          0 : }
     911                 :            : 
     912                 :            : 
     913                 :            : /////////////////////
     914                 :            : 
     915                 :          0 : QgsRuleBasedRenderer::QgsRuleBasedRenderer( QgsRuleBasedRenderer::Rule *root )
     916                 :          0 :   : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
     917                 :          0 :   , mRootRule( root )
     918                 :          0 : {
     919                 :          0 : }
     920                 :            : 
     921                 :          0 : QgsRuleBasedRenderer::QgsRuleBasedRenderer( QgsSymbol *defaultSymbol )
     922                 :          0 :   : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
     923                 :          0 : {
     924                 :          0 :   mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
     925                 :          0 :   mRootRule->appendChild( new Rule( defaultSymbol ) );
     926                 :          0 : }
     927                 :            : 
     928                 :          0 : QgsRuleBasedRenderer::~QgsRuleBasedRenderer()
     929                 :          0 : {
     930                 :          0 :   delete mRootRule;
     931                 :          0 : }
     932                 :            : 
     933                 :            : 
     934                 :          0 : QgsSymbol *QgsRuleBasedRenderer::symbolForFeature( const QgsFeature &, QgsRenderContext & ) const
     935                 :            : {
     936                 :            :   // not used at all
     937                 :          0 :   return nullptr;
     938                 :            : }
     939                 :            : 
     940                 :          0 : bool QgsRuleBasedRenderer::renderFeature( const QgsFeature &feature,
     941                 :            :     QgsRenderContext &context,
     942                 :            :     int layer,
     943                 :            :     bool selected,
     944                 :            :     bool drawVertexMarker )
     945                 :            : {
     946                 :            :   Q_UNUSED( layer )
     947                 :            : 
     948                 :          0 :   int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
     949                 :          0 :   mCurrentFeatures.append( FeatureToRender( feature, flags ) );
     950                 :            : 
     951                 :            :   // check each active rule
     952                 :          0 :   return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue ) == Rule::Rendered;
     953                 :          0 : }
     954                 :            : 
     955                 :            : 
     956                 :          0 : void QgsRuleBasedRenderer::startRender( QgsRenderContext &context, const QgsFields &fields )
     957                 :            : {
     958                 :          0 :   QgsFeatureRenderer::startRender( context, fields );
     959                 :            : 
     960                 :            :   // prepare active children
     961                 :          0 :   mRootRule->startRender( context, fields, mFilter );
     962                 :            : 
     963                 :          0 :   QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
     964                 :          0 :   QList<int> symbolZLevels = qgis::setToList( symbolZLevelsSet );
     965                 :          0 :   std::sort( symbolZLevels.begin(), symbolZLevels.end() );
     966                 :            : 
     967                 :            :   // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
     968                 :            :   // and prepare rendering queue
     969                 :          0 :   QMap<int, int> zLevelsToNormLevels;
     970                 :          0 :   int maxNormLevel = -1;
     971                 :          0 :   const auto constSymbolZLevels = symbolZLevels;
     972                 :          0 :   for ( int zLevel : constSymbolZLevels )
     973                 :            :   {
     974                 :          0 :     zLevelsToNormLevels[zLevel] = ++maxNormLevel;
     975                 :          0 :     mRenderQueue.append( RenderLevel( zLevel ) );
     976                 :          0 :     QgsDebugMsgLevel( QStringLiteral( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ), 4 );
     977                 :            :   }
     978                 :            : 
     979                 :          0 :   mRootRule->setNormZLevels( zLevelsToNormLevels );
     980                 :          0 : }
     981                 :            : 
     982                 :          0 : void QgsRuleBasedRenderer::stopRender( QgsRenderContext &context )
     983                 :            : {
     984                 :          0 :   QgsFeatureRenderer::stopRender( context );
     985                 :            : 
     986                 :            :   //
     987                 :            :   // do the actual rendering
     988                 :            :   //
     989                 :            : 
     990                 :            :   // go through all levels
     991                 :          0 :   if ( !context.renderingStopped() )
     992                 :            :   {
     993                 :          0 :     const auto constMRenderQueue = mRenderQueue;
     994                 :          0 :     for ( const RenderLevel &level : constMRenderQueue )
     995                 :            :     {
     996                 :            :       //QgsDebugMsg(QString("level %1").arg(level.zIndex));
     997                 :            :       // go through all jobs at the level
     998                 :          0 :       for ( const RenderJob *job : std::as_const( level.jobs ) )
     999                 :            :       {
    1000                 :          0 :         context.expressionContext().setFeature( job->ftr.feat );
    1001                 :            :         //QgsDebugMsg(QString("job fid %1").arg(job->f->id()));
    1002                 :            :         // render feature - but only with symbol layers with specified zIndex
    1003                 :          0 :         QgsSymbol *s = job->symbol;
    1004                 :          0 :         int count = s->symbolLayerCount();
    1005                 :          0 :         for ( int i = 0; i < count; i++ )
    1006                 :            :         {
    1007                 :            :           // TODO: better solution for this
    1008                 :            :           // renderFeatureWithSymbol asks which symbol layer to draw
    1009                 :            :           // but there are multiple transforms going on!
    1010                 :          0 :           if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
    1011                 :            :           {
    1012                 :          0 :             int flags = job->ftr.flags;
    1013                 :          0 :             renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
    1014                 :          0 :           }
    1015                 :          0 :         }
    1016                 :            :       }
    1017                 :            :     }
    1018                 :          0 :   }
    1019                 :            : 
    1020                 :            :   // clean current features
    1021                 :          0 :   mCurrentFeatures.clear();
    1022                 :            : 
    1023                 :            :   // clean render queue
    1024                 :          0 :   mRenderQueue.clear();
    1025                 :            : 
    1026                 :            :   // clean up rules from temporary stuff
    1027                 :          0 :   mRootRule->stopRender( context );
    1028                 :          0 : }
    1029                 :            : 
    1030                 :          0 : QString QgsRuleBasedRenderer::filter( const QgsFields & )
    1031                 :            : {
    1032                 :          0 :   return mFilter;
    1033                 :            : }
    1034                 :            : 
    1035                 :          0 : QSet<QString> QgsRuleBasedRenderer::usedAttributes( const QgsRenderContext &context ) const
    1036                 :            : {
    1037                 :          0 :   return mRootRule->usedAttributes( context );
    1038                 :            : }
    1039                 :            : 
    1040                 :          0 : bool QgsRuleBasedRenderer::filterNeedsGeometry() const
    1041                 :            : {
    1042                 :          0 :   return mRootRule->needsGeometry();
    1043                 :            : }
    1044                 :            : 
    1045                 :          0 : QgsRuleBasedRenderer *QgsRuleBasedRenderer::clone() const
    1046                 :            : {
    1047                 :          0 :   QgsRuleBasedRenderer::Rule *clonedRoot = mRootRule->clone();
    1048                 :            : 
    1049                 :            :   // normally with clone() the individual rules get new keys (UUID), but here we want to keep
    1050                 :            :   // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
    1051                 :          0 :   clonedRoot->setRuleKey( mRootRule->ruleKey() );
    1052                 :          0 :   RuleList origDescendants = mRootRule->descendants();
    1053                 :          0 :   RuleList clonedDescendants = clonedRoot->descendants();
    1054                 :            :   Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
    1055                 :          0 :   for ( int i = 0; i < origDescendants.count(); ++i )
    1056                 :          0 :     clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
    1057                 :            : 
    1058                 :          0 :   QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( clonedRoot );
    1059                 :            : 
    1060                 :          0 :   r->setUsingSymbolLevels( usingSymbolLevels() );
    1061                 :          0 :   copyRendererData( r );
    1062                 :          0 :   return r;
    1063                 :          0 : }
    1064                 :            : 
    1065                 :          0 : void QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
    1066                 :            : {
    1067                 :          0 :   mRootRule->toSld( doc, element, props );
    1068                 :          0 : }
    1069                 :            : 
    1070                 :            : // TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
    1071                 :          0 : QgsSymbolList QgsRuleBasedRenderer::symbols( QgsRenderContext &context ) const
    1072                 :            : {
    1073                 :          0 :   return mRootRule->symbols( context );
    1074                 :            : }
    1075                 :            : 
    1076                 :          0 : QDomElement QgsRuleBasedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
    1077                 :            : {
    1078                 :          0 :   QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
    1079                 :          0 :   rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "RuleRenderer" ) );
    1080                 :          0 :   rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
    1081                 :          0 :   rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
    1082                 :            : 
    1083                 :          0 :   QgsSymbolMap symbols;
    1084                 :            : 
    1085                 :          0 :   QDomElement rulesElem = mRootRule->save( doc, symbols );
    1086                 :          0 :   rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
    1087                 :          0 :   rendererElem.appendChild( rulesElem );
    1088                 :            : 
    1089                 :          0 :   QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
    1090                 :          0 :   rendererElem.appendChild( symbolsElem );
    1091                 :            : 
    1092                 :          0 :   if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect ) )
    1093                 :          0 :     mPaintEffect->saveProperties( doc, rendererElem );
    1094                 :            : 
    1095                 :          0 :   if ( !mOrderBy.isEmpty() )
    1096                 :            :   {
    1097                 :          0 :     QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
    1098                 :          0 :     mOrderBy.save( orderBy );
    1099                 :          0 :     rendererElem.appendChild( orderBy );
    1100                 :          0 :   }
    1101                 :          0 :   rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
    1102                 :            : 
    1103                 :          0 :   return rendererElem;
    1104                 :          0 : }
    1105                 :            : 
    1106                 :          0 : bool QgsRuleBasedRenderer::legendSymbolItemsCheckable() const
    1107                 :            : {
    1108                 :          0 :   return true;
    1109                 :            : }
    1110                 :            : 
    1111                 :          0 : bool QgsRuleBasedRenderer::legendSymbolItemChecked( const QString &key )
    1112                 :            : {
    1113                 :          0 :   Rule *rule = mRootRule->findRuleByKey( key );
    1114                 :          0 :   return rule ? rule->active() : true;
    1115                 :            : }
    1116                 :            : 
    1117                 :          0 : void QgsRuleBasedRenderer::checkLegendSymbolItem( const QString &key, bool state )
    1118                 :            : {
    1119                 :          0 :   Rule *rule = mRootRule->findRuleByKey( key );
    1120                 :          0 :   if ( rule )
    1121                 :          0 :     rule->setActive( state );
    1122                 :          0 : }
    1123                 :            : 
    1124                 :          0 : void QgsRuleBasedRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
    1125                 :            : {
    1126                 :          0 :   Rule *rule = mRootRule->findRuleByKey( key );
    1127                 :          0 :   if ( rule )
    1128                 :          0 :     rule->setSymbol( symbol );
    1129                 :            :   else
    1130                 :          0 :     delete symbol;
    1131                 :          0 : }
    1132                 :            : 
    1133                 :          0 : QgsLegendSymbolList QgsRuleBasedRenderer::legendSymbolItems() const
    1134                 :            : {
    1135                 :          0 :   return mRootRule->legendSymbolItems();
    1136                 :            : }
    1137                 :            : 
    1138                 :            : 
    1139                 :          0 : QgsFeatureRenderer *QgsRuleBasedRenderer::create( QDomElement &element, const QgsReadWriteContext &context )
    1140                 :            : {
    1141                 :            :   // load symbols
    1142                 :          0 :   QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
    1143                 :          0 :   if ( symbolsElem.isNull() )
    1144                 :          0 :     return nullptr;
    1145                 :            : 
    1146                 :          0 :   QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
    1147                 :            : 
    1148                 :          0 :   QDomElement rulesElem = element.firstChildElement( QStringLiteral( "rules" ) );
    1149                 :            : 
    1150                 :          0 :   Rule *root = Rule::create( rulesElem, symbolMap );
    1151                 :          0 :   if ( !root )
    1152                 :          0 :     return nullptr;
    1153                 :            : 
    1154                 :          0 :   QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( root );
    1155                 :            : 
    1156                 :            :   // delete symbols if there are any more
    1157                 :          0 :   QgsSymbolLayerUtils::clearSymbolMap( symbolMap );
    1158                 :            : 
    1159                 :          0 :   return r;
    1160                 :          0 : }
    1161                 :            : 
    1162                 :          0 : QgsFeatureRenderer *QgsRuleBasedRenderer::createFromSld( QDomElement &element, QgsWkbTypes::GeometryType geomType )
    1163                 :            : {
    1164                 :            :   // retrieve child rules
    1165                 :          0 :   Rule *root = nullptr;
    1166                 :            : 
    1167                 :          0 :   QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
    1168                 :          0 :   while ( !ruleElem.isNull() )
    1169                 :            :   {
    1170                 :          0 :     Rule *child = Rule::createFromSld( ruleElem, geomType );
    1171                 :          0 :     if ( child )
    1172                 :            :     {
    1173                 :            :       // create the root rule if not done before
    1174                 :          0 :       if ( !root )
    1175                 :          0 :         root = new Rule( nullptr );
    1176                 :            : 
    1177                 :          0 :       root->appendChild( child );
    1178                 :          0 :     }
    1179                 :            : 
    1180                 :          0 :     ruleElem = ruleElem.nextSiblingElement( QStringLiteral( "Rule" ) );
    1181                 :            :   }
    1182                 :            : 
    1183                 :          0 :   if ( !root )
    1184                 :            :   {
    1185                 :            :     // no valid rules was found
    1186                 :          0 :     return nullptr;
    1187                 :            :   }
    1188                 :            : 
    1189                 :            :   // create and return the new renderer
    1190                 :          0 :   return new QgsRuleBasedRenderer( root );
    1191                 :          0 : }
    1192                 :            : 
    1193                 :            : #include "qgscategorizedsymbolrenderer.h"
    1194                 :            : #include "qgsgraduatedsymbolrenderer.h"
    1195                 :            : 
    1196                 :          0 : void QgsRuleBasedRenderer::refineRuleCategories( QgsRuleBasedRenderer::Rule *initialRule, QgsCategorizedSymbolRenderer *r )
    1197                 :            : {
    1198                 :          0 :   QString attr = r->classAttribute();
    1199                 :            :   // categorizedAttr could be either an attribute name or an expression.
    1200                 :            :   // the only way to differentiate is to test it as an expression...
    1201                 :          0 :   QgsExpression testExpr( attr );
    1202                 :          0 :   if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
    1203                 :            :   {
    1204                 :            :     //not an expression, so need to quote column name
    1205                 :          0 :     attr = QgsExpression::quotedColumnRef( attr );
    1206                 :          0 :   }
    1207                 :            : 
    1208                 :          0 :   const auto constCategories = r->categories();
    1209                 :          0 :   for ( const QgsRendererCategory &cat : constCategories )
    1210                 :            :   {
    1211                 :          0 :     QString value;
    1212                 :            :     // not quoting numbers saves a type cast
    1213                 :          0 :     if ( cat.value().type() == QVariant::Int )
    1214                 :          0 :       value = cat.value().toString();
    1215                 :          0 :     else if ( cat.value().type() == QVariant::Double )
    1216                 :            :       // we loose precision here - so we may miss some categories :-(
    1217                 :            :       // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
    1218                 :          0 :       value = QString::number( cat.value().toDouble(), 'f', 4 );
    1219                 :            :     else
    1220                 :          0 :       value = QgsExpression::quotedString( cat.value().toString() );
    1221                 :          0 :     QString filter = QStringLiteral( "%1 = %2" ).arg( attr, value );
    1222                 :          0 :     QString label = filter;
    1223                 :          0 :     initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
    1224                 :          0 :   }
    1225                 :          0 : }
    1226                 :            : 
    1227                 :          0 : void QgsRuleBasedRenderer::refineRuleRanges( QgsRuleBasedRenderer::Rule *initialRule, QgsGraduatedSymbolRenderer *r )
    1228                 :            : {
    1229                 :          0 :   QString attr = r->classAttribute();
    1230                 :            :   // categorizedAttr could be either an attribute name or an expression.
    1231                 :            :   // the only way to differentiate is to test it as an expression...
    1232                 :          0 :   QgsExpression testExpr( attr );
    1233                 :          0 :   if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
    1234                 :            :   {
    1235                 :            :     //not an expression, so need to quote column name
    1236                 :          0 :     attr = QgsExpression::quotedColumnRef( attr );
    1237                 :          0 :   }
    1238                 :          0 :   else if ( !testExpr.isField() )
    1239                 :            :   {
    1240                 :            :     //otherwise wrap expression in brackets
    1241                 :          0 :     attr = QStringLiteral( "(%1)" ).arg( attr );
    1242                 :          0 :   }
    1243                 :            : 
    1244                 :          0 :   bool firstRange = true;
    1245                 :          0 :   const auto constRanges = r->ranges();
    1246                 :          0 :   for ( const QgsRendererRange &rng : constRanges )
    1247                 :            :   {
    1248                 :            :     // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
    1249                 :            :     // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
    1250                 :          0 :     QString filter = QStringLiteral( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
    1251                 :          0 :                      QString::number( rng.lowerValue(), 'f', 4 ),
    1252                 :          0 :                      QString::number( rng.upperValue(), 'f', 4 ) );
    1253                 :          0 :     firstRange = false;
    1254                 :          0 :     QString label =  rng.label().isEmpty() ? filter : rng.label();
    1255                 :          0 :     initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
    1256                 :          0 :   }
    1257                 :          0 : }
    1258                 :            : 
    1259                 :          0 : void QgsRuleBasedRenderer::refineRuleScales( QgsRuleBasedRenderer::Rule *initialRule, QList<int> scales )
    1260                 :            : {
    1261                 :          0 :   std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
    1262                 :          0 :   double oldScale = initialRule->maximumScale();
    1263                 :          0 :   double maxDenom = initialRule->minimumScale();
    1264                 :          0 :   QgsSymbol *symbol = initialRule->symbol();
    1265                 :          0 :   const auto constScales = scales;
    1266                 :          0 :   for ( int scale : constScales )
    1267                 :            :   {
    1268                 :          0 :     if ( initialRule->maximumScale() >= scale )
    1269                 :          0 :       continue; // jump over the first scales out of the interval
    1270                 :          0 :     if ( maxDenom != 0 && maxDenom  <= scale )
    1271                 :          0 :       break; // ignore the latter scales out of the interval
    1272                 :          0 :     initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
    1273                 :          0 :     oldScale = scale;
    1274                 :            :   }
    1275                 :            :   // last rule
    1276                 :          0 :   initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
    1277                 :          0 : }
    1278                 :            : 
    1279                 :          0 : QString QgsRuleBasedRenderer::dump() const
    1280                 :            : {
    1281                 :          0 :   QString msg( QStringLiteral( "Rule-based renderer:\n" ) );
    1282                 :          0 :   msg += mRootRule->dump();
    1283                 :          0 :   return msg;
    1284                 :          0 : }
    1285                 :            : 
    1286                 :          0 : bool QgsRuleBasedRenderer::willRenderFeature( const QgsFeature &feature, QgsRenderContext &context ) const
    1287                 :            : {
    1288                 :          0 :   return mRootRule->willRenderFeature( feature, &context );
    1289                 :            : }
    1290                 :            : 
    1291                 :          0 : QgsSymbolList QgsRuleBasedRenderer::symbolsForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
    1292                 :            : {
    1293                 :          0 :   return mRootRule->symbolsForFeature( feature, &context );
    1294                 :            : }
    1295                 :            : 
    1296                 :          0 : QgsSymbolList QgsRuleBasedRenderer::originalSymbolsForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
    1297                 :            : {
    1298                 :          0 :   return mRootRule->symbolsForFeature( feature, &context );
    1299                 :            : }
    1300                 :            : 
    1301                 :          0 : QSet< QString > QgsRuleBasedRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
    1302                 :            : {
    1303                 :          0 :   return mRootRule->legendKeysForFeature( feature, &context );
    1304                 :            : }
    1305                 :            : 
    1306                 :          0 : bool QgsRuleBasedRenderer::accept( QgsStyleEntityVisitorInterface *visitor ) const
    1307                 :            : {
    1308                 :          0 :   return mRootRule->accept( visitor );
    1309                 :            : }
    1310                 :            : 
    1311                 :          0 : QgsRuleBasedRenderer *QgsRuleBasedRenderer::convertFromRenderer( const QgsFeatureRenderer *renderer, QgsVectorLayer *layer )
    1312                 :            : {
    1313                 :          0 :   std::unique_ptr< QgsRuleBasedRenderer > r;
    1314                 :          0 :   if ( renderer->type() == QLatin1String( "RuleRenderer" ) )
    1315                 :            :   {
    1316                 :          0 :     r.reset( dynamic_cast<QgsRuleBasedRenderer *>( renderer->clone() ) );
    1317                 :          0 :   }
    1318                 :          0 :   else if ( renderer->type() == QLatin1String( "singleSymbol" ) )
    1319                 :            :   {
    1320                 :          0 :     const QgsSingleSymbolRenderer *singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRenderer *>( renderer );
    1321                 :          0 :     if ( !singleSymbolRenderer )
    1322                 :          0 :       return nullptr;
    1323                 :            : 
    1324                 :          0 :     std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->symbol()->clone() );
    1325                 :          0 :     r = std::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
    1326                 :          0 :   }
    1327                 :          0 :   else if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
    1328                 :            :   {
    1329                 :          0 :     const QgsCategorizedSymbolRenderer *categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
    1330                 :          0 :     if ( !categorizedRenderer )
    1331                 :          0 :       return nullptr;
    1332                 :            : 
    1333                 :          0 :     QString attr = categorizedRenderer->classAttribute();
    1334                 :            :     // categorizedAttr could be either an attribute name or an expression.
    1335                 :            :     // the only way to differentiate is to test it as an expression...
    1336                 :          0 :     QgsExpression testExpr( attr );
    1337                 :          0 :     if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
    1338                 :            :     {
    1339                 :            :       //not an expression, so need to quote column name
    1340                 :          0 :       attr = QgsExpression::quotedColumnRef( attr );
    1341                 :          0 :     }
    1342                 :            : 
    1343                 :          0 :     std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
    1344                 :            : 
    1345                 :          0 :     QString expression;
    1346                 :          0 :     QString value;
    1347                 :          0 :     QgsRendererCategory category;
    1348                 :          0 :     for ( const QgsRendererCategory &category : categorizedRenderer->categories() )
    1349                 :            :     {
    1350                 :          0 :       std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
    1351                 :            : 
    1352                 :          0 :       rule->setLabel( category.label() );
    1353                 :            : 
    1354                 :            :       //We first define the rule corresponding to the category
    1355                 :          0 :       if ( category.value().type() == QVariant::List )
    1356                 :            :       {
    1357                 :          0 :         QStringList values;
    1358                 :          0 :         const QVariantList list = category.value().toList();
    1359                 :          0 :         for ( const QVariant &v : list )
    1360                 :            :         {
    1361                 :            :           //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
    1362                 :          0 :           if ( QVariant( v ).convert( QVariant::Double ) )
    1363                 :            :           {
    1364                 :          0 :             values << v.toString();
    1365                 :          0 :           }
    1366                 :            :           else
    1367                 :            :           {
    1368                 :          0 :             values << QgsExpression::quotedString( v.toString() );
    1369                 :            :           }
    1370                 :            :         }
    1371                 :            : 
    1372                 :          0 :         if ( values.empty() )
    1373                 :            :         {
    1374                 :          0 :           expression = QStringLiteral( "ELSE" );
    1375                 :          0 :         }
    1376                 :            :         else
    1377                 :            :         {
    1378                 :          0 :           expression = QStringLiteral( "%1 IN (%2)" ).arg( attr, values.join( ',' ) );
    1379                 :            :         }
    1380                 :          0 :       }
    1381                 :            :       else
    1382                 :            :       {
    1383                 :            :         //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
    1384                 :          0 :         if ( category.value().convert( QVariant::Double ) )
    1385                 :            :         {
    1386                 :          0 :           value = category.value().toString();
    1387                 :          0 :         }
    1388                 :            :         else
    1389                 :            :         {
    1390                 :          0 :           value = QgsExpression::quotedString( category.value().toString() );
    1391                 :            :         }
    1392                 :            : 
    1393                 :            :         //An empty category is equivalent to the ELSE keyword
    1394                 :          0 :         if ( value == QLatin1String( "''" ) )
    1395                 :            :         {
    1396                 :          0 :           expression = QStringLiteral( "ELSE" );
    1397                 :          0 :         }
    1398                 :            :         else
    1399                 :            :         {
    1400                 :          0 :           expression = QStringLiteral( "%1 = %2" ).arg( attr, value );
    1401                 :            :         }
    1402                 :            :       }
    1403                 :          0 :       rule->setFilterExpression( expression );
    1404                 :            : 
    1405                 :            :       //Then we construct an equivalent symbol.
    1406                 :            :       //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
    1407                 :            :       //data dependent area and rotation, so we need to convert these to obtain the same rendering
    1408                 :            : 
    1409                 :          0 :       std::unique_ptr< QgsSymbol > origSymbol( category.symbol()->clone() );
    1410                 :          0 :       rule->setSymbol( origSymbol.release() );
    1411                 :            : 
    1412                 :          0 :       rootrule->appendChild( rule.release() );
    1413                 :          0 :     }
    1414                 :            : 
    1415                 :          0 :     r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
    1416                 :          0 :   }
    1417                 :          0 :   else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
    1418                 :            :   {
    1419                 :          0 :     const QgsGraduatedSymbolRenderer *graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
    1420                 :          0 :     if ( !graduatedRenderer )
    1421                 :          0 :       return nullptr;
    1422                 :            : 
    1423                 :          0 :     QString attr = graduatedRenderer->classAttribute();
    1424                 :            :     // categorizedAttr could be either an attribute name or an expression.
    1425                 :            :     // the only way to differentiate is to test it as an expression...
    1426                 :          0 :     QgsExpression testExpr( attr );
    1427                 :          0 :     if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
    1428                 :            :     {
    1429                 :            :       //not an expression, so need to quote column name
    1430                 :          0 :       attr = QgsExpression::quotedColumnRef( attr );
    1431                 :          0 :     }
    1432                 :          0 :     else if ( !testExpr.isField() )
    1433                 :            :     {
    1434                 :            :       //otherwise wrap expression in brackets
    1435                 :          0 :       attr = QStringLiteral( "(%1)" ).arg( attr );
    1436                 :          0 :     }
    1437                 :            : 
    1438                 :          0 :     std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
    1439                 :            : 
    1440                 :          0 :     QString expression;
    1441                 :          0 :     QgsRendererRange range;
    1442                 :          0 :     for ( int i = 0; i < graduatedRenderer->ranges().size(); ++i )
    1443                 :            :     {
    1444                 :          0 :       range = graduatedRenderer->ranges().value( i );
    1445                 :          0 :       std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
    1446                 :          0 :       rule->setLabel( range.label() );
    1447                 :          0 :       if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
    1448                 :            :       {
    1449                 :          0 :         expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
    1450                 :          0 :                      attr + " <= " + QString::number( range.upperValue(), 'f' );
    1451                 :          0 :       }
    1452                 :            :       else
    1453                 :            :       {
    1454                 :          0 :         expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
    1455                 :          0 :                      attr + " <= " + QString::number( range.upperValue(), 'f' );
    1456                 :            :       }
    1457                 :          0 :       rule->setFilterExpression( expression );
    1458                 :            : 
    1459                 :            :       //Then we construct an equivalent symbol.
    1460                 :            :       //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
    1461                 :            :       //data dependent area and rotation, so we need to convert these to obtain the same rendering
    1462                 :            : 
    1463                 :          0 :       std::unique_ptr< QgsSymbol > symbol( range.symbol()->clone() );
    1464                 :          0 :       rule->setSymbol( symbol.release() );
    1465                 :            : 
    1466                 :          0 :       rootrule->appendChild( rule.release() );
    1467                 :          0 :     }
    1468                 :            : 
    1469                 :          0 :     r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
    1470                 :          0 :   }
    1471                 :          0 :   else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
    1472                 :            :   {
    1473                 :          0 :     if ( const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer ) )
    1474                 :          0 :       return convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
    1475                 :          0 :   }
    1476                 :          0 :   else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
    1477                 :            :   {
    1478                 :          0 :     if ( const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer ) )
    1479                 :          0 :       r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
    1480                 :          0 :   }
    1481                 :          0 :   else if ( renderer->type() == QLatin1String( "mergedFeatureRenderer" ) )
    1482                 :            :   {
    1483                 :          0 :     if ( const QgsMergedFeatureRenderer *mergedRenderer = dynamic_cast<const QgsMergedFeatureRenderer *>( renderer ) )
    1484                 :          0 :       r.reset( convertFromRenderer( mergedRenderer->embeddedRenderer() ) );
    1485                 :          0 :   }
    1486                 :          0 :   else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
    1487                 :            :   {
    1488                 :          0 :     const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
    1489                 :            : 
    1490                 :          0 :     std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
    1491                 :            : 
    1492                 :          0 :     QgsFeatureRequest req;
    1493                 :          0 :     req.setFlags( QgsFeatureRequest::EmbeddedSymbols | QgsFeatureRequest::NoGeometry );
    1494                 :          0 :     req.setNoAttributes();
    1495                 :          0 :     QgsFeatureIterator it = layer->getFeatures( req );
    1496                 :          0 :     QgsFeature feature;
    1497                 :          0 :     while ( it.nextFeature( feature ) && rootrule->children().size() < 500 )
    1498                 :            :     {
    1499                 :          0 :       if ( feature.embeddedSymbol() )
    1500                 :            :       {
    1501                 :          0 :         std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
    1502                 :          0 :         rule->setFilterExpression( QStringLiteral( "$id=%1" ).arg( feature.id() ) );
    1503                 :          0 :         rule->setLabel( QString::number( feature.id() ) );
    1504                 :          0 :         rule->setSymbol( feature.embeddedSymbol()->clone() );
    1505                 :          0 :         rootrule->appendChild( rule.release() );
    1506                 :          0 :       }
    1507                 :            :     }
    1508                 :            : 
    1509                 :          0 :     std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
    1510                 :          0 :     rule->setFilterExpression( QStringLiteral( "ELSE" ) );
    1511                 :          0 :     rule->setLabel( QObject::tr( "All other features" ) );
    1512                 :          0 :     rule->setSymbol( embeddedRenderer->defaultSymbol()->clone() );
    1513                 :          0 :     rootrule->appendChild( rule.release() );
    1514                 :            : 
    1515                 :          0 :     r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
    1516                 :          0 :   }
    1517                 :            : 
    1518                 :          0 :   if ( r )
    1519                 :            :   {
    1520                 :          0 :     r->setOrderBy( renderer->orderBy() );
    1521                 :          0 :     r->setOrderByEnabled( renderer->orderByEnabled() );
    1522                 :          0 :   }
    1523                 :            : 
    1524                 :          0 :   return r.release();
    1525                 :          0 : }
    1526                 :            : 
    1527                 :          0 : void QgsRuleBasedRenderer::convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField )
    1528                 :            : {
    1529                 :          0 :   QString sizeExpression;
    1530                 :          0 :   switch ( symbol->type() )
    1531                 :            :   {
    1532                 :            :     case QgsSymbol::Marker:
    1533                 :          0 :       for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
    1534                 :            :       {
    1535                 :          0 :         QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( symbol->symbolLayer( j ) );
    1536                 :          0 :         if ( ! sizeScaleField.isEmpty() )
    1537                 :            :         {
    1538                 :          0 :           sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
    1539                 :          0 :           msl->setDataDefinedProperty( QgsSymbolLayer::PropertySize, QgsProperty::fromExpression( sizeExpression ) );
    1540                 :          0 :         }
    1541                 :          0 :         if ( ! rotationField.isEmpty() )
    1542                 :            :         {
    1543                 :          0 :           msl->setDataDefinedProperty( QgsSymbolLayer::PropertyAngle, QgsProperty::fromField( rotationField ) );
    1544                 :          0 :         }
    1545                 :          0 :       }
    1546                 :          0 :       break;
    1547                 :            :     case QgsSymbol::Line:
    1548                 :          0 :       if ( ! sizeScaleField.isEmpty() )
    1549                 :            :       {
    1550                 :          0 :         for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
    1551                 :            :         {
    1552                 :          0 :           if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "SimpleLine" ) )
    1553                 :            :           {
    1554                 :          0 :             QgsLineSymbolLayer *lsl = static_cast<QgsLineSymbolLayer *>( symbol->symbolLayer( j ) );
    1555                 :          0 :             sizeExpression = QStringLiteral( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
    1556                 :          0 :             lsl->setDataDefinedProperty( QgsSymbolLayer::PropertyStrokeWidth, QgsProperty::fromExpression( sizeExpression ) );
    1557                 :          0 :           }
    1558                 :          0 :           if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "MarkerLine" ) )
    1559                 :            :           {
    1560                 :          0 :             QgsSymbol *marker = symbol->symbolLayer( j )->subSymbol();
    1561                 :          0 :             for ( int k = 0; k < marker->symbolLayerCount(); ++k )
    1562                 :            :             {
    1563                 :          0 :               QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( marker->symbolLayer( k ) );
    1564                 :          0 :               sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
    1565                 :          0 :               msl->setDataDefinedProperty( QgsSymbolLayer::PropertySize, QgsProperty::fromExpression( sizeExpression ) );
    1566                 :          0 :             }
    1567                 :          0 :           }
    1568                 :          0 :         }
    1569                 :          0 :       }
    1570                 :          0 :       break;
    1571                 :            :     default:
    1572                 :          0 :       break;
    1573                 :            :   }
    1574                 :          0 : }

Generated by: LCOV version 1.14