QGIS API Documentation  3.27.0-Master (bef583a8ef)
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 }
@ Marker
Marker symbol.
@ Line
Line symbol.
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
QString classAttribute() const
Returns the class attribute for the renderer, which is the field name or expression string from the l...
A vector feature renderer which uses embedded feature symbology to render per-feature symbols.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
bool isField() const
Checks whether an expression consists only of a single field reference.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
static int expressionToLayerFieldIndex(const QString &expression, const QgsVectorLayer *layer)
Attempts to resolve an expression to a field index from the given layer.
static bool attemptReduceToInClause(const QStringList &expressions, QString &result)
Attempts to reduce a list of expressions to a single "field IN (val1, val2, ... )" type expression.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
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.
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QString type() const
Definition: qgsrenderer.h:142
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:52
void saveRendererData(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context)
Saves generic renderer data into the specified element.
virtual const QgsFeatureRenderer * embeddedRenderer() const
Returns the current embedded renderer (subrenderer) for this feature renderer.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:96
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
@ EmbeddedSymbols
Retrieve any embedded feature symbology (since QGIS 3.20)
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
const QgsSymbol * embeddedSymbol() const
Returns the feature's embedded symbology, or nullptr if the feature has no embedded symbol.
Definition: qgsfeature.cpp:306
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Container of fields for a vector layer.
Definition: qgsfields.h:45
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
QString classAttribute() const
Returns the attribute name (or expression) used for the classification.
const QgsRangeList & ranges() const
Returns a list of all ranges used in the classification.
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted,...
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
virtual double width() const
Returns the estimated width for the line symbol layer.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
Abstract base class for marker symbol layers.
double size() const
Returns the symbol size.
A marker symbol type, for rendering Point and MultiPoint geometries.
QgsMergedFeatureRenderer is a polygon or line-only feature renderer used to renderer a set of feature...
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
double rendererScale() const
Returns the renderer map scale.
QgsExpressionContext & expressionContext()
Gets the expression context.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
QVariant value() const
Returns the value corresponding to this category.
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
QString label() const
Returns the label used for the range.
QgsSymbol * symbol() const
Returns the symbol used for the range.
double upperValue() const
Returns the upper bound of the range.
double lowerValue() const
Returns the lower bound of the range.
This class keeps data about a rules for rule-based renderer.
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified symbology visitor, causing it to visit all child rules associated with the rule...
QgsRuleBasedRenderer::RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra...
void setSymbol(QgsSymbol *sym)
Sets a new symbol (or nullptr). Deletes old symbol.
void removeChild(QgsRuleBasedRenderer::Rule *rule)
delete child rule
QgsRuleBasedRenderer::Rule * findRuleByKey(const QString &key)
Try to find a rule given its unique key.
void insertChild(int i, QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
QString ruleKey() const
Unique rule identifier (for identification of rule within renderer)
bool needsGeometry() const
Returns true if this rule or one of its children needs the geometry to be applied.
QgsRuleBasedRenderer::Rule * takeChild(QgsRuleBasedRenderer::Rule *rule)
take child rule out, set parent as nullptr
RenderResult
The result of rendering a rule.
@ Rendered
Something was rendered.
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.
double maximumScale() const
Returns the maximum map scale (i.e.
void setIsElse(bool iselse)
Sets if this rule is an ELSE rule.
QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
tell which symbols will be used to render the feature
const QgsRuleBasedRenderer::RuleList & children() const
Returns all children rules of this rule.
bool isElse() const
Check if this rule is an ELSE rule.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
Returns which legend keys match the feature.
QgsRuleBasedRenderer::Rule * clone() const
clone this rule, return new instance
bool willRenderFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
only tell whether a feature will be rendered without actually rendering it
static QgsRuleBasedRenderer::Rule * create(QDomElement &ruleElem, QgsSymbolMap &symbolMap)
Create a rule from an XML definition.
QgsRuleBasedRenderer::Rule * parent()
The parent rule.
void removeChildAt(int i)
delete child rule
void setActive(bool state)
Sets if this rule is active.
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.
bool isFilterOK(const QgsFeature &f, QgsRenderContext *context=nullptr) const
Check if a given feature shall be rendered by this rule.
QgsSymbolList symbols(const QgsRenderContext &context=QgsRenderContext()) const
bool isScaleOK(double scale) const
Check if this rule applies for a given scale.
void setNormZLevels(const QMap< int, int > &zLevelsToNormLevels)
assign normalized z-levels [0..N-1] for this rule's symbol for quick access during rendering
QDomElement save(QDomDocument &doc, QgsSymbolMap &symbolMap) const
void appendChild(QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
QgsRuleBasedRenderer::Rule * takeChildAt(int i)
take child rule out, set parent as nullptr
QSet< int > collectZLevels()
Gets all used z-levels from this rule and children.
double minimumScale() const
Returns the minimum map scale (i.e.
void stopRender(QgsRenderContext &context)
Stop a rendering process.
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.
QgsLegendSymbolList legendSymbolItems(int currentLevel=-1) const
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the attributes used to evaluate the expression of this rule.
void setFilterExpression(const QString &filterExp)
Set the expression used to check if a given feature shall be rendered with this rule.
static QgsRuleBasedRenderer::Rule * createFromSld(QDomElement &element, QgsWkbTypes::GeometryType geomType)
Create a rule from the SLD provided in element and for the specified geometry type.
QString dump(int indent=0) const
Dump for debug purpose.
void setRuleKey(const QString &key)
Override the assigned rule key (should be used just internally by rule-based renderer)
bool startRender(QgsRenderContext &context, const QgsFields &fields, QString &filter)
prepare the rule for rendering and its children (build active children array)
QString filterExpression() const
A filter that will check if this rule applies.
bool active() const
Returns if this rule is active.
void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Saves the symbol layer as SLD.
Rule based renderer.
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
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...
bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
Stores renderer properties to an XML element.
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
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 ...
QList< QgsRuleBasedRenderer::RenderLevel > RenderQueue
Rendering queue: a list of rendering levels.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
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
QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns list of symbols used for rendering the feature.
QString dump() const override
Returns debug information about this renderer.
QgsSymbolList originalSymbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Equivalent of originalSymbolsForFeature() call extended to support renderers that may use more symbol...
static QgsRuleBasedRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer, QgsVectorLayer *layer=nullptr)
Creates a new QgsRuleBasedRenderer from an existing renderer.
bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
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...
bool willRenderFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns whether the renderer will render a feature or not.
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...
QList< QgsRuleBasedRenderer::Rule * > RuleList
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
Rule * mRootRule
the root node with hierarchical list of rules
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
QgsRuleBasedRenderer * clone() const override
Create a deep copy of this renderer.
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
Creates a new rule-based renderer instance from XML.
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.
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
QList< FeatureToRender > mCurrentFeatures
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...
static QgsFeatureRenderer * createFromSld(QDomElement &element, QgsWkbTypes::GeometryType geomType)
QgsRuleBasedRenderer(QgsRuleBasedRenderer::Rule *root)
Constructs the renderer from given tree of rules (takes ownership)
QgsSymbolList symbols(QgsRenderContext &context) const override
Returns list of symbols used by the renderer.
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
QgsSymbol * symbol() const
Returns the symbol which will be rendered for every feature.
An interface for classes which can visit style entity (e.g.
@ SymbolRule
Rule based symbology or label child rule.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
Definition: qgsstyle.h:1342
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, QVariantMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
static bool createSymbolLayerListFromSld(QDomElement &element, QgsWkbTypes::GeometryType geomType, QList< QgsSymbolLayer * > &layers)
Creates a symbol layer list from a DOM element.
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.
static void clearSymbolMap(QgsSymbolMap &symbols)
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 ...
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.
@ PropertyAngle
Symbol angle.
@ PropertySize
Symbol size.
@ PropertyStrokeWidth
Stroke width.
virtual QString layerType() const =0
Returns a string that represents this layer type.
int renderingPass() const
Specifies the rendering pass in which this symbol layer should be rendered.
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
virtual QgsSymbol * subSymbol()
Returns the symbol's sub symbol, if present.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:727
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:215
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2260
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
#define RENDERER_TAG_NAME
Definition: qgsrenderer.h:50
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition: qgsrenderer.h:45
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:44
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:27
Feature for rendering by a QgsRuleBasedRenderer.
A QgsRuleBasedRenderer rendering job, consisting of a feature to be rendered with a particular symbol...
Render level: a list of jobs to be drawn at particular level for a QgsRuleBasedRenderer.
Contains information relating to a node (i.e.
Contains information relating to the style entity currently being visited.