QGIS API Documentation  3.2.0-Bonn (bc43194)
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 )
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" ), QLatin1String( "" ) ).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" ), QLatin1String( "" ) ).isEmpty() )
358  {
359  QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( QStringLiteral( "filter" ), QLatin1String( "" ) ) );
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  sf = QStringLiteral( "TRUE" );
413  else
414  sf = subfilters.join( QStringLiteral( ") OR (" ) ).prepend( '(' ).append( ')' );
415  }
416 
417  // Now join the subfilters with their parent (this) based on if
418  // * The parent is an else rule
419  // * The existence of parent filter and subfilters
420 
421  // No filter expression: ELSE rule or catchall rule
422  if ( !mFilter )
423  {
424  if ( mSymbol || sf.isEmpty() )
425  filter = QStringLiteral( "TRUE" );
426  else
427  filter = sf;
428  }
429  else if ( mSymbol )
430  filter = mFilterExp;
431  else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
432  filter = QStringLiteral( "(%1) AND (%2)" ).arg( mFilterExp, sf );
433  else if ( !mFilterExp.trimmed().isEmpty() )
434  filter = mFilterExp;
435  else if ( sf.isEmpty() )
436  filter = QStringLiteral( "TRUE" );
437  else
438  filter = sf;
439 
440  filter = filter.trimmed();
441 
442  return true;
443 }
444 
446 {
447  QSet<int> symbolZLevelsSet;
448 
449  // process this rule
450  if ( mSymbol )
451  {
452  // find out which Z-levels are used
453  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
454  {
455  symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
456  }
457  }
458 
459  // process children
460  QList<Rule *>::iterator it;
461  for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
462  {
463  Rule *rule = *it;
464  symbolZLevelsSet.unite( rule->collectZLevels() );
465  }
466  return symbolZLevelsSet;
467 }
468 
469 void QgsRuleBasedRenderer::Rule::setNormZLevels( const QMap<int, int> &zLevelsToNormLevels )
470 {
471  if ( mSymbol )
472  {
473  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
474  {
475  int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
476  mSymbolNormZLevels.insert( normLevel );
477  }
478  }
479 
480  // prepare list of normalized levels for each rule
481  Q_FOREACH ( Rule *rule, mActiveChildren )
482  {
483  rule->setNormZLevels( zLevelsToNormLevels );
484  }
485 }
486 
487 
489 {
490  if ( !isFilterOK( featToRender.feat, &context ) )
491  return Filtered;
492 
493  bool rendered = false;
494 
495  // create job for this feature and this symbol, add to list of jobs
496  if ( mSymbol && mIsActive )
497  {
498  // add job to the queue: each symbol's zLevel must be added
499  Q_FOREACH ( int normZLevel, mSymbolNormZLevels )
500  {
501  //QgsDebugMsg(QString("add job at level %1").arg(normZLevel));
502  renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol.get() ) );
503  rendered = true;
504  }
505  }
506 
507  bool willrendersomething = false;
508 
509  // process children
510  Q_FOREACH ( Rule *rule, mChildren )
511  {
512  // Don't process else rules yet
513  if ( !rule->isElse() )
514  {
515  RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
516  // consider inactive items as "rendered" so the else rule will ignore them
517  willrendersomething |= ( res == Rendered || res == Inactive );
518  rendered |= ( res == Rendered );
519  }
520  }
521 
522  // If none of the rules passed then we jump into the else rules and process them.
523  if ( !willrendersomething )
524  {
525  Q_FOREACH ( Rule *rule, mElseRules )
526  {
527  rendered |= rule->renderFeature( featToRender, context, renderQueue ) == Rendered;
528  }
529  }
530  if ( !mIsActive || ( mSymbol && !rendered ) )
531  return Inactive;
532  else if ( rendered )
533  return Rendered;
534  else
535  return Filtered;
536 }
537 
539 {
540  if ( !isFilterOK( feature, context ) )
541  return false;
542 
543  if ( mSymbol )
544  return true;
545 
546  Q_FOREACH ( Rule *rule, mActiveChildren )
547  {
548  if ( rule->isElse() )
549  {
550  if ( rule->children().isEmpty() )
551  {
552  RuleList lst = rulesForFeature( feature, context, false );
553  lst.removeOne( rule );
554 
555  if ( lst.empty() )
556  {
557  return true;
558  }
559  }
560  else
561  {
562  return rule->willRenderFeature( feature, context );
563  }
564  }
565  else if ( rule->willRenderFeature( feature, context ) )
566  {
567  return true;
568  }
569  }
570  return false;
571 }
572 
574 {
575  QgsSymbolList lst;
576  if ( !isFilterOK( feature, context ) )
577  return lst;
578  if ( mSymbol )
579  lst.append( mSymbol.get() );
580 
581  Q_FOREACH ( Rule *rule, mActiveChildren )
582  {
583  lst += rule->symbolsForFeature( feature, context );
584  }
585  return lst;
586 }
587 
589 {
590  QSet< QString> lst;
591  if ( !isFilterOK( feature, context ) )
592  return lst;
593  lst.insert( mRuleKey );
594 
595  Q_FOREACH ( Rule *rule, mActiveChildren )
596  {
597  bool validKey = false;
598  if ( rule->isElse() )
599  {
600  RuleList lst = rulesForFeature( feature, context, false );
601  lst.removeOne( rule );
602 
603  if ( lst.empty() )
604  {
605  validKey = true;
606  }
607  }
608  else if ( !rule->isElse( ) && rule->willRenderFeature( feature, context ) )
609  {
610  validKey = true;
611  }
612 
613  if ( validKey )
614  {
615  lst.unite( rule->legendKeysForFeature( feature, context ) );
616  }
617  }
618  return lst;
619 }
620 
622 {
623  RuleList lst;
624  if ( !isFilterOK( feature, context ) )
625  return lst;
626 
627  if ( mSymbol )
628  lst.append( this );
629 
630  RuleList listChildren = children();
631  if ( onlyActive )
632  listChildren = mActiveChildren;
633 
634  Q_FOREACH ( Rule *rule, listChildren )
635  {
636  lst += rule->rulesForFeature( feature, context, onlyActive );
637  }
638  return lst;
639 }
640 
642 {
643  if ( mSymbol )
644  mSymbol->stopRender( context );
645 
646  Q_FOREACH ( Rule *rule, mActiveChildren )
647  {
648  rule->stopRender( context );
649  }
650 
651  mActiveChildren.clear();
652  mSymbolNormZLevels.clear();
653 }
654 
656 {
657  QString symbolIdx = ruleElem.attribute( QStringLiteral( "symbol" ) );
658  QgsSymbol *symbol = nullptr;
659  if ( !symbolIdx.isEmpty() )
660  {
661  if ( symbolMap.contains( symbolIdx ) )
662  {
663  symbol = symbolMap.take( symbolIdx );
664  }
665  else
666  {
667  QgsDebugMsg( "symbol for rule " + symbolIdx + " not found!" );
668  }
669  }
670 
671  QString filterExp = ruleElem.attribute( QStringLiteral( "filter" ) );
672  QString label = ruleElem.attribute( QStringLiteral( "label" ) );
673  QString description = ruleElem.attribute( QStringLiteral( "description" ) );
674  int scaleMinDenom = ruleElem.attribute( QStringLiteral( "scalemindenom" ), QStringLiteral( "0" ) ).toInt();
675  int scaleMaxDenom = ruleElem.attribute( QStringLiteral( "scalemaxdenom" ), QStringLiteral( "0" ) ).toInt();
676  QString ruleKey = ruleElem.attribute( QStringLiteral( "key" ) );
677  Rule *rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
678 
679  if ( !ruleKey.isEmpty() )
680  rule->mRuleKey = ruleKey;
681 
682  rule->setActive( ruleElem.attribute( QStringLiteral( "checkstate" ), QStringLiteral( "1" ) ).toInt() );
683 
684  QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
685  while ( !childRuleElem.isNull() )
686  {
687  Rule *childRule = create( childRuleElem, symbolMap );
688  if ( childRule )
689  {
690  rule->appendChild( childRule );
691  }
692  else
693  {
694  QgsDebugMsg( "failed to init a child rule!" );
695  }
696  childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
697  }
698 
699  return rule;
700 }
701 
703 {
704  RuleList l;
705  for ( QgsRuleBasedRenderer::Rule *c : mChildren )
706  {
707  l += c;
708  l += c->descendants();
709  }
710  return l;
711 }
712 
714 {
715  if ( ruleElem.localName() != QLatin1String( "Rule" ) )
716  {
717  QgsDebugMsg( QString( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
718  return nullptr;
719  }
720 
721  QString label, description, filterExp;
722  int scaleMinDenom = 0, scaleMaxDenom = 0;
723  QgsSymbolLayerList layers;
724 
725  // retrieve the Rule element child nodes
726  QDomElement childElem = ruleElem.firstChildElement();
727  while ( !childElem.isNull() )
728  {
729  if ( childElem.localName() == QLatin1String( "Name" ) )
730  {
731  // <se:Name> tag contains the rule identifier,
732  // so prefer title tag for the label property value
733  if ( label.isEmpty() )
734  label = childElem.firstChild().nodeValue();
735  }
736  else if ( childElem.localName() == QLatin1String( "Description" ) )
737  {
738  // <se:Description> can contains a title and an abstract
739  QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
740  if ( !titleElem.isNull() )
741  {
742  label = titleElem.firstChild().nodeValue();
743  }
744 
745  QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
746  if ( !abstractElem.isNull() )
747  {
748  description = abstractElem.firstChild().nodeValue();
749  }
750  }
751  else if ( childElem.localName() == QLatin1String( "Abstract" ) )
752  {
753  // <sld:Abstract> (v1.0)
754  description = childElem.firstChild().nodeValue();
755  }
756  else if ( childElem.localName() == QLatin1String( "Title" ) )
757  {
758  // <sld:Title> (v1.0)
759  label = childElem.firstChild().nodeValue();
760  }
761  else if ( childElem.localName() == QLatin1String( "Filter" ) )
762  {
764  if ( filter )
765  {
766  if ( filter->hasParserError() )
767  {
768  QgsDebugMsg( "parser error: " + filter->parserErrorString() );
769  }
770  else
771  {
772  filterExp = filter->expression();
773  }
774  delete filter;
775  }
776  }
777  else if ( childElem.localName() == QLatin1String( "MinScaleDenominator" ) )
778  {
779  bool ok;
780  int v = childElem.firstChild().nodeValue().toInt( &ok );
781  if ( ok )
782  scaleMinDenom = v;
783  }
784  else if ( childElem.localName() == QLatin1String( "MaxScaleDenominator" ) )
785  {
786  bool ok;
787  int v = childElem.firstChild().nodeValue().toInt( &ok );
788  if ( ok )
789  scaleMaxDenom = v;
790  }
791  else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
792  {
793  // create symbol layers for this symbolizer
794  QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
795  }
796 
797  childElem = childElem.nextSiblingElement();
798  }
799 
800  // now create the symbol
801  QgsSymbol *symbol = nullptr;
802  if ( !layers.isEmpty() )
803  {
804  switch ( geomType )
805  {
807  symbol = new QgsLineSymbol( layers );
808  break;
809 
811  symbol = new QgsFillSymbol( layers );
812  break;
813 
815  symbol = new QgsMarkerSymbol( layers );
816  break;
817 
818  default:
819  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
820  return nullptr;
821  }
822  }
823 
824  // and then create and return the new rule
825  return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
826 }
827 
828 
830 
832  : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
833  , mRootRule( root )
834 {
835 }
836 
838  : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
839 {
840  mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
841  mRootRule->appendChild( new Rule( defaultSymbol ) );
842 }
843 
845 {
846  delete mRootRule;
847 }
848 
849 
851 {
852  // not used at all
853  return nullptr;
854 }
855 
857  QgsRenderContext &context,
858  int layer,
859  bool selected,
860  bool drawVertexMarker )
861 {
862  Q_UNUSED( layer );
863 
864  int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
865  mCurrentFeatures.append( FeatureToRender( feature, flags ) );
866 
867  // check each active rule
868  return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue ) == Rule::Rendered;
869 }
870 
871 
873 {
874  QgsFeatureRenderer::startRender( context, fields );
875 
876  // prepare active children
877  mRootRule->startRender( context, fields, mFilter );
878 
879  QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
880  QList<int> symbolZLevels = symbolZLevelsSet.toList();
881  std::sort( symbolZLevels.begin(), symbolZLevels.end() );
882 
883  // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
884  // and prepare rendering queue
885  QMap<int, int> zLevelsToNormLevels;
886  int maxNormLevel = -1;
887  Q_FOREACH ( int zLevel, symbolZLevels )
888  {
889  zLevelsToNormLevels[zLevel] = ++maxNormLevel;
890  mRenderQueue.append( RenderLevel( zLevel ) );
891  QgsDebugMsgLevel( QString( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ), 4 );
892  }
893 
894  mRootRule->setNormZLevels( zLevelsToNormLevels );
895 }
896 
898 {
900 
901  //
902  // do the actual rendering
903  //
904 
905  // go through all levels
906  Q_FOREACH ( const RenderLevel &level, mRenderQueue )
907  {
908  //QgsDebugMsg(QString("level %1").arg(level.zIndex));
909  // go through all jobs at the level
910  Q_FOREACH ( const RenderJob *job, level.jobs )
911  {
912  context.expressionContext().setFeature( job->ftr.feat );
913  //QgsDebugMsg(QString("job fid %1").arg(job->f->id()));
914  // render feature - but only with symbol layers with specified zIndex
915  QgsSymbol *s = job->symbol;
916  int count = s->symbolLayerCount();
917  for ( int i = 0; i < count; i++ )
918  {
919  // TODO: better solution for this
920  // renderFeatureWithSymbol asks which symbol layer to draw
921  // but there are multiple transforms going on!
922  if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
923  {
924  int flags = job->ftr.flags;
925  renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
926  }
927  }
928  }
929  }
930 
931  // clean current features
932  mCurrentFeatures.clear();
933 
934  // clean render queue
935  mRenderQueue.clear();
936 
937  // clean up rules from temporary stuff
938  mRootRule->stopRender( context );
939 }
940 
942 {
943  return mFilter;
944 }
945 
946 QSet<QString> QgsRuleBasedRenderer::usedAttributes( const QgsRenderContext &context ) const
947 {
948  return mRootRule->usedAttributes( context );
949 }
950 
952 {
953  return mRootRule->needsGeometry();
954 }
955 
957 {
958  QgsRuleBasedRenderer::Rule *clonedRoot = mRootRule->clone();
959 
960  // normally with clone() the individual rules get new keys (UUID), but here we want to keep
961  // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
962  clonedRoot->setRuleKey( mRootRule->ruleKey() );
963  RuleList origDescendants = mRootRule->descendants();
964  RuleList clonedDescendants = clonedRoot->descendants();
965  Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
966  for ( int i = 0; i < origDescendants.count(); ++i )
967  clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
968 
969  QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( clonedRoot );
970 
972  copyRendererData( r );
973  return r;
974 }
975 
976 void QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
977 {
978  mRootRule->toSld( doc, element, props );
979 }
980 
981 // TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
983 {
984  return mRootRule->symbols( context );
985 }
986 
987 QDomElement QgsRuleBasedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
988 {
989  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
990  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "RuleRenderer" ) );
991  rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
992  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
993 
995 
996  QDomElement rulesElem = mRootRule->save( doc, symbols );
997  rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
998  rendererElem.appendChild( rulesElem );
999 
1000  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
1001  rendererElem.appendChild( symbolsElem );
1002 
1004  mPaintEffect->saveProperties( doc, rendererElem );
1005 
1006  if ( !mOrderBy.isEmpty() )
1007  {
1008  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
1009  mOrderBy.save( orderBy );
1010  rendererElem.appendChild( orderBy );
1011  }
1012  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
1013 
1014  return rendererElem;
1015 }
1016 
1018 {
1019  return true;
1020 }
1021 
1023 {
1024  Rule *rule = mRootRule->findRuleByKey( key );
1025  return rule ? rule->active() : true;
1026 }
1027 
1028 void QgsRuleBasedRenderer::checkLegendSymbolItem( const QString &key, bool state )
1029 {
1030  Rule *rule = mRootRule->findRuleByKey( key );
1031  if ( rule )
1032  rule->setActive( state );
1033 }
1034 
1035 void QgsRuleBasedRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
1036 {
1037  Rule *rule = mRootRule->findRuleByKey( key );
1038  if ( rule )
1039  rule->setSymbol( symbol );
1040  else
1041  delete symbol;
1042 }
1043 
1045 {
1046  return mRootRule->legendSymbolItems();
1047 }
1048 
1049 
1051 {
1052  // load symbols
1053  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
1054  if ( symbolsElem.isNull() )
1055  return nullptr;
1056 
1057  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
1058 
1059  QDomElement rulesElem = element.firstChildElement( QStringLiteral( "rules" ) );
1060 
1061  Rule *root = Rule::create( rulesElem, symbolMap );
1062  if ( !root )
1063  return nullptr;
1064 
1065  QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( root );
1066 
1067  // delete symbols if there are any more
1069 
1070  return r;
1071 }
1072 
1074 {
1075  // retrieve child rules
1076  Rule *root = nullptr;
1077 
1078  QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
1079  while ( !ruleElem.isNull() )
1080  {
1081  Rule *child = Rule::createFromSld( ruleElem, geomType );
1082  if ( child )
1083  {
1084  // create the root rule if not done before
1085  if ( !root )
1086  root = new Rule( nullptr );
1087 
1088  root->appendChild( child );
1089  }
1090 
1091  ruleElem = ruleElem.nextSiblingElement( QStringLiteral( "Rule" ) );
1092  }
1093 
1094  if ( !root )
1095  {
1096  // no valid rules was found
1097  return nullptr;
1098  }
1099 
1100  // create and return the new renderer
1101  return new QgsRuleBasedRenderer( root );
1102 }
1103 
1106 
1108 {
1109  QString attr = r->classAttribute();
1110  // categorizedAttr could be either an attribute name or an expression.
1111  // the only way to differentiate is to test it as an expression...
1112  QgsExpression testExpr( attr );
1113  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1114  {
1115  //not an expression, so need to quote column name
1116  attr = QgsExpression::quotedColumnRef( attr );
1117  }
1118 
1119  Q_FOREACH ( const QgsRendererCategory &cat, r->categories() )
1120  {
1121  QString value;
1122  // not quoting numbers saves a type cast
1123  if ( cat.value().type() == QVariant::Int )
1124  value = cat.value().toString();
1125  else if ( cat.value().type() == QVariant::Double )
1126  // we loose precision here - so we may miss some categories :-(
1127  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1128  value = QString::number( cat.value().toDouble(), 'f', 4 );
1129  else
1130  value = QgsExpression::quotedString( cat.value().toString() );
1131  QString filter = QStringLiteral( "%1 = %2" ).arg( attr, value );
1132  QString label = filter;
1133  initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1134  }
1135 }
1136 
1138 {
1139  QString attr = r->classAttribute();
1140  // categorizedAttr could be either an attribute name or an expression.
1141  // the only way to differentiate is to test it as an expression...
1142  QgsExpression testExpr( attr );
1143  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1144  {
1145  //not an expression, so need to quote column name
1146  attr = QgsExpression::quotedColumnRef( attr );
1147  }
1148  else if ( !testExpr.isField() )
1149  {
1150  //otherwise wrap expression in brackets
1151  attr = QStringLiteral( "(%1)" ).arg( attr );
1152  }
1153 
1154  bool firstRange = true;
1155  Q_FOREACH ( const QgsRendererRange &rng, r->ranges() )
1156  {
1157  // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1158  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1159  QString filter = QStringLiteral( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
1160  QString::number( rng.lowerValue(), 'f', 4 ),
1161  QString::number( rng.upperValue(), 'f', 4 ) );
1162  firstRange = false;
1163  QString label = filter;
1164  initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1165  }
1166 }
1167 
1169 {
1170  std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
1171  double oldScale = initialRule->maximumScale();
1172  double maxDenom = initialRule->minimumScale();
1173  QgsSymbol *symbol = initialRule->symbol();
1174  Q_FOREACH ( int scale, scales )
1175  {
1176  if ( initialRule->maximumScale() >= scale )
1177  continue; // jump over the first scales out of the interval
1178  if ( maxDenom != 0 && maxDenom <= scale )
1179  break; // ignore the latter scales out of the interval
1180  initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1181  oldScale = scale;
1182  }
1183  // last rule
1184  initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1185 }
1186 
1188 {
1189  QString msg( QStringLiteral( "Rule-based renderer:\n" ) );
1190  msg += mRootRule->dump();
1191  return msg;
1192 }
1193 
1195 {
1196  return mRootRule->willRenderFeature( feature, &context );
1197 }
1198 
1200 {
1201  return mRootRule->symbolsForFeature( feature, &context );
1202 }
1203 
1205 {
1206  return mRootRule->symbolsForFeature( feature, &context );
1207 }
1208 
1209 QSet< QString > QgsRuleBasedRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
1210 {
1211  return mRootRule->legendKeysForFeature( feature, &context );
1212 }
1213 
1215 {
1216  QgsRuleBasedRenderer *r = nullptr;
1217  if ( renderer->type() == QLatin1String( "RuleRenderer" ) )
1218  {
1219  r = dynamic_cast<QgsRuleBasedRenderer *>( renderer->clone() );
1220  }
1221  else if ( renderer->type() == QLatin1String( "singleSymbol" ) )
1222  {
1223  const QgsSingleSymbolRenderer *singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRenderer *>( renderer );
1224  if ( !singleSymbolRenderer )
1225  return nullptr;
1226 
1227  QgsSymbol *origSymbol = singleSymbolRenderer->symbol()->clone();
1228  r = new QgsRuleBasedRenderer( origSymbol );
1229  }
1230  else if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1231  {
1232  const QgsCategorizedSymbolRenderer *categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
1233  if ( !categorizedRenderer )
1234  return nullptr;
1235 
1236  QString attr = categorizedRenderer->classAttribute();
1237  // categorizedAttr could be either an attribute name or an expression.
1238  // the only way to differentiate is to test it as an expression...
1239  QgsExpression testExpr( attr );
1240  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1241  {
1242  //not an expression, so need to quote column name
1243  attr = QgsExpression::quotedColumnRef( attr );
1244  }
1245 
1246  QgsRuleBasedRenderer::Rule *rootrule = new QgsRuleBasedRenderer::Rule( nullptr );
1247 
1248  QString expression;
1249  QString value;
1250  QgsRendererCategory category;
1251  for ( int i = 0; i < categorizedRenderer->categories().size(); ++i )
1252  {
1253  category = categorizedRenderer->categories().value( i );
1255 
1256  rule->setLabel( category.label() );
1257 
1258  //We first define the rule corresponding to the category
1259  //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1260  if ( QVariant( category.value() ).convert( QVariant::Double ) )
1261  {
1262  value = category.value().toString();
1263  }
1264  else
1265  {
1266  value = QgsExpression::quotedString( category.value().toString() );
1267  }
1268 
1269  //An empty category is equivalent to the ELSE keyword
1270  if ( value == QLatin1String( "''" ) )
1271  {
1272  expression = QStringLiteral( "ELSE" );
1273  }
1274  else
1275  {
1276  expression = QStringLiteral( "%1 = %2" ).arg( attr, value );
1277  }
1278  rule->setFilterExpression( expression );
1279 
1280  //Then we construct an equivalent symbol.
1281  //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1282  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1283 
1284  QgsSymbol *origSymbol = category.symbol()->clone();
1285  rule->setSymbol( origSymbol );
1286 
1287  rootrule->appendChild( rule );
1288  }
1289 
1290  r = new QgsRuleBasedRenderer( rootrule );
1291  }
1292  else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1293  {
1294  const QgsGraduatedSymbolRenderer *graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1295  if ( !graduatedRenderer )
1296  return nullptr;
1297 
1298  QString attr = graduatedRenderer->classAttribute();
1299  // categorizedAttr could be either an attribute name or an expression.
1300  // the only way to differentiate is to test it as an expression...
1301  QgsExpression testExpr( attr );
1302  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1303  {
1304  //not an expression, so need to quote column name
1305  attr = QgsExpression::quotedColumnRef( attr );
1306  }
1307  else if ( !testExpr.isField() )
1308  {
1309  //otherwise wrap expression in brackets
1310  attr = QStringLiteral( "(%1)" ).arg( attr );
1311  }
1312 
1313  QgsRuleBasedRenderer::Rule *rootrule = new QgsRuleBasedRenderer::Rule( nullptr );
1314 
1315  QString expression;
1316  QgsRendererRange range;
1317  for ( int i = 0; i < graduatedRenderer->ranges().size(); ++i )
1318  {
1319  range = graduatedRenderer->ranges().value( i );
1321  rule->setLabel( range.label() );
1322  if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1323  {
1324  expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1325  attr + " <= " + QString::number( range.upperValue(), 'f' );
1326  }
1327  else
1328  {
1329  expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1330  attr + " <= " + QString::number( range.upperValue(), 'f' );
1331  }
1332  rule->setFilterExpression( expression );
1333 
1334  //Then we construct an equivalent symbol.
1335  //Ideally we could simply copy the symbol, but the graduated 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  QgsSymbol *symbol = range.symbol()->clone();
1339  rule->setSymbol( symbol );
1340 
1341  rootrule->appendChild( rule );
1342  }
1343 
1344  r = new QgsRuleBasedRenderer( rootrule );
1345  }
1346  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1347  {
1348  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1349  if ( pointDistanceRenderer )
1350  return convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1351  }
1352  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1353  {
1354  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1355  if ( invertedPolygonRenderer )
1356  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1357  }
1358 
1359  if ( r )
1360  {
1361  r->setOrderBy( renderer->orderBy() );
1362  r->setOrderByEnabled( renderer->orderByEnabled() );
1363  }
1364 
1365  return r;
1366 }
1367 
1368 void QgsRuleBasedRenderer::convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField )
1369 {
1370  QString sizeExpression;
1371  switch ( symbol->type() )
1372  {
1373  case QgsSymbol::Marker:
1374  for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1375  {
1376  QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( symbol->symbolLayer( j ) );
1377  if ( ! sizeScaleField.isEmpty() )
1378  {
1379  sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1381  }
1382  if ( ! rotationField.isEmpty() )
1383  {
1385  }
1386  }
1387  break;
1388  case QgsSymbol::Line:
1389  if ( ! sizeScaleField.isEmpty() )
1390  {
1391  for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1392  {
1393  if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "SimpleLine" ) )
1394  {
1395  QgsLineSymbolLayer *lsl = static_cast<QgsLineSymbolLayer *>( symbol->symbolLayer( j ) );
1396  sizeExpression = QStringLiteral( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1398  }
1399  if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "MarkerLine" ) )
1400  {
1401  QgsSymbol *marker = symbol->symbolLayer( j )->subSymbol();
1402  for ( int k = 0; k < marker->symbolLayerCount(); ++k )
1403  {
1404  QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( marker->symbolLayer( k ) );
1405  sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1407  }
1408  }
1409  }
1410  }
1411  break;
1412  default:
1413  break;
1414  }
1415 }
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...
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...
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
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)
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:251
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.
static void mergeScaleDependencies(int mScaleMinDenom, int mScaleMaxDenom, QgsStringMap &props)
Merges the local scale limits, if any, with the ones already in the map, if any.
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 total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:143
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:62
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:501
virtual double width() const
bool isElse() const
Check if this rule is an ELSE rule.
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
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:53
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 layers contained in the symbol.
Definition: qgssymbol.cpp:332
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.
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:137
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.
void setLabel(const QString &label)
bool usingSymbolLevels() const
Definition: qgsrenderer.h:271
QgsSymbol * symbol
Symbol to render feature with (not owned by this object).
SymbolType type() const
Definition: qgssymbol.h:113
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
Gets 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:48
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...
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
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...
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.