QGIS API Documentation  2.17.0-Master (06698cd)
qgsrulebasedrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrulebasedrendererv2.cpp - Rule-based renderer (symbology-ng)
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 "qgsrulebasedrendererv2.h"
17 #include "qgssymbollayerv2.h"
18 #include "qgsexpression.h"
19 #include "qgssymbollayerv2utils.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 "qgsdatadefined.h"
30 
31 #include <QSet>
32 
33 #include <QDomDocument>
34 #include <QDomElement>
35 #include <QUuid>
36 
37 
38 QgsRuleBasedRendererV2::Rule::Rule( QgsSymbolV2* symbol, int scaleMinDenom, int scaleMaxDenom, const QString& filterExp, const QString& label, const QString& description, bool elseRule )
39  : mParent( nullptr )
40  , mSymbol( symbol )
41  , mScaleMinDenom( scaleMinDenom )
42  , mScaleMaxDenom( scaleMaxDenom )
43  , mFilterExp( filterExp )
44  , mLabel( label )
45  , mDescription( description )
46  , mElseRule( elseRule )
47  , mIsActive( true )
48  , mFilter( nullptr )
49 {
50  if ( mElseRule )
51  mFilterExp = "ELSE";
52 
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( "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;
84  }
85 }
86 
88 {
89  mChildren.append( rule );
90  rule->mParent = this;
92 }
93 
95 {
96  mChildren.insert( i, rule );
97  rule->mParent = this;
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 
147 {
148  mElseRules.clear();
149  Q_FOREACH ( Rule* rule, mChildren )
150  {
151  if ( rule->isElse() )
152  mElseRules << rule;
153  }
154 }
155 
157 {
158  mFilterExp = "ELSE";
159  mElseRule = iselse;
160  delete mFilter;
161  mFilter = nullptr;
162 }
163 
164 
166 {
167  QString off;
168  off.fill( QChar( ' ' ), indent );
169  QString symbolDump = ( mSymbol ? mSymbol->dump() : QString( "[]" ) );
170  QString msg = off + QString( "RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
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( "\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().toSet() );
189  if ( mSymbol )
190  attrs.unite( mSymbol->usedAttributes() );
191 
192  // attributes needed by child rules
193  Q_FOREACH ( Rule* rule, mChildren )
194  {
195  attrs.unite( rule->usedAttributes() );
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  QgsSymbolV2List 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 
234 {
235  mFilterExp = filterExp;
236  initFilter();
237 }
238 
239 QgsLegendSymbolList QgsRuleBasedRendererV2::Rule::legendSymbolItems( double scaleDenominator, const QString& ruleFilter ) const
240 {
242  if ( mSymbol && ( ruleFilter.isEmpty() || mLabel == ruleFilter ) )
243  lst << qMakePair( mLabel, mSymbol );
244 
245  Q_FOREACH ( Rule* rule, mChildren )
246  {
247  if ( qgsDoubleNear( scaleDenominator, -1 ) || rule->isScaleOK( scaleDenominator ) )
248  {
249  lst << rule->legendSymbolItems( scaleDenominator, ruleFilter );
250  }
251  }
252  return lst;
253 }
254 
256 {
258  if ( currentLevel != -1 ) // root rule should not be shown
259  {
261  }
262 
263  for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
264  {
265  Rule* rule = *it;
266  lst << rule->legendSymbolItemsV2( currentLevel + 1 );
267  }
268  return lst;
269 }
270 
271 
273 {
274  if ( ! mFilter || mElseRule )
275  return true;
276 
277  context->expressionContext().setFeature( f );
278  QVariant res = mFilter->evaluate( &context->expressionContext() );
279  return res.toInt() != 0;
280 }
281 
282 bool QgsRuleBasedRendererV2::Rule::isScaleOK( double scale ) const
283 {
284  if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
285  return true;
286  if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 )
287  return true;
288  if ( mScaleMinDenom != 0 && mScaleMinDenom > scale )
289  return false;
290  if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale )
291  return false;
292  return true;
293 }
294 
296 {
297  QgsSymbolV2* sym = mSymbol ? mSymbol->clone() : nullptr;
299  newrule->setActive( mIsActive );
300  // clone children
301  Q_FOREACH ( Rule* rule, mChildren )
302  newrule->appendChild( rule->clone() );
303  return newrule;
304 }
305 
307 {
308  QDomElement ruleElem = doc.createElement( "rule" );
309 
310  if ( mSymbol )
311  {
312  int symbolIndex = symbolMap.size();
313  symbolMap[QString::number( symbolIndex )] = mSymbol;
314  ruleElem.setAttribute( "symbol", symbolIndex );
315  }
316  if ( !mFilterExp.isEmpty() )
317  ruleElem.setAttribute( "filter", mFilterExp );
318  if ( mScaleMinDenom != 0 )
319  ruleElem.setAttribute( "scalemindenom", mScaleMinDenom );
320  if ( mScaleMaxDenom != 0 )
321  ruleElem.setAttribute( "scalemaxdenom", mScaleMaxDenom );
322  if ( !mLabel.isEmpty() )
323  ruleElem.setAttribute( "label", mLabel );
324  if ( !mDescription.isEmpty() )
325  ruleElem.setAttribute( "description", mDescription );
326  if ( !mIsActive )
327  ruleElem.setAttribute( "checkstate", 0 );
328  ruleElem.setAttribute( "key", mRuleKey );
329 
330  Q_FOREACH ( Rule* rule, mChildren )
331  {
332  ruleElem.appendChild( rule->save( doc, symbolMap ) );
333  }
334  return ruleElem;
335 }
336 
338 {
339  // do not convert this rule if there are no symbols
340  QgsRenderContext context;
341  if ( symbols( context ).isEmpty() )
342  return;
343 
344  if ( !mFilterExp.isEmpty() )
345  {
346  if ( !props.value( "filter", "" ).isEmpty() )
347  props[ "filter" ] += " AND ";
348  props[ "filter" ] += mFilterExp;
349  }
350 
351  if ( mScaleMinDenom != 0 )
352  {
353  bool ok;
354  int parentScaleMinDenom = props.value( "scaleMinDenom", "0" ).toInt( &ok );
355  if ( !ok || parentScaleMinDenom <= 0 )
356  props[ "scaleMinDenom" ] = QString::number( mScaleMinDenom );
357  else
358  props[ "scaleMinDenom" ] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) );
359  }
360 
361  if ( mScaleMaxDenom != 0 )
362  {
363  bool ok;
364  int parentScaleMaxDenom = props.value( "scaleMaxDenom", "0" ).toInt( &ok );
365  if ( !ok || parentScaleMaxDenom <= 0 )
366  props[ "scaleMaxDenom" ] = QString::number( mScaleMaxDenom );
367  else
368  props[ "scaleMaxDenom" ] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) );
369  }
370 
371  if ( mSymbol )
372  {
373  QDomElement ruleElem = doc.createElement( "se:Rule" );
374  element.appendChild( ruleElem );
375 
376  //XXX: <se:Name> is the rule identifier, but our the Rule objects
377  // have no properties could be used as identifier. Use the label.
378  QDomElement nameElem = doc.createElement( "se:Name" );
379  nameElem.appendChild( doc.createTextNode( mLabel ) );
380  ruleElem.appendChild( nameElem );
381 
382  if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
383  {
384  QDomElement descrElem = doc.createElement( "se:Description" );
385  if ( !mLabel.isEmpty() )
386  {
387  QDomElement titleElem = doc.createElement( "se:Title" );
388  titleElem.appendChild( doc.createTextNode( mLabel ) );
389  descrElem.appendChild( titleElem );
390  }
391  if ( !mDescription.isEmpty() )
392  {
393  QDomElement abstractElem = doc.createElement( "se:Abstract" );
394  abstractElem.appendChild( doc.createTextNode( mDescription ) );
395  descrElem.appendChild( abstractElem );
396  }
397  ruleElem.appendChild( descrElem );
398  }
399 
400  if ( !props.value( "filter", "" ).isEmpty() )
401  {
402  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, props.value( "filter", "" ) );
403  }
404 
405  if ( !props.value( "scaleMinDenom", "" ).isEmpty() )
406  {
407  QDomElement scaleMinDenomElem = doc.createElement( "se:MinScaleDenominator" );
408  scaleMinDenomElem.appendChild( doc.createTextNode( props.value( "scaleMinDenom", "" ) ) );
409  ruleElem.appendChild( scaleMinDenomElem );
410  }
411 
412  if ( !props.value( "scaleMaxDenom", "" ).isEmpty() )
413  {
414  QDomElement scaleMaxDenomElem = doc.createElement( "se:MaxScaleDenominator" );
415  scaleMaxDenomElem.appendChild( doc.createTextNode( props.value( "scaleMaxDenom", "" ) ) );
416  ruleElem.appendChild( scaleMaxDenomElem );
417  }
418 
419  mSymbol->toSld( doc, ruleElem, props );
420  }
421 
422  // loop into childern rule list
423  Q_FOREACH ( Rule* rule, mChildren )
424  {
425  rule->toSld( doc, element, props );
426  }
427 }
428 
430 {
431  QString filter;
432  return startRender( context, fields, filter );
433 }
434 
436 {
438 
439  if ( ! mIsActive )
440  return false;
441 
442  // filter out rules which are not compatible with this scale
443  if ( !isScaleOK( context.rendererScale() ) )
444  return false;
445 
446  // init this rule
447  if ( mFilter )
448  mFilter->prepare( &context.expressionContext() );
449  if ( mSymbol )
450  mSymbol->startRender( context, &fields );
451 
452  // init children
453  // build temporary list of active rules (usable with this scale)
454  QStringList subfilters;
455  Q_FOREACH ( Rule* rule, mChildren )
456  {
457  QString subfilter;
458  if ( rule->startRender( context, fields , subfilter ) )
459  {
460  // only add those which are active with current scale
461  mActiveChildren.append( rule );
462  subfilters.append( subfilter );
463  }
464  }
465 
466  // subfilters (on the same level) are joined with OR
467  // Finally they are joined with their parent (this) with AND
468  QString sf;
469  // If there are subfilters present (and it's not a single empty one), group them and join them with OR
470  if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
471  {
472  if ( subfilters.contains( "TRUE" ) )
473  sf = "TRUE";
474  else
475  sf = subfilters.join( ") OR (" ).prepend( '(' ).append( ')' );
476  }
477 
478  // Now join the subfilters with their parent (this) based on if
479  // * The parent is an else rule
480  // * The existence of parent filter and subfilters
481 
482  // No filter expression: ELSE rule or catchall rule
483  if ( !mFilter )
484  {
485  if ( mSymbol || sf.isEmpty() )
486  filter = "TRUE";
487  else
488  filter = sf;
489  }
490  else if ( mSymbol )
491  filter = mFilterExp;
492  else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
493  filter = QString( "(%1) AND (%2)" ).arg( mFilterExp, sf );
494  else if ( !mFilterExp.trimmed().isEmpty() )
495  filter = mFilterExp;
496  else if ( sf.isEmpty() )
497  filter = "TRUE";
498  else
499  filter = sf;
500 
501  filter = filter.trimmed();
502 
503  return true;
504 }
505 
507 {
508  QSet<int> symbolZLevelsSet;
509 
510  // process this rule
511  if ( mSymbol )
512  {
513  // find out which Z-levels are used
514  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
515  {
516  symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
517  }
518  }
519 
520  // process children
522  for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
523  {
524  Rule* rule = *it;
525  symbolZLevelsSet.unite( rule->collectZLevels() );
526  }
527  return symbolZLevelsSet;
528 }
529 
531 {
532  if ( mSymbol )
533  {
534  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
535  {
536  int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
537  mSymbolNormZLevels.insert( normLevel );
538  }
539  }
540 
541  // prepare list of normalized levels for each rule
542  Q_FOREACH ( Rule* rule, mActiveChildren )
543  {
544  rule->setNormZLevels( zLevelsToNormLevels );
545  }
546 }
547 
548 
550 {
551  if ( !isFilterOK( featToRender.feat, &context ) )
552  return Filtered;
553 
554  bool rendered = false;
555 
556  // create job for this feature and this symbol, add to list of jobs
557  if ( mSymbol && mIsActive )
558  {
559  // add job to the queue: each symbol's zLevel must be added
560  Q_FOREACH ( int normZLevel, mSymbolNormZLevels )
561  {
562  //QgsDebugMsg(QString("add job at level %1").arg(normZLevel));
563  renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol ) );
564  rendered = true;
565  }
566  }
567 
568  bool willrendersomething = false;
569 
570  // process children
571  Q_FOREACH ( Rule* rule, mChildren )
572  {
573  // Don't process else rules yet
574  if ( !rule->isElse() )
575  {
576  RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
577  // consider inactive items as "rendered" so the else rule will ignore them
578  willrendersomething |= ( res == Rendered || res == Inactive );
579  rendered |= ( res == Rendered );
580  }
581  }
582 
583  // If none of the rules passed then we jump into the else rules and process them.
584  if ( !willrendersomething )
585  {
586  Q_FOREACH ( Rule* rule, mElseRules )
587  {
588  rendered |= rule->renderFeature( featToRender, context, renderQueue ) == Rendered;
589  }
590  }
591  if ( !mIsActive || ( mSymbol && !rendered ) )
592  return Inactive;
593  else if ( rendered )
594  return Rendered;
595  else
596  return Filtered;
597 }
598 
600 {
601  if ( !isFilterOK( feat, context ) )
602  return false;
603  if ( mSymbol )
604  return true;
605 
606  Q_FOREACH ( Rule* rule, mActiveChildren )
607  {
608  if ( rule->willRenderFeature( feat, context ) )
609  return true;
610  }
611  return false;
612 }
613 
615 {
616  QgsSymbolV2List lst;
617  if ( !isFilterOK( feat, context ) )
618  return lst;
619  if ( mSymbol )
620  lst.append( mSymbol );
621 
622  Q_FOREACH ( Rule* rule, mActiveChildren )
623  {
624  lst += rule->symbolsForFeature( feat, context );
625  }
626  return lst;
627 }
628 
630 {
631  QSet< QString> lst;
632  if ( !isFilterOK( feat, context ) )
633  return lst;
634  lst.insert( mRuleKey );
635 
636  Q_FOREACH ( Rule* rule, mActiveChildren )
637  {
638  lst.unite( rule->legendKeysForFeature( feat, context ) );
639  }
640  return lst;
641 }
642 
644 {
645  RuleList lst;
646  if ( !isFilterOK( feat, context ) )
647  return lst;
648 
649  if ( mSymbol )
650  lst.append( this );
651 
652  Q_FOREACH ( Rule* rule, mActiveChildren )
653  {
654  lst += rule->rulesForFeature( feat, context );
655  }
656  return lst;
657 }
658 
660 {
661  if ( mSymbol )
662  mSymbol->stopRender( context );
663 
664  Q_FOREACH ( Rule* rule, mActiveChildren )
665  {
666  rule->stopRender( context );
667  }
668 
671 }
672 
674 {
675  QString symbolIdx = ruleElem.attribute( "symbol" );
676  QgsSymbolV2* symbol = nullptr;
677  if ( !symbolIdx.isEmpty() )
678  {
679  if ( symbolMap.contains( symbolIdx ) )
680  {
681  symbol = symbolMap.take( symbolIdx );
682  }
683  else
684  {
685  QgsDebugMsg( "symbol for rule " + symbolIdx + " not found!" );
686  }
687  }
688 
689  QString filterExp = ruleElem.attribute( "filter" );
690  QString label = ruleElem.attribute( "label" );
691  QString description = ruleElem.attribute( "description" );
692  int scaleMinDenom = ruleElem.attribute( "scalemindenom", "0" ).toInt();
693  int scaleMaxDenom = ruleElem.attribute( "scalemaxdenom", "0" ).toInt();
694  QString ruleKey = ruleElem.attribute( "key" );
695  Rule* rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
696 
697  if ( !ruleKey.isEmpty() )
698  rule->mRuleKey = ruleKey;
699 
700  rule->setActive( ruleElem.attribute( "checkstate", "1" ).toInt() );
701 
702  QDomElement childRuleElem = ruleElem.firstChildElement( "rule" );
703  while ( !childRuleElem.isNull() )
704  {
705  Rule* childRule = create( childRuleElem, symbolMap );
706  if ( childRule )
707  {
708  rule->appendChild( childRule );
709  }
710  else
711  {
712  QgsDebugMsg( "failed to init a child rule!" );
713  }
714  childRuleElem = childRuleElem.nextSiblingElement( "rule" );
715  }
716 
717  return rule;
718 }
719 
721 {
722  if ( ruleElem.localName() != "Rule" )
723  {
724  QgsDebugMsg( QString( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
725  return nullptr;
726  }
727 
728  QString label, description, filterExp;
729  int scaleMinDenom = 0, scaleMaxDenom = 0;
730  QgsSymbolLayerV2List layers;
731 
732  // retrieve the Rule element child nodes
733  QDomElement childElem = ruleElem.firstChildElement();
734  while ( !childElem.isNull() )
735  {
736  if ( childElem.localName() == "Name" )
737  {
738  // <se:Name> tag contains the rule identifier,
739  // so prefer title tag for the label property value
740  if ( label.isEmpty() )
741  label = childElem.firstChild().nodeValue();
742  }
743  else if ( childElem.localName() == "Description" )
744  {
745  // <se:Description> can contains a title and an abstract
746  QDomElement titleElem = childElem.firstChildElement( "Title" );
747  if ( !titleElem.isNull() )
748  {
749  label = titleElem.firstChild().nodeValue();
750  }
751 
752  QDomElement abstractElem = childElem.firstChildElement( "Abstract" );
753  if ( !abstractElem.isNull() )
754  {
755  description = abstractElem.firstChild().nodeValue();
756  }
757  }
758  else if ( childElem.localName() == "Abstract" )
759  {
760  // <sld:Abstract> (v1.0)
761  description = childElem.firstChild().nodeValue();
762  }
763  else if ( childElem.localName() == "Title" )
764  {
765  // <sld:Title> (v1.0)
766  label = childElem.firstChild().nodeValue();
767  }
768  else if ( childElem.localName() == "Filter" )
769  {
771  if ( filter )
772  {
773  if ( filter->hasParserError() )
774  {
775  QgsDebugMsg( "parser error: " + filter->parserErrorString() );
776  }
777  else
778  {
779  filterExp = filter->expression();
780  }
781  delete filter;
782  }
783  }
784  else if ( childElem.localName() == "MinScaleDenominator" )
785  {
786  bool ok;
787  int v = childElem.firstChild().nodeValue().toInt( &ok );
788  if ( ok )
789  scaleMinDenom = v;
790  }
791  else if ( childElem.localName() == "MaxScaleDenominator" )
792  {
793  bool ok;
794  int v = childElem.firstChild().nodeValue().toInt( &ok );
795  if ( ok )
796  scaleMaxDenom = v;
797  }
798  else if ( childElem.localName().endsWith( "Symbolizer" ) )
799  {
800  // create symbol layers for this symbolizer
801  QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
802  }
803 
804  childElem = childElem.nextSiblingElement();
805  }
806 
807  // now create the symbol
808  QgsSymbolV2 *symbol = nullptr;
809  if ( !layers.isEmpty() )
810  {
811  switch ( geomType )
812  {
813  case QGis::Line:
814  symbol = new QgsLineSymbolV2( layers );
815  break;
816 
817  case QGis::Polygon:
818  symbol = new QgsFillSymbolV2( layers );
819  break;
820 
821  case QGis::Point:
822  symbol = new QgsMarkerSymbolV2( layers );
823  break;
824 
825  default:
826  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
827  return nullptr;
828  }
829  }
830 
831  // and then create and return the new rule
832  return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
833 }
834 
835 
837 
839  : QgsFeatureRendererV2( "RuleRenderer" )
840  , mRootRule( root )
841 {
842 }
843 
845  : QgsFeatureRendererV2( "RuleRenderer" )
846 {
847  mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
848  mRootRule->appendChild( new Rule( defaultSymbol ) );
849 }
850 
852 {
853  delete mRootRule;
854 }
855 
856 
858 {
859  // not used at all
860  return nullptr;
861 }
862 
864  QgsRenderContext& context,
865  int layer,
866  bool selected,
867  bool drawVertexMarker )
868 {
869  Q_UNUSED( layer );
870 
871  int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
872  mCurrentFeatures.append( FeatureToRender( feature, flags ) );
873 
874  // check each active rule
875  return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue ) == Rule::Rendered;
876 }
877 
878 
880 {
881  // prepare active children
882  mRootRule->startRender( context, fields, mFilter );
883 
884  QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
885  QList<int> symbolZLevels = symbolZLevelsSet.toList();
886  qSort( symbolZLevels );
887 
888  // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
889  // and prepare rendering queue
890  QMap<int, int> zLevelsToNormLevels;
891  int maxNormLevel = -1;
892  Q_FOREACH ( int zLevel, symbolZLevels )
893  {
894  zLevelsToNormLevels[zLevel] = ++maxNormLevel;
895  mRenderQueue.append( RenderLevel( zLevel ) );
896  QgsDebugMsg( QString( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ) );
897  }
898 
899  mRootRule->setNormZLevels( zLevelsToNormLevels );
900 }
901 
903 {
904  //
905  // do the actual rendering
906  //
907 
908  // go through all levels
909  Q_FOREACH ( const RenderLevel& level, mRenderQueue )
910  {
911  //QgsDebugMsg(QString("level %1").arg(level.zIndex));
912  // go through all jobs at the level
913  Q_FOREACH ( const RenderJob* job, level.jobs )
914  {
915  context.expressionContext().setFeature( job->ftr.feat );
916  //QgsDebugMsg(QString("job fid %1").arg(job->f->id()));
917  // render feature - but only with symbol layers with specified zIndex
918  QgsSymbolV2* s = job->symbol;
919  int count = s->symbolLayerCount();
920  for ( int i = 0; i < count; i++ )
921  {
922  // TODO: better solution for this
923  // renderFeatureWithSymbol asks which symbol layer to draw
924  // but there are multiple transforms going on!
925  if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
926  {
927  int flags = job->ftr.flags;
928  renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
929  }
930  }
931  }
932  }
933 
934  // clean current features
935  mCurrentFeatures.clear();
936 
937  // clean render queue
939 
940  // clean up rules from temporary stuff
941  mRootRule->stopRender( context );
942 }
943 
945 {
946  return mFilter;
947 }
948 
950 {
952  return attrs.toList();
953 }
954 
956 {
957  return mRootRule->needsGeometry();
958 }
959 
961 {
963 
964  // normally with clone() the individual rules get new keys (UUID), but here we want to keep
965  // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. visibility presets)
966  clonedRoot->setRuleKey( mRootRule->ruleKey() );
967  RuleList origDescendants = mRootRule->descendants();
968  RuleList clonedDescendants = clonedRoot->descendants();
969  Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
970  for ( int i = 0; i < origDescendants.count(); ++i )
971  clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
972 
973  QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( clonedRoot );
974 
976  copyRendererData( r );
977  return r;
978 }
979 
981 {
982  mRootRule->toSld( doc, element, QgsStringMap() );
983 }
984 
985 // TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
987 {
988  return mRootRule->symbols( context );
989 }
990 
992 {
993  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
994  rendererElem.setAttribute( "type", "RuleRenderer" );
995  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
996  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
997 
999 
1000  QDomElement rulesElem = mRootRule->save( doc, symbols );
1001  rulesElem.setTagName( "rules" ); // instead of just "rule"
1002  rendererElem.appendChild( rulesElem );
1003 
1004  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1005  rendererElem.appendChild( symbolsElem );
1006 
1008  mPaintEffect->saveProperties( doc, rendererElem );
1009 
1010  if ( !mOrderBy.isEmpty() )
1011  {
1012  QDomElement orderBy = doc.createElement( "orderby" );
1013  mOrderBy.save( orderBy );
1014  rendererElem.appendChild( orderBy );
1015  }
1016  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
1017 
1018  return rendererElem;
1019 }
1020 
1022 {
1025  for ( QgsLegendSymbolList::iterator it = items.begin(); it != items.end(); ++it )
1026  {
1027  QPair<QString, QgsSymbolV2*> pair = *it;
1028  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( pair.second, iconSize );
1029  lst << qMakePair( pair.first, pix );
1030  }
1031  return lst;
1032 }
1033 
1035 {
1036  return true;
1037 }
1038 
1040 {
1041  Rule* rule = mRootRule->findRuleByKey( key );
1042  return rule ? rule->active() : true;
1043 }
1044 
1046 {
1047  Rule* rule = mRootRule->findRuleByKey( key );
1048  if ( rule )
1049  rule->setActive( state );
1050 }
1051 
1053 {
1054  Rule* rule = mRootRule->findRuleByKey( key );
1055  if ( rule )
1056  rule->setSymbol( symbol );
1057  else
1058  delete symbol;
1059 }
1060 
1062 {
1063  return mRootRule->legendSymbolItems( scaleDenominator, rule );
1064 }
1065 
1067 {
1068  return mRootRule->legendSymbolItemsV2();
1069 }
1070 
1071 
1073 {
1074  // load symbols
1075  QDomElement symbolsElem = element.firstChildElement( "symbols" );
1076  if ( symbolsElem.isNull() )
1077  return nullptr;
1078 
1079  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1080 
1081  QDomElement rulesElem = element.firstChildElement( "rules" );
1082 
1083  Rule* root = Rule::create( rulesElem, symbolMap );
1084  if ( !root )
1085  return nullptr;
1086 
1088 
1089  // delete symbols if there are any more
1091 
1092  return r;
1093 }
1094 
1096 {
1097  // retrieve child rules
1098  Rule* root = nullptr;
1099 
1100  QDomElement ruleElem = element.firstChildElement( "Rule" );
1101  while ( !ruleElem.isNull() )
1102  {
1103  Rule *child = Rule::createFromSld( ruleElem, geomType );
1104  if ( child )
1105  {
1106  // create the root rule if not done before
1107  if ( !root )
1108  root = new Rule( nullptr );
1109 
1110  root->appendChild( child );
1111  }
1112 
1113  ruleElem = ruleElem.nextSiblingElement( "Rule" );
1114  }
1115 
1116  if ( !root )
1117  {
1118  // no valid rules was found
1119  return nullptr;
1120  }
1121 
1122  // create and return the new renderer
1123  return new QgsRuleBasedRendererV2( root );
1124 }
1125 
1128 
1130 {
1131  QString attr = r->classAttribute();
1132  // categorizedAttr could be either an attribute name or an expression.
1133  // the only way to differentiate is to test it as an expression...
1134  QgsExpression testExpr( attr );
1135  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1136  {
1137  //not an expression, so need to quote column name
1138  attr = QgsExpression::quotedColumnRef( attr );
1139  }
1140 
1141  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->categories() )
1142  {
1143  QString value;
1144  // not quoting numbers saves a type cast
1145  if ( cat.value().type() == QVariant::Int )
1146  value = cat.value().toString();
1147  else if ( cat.value().type() == QVariant::Double )
1148  // we loose precision here - so we may miss some categories :-(
1149  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1150  value = QString::number( cat.value().toDouble(), 'f', 4 );
1151  else
1152  value = QgsExpression::quotedString( cat.value().toString() );
1153  QString filter = QString( "%1 = %2" ).arg( attr, value );
1154  QString label = filter;
1155  initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1156  }
1157 }
1158 
1160 {
1161  QString attr = r->classAttribute();
1162  // categorizedAttr could be either an attribute name or an expression.
1163  // the only way to differentiate is to test it as an expression...
1164  QgsExpression testExpr( attr );
1165  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1166  {
1167  //not an expression, so need to quote column name
1168  attr = QgsExpression::quotedColumnRef( attr );
1169  }
1170  else if ( !testExpr.isField() )
1171  {
1172  //otherwise wrap expression in brackets
1173  attr = QString( "(%1)" ).arg( attr );
1174  }
1175 
1176  bool firstRange = true;
1177  Q_FOREACH ( const QgsRendererRangeV2& rng, r->ranges() )
1178  {
1179  // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1180  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1181  QString filter = QString( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? ">=" : ">",
1182  QString::number( rng.lowerValue(), 'f', 4 ),
1183  QString::number( rng.upperValue(), 'f', 4 ) );
1184  firstRange = false;
1185  QString label = filter;
1186  initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1187  }
1188 }
1189 
1191 {
1192  qSort( scales ); // make sure the scales are in ascending order
1193  int oldScale = initialRule->scaleMinDenom();
1194  int maxDenom = initialRule->scaleMaxDenom();
1195  QgsSymbolV2* symbol = initialRule->symbol();
1196  Q_FOREACH ( int scale, scales )
1197  {
1198  if ( initialRule->scaleMinDenom() >= scale )
1199  continue; // jump over the first scales out of the interval
1200  if ( maxDenom != 0 && maxDenom <= scale )
1201  break; // ignore the latter scales out of the interval
1202  initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1203  oldScale = scale;
1204  }
1205  // last rule
1206  initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1207 }
1208 
1210 {
1211  QString msg( "Rule-based renderer:\n" );
1212  msg += mRootRule->dump();
1213  return msg;
1214 }
1215 
1217 {
1218  return mRootRule->willRenderFeature( feat, &context );
1219 }
1220 
1222 {
1223  return mRootRule->symbolsForFeature( feat, &context );
1224 }
1225 
1227 {
1228  return mRootRule->symbolsForFeature( feat, &context );
1229 }
1230 
1232 {
1233  return mRootRule->legendKeysForFeature( feature, &context );
1234 }
1235 
1237 {
1238  QgsRuleBasedRendererV2* r = nullptr;
1239  if ( renderer->type() == "RuleRenderer" )
1240  {
1241  r = dynamic_cast<QgsRuleBasedRendererV2*>( renderer->clone() );
1242  }
1243  else if ( renderer->type() == "singleSymbol" )
1244  {
1245  const QgsSingleSymbolRendererV2* singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRendererV2*>( renderer );
1246  if ( !singleSymbolRenderer )
1247  return nullptr;
1248 
1249  QgsSymbolV2* origSymbol = singleSymbolRenderer->symbol()->clone();
1250  convertToDataDefinedSymbology( origSymbol, singleSymbolRenderer->sizeScaleField() );
1251  r = new QgsRuleBasedRendererV2( origSymbol );
1252  }
1253  else if ( renderer->type() == "categorizedSymbol" )
1254  {
1255  const QgsCategorizedSymbolRendererV2* categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRendererV2*>( renderer );
1256  if ( !categorizedRenderer )
1257  return nullptr;
1258 
1259  QString attr = categorizedRenderer->classAttribute();
1260  // categorizedAttr could be either an attribute name or an expression.
1261  // the only way to differentiate is to test it as an expression...
1262  QgsExpression testExpr( attr );
1263  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1264  {
1265  //not an expression, so need to quote column name
1266  attr = QgsExpression::quotedColumnRef( attr );
1267  }
1268 
1270 
1271  QString expression;
1272  QString value;
1273  QgsRendererCategoryV2 category;
1274  for ( int i = 0; i < categorizedRenderer->categories().size(); ++i )
1275  {
1276  category = categorizedRenderer->categories().value( i );
1278 
1279  rule->setLabel( category.label() );
1280 
1281  //We first define the rule corresponding to the category
1282  //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1283  if ( QVariant( category.value() ).convert( QVariant::Double ) )
1284  {
1285  value = category.value().toString();
1286  }
1287  else
1288  {
1289  value = QgsExpression::quotedString( category.value().toString() );
1290  }
1291 
1292  //An empty category is equivalent to the ELSE keyword
1293  if ( value == "''" )
1294  {
1295  expression = "ELSE";
1296  }
1297  else
1298  {
1299  expression = QString( "%1 = %2" ).arg( attr, value );
1300  }
1301  rule->setFilterExpression( expression );
1302 
1303  //Then we construct an equivalent symbol.
1304  //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1305  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1306 
1307  QgsSymbolV2* origSymbol = category.symbol()->clone();
1308  convertToDataDefinedSymbology( origSymbol, categorizedRenderer->sizeScaleField() );
1309  rule->setSymbol( origSymbol );
1310 
1311  rootrule->appendChild( rule );
1312  }
1313 
1314  r = new QgsRuleBasedRendererV2( rootrule );
1315  }
1316  else if ( renderer->type() == "graduatedSymbol" )
1317  {
1318  const QgsGraduatedSymbolRendererV2* graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRendererV2*>( renderer );
1319  if ( !graduatedRenderer )
1320  return nullptr;
1321 
1322  QString attr = graduatedRenderer->classAttribute();
1323  // categorizedAttr could be either an attribute name or an expression.
1324  // the only way to differentiate is to test it as an expression...
1325  QgsExpression testExpr( attr );
1326  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1327  {
1328  //not an expression, so need to quote column name
1329  attr = QgsExpression::quotedColumnRef( attr );
1330  }
1331  else if ( !testExpr.isField() )
1332  {
1333  //otherwise wrap expression in brackets
1334  attr = QString( "(%1)" ).arg( attr );
1335  }
1336 
1338 
1339  QString expression;
1340  QgsRendererRangeV2 range;
1341  for ( int i = 0; i < graduatedRenderer->ranges().size();++i )
1342  {
1343  range = graduatedRenderer->ranges().value( i );
1345  rule->setLabel( range.label() );
1346  if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1347  {
1348  expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1349  attr + " <= " + QString::number( range.upperValue(), 'f' );
1350  }
1351  else
1352  {
1353  expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1354  attr + " <= " + QString::number( range.upperValue(), 'f' );
1355  }
1356  rule->setFilterExpression( expression );
1357 
1358  //Then we construct an equivalent symbol.
1359  //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1360  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1361 
1362  QgsSymbolV2* symbol = range.symbol()->clone();
1363  convertToDataDefinedSymbology( symbol, graduatedRenderer->sizeScaleField() );
1364 
1365  rule->setSymbol( symbol );
1366 
1367  rootrule->appendChild( rule );
1368  }
1369 
1370  r = new QgsRuleBasedRendererV2( rootrule );
1371  }
1372  else if ( renderer->type() == "pointDisplacement" )
1373  {
1374  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1375  if ( pointDisplacementRenderer )
1376  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1377  }
1378  else if ( renderer->type() == "invertedPolygonRenderer" )
1379  {
1380  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1381  if ( invertedPolygonRenderer )
1382  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1383  }
1384 
1385  if ( r )
1386  {
1387  r->setOrderBy( renderer->orderBy() );
1388  r->setOrderByEnabled( renderer->orderByEnabled() );
1389  }
1390 
1391  return r;
1392 }
1393 
1395 {
1396  QString sizeExpression;
1397  switch ( symbol->type() )
1398  {
1399  case QgsSymbolV2::Marker:
1400  for ( int j = 0; j < symbol->symbolLayerCount();++j )
1401  {
1402  QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( symbol->symbolLayer( j ) );
1403  if ( ! sizeScaleField.isEmpty() )
1404  {
1405  sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1406  msl->setDataDefinedProperty( "size", new QgsDataDefined( sizeExpression ) );
1407  }
1408  if ( ! rotationField.isEmpty() )
1409  {
1410  msl->setDataDefinedProperty( "angle", new QgsDataDefined( true, false, QString(), rotationField ) );
1411  }
1412  }
1413  break;
1414  case QgsSymbolV2::Line:
1415  if ( ! sizeScaleField.isEmpty() )
1416  {
1417  for ( int j = 0; j < symbol->symbolLayerCount();++j )
1418  {
1419  if ( symbol->symbolLayer( j )->layerType() == "SimpleLine" )
1420  {
1421  QgsLineSymbolLayerV2* lsl = static_cast<QgsLineSymbolLayerV2*>( symbol->symbolLayer( j ) );
1422  sizeExpression = QString( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1423  lsl->setDataDefinedProperty( "width", new QgsDataDefined( sizeExpression ) );
1424  }
1425  if ( symbol->symbolLayer( j )->layerType() == "MarkerLine" )
1426  {
1427  QgsSymbolV2* marker = symbol->symbolLayer( j )->subSymbol();
1428  for ( int k = 0; k < marker->symbolLayerCount();++k )
1429  {
1430  QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( marker->symbolLayer( k ) );
1431  sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1432  msl->setDataDefinedProperty( "size", new QgsDataDefined( sizeExpression ) );
1433  }
1434  }
1435  }
1436  }
1437  break;
1438  default:
1439  break;
1440  }
1441 }
QString dump(int indent=0) const
Dump for debug purpose.
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
Class for parsing and evaluation of expressions (formerly called "search strings").
void setLabel(const QString &label)
QString description() const
A human readable description for this rule.
void clear()
static QgsSymbolV2Map loadSymbols(QDomElement &element)
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 ...
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:49
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
QString & append(QChar ch)
virtual 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...
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QStringList referencedColumns() const
Get list of columns referenced by the expression.
A container class for data source field mapping or expression.
const QgsCategoryList & categories() const
bool contains(const Key &key) const
GeometryType
Definition: qgis.h:115
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
virtual double width() const
QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, const QString &rule="") const
QString & fill(QChar ch, int size)
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
QSet< int > collectZLevels()
get all used z-levels from this rule and children
QDomNode appendChild(const QDomNode &newChild)
static QgsFeatureRendererV2 * create(QDomElement &element)
SymbolType type() const
Definition: qgssymbolv2.h:107
virtual QSet< QString > legendKeysForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return legend keys matching a specified feature.
QSet< QString > usedAttributes() const
Return a list of attributes required to render this feature.
QString attribute(const QString &name, const QString &defValue) const
int length() const
bool needsGeometry() const
Returns true if this rule or one of its chilren needs the geometry to be applied. ...
void setTagName(const QString &name)
QString nodeValue() const
Q_DECL_DEPRECATED bool prepare(const QgsFields &fields)
Get the expression ready for evaluation - find out column indexes.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setActive(bool state)
Sets if this rule is active.
QString & prepend(QChar ch)
double rendererScale() const
bool isFilterOK(QgsFeature &f, QgsRenderContext *context=nullptr) const
Check if a given feature shall be rendered by this rule.
virtual QgsSymbolV2 * clone() const =0
static QgsFeatureRendererV2 * createFromSld(QDomElement &element, QGis::GeometryType geomType)
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
QgsSymbolV2List symbols(const QgsRenderContext &context=QgsRenderContext()) const
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
bool contains(const QString &str, Qt::CaseSensitivity cs) const
virtual bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
bool isScaleOK(double scale) const
Check if this rule applies for a given scale.
QDomElement nextSiblingElement(const QString &tagName) const
Q_DECL_DEPRECATED bool startRender(QgsRenderContext &context, const QgsFields &fields)
Prepare the rule for rendering and its children (build active children array)
Rule * clone() const
clone this rule, return new instance
Container of fields for a vector layer.
Definition: qgsfield.h:209
void removeChild(Rule *rule)
delete child rule
Line symbol.
Definition: qgssymbolv2.h:82
virtual void setLegendSymbolItem(const QString &key, QgsSymbolV2 *symbol) override
Sets the symbol to be used for a legend symbol item.
T takeAt(int i)
QSet< T > toSet() const
QString join(const QString &separator) const
const_iterator insert(const T &value)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
virtual bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
void setIsElse(bool iselse)
Sets if this rule is an ELSE rule.
bool isElse()
Check if this rule is an ELSE rule.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:492
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:353
QgsPaintEffect * mPaintEffect
Rule * takeChild(Rule *rule)
take child rule out, set parent as null
void removeChildAt(int i)
delete child rule
Marker symbol.
Definition: qgssymbolv2.h:81
int size() const
virtual QgsSymbolV2List originalSymbolsForFeature(QgsFeature &feat, QgsRenderContext &context) override
Equivalent of originalSymbolsForFeature() call extended to support renderers that may use more symbol...
T value(int i) const
virtual bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
Rule * findRuleByKey(const QString &key)
Try to find a rule given its unique key.
void renderFeatureWithSymbol(QgsFeature &feature, QgsSymbolV2 *symbol, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker)
QString type() const
Definition: qgsrendererv2.h:92
virtual void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
static bool createSymbolLayerV2ListFromSld(QDomElement &element, QGis::GeometryType geomType, QgsSymbolLayerV2List &layers)
void stopRender(QgsRenderContext &context)
Stop a rendering process.
virtual QgsFeatureRendererV2 * clone() const =0
QString number(int n, int base)
int count(const T &value) const
virtual QDomElement save(QDomDocument &doc) override
store renderer info to XML element
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
void append(const T &value)
static void refineRuleCategories(Rule *initialRule, QgsCategorizedSymbolRendererV2 *r)
take a rule and create a list of new rules based on the categories from categorized symbol renderer ...
QString localName() const
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
void startRender(QgsRenderContext &context, const QgsFields *fields=nullptr)
int toInt(bool *ok) const
virtual void stopRender(QgsRenderContext &context) override
Needs to be called when a render cycle has finished to clean up.
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
void setAttribute(const QString &name, const QString &value)
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
int toInt(bool *ok, int base) const
virtual Q_DECL_DEPRECATED QString rotationField() const
return rotation field name (or empty string if not set or not supported by renderer) ...
bool isEmpty() const
void appendChild(Rule *rule)
add child rule, take ownership, sets this as parent
static Rule * create(QDomElement &ruleElem, QgsSymbolV2Map &symbolMap)
Create a rule from an XML definition.
bool isEmpty() const
int removeAll(const T &value)
QString trimmed() const
RenderResult renderFeature(FeatureToRender &featToRender, QgsRenderContext &context, RenderQueue &renderQueue)
Render a given feature, will recursively call subclasses and only render if the constraints apply...
QgsSymbolV2 * symbol() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
QSet< QString > legendKeysForFeature(QgsFeature &feat, QgsRenderContext *context=nullptr)
Returns which legend keys match the feature.
int symbolLayerCount()
Returns total number of symbol layers contained in the symbol.
Definition: qgssymbolv2.h:134
This class keeps data about a rules for rule-based renderer.
void setRuleKey(const QString &key)
Override the assigned rule key (should be used just internally by rule-based renderer) ...
QgsExpression * filter() const
A filter that will check if this rule applies.
virtual void toSld(QDomDocument &doc, QDomElement &element) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
bool isField() const
Checks whether an expression consists only of a single field reference.
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize) override
return a list of symbology items for the legend
QString ruleKey() const
Unique rule identifier (for identification of rule within renderer)
bool willRenderFeature(QgsFeature &feat, QgsRenderContext *context=nullptr)
only tell whether a feature will be rendered without actually rendering it
virtual Q_DECL_DEPRECATED QgsSymbolV2List symbols()
For symbol levels.
Rule * mRootRule
the root node with hierarchical list of rules
virtual 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.
static void refineRuleRanges(Rule *initialRule, QgsGraduatedSymbolRendererV2 *r)
take a rule and create a list of new rules based on the ranges from graduated symbol renderer ...
Rule * takeChildAt(int i)
take child rule out, set parent as null
double size() const
Returns the symbol size.
RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra...
QDomText createTextNode(const QString &value)
iterator end()
QgsSymbolV2List symbolsForFeature(QgsFeature &feat, QgsRenderContext *context=nullptr)
tell which symbols will be used to render the feature
void updateElseRules()
Check which child rules are else rules and update the internal list of else rules.
QgsLegendSymbolListV2 legendSymbolItemsV2(int currentLevel=-1) const
int renderingPass() const
virtual QString layerType() const =0
Returns a string that represents this layer type.
QgsExpressionContext & expressionContext()
Gets the expression context.
void copyRendererData(QgsFeatureRendererV2 *destRenderer) const
Clones generic renderer data to another renderer.
A renderer that automatically displaces points with the same position.
bool isNull() const
virtual QgsSymbolV2 * subSymbol()
QgsRuleBasedRendererV2(QgsRuleBasedRendererV2::Rule *root)
Constructs the renderer from given tree of rules (takes ownership)
void setUsingSymbolLevels(bool usingSymbolLevels)
virtual QString dump() const override
for debugging
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
Contains information about the context of a rendering operation.
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
virtual QgsSymbolV2List symbolsForFeature(QgsFeature &feat, QgsRenderContext &context) override
return list of symbols used for rendering the feature.
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
return symbol for current feature. Should not be used individually: there could be more symbols for a...
QDomNode firstChild() const
static void convertToDataDefinedSymbology(QgsSymbolV2 *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...
RenderResult
The result of rendering a rule.
void stopRender(QgsRenderContext &context)
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
QSet< T > & unite(const QSet< T > &other)
Rule(QgsSymbolV2 *symbol, int scaleMinDenom=0, int scaleMaxDenom=0, const QString &filterExp=QString(), const QString &label=QString(), const QString &description=QString(), bool elseRule=false)
Constructor takes ownership of the symbol.
void insert(int i, const T &value)
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=nullptr)
const QgsRangeList & ranges() const
QgsFeatureRequest::OrderBy mOrderBy
When drawing a vector layer with rule-based renderer, it goes through the rules and draws features wi...
static Rule * createFromSld(QDomElement &element, QGis::GeometryType geomType)
RuleList rulesForFeature(QgsFeature &feat, QgsRenderContext *context=nullptr)
tell which rules will be used to render the feature
QDomElement firstChildElement(const QString &tagName) const
bool usingSymbolLevels() const
static QgsExpression * expressionFromOgcFilter(const QDomElement &element)
Parse XML with OGC filter into QGIS expression.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
Needs to be called when a new render cycle is started.
virtual QgsRuleBasedRendererV2 * clone() const override
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const override
Return a list of symbology items for the legend.
QString expression() const
Return the original, unmodified expression string.
QList< T > toList() const
static void clearSymbolMap(QgsSymbolV2Map &symbols)
void setSymbol(QgsSymbolV2 *sym)
set a new symbol (or NULL). Deletes old symbol.
virtual QList< QString > usedAttributes() override
Returns a set of attributes required for this renderer.
double toDouble(bool *ok) const
Abstract base class for marker symbol layers.
static QgsRuleBasedRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsRuleBasedRendererV2 from an existing renderer.
QSet< QString > usedAttributes() const
Return the attributes used to evaluate the expression of this rule.
QString tagName() const
QgsSymbolLayerV2 * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
const_iterator constEnd() const
QString dump() const
void insertChild(int i, Rule *rule)
add child rule, take ownership, sets this as parent
void setFilterExpression(const QString &filterExp)
Set the expression used to check if a given feature shall be rendered with this rule.
QDomElement createElement(const QString &tagName)
void clear()
const_iterator constBegin() const
Type type() const
int compare(const QString &other) const
QString parserErrorString() const
Returns parser error.
bool active() const
Returns if this rule is active.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
QString toString() const
iterator begin()
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, const QString &rule="") override
return a list of item text / symbol
T take(const Key &key)
int size() const
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QUuid createUuid()
QList< FeatureToRender > mCurrentFeatures
QDomElement save(QDomDocument &doc, QgsSymbolV2Map &symbolMap) const
virtual bool willRenderFeature(QgsFeature &feat, QgsRenderContext &context) override
return whether the renderer will render a feature or not.
static void refineRuleScales(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...
const T value(const Key &key) const
virtual Q_DECL_DEPRECATED void setDataDefinedProperty(const QString &property, const QString &expressionString)
Sets a data defined expression for a property.