QGIS API Documentation  3.6.0-Noosa (5873452)
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  Q_FOREACH ( const RenderLevel &level, mRenderQueue )
933  {
934  //QgsDebugMsg(QString("level %1").arg(level.zIndex));
935  // go through all jobs at the level
936  Q_FOREACH ( const RenderJob *job, level.jobs )
937  {
938  context.expressionContext().setFeature( job->ftr.feat );
939  //QgsDebugMsg(QString("job fid %1").arg(job->f->id()));
940  // render feature - but only with symbol layers with specified zIndex
941  QgsSymbol *s = job->symbol;
942  int count = s->symbolLayerCount();
943  for ( int i = 0; i < count; i++ )
944  {
945  // TODO: better solution for this
946  // renderFeatureWithSymbol asks which symbol layer to draw
947  // but there are multiple transforms going on!
948  if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
949  {
950  int flags = job->ftr.flags;
951  renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
952  }
953  }
954  }
955  }
956 
957  // clean current features
958  mCurrentFeatures.clear();
959 
960  // clean render queue
961  mRenderQueue.clear();
962 
963  // clean up rules from temporary stuff
964  mRootRule->stopRender( context );
965 }
966 
968 {
969  return mFilter;
970 }
971 
972 QSet<QString> QgsRuleBasedRenderer::usedAttributes( const QgsRenderContext &context ) const
973 {
974  return mRootRule->usedAttributes( context );
975 }
976 
978 {
979  return mRootRule->needsGeometry();
980 }
981 
983 {
984  QgsRuleBasedRenderer::Rule *clonedRoot = mRootRule->clone();
985 
986  // normally with clone() the individual rules get new keys (UUID), but here we want to keep
987  // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
988  clonedRoot->setRuleKey( mRootRule->ruleKey() );
989  RuleList origDescendants = mRootRule->descendants();
990  RuleList clonedDescendants = clonedRoot->descendants();
991  Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
992  for ( int i = 0; i < origDescendants.count(); ++i )
993  clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
994 
995  QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( clonedRoot );
996 
998  copyRendererData( r );
999  return r;
1000 }
1001 
1002 void QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1003 {
1004  mRootRule->toSld( doc, element, props );
1005 }
1006 
1007 // TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
1009 {
1010  return mRootRule->symbols( context );
1011 }
1012 
1013 QDomElement QgsRuleBasedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
1014 {
1015  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1016  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "RuleRenderer" ) );
1017  rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
1018  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
1019 
1021 
1022  QDomElement rulesElem = mRootRule->save( doc, symbols );
1023  rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
1024  rendererElem.appendChild( rulesElem );
1025 
1026  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
1027  rendererElem.appendChild( symbolsElem );
1028 
1030  mPaintEffect->saveProperties( doc, rendererElem );
1031 
1032  if ( !mOrderBy.isEmpty() )
1033  {
1034  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
1035  mOrderBy.save( orderBy );
1036  rendererElem.appendChild( orderBy );
1037  }
1038  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
1039 
1040  return rendererElem;
1041 }
1042 
1044 {
1045  return true;
1046 }
1047 
1049 {
1050  Rule *rule = mRootRule->findRuleByKey( key );
1051  return rule ? rule->active() : true;
1052 }
1053 
1054 void QgsRuleBasedRenderer::checkLegendSymbolItem( const QString &key, bool state )
1055 {
1056  Rule *rule = mRootRule->findRuleByKey( key );
1057  if ( rule )
1058  rule->setActive( state );
1059 }
1060 
1061 void QgsRuleBasedRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
1062 {
1063  Rule *rule = mRootRule->findRuleByKey( key );
1064  if ( rule )
1065  rule->setSymbol( symbol );
1066  else
1067  delete symbol;
1068 }
1069 
1071 {
1072  return mRootRule->legendSymbolItems();
1073 }
1074 
1075 
1077 {
1078  // load symbols
1079  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
1080  if ( symbolsElem.isNull() )
1081  return nullptr;
1082 
1083  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
1084 
1085  QDomElement rulesElem = element.firstChildElement( QStringLiteral( "rules" ) );
1086 
1087  Rule *root = Rule::create( rulesElem, symbolMap );
1088  if ( !root )
1089  return nullptr;
1090 
1091  QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( root );
1092 
1093  // delete symbols if there are any more
1095 
1096  return r;
1097 }
1098 
1100 {
1101  // retrieve child rules
1102  Rule *root = nullptr;
1103 
1104  QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
1105  while ( !ruleElem.isNull() )
1106  {
1107  Rule *child = Rule::createFromSld( ruleElem, geomType );
1108  if ( child )
1109  {
1110  // create the root rule if not done before
1111  if ( !root )
1112  root = new Rule( nullptr );
1113 
1114  root->appendChild( child );
1115  }
1116 
1117  ruleElem = ruleElem.nextSiblingElement( QStringLiteral( "Rule" ) );
1118  }
1119 
1120  if ( !root )
1121  {
1122  // no valid rules was found
1123  return nullptr;
1124  }
1125 
1126  // create and return the new renderer
1127  return new QgsRuleBasedRenderer( root );
1128 }
1129 
1132 
1134 {
1135  QString attr = r->classAttribute();
1136  // categorizedAttr could be either an attribute name or an expression.
1137  // the only way to differentiate is to test it as an expression...
1138  QgsExpression testExpr( attr );
1139  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1140  {
1141  //not an expression, so need to quote column name
1142  attr = QgsExpression::quotedColumnRef( attr );
1143  }
1144 
1145  Q_FOREACH ( const QgsRendererCategory &cat, r->categories() )
1146  {
1147  QString value;
1148  // not quoting numbers saves a type cast
1149  if ( cat.value().type() == QVariant::Int )
1150  value = cat.value().toString();
1151  else if ( cat.value().type() == QVariant::Double )
1152  // we loose precision here - so we may miss some categories :-(
1153  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1154  value = QString::number( cat.value().toDouble(), 'f', 4 );
1155  else
1156  value = QgsExpression::quotedString( cat.value().toString() );
1157  QString filter = QStringLiteral( "%1 = %2" ).arg( attr, value );
1158  QString label = filter;
1159  initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1160  }
1161 }
1162 
1164 {
1165  QString attr = r->classAttribute();
1166  // categorizedAttr could be either an attribute name or an expression.
1167  // the only way to differentiate is to test it as an expression...
1168  QgsExpression testExpr( attr );
1169  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1170  {
1171  //not an expression, so need to quote column name
1172  attr = QgsExpression::quotedColumnRef( attr );
1173  }
1174  else if ( !testExpr.isField() )
1175  {
1176  //otherwise wrap expression in brackets
1177  attr = QStringLiteral( "(%1)" ).arg( attr );
1178  }
1179 
1180  bool firstRange = true;
1181  Q_FOREACH ( const QgsRendererRange &rng, r->ranges() )
1182  {
1183  // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1184  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1185  QString filter = QStringLiteral( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
1186  QString::number( rng.lowerValue(), 'f', 4 ),
1187  QString::number( rng.upperValue(), 'f', 4 ) );
1188  firstRange = false;
1189  QString label = filter;
1190  initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1191  }
1192 }
1193 
1195 {
1196  std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
1197  double oldScale = initialRule->maximumScale();
1198  double maxDenom = initialRule->minimumScale();
1199  QgsSymbol *symbol = initialRule->symbol();
1200  Q_FOREACH ( int scale, scales )
1201  {
1202  if ( initialRule->maximumScale() >= scale )
1203  continue; // jump over the first scales out of the interval
1204  if ( maxDenom != 0 && maxDenom <= scale )
1205  break; // ignore the latter scales out of the interval
1206  initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1207  oldScale = scale;
1208  }
1209  // last rule
1210  initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1211 }
1212 
1214 {
1215  QString msg( QStringLiteral( "Rule-based renderer:\n" ) );
1216  msg += mRootRule->dump();
1217  return msg;
1218 }
1219 
1221 {
1222  return mRootRule->willRenderFeature( feature, &context );
1223 }
1224 
1226 {
1227  return mRootRule->symbolsForFeature( feature, &context );
1228 }
1229 
1231 {
1232  return mRootRule->symbolsForFeature( feature, &context );
1233 }
1234 
1235 QSet< QString > QgsRuleBasedRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
1236 {
1237  return mRootRule->legendKeysForFeature( feature, &context );
1238 }
1239 
1241 {
1242  std::unique_ptr< QgsRuleBasedRenderer > r;
1243  if ( renderer->type() == QLatin1String( "RuleRenderer" ) )
1244  {
1245  r.reset( dynamic_cast<QgsRuleBasedRenderer *>( renderer->clone() ) );
1246  }
1247  else if ( renderer->type() == QLatin1String( "singleSymbol" ) )
1248  {
1249  const QgsSingleSymbolRenderer *singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRenderer *>( renderer );
1250  if ( !singleSymbolRenderer )
1251  return nullptr;
1252 
1253  std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->symbol()->clone() );
1254  r = qgis::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
1255  }
1256  else if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1257  {
1258  const QgsCategorizedSymbolRenderer *categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
1259  if ( !categorizedRenderer )
1260  return nullptr;
1261 
1262  QString attr = categorizedRenderer->classAttribute();
1263  // categorizedAttr could be either an attribute name or an expression.
1264  // the only way to differentiate is to test it as an expression...
1265  QgsExpression testExpr( attr );
1266  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1267  {
1268  //not an expression, so need to quote column name
1269  attr = QgsExpression::quotedColumnRef( attr );
1270  }
1271 
1272  std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = qgis::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1273 
1274  QString expression;
1275  QString value;
1276  QgsRendererCategory category;
1277  for ( const QgsRendererCategory &category : categorizedRenderer->categories() )
1278  {
1279  std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = qgis::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1280 
1281  rule->setLabel( category.label() );
1282 
1283  //We first define the rule corresponding to the category
1284  if ( category.value().type() == QVariant::List )
1285  {
1286  QStringList values;
1287  const QVariantList list = category.value().toList();
1288  for ( const QVariant &v : list )
1289  {
1290  //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1291  if ( QVariant( v ).convert( QVariant::Double ) )
1292  {
1293  values << v.toString();
1294  }
1295  else
1296  {
1297  values << QgsExpression::quotedString( v.toString() );
1298  }
1299  }
1300 
1301  if ( values.empty() )
1302  {
1303  expression = QStringLiteral( "ELSE" );
1304  }
1305  else
1306  {
1307  expression = QStringLiteral( "%1 IN (%2)" ).arg( attr, values.join( ',' ) );
1308  }
1309  }
1310  else
1311  {
1312  //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1313  if ( category.value().convert( QVariant::Double ) )
1314  {
1315  value = category.value().toString();
1316  }
1317  else
1318  {
1319  value = QgsExpression::quotedString( category.value().toString() );
1320  }
1321 
1322  //An empty category is equivalent to the ELSE keyword
1323  if ( value == QLatin1String( "''" ) )
1324  {
1325  expression = QStringLiteral( "ELSE" );
1326  }
1327  else
1328  {
1329  expression = QStringLiteral( "%1 = %2" ).arg( attr, value );
1330  }
1331  }
1332  rule->setFilterExpression( expression );
1333 
1334  //Then we construct an equivalent symbol.
1335  //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1336  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1337 
1338  std::unique_ptr< QgsSymbol > origSymbol( category.symbol()->clone() );
1339  rule->setSymbol( origSymbol.release() );
1340 
1341  rootrule->appendChild( rule.release() );
1342  }
1343 
1344  r = qgis::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1345  }
1346  else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1347  {
1348  const QgsGraduatedSymbolRenderer *graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1349  if ( !graduatedRenderer )
1350  return nullptr;
1351 
1352  QString attr = graduatedRenderer->classAttribute();
1353  // categorizedAttr could be either an attribute name or an expression.
1354  // the only way to differentiate is to test it as an expression...
1355  QgsExpression testExpr( attr );
1356  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1357  {
1358  //not an expression, so need to quote column name
1359  attr = QgsExpression::quotedColumnRef( attr );
1360  }
1361  else if ( !testExpr.isField() )
1362  {
1363  //otherwise wrap expression in brackets
1364  attr = QStringLiteral( "(%1)" ).arg( attr );
1365  }
1366 
1367  std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = qgis::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1368 
1369  QString expression;
1370  QgsRendererRange range;
1371  for ( int i = 0; i < graduatedRenderer->ranges().size(); ++i )
1372  {
1373  range = graduatedRenderer->ranges().value( i );
1374  std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = qgis::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1375  rule->setLabel( range.label() );
1376  if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1377  {
1378  expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1379  attr + " <= " + QString::number( range.upperValue(), 'f' );
1380  }
1381  else
1382  {
1383  expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1384  attr + " <= " + QString::number( range.upperValue(), 'f' );
1385  }
1386  rule->setFilterExpression( expression );
1387 
1388  //Then we construct an equivalent symbol.
1389  //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1390  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1391 
1392  std::unique_ptr< QgsSymbol > symbol( range.symbol()->clone() );
1393  rule->setSymbol( symbol.release() );
1394 
1395  rootrule->appendChild( rule.release() );
1396  }
1397 
1398  r = qgis::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1399  }
1400  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1401  {
1402  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1403  if ( pointDistanceRenderer )
1404  return convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1405  }
1406  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1407  {
1408  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1409  if ( invertedPolygonRenderer )
1410  r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1411  }
1412 
1413  if ( r )
1414  {
1415  r->setOrderBy( renderer->orderBy() );
1416  r->setOrderByEnabled( renderer->orderByEnabled() );
1417  }
1418 
1419  return r.release();
1420 }
1421 
1422 void QgsRuleBasedRenderer::convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField )
1423 {
1424  QString sizeExpression;
1425  switch ( symbol->type() )
1426  {
1427  case QgsSymbol::Marker:
1428  for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1429  {
1430  QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( symbol->symbolLayer( j ) );
1431  if ( ! sizeScaleField.isEmpty() )
1432  {
1433  sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1435  }
1436  if ( ! rotationField.isEmpty() )
1437  {
1439  }
1440  }
1441  break;
1442  case QgsSymbol::Line:
1443  if ( ! sizeScaleField.isEmpty() )
1444  {
1445  for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1446  {
1447  if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "SimpleLine" ) )
1448  {
1449  QgsLineSymbolLayer *lsl = static_cast<QgsLineSymbolLayer *>( symbol->symbolLayer( j ) );
1450  sizeExpression = QStringLiteral( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1452  }
1453  if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "MarkerLine" ) )
1454  {
1455  QgsSymbol *marker = symbol->symbolLayer( j )->subSymbol();
1456  for ( int k = 0; k < marker->symbolLayerCount(); ++k )
1457  {
1458  QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( marker->symbolLayer( k ) );
1459  sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1461  }
1462  }
1463  }
1464  }
1465  break;
1466  default:
1467  break;
1468  }
1469 }
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
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:526
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 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.
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.
bool renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false) override
Render a feature using this renderer in the given context.
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:265
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.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
QgsRuleBasedRenderer::FeatureToRender & ftr
Feature to render.
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
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:150
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:510
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 ...
QString description() const
A human readable description for this rule.
QString parserErrorString() const
Returns parser error.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
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:732
bool isElse() const
Check if this rule is an ELSE rule.
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.
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
int renderingPass() const
RenderResult
The result of rendering a rule.
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:43
QString type() const
Definition: qgsrenderer.h:129
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.
double size() const
Returns the symbol size.
void renderFeatureWithSymbol(const QgsFeature &feature, QgsSymbol *symbol, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker)
Render the feature with the symbol using context.
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.
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 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.
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 a specific symbol layer contained in the symbol.
Definition: qgssymbol.cpp:342
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.
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.
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.
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 ...
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:271
QgsSymbol * symbol
Symbol to render feature with (not owned by this object).
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.
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
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: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 ...
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)
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.