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