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