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