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