QGIS API Documentation  2.14.0-Essen
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  QgsSymbolV2List lst;
203  if ( mSymbol )
204  lst.append( mSymbol );
205 
206  Q_FOREACH ( Rule* rule, mChildren )
207  {
208  lst += rule->symbols( context );
209  }
210  return lst;
211 }
212 
214 {
215  delete mSymbol;
216  mSymbol = sym;
217 }
218 
220 {
221  mFilterExp = filterExp;
222  initFilter();
223 }
224 
225 QgsLegendSymbolList QgsRuleBasedRendererV2::Rule::legendSymbolItems( double scaleDenominator, const QString& ruleFilter ) const
226 {
228  if ( mSymbol && ( ruleFilter.isEmpty() || mLabel == ruleFilter ) )
229  lst << qMakePair( mLabel, mSymbol );
230 
231  Q_FOREACH ( Rule* rule, mChildren )
232  {
233  if ( qgsDoubleNear( scaleDenominator, -1 ) || rule->isScaleOK( scaleDenominator ) )
234  {
235  lst << rule->legendSymbolItems( scaleDenominator, ruleFilter );
236  }
237  }
238  return lst;
239 }
240 
242 {
244  if ( currentLevel != -1 ) // root rule should not be shown
245  {
247  }
248 
249  for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
250  {
251  Rule* rule = *it;
252  lst << rule->legendSymbolItemsV2( currentLevel + 1 );
253  }
254  return lst;
255 }
256 
257 
259 {
260  if ( ! mFilter || mElseRule )
261  return true;
262 
263  context->expressionContext().setFeature( f );
264  QVariant res = mFilter->evaluate( &context->expressionContext() );
265  return res.toInt() != 0;
266 }
267 
268 bool QgsRuleBasedRendererV2::Rule::isScaleOK( double scale ) const
269 {
270  if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
271  return true;
272  if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 )
273  return true;
274  if ( mScaleMinDenom != 0 && mScaleMinDenom > scale )
275  return false;
276  if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale )
277  return false;
278  return true;
279 }
280 
282 {
283  QgsSymbolV2* sym = mSymbol ? mSymbol->clone() : nullptr;
285  newrule->setActive( mIsActive );
286  // clone children
287  Q_FOREACH ( Rule* rule, mChildren )
288  newrule->appendChild( rule->clone() );
289  return newrule;
290 }
291 
293 {
294  QDomElement ruleElem = doc.createElement( "rule" );
295 
296  if ( mSymbol )
297  {
298  int symbolIndex = symbolMap.size();
299  symbolMap[QString::number( symbolIndex )] = mSymbol;
300  ruleElem.setAttribute( "symbol", symbolIndex );
301  }
302  if ( !mFilterExp.isEmpty() )
303  ruleElem.setAttribute( "filter", mFilterExp );
304  if ( mScaleMinDenom != 0 )
305  ruleElem.setAttribute( "scalemindenom", mScaleMinDenom );
306  if ( mScaleMaxDenom != 0 )
307  ruleElem.setAttribute( "scalemaxdenom", mScaleMaxDenom );
308  if ( !mLabel.isEmpty() )
309  ruleElem.setAttribute( "label", mLabel );
310  if ( !mDescription.isEmpty() )
311  ruleElem.setAttribute( "description", mDescription );
312  if ( !mIsActive )
313  ruleElem.setAttribute( "checkstate", 0 );
314  ruleElem.setAttribute( "key", mRuleKey );
315 
316  Q_FOREACH ( Rule* rule, mChildren )
317  {
318  ruleElem.appendChild( rule->save( doc, symbolMap ) );
319  }
320  return ruleElem;
321 }
322 
324 {
325  // do not convert this rule if there are no symbols
326  QgsRenderContext context;
327  if ( symbols( context ).isEmpty() )
328  return;
329 
330  if ( !mFilterExp.isEmpty() )
331  {
332  if ( !props.value( "filter", "" ).isEmpty() )
333  props[ "filter" ] += " AND ";
334  props[ "filter" ] += mFilterExp;
335  }
336 
337  if ( mScaleMinDenom != 0 )
338  {
339  bool ok;
340  int parentScaleMinDenom = props.value( "scaleMinDenom", "0" ).toInt( &ok );
341  if ( !ok || parentScaleMinDenom <= 0 )
342  props[ "scaleMinDenom" ] = QString::number( mScaleMinDenom );
343  else
344  props[ "scaleMinDenom" ] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) );
345  }
346 
347  if ( mScaleMaxDenom != 0 )
348  {
349  bool ok;
350  int parentScaleMaxDenom = props.value( "scaleMaxDenom", "0" ).toInt( &ok );
351  if ( !ok || parentScaleMaxDenom <= 0 )
352  props[ "scaleMaxDenom" ] = QString::number( mScaleMaxDenom );
353  else
354  props[ "scaleMaxDenom" ] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) );
355  }
356 
357  if ( mSymbol )
358  {
359  QDomElement ruleElem = doc.createElement( "se:Rule" );
360  element.appendChild( ruleElem );
361 
362  //XXX: <se:Name> is the rule identifier, but our the Rule objects
363  // have no properties could be used as identifier. Use the label.
364  QDomElement nameElem = doc.createElement( "se:Name" );
365  nameElem.appendChild( doc.createTextNode( mLabel ) );
366  ruleElem.appendChild( nameElem );
367 
368  if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
369  {
370  QDomElement descrElem = doc.createElement( "se:Description" );
371  if ( !mLabel.isEmpty() )
372  {
373  QDomElement titleElem = doc.createElement( "se:Title" );
374  titleElem.appendChild( doc.createTextNode( mLabel ) );
375  descrElem.appendChild( titleElem );
376  }
377  if ( !mDescription.isEmpty() )
378  {
379  QDomElement abstractElem = doc.createElement( "se:Abstract" );
380  abstractElem.appendChild( doc.createTextNode( mDescription ) );
381  descrElem.appendChild( abstractElem );
382  }
383  ruleElem.appendChild( descrElem );
384  }
385 
386  if ( !props.value( "filter", "" ).isEmpty() )
387  {
388  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, props.value( "filter", "" ) );
389  }
390 
391  if ( !props.value( "scaleMinDenom", "" ).isEmpty() )
392  {
393  QDomElement scaleMinDenomElem = doc.createElement( "se:MinScaleDenominator" );
394  scaleMinDenomElem.appendChild( doc.createTextNode( props.value( "scaleMinDenom", "" ) ) );
395  ruleElem.appendChild( scaleMinDenomElem );
396  }
397 
398  if ( !props.value( "scaleMaxDenom", "" ).isEmpty() )
399  {
400  QDomElement scaleMaxDenomElem = doc.createElement( "se:MaxScaleDenominator" );
401  scaleMaxDenomElem.appendChild( doc.createTextNode( props.value( "scaleMaxDenom", "" ) ) );
402  ruleElem.appendChild( scaleMaxDenomElem );
403  }
404 
405  mSymbol->toSld( doc, ruleElem, props );
406  }
407 
408  // loop into childern rule list
409  Q_FOREACH ( Rule* rule, mChildren )
410  {
411  rule->toSld( doc, element, props );
412  }
413 }
414 
416 {
417  QString filter;
418  return startRender( context, fields, filter );
419 }
420 
422 {
424 
425  if ( ! mIsActive )
426  return false;
427 
428  // filter out rules which are not compatible with this scale
429  if ( !isScaleOK( context.rendererScale() ) )
430  return false;
431 
432  // init this rule
433  if ( mFilter )
434  mFilter->prepare( &context.expressionContext() );
435  if ( mSymbol )
436  mSymbol->startRender( context, &fields );
437 
438  // init children
439  // build temporary list of active rules (usable with this scale)
440  QStringList subfilters;
441  Q_FOREACH ( Rule* rule, mChildren )
442  {
443  QString subfilter;
444  if ( rule->startRender( context, fields , subfilter ) )
445  {
446  // only add those which are active with current scale
447  mActiveChildren.append( rule );
448  subfilters.append( subfilter );
449  }
450  }
451 
452  // subfilters (on the same level) are joined with OR
453  // Finally they are joined with their parent (this) with AND
454  QString sf;
455  // If there are subfilters present (and it's not a single empty one), group them and join them with OR
456  if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
457  {
458  if ( subfilters.contains( "TRUE" ) )
459  sf = "TRUE";
460  else
461  sf = subfilters.join( ") OR (" ).prepend( '(' ).append( ')' );
462  }
463 
464  // Now join the subfilters with their parent (this) based on if
465  // * The parent is an else rule
466  // * The existence of parent filter and subfilters
467 
468  // No filter expression: ELSE rule or catchall rule
469  if ( !mFilter )
470  {
471  if ( mSymbol || sf.isEmpty() )
472  filter = "TRUE";
473  else
474  filter = sf;
475  }
476  else if ( mSymbol )
477  filter = mFilterExp;
478  else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
479  filter = QString( "(%1) AND (%2)" ).arg( mFilterExp, sf );
480  else if ( !mFilterExp.trimmed().isEmpty() )
481  filter = mFilterExp;
482  else if ( sf.isEmpty() )
483  filter = "TRUE";
484  else
485  filter = sf;
486 
487  filter = filter.trimmed();
488 
489  return true;
490 }
491 
493 {
494  QSet<int> symbolZLevelsSet;
495 
496  // process this rule
497  if ( mSymbol )
498  {
499  // find out which Z-levels are used
500  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
501  {
502  symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
503  }
504  }
505 
506  // process children
508  for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
509  {
510  Rule* rule = *it;
511  symbolZLevelsSet.unite( rule->collectZLevels() );
512  }
513  return symbolZLevelsSet;
514 }
515 
517 {
518  if ( mSymbol )
519  {
520  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
521  {
522  int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
523  mSymbolNormZLevels.insert( normLevel );
524  }
525  }
526 
527  // prepare list of normalized levels for each rule
528  Q_FOREACH ( Rule* rule, mActiveChildren )
529  {
530  rule->setNormZLevels( zLevelsToNormLevels );
531  }
532 }
533 
534 
536 {
537  if ( !isFilterOK( featToRender.feat, &context ) )
538  return Filtered;
539 
540  bool rendered = false;
541 
542  // create job for this feature and this symbol, add to list of jobs
543  if ( mSymbol && mIsActive )
544  {
545  // add job to the queue: each symbol's zLevel must be added
546  Q_FOREACH ( int normZLevel, mSymbolNormZLevels )
547  {
548  //QgsDebugMsg(QString("add job at level %1").arg(normZLevel));
549  renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol ) );
550  rendered = true;
551  }
552  }
553 
554  bool willrendersomething = false;
555 
556  // process children
557  Q_FOREACH ( Rule* rule, mChildren )
558  {
559  // Don't process else rules yet
560  if ( !rule->isElse() )
561  {
562  RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
563  // consider inactive items as "rendered" so the else rule will ignore them
564  willrendersomething |= ( res == Rendered || res == Inactive );
565  rendered |= ( res == Rendered );
566  }
567  }
568 
569  // If none of the rules passed then we jump into the else rules and process them.
570  if ( !willrendersomething )
571  {
572  Q_FOREACH ( Rule* rule, mElseRules )
573  {
574  rendered |= rule->renderFeature( featToRender, context, renderQueue ) == Rendered;
575  }
576  }
577  if ( !mIsActive || ( mSymbol && !rendered ) )
578  return Inactive;
579  else if ( rendered )
580  return Rendered;
581  else
582  return Filtered;
583 }
584 
586 {
587  if ( !isFilterOK( feat, context ) )
588  return false;
589  if ( mSymbol )
590  return true;
591 
592  Q_FOREACH ( Rule* rule, mActiveChildren )
593  {
594  if ( rule->willRenderFeature( feat, context ) )
595  return true;
596  }
597  return false;
598 }
599 
601 {
602  QgsSymbolV2List lst;
603  if ( !isFilterOK( feat, context ) )
604  return lst;
605  if ( mSymbol )
606  lst.append( mSymbol );
607 
608  Q_FOREACH ( Rule* rule, mActiveChildren )
609  {
610  lst += rule->symbolsForFeature( feat, context );
611  }
612  return lst;
613 }
614 
616 {
617  QSet< QString> lst;
618  if ( !isFilterOK( feat, context ) )
619  return lst;
620  lst.insert( mRuleKey );
621 
622  Q_FOREACH ( Rule* rule, mActiveChildren )
623  {
624  lst.unite( rule->legendKeysForFeature( feat, context ) );
625  }
626  return lst;
627 }
628 
630 {
631  RuleList lst;
632  if ( !isFilterOK( feat, context ) )
633  return lst;
634 
635  if ( mSymbol )
636  lst.append( this );
637 
638  Q_FOREACH ( Rule* rule, mActiveChildren )
639  {
640  lst += rule->rulesForFeature( feat, context );
641  }
642  return lst;
643 }
644 
646 {
647  if ( mSymbol )
648  mSymbol->stopRender( context );
649 
650  Q_FOREACH ( Rule* rule, mActiveChildren )
651  {
652  rule->stopRender( context );
653  }
654 
657 }
658 
660 {
661  QString symbolIdx = ruleElem.attribute( "symbol" );
662  QgsSymbolV2* symbol = nullptr;
663  if ( !symbolIdx.isEmpty() )
664  {
665  if ( symbolMap.contains( symbolIdx ) )
666  {
667  symbol = symbolMap.take( symbolIdx );
668  }
669  else
670  {
671  QgsDebugMsg( "symbol for rule " + symbolIdx + " not found!" );
672  }
673  }
674 
675  QString filterExp = ruleElem.attribute( "filter" );
676  QString label = ruleElem.attribute( "label" );
677  QString description = ruleElem.attribute( "description" );
678  int scaleMinDenom = ruleElem.attribute( "scalemindenom", "0" ).toInt();
679  int scaleMaxDenom = ruleElem.attribute( "scalemaxdenom", "0" ).toInt();
680  QString ruleKey = ruleElem.attribute( "key" );
681  Rule* rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
682 
683  if ( !ruleKey.isEmpty() )
684  rule->mRuleKey = ruleKey;
685 
686  rule->setActive( ruleElem.attribute( "checkstate", "1" ).toInt() );
687 
688  QDomElement childRuleElem = ruleElem.firstChildElement( "rule" );
689  while ( !childRuleElem.isNull() )
690  {
691  Rule* childRule = create( childRuleElem, symbolMap );
692  if ( childRule )
693  {
694  rule->appendChild( childRule );
695  }
696  else
697  {
698  QgsDebugMsg( "failed to init a child rule!" );
699  }
700  childRuleElem = childRuleElem.nextSiblingElement( "rule" );
701  }
702 
703  return rule;
704 }
705 
707 {
708  if ( ruleElem.localName() != "Rule" )
709  {
710  QgsDebugMsg( QString( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
711  return nullptr;
712  }
713 
714  QString label, description, filterExp;
715  int scaleMinDenom = 0, scaleMaxDenom = 0;
716  QgsSymbolLayerV2List layers;
717 
718  // retrieve the Rule element child nodes
719  QDomElement childElem = ruleElem.firstChildElement();
720  while ( !childElem.isNull() )
721  {
722  if ( childElem.localName() == "Name" )
723  {
724  // <se:Name> tag contains the rule identifier,
725  // so prefer title tag for the label property value
726  if ( label.isEmpty() )
727  label = childElem.firstChild().nodeValue();
728  }
729  else if ( childElem.localName() == "Description" )
730  {
731  // <se:Description> can contains a title and an abstract
732  QDomElement titleElem = childElem.firstChildElement( "Title" );
733  if ( !titleElem.isNull() )
734  {
735  label = titleElem.firstChild().nodeValue();
736  }
737 
738  QDomElement abstractElem = childElem.firstChildElement( "Abstract" );
739  if ( !abstractElem.isNull() )
740  {
741  description = abstractElem.firstChild().nodeValue();
742  }
743  }
744  else if ( childElem.localName() == "Abstract" )
745  {
746  // <sld:Abstract> (v1.0)
747  description = childElem.firstChild().nodeValue();
748  }
749  else if ( childElem.localName() == "Title" )
750  {
751  // <sld:Title> (v1.0)
752  label = childElem.firstChild().nodeValue();
753  }
754  else if ( childElem.localName() == "Filter" )
755  {
757  if ( filter )
758  {
759  if ( filter->hasParserError() )
760  {
761  QgsDebugMsg( "parser error: " + filter->parserErrorString() );
762  }
763  else
764  {
765  filterExp = filter->expression();
766  }
767  delete filter;
768  }
769  }
770  else if ( childElem.localName() == "MinScaleDenominator" )
771  {
772  bool ok;
773  int v = childElem.firstChild().nodeValue().toInt( &ok );
774  if ( ok )
775  scaleMinDenom = v;
776  }
777  else if ( childElem.localName() == "MaxScaleDenominator" )
778  {
779  bool ok;
780  int v = childElem.firstChild().nodeValue().toInt( &ok );
781  if ( ok )
782  scaleMaxDenom = v;
783  }
784  else if ( childElem.localName().endsWith( "Symbolizer" ) )
785  {
786  // create symbol layers for this symbolizer
787  QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
788  }
789 
790  childElem = childElem.nextSiblingElement();
791  }
792 
793  // now create the symbol
794  QgsSymbolV2 *symbol = nullptr;
795  if ( !layers.isEmpty() )
796  {
797  switch ( geomType )
798  {
799  case QGis::Line:
800  symbol = new QgsLineSymbolV2( layers );
801  break;
802 
803  case QGis::Polygon:
804  symbol = new QgsFillSymbolV2( layers );
805  break;
806 
807  case QGis::Point:
808  symbol = new QgsMarkerSymbolV2( layers );
809  break;
810 
811  default:
812  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
813  return nullptr;
814  }
815  }
816 
817  // and then create and return the new rule
818  return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
819 }
820 
821 
823 
825  : QgsFeatureRendererV2( "RuleRenderer" ), mRootRule( root )
826 {
827 }
828 
830  : QgsFeatureRendererV2( "RuleRenderer" )
831 {
832  mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
833  mRootRule->appendChild( new Rule( defaultSymbol ) );
834 }
835 
837 {
838  delete mRootRule;
839 }
840 
841 
843 {
844  // not used at all
845  return nullptr;
846 }
847 
849  QgsRenderContext& context,
850  int layer,
851  bool selected,
852  bool drawVertexMarker )
853 {
854  Q_UNUSED( layer );
855 
856  int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
857  mCurrentFeatures.append( FeatureToRender( feature, flags ) );
858 
859  // check each active rule
860  return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue ) == Rule::Rendered;
861 }
862 
863 
865 {
866  // prepare active children
867  mRootRule->startRender( context, fields, mFilter );
868 
869  QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
870  QList<int> symbolZLevels = symbolZLevelsSet.toList();
871  qSort( symbolZLevels );
872 
873  // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
874  // and prepare rendering queue
875  QMap<int, int> zLevelsToNormLevels;
876  int maxNormLevel = -1;
877  Q_FOREACH ( int zLevel, symbolZLevels )
878  {
879  zLevelsToNormLevels[zLevel] = ++maxNormLevel;
880  mRenderQueue.append( RenderLevel( zLevel ) );
881  QgsDebugMsg( QString( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ) );
882  }
883 
884  mRootRule->setNormZLevels( zLevelsToNormLevels );
885 }
886 
888 {
889  //
890  // do the actual rendering
891  //
892 
893  // go through all levels
894  Q_FOREACH ( const RenderLevel& level, mRenderQueue )
895  {
896  //QgsDebugMsg(QString("level %1").arg(level.zIndex));
897  // go through all jobs at the level
898  Q_FOREACH ( const RenderJob* job, level.jobs )
899  {
900  context.expressionContext().setFeature( job->ftr.feat );
901  //QgsDebugMsg(QString("job fid %1").arg(job->f->id()));
902  // render feature - but only with symbol layers with specified zIndex
903  QgsSymbolV2* s = job->symbol;
904  int count = s->symbolLayerCount();
905  for ( int i = 0; i < count; i++ )
906  {
907  // TODO: better solution for this
908  // renderFeatureWithSymbol asks which symbol layer to draw
909  // but there are multiple transforms going on!
910  if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
911  {
912  int flags = job->ftr.flags;
913  renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
914  }
915  }
916  }
917  }
918 
919  // clean current features
920  mCurrentFeatures.clear();
921 
922  // clean render queue
924 
925  // clean up rules from temporary stuff
926  mRootRule->stopRender( context );
927 }
928 
930 {
931  return mFilter;
932 }
933 
935 {
937  return attrs.values();
938 }
939 
941 {
943 
944  // normally with clone() the individual rules get new keys (UUID), but here we want to keep
945  // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. visibility presets)
946  clonedRoot->setRuleKey( mRootRule->ruleKey() );
947  RuleList origDescendants = mRootRule->descendants();
948  RuleList clonedDescendants = clonedRoot->descendants();
949  Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
950  for ( int i = 0; i < origDescendants.count(); ++i )
951  clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
952 
953  QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( clonedRoot );
954 
956  copyRendererData( r );
957  return r;
958 }
959 
961 {
962  mRootRule->toSld( doc, element, QgsStringMap() );
963 }
964 
965 // TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
967 {
968  return mRootRule->symbols( context );
969 }
970 
972 {
973  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
974  rendererElem.setAttribute( "type", "RuleRenderer" );
975  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
976  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
977 
979 
980  QDomElement rulesElem = mRootRule->save( doc, symbols );
981  rulesElem.setTagName( "rules" ); // instead of just "rule"
982  rendererElem.appendChild( rulesElem );
983 
984  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
985  rendererElem.appendChild( symbolsElem );
986 
988  mPaintEffect->saveProperties( doc, rendererElem );
989 
990  if ( !mOrderBy.isEmpty() )
991  {
992  QDomElement orderBy = doc.createElement( "orderby" );
993  mOrderBy.save( orderBy );
994  rendererElem.appendChild( orderBy );
995  }
996  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
997 
998  return rendererElem;
999 }
1000 
1002 {
1005  for ( QgsLegendSymbolList::iterator it = items.begin(); it != items.end(); ++it )
1006  {
1007  QPair<QString, QgsSymbolV2*> pair = *it;
1008  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( pair.second, iconSize );
1009  lst << qMakePair( pair.first, pix );
1010  }
1011  return lst;
1012 }
1013 
1015 {
1016  return true;
1017 }
1018 
1020 {
1021  Rule* rule = mRootRule->findRuleByKey( key );
1022  return rule ? rule->active() : true;
1023 }
1024 
1026 {
1027  Rule* rule = mRootRule->findRuleByKey( key );
1028  if ( rule )
1029  rule->setActive( state );
1030 }
1031 
1033 {
1034  Rule* rule = mRootRule->findRuleByKey( key );
1035  if ( rule )
1036  rule->setSymbol( symbol );
1037  else
1038  delete symbol;
1039 }
1040 
1042 {
1043  return mRootRule->legendSymbolItems( scaleDenominator, rule );
1044 }
1045 
1047 {
1048  return mRootRule->legendSymbolItemsV2();
1049 }
1050 
1051 
1053 {
1054  // load symbols
1055  QDomElement symbolsElem = element.firstChildElement( "symbols" );
1056  if ( symbolsElem.isNull() )
1057  return nullptr;
1058 
1059  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1060 
1061  QDomElement rulesElem = element.firstChildElement( "rules" );
1062 
1063  Rule* root = Rule::create( rulesElem, symbolMap );
1064  if ( !root )
1065  return nullptr;
1066 
1068 
1069  // delete symbols if there are any more
1071 
1072  return r;
1073 }
1074 
1076 {
1077  // retrieve child rules
1078  Rule* root = nullptr;
1079 
1080  QDomElement ruleElem = element.firstChildElement( "Rule" );
1081  while ( !ruleElem.isNull() )
1082  {
1083  Rule *child = Rule::createFromSld( ruleElem, geomType );
1084  if ( child )
1085  {
1086  // create the root rule if not done before
1087  if ( !root )
1088  root = new Rule( nullptr );
1089 
1090  root->appendChild( child );
1091  }
1092 
1093  ruleElem = ruleElem.nextSiblingElement( "Rule" );
1094  }
1095 
1096  if ( !root )
1097  {
1098  // no valid rules was found
1099  return nullptr;
1100  }
1101 
1102  // create and return the new renderer
1103  return new QgsRuleBasedRendererV2( root );
1104 }
1105 
1108 
1110 {
1111  QString attr = r->classAttribute();
1112  // categorizedAttr could be either an attribute name or an expression.
1113  // the only way to differentiate is to test it as an expression...
1114  QgsExpression testExpr( attr );
1115  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1116  {
1117  //not an expression, so need to quote column name
1118  attr = QgsExpression::quotedColumnRef( attr );
1119  }
1120 
1121  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->categories() )
1122  {
1123  QString value;
1124  // not quoting numbers saves a type cast
1125  if ( cat.value().type() == QVariant::Int )
1126  value = cat.value().toString();
1127  else if ( cat.value().type() == QVariant::Double )
1128  // we loose precision here - so we may miss some categories :-(
1129  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1130  value = QString::number( cat.value().toDouble(), 'f', 4 );
1131  else
1132  value = QgsExpression::quotedString( cat.value().toString() );
1133  QString filter = QString( "%1 = %2" ).arg( attr, value );
1134  QString label = filter;
1135  initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1136  }
1137 }
1138 
1140 {
1141  QString attr = r->classAttribute();
1142  // categorizedAttr could be either an attribute name or an expression.
1143  // the only way to differentiate is to test it as an expression...
1144  QgsExpression testExpr( attr );
1145  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1146  {
1147  //not an expression, so need to quote column name
1148  attr = QgsExpression::quotedColumnRef( attr );
1149  }
1150  else if ( !testExpr.isField() )
1151  {
1152  //otherwise wrap expression in brackets
1153  attr = QString( "(%1)" ).arg( attr );
1154  }
1155 
1156  bool firstRange = true;
1157  Q_FOREACH ( const QgsRendererRangeV2& rng, r->ranges() )
1158  {
1159  // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1160  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1161  QString filter = QString( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? ">=" : ">",
1162  QString::number( rng.lowerValue(), 'f', 4 ),
1163  QString::number( rng.upperValue(), 'f', 4 ) );
1164  firstRange = false;
1165  QString label = filter;
1166  initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1167  }
1168 }
1169 
1171 {
1172  qSort( scales ); // make sure the scales are in ascending order
1173  int oldScale = initialRule->scaleMinDenom();
1174  int maxDenom = initialRule->scaleMaxDenom();
1175  QgsSymbolV2* symbol = initialRule->symbol();
1176  Q_FOREACH ( int scale, scales )
1177  {
1178  if ( initialRule->scaleMinDenom() >= scale )
1179  continue; // jump over the first scales out of the interval
1180  if ( maxDenom != 0 && maxDenom <= scale )
1181  break; // ignore the latter scales out of the interval
1182  initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1183  oldScale = scale;
1184  }
1185  // last rule
1186  initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1187 }
1188 
1190 {
1191  QString msg( "Rule-based renderer:\n" );
1192  msg += mRootRule->dump();
1193  return msg;
1194 }
1195 
1197 {
1198  return mRootRule->willRenderFeature( feat, &context );
1199 }
1200 
1202 {
1203  return mRootRule->symbolsForFeature( feat, &context );
1204 }
1205 
1207 {
1208  return mRootRule->symbolsForFeature( feat, &context );
1209 }
1210 
1212 {
1213  return mRootRule->legendKeysForFeature( feature, &context );
1214 }
1215 
1217 {
1218  if ( renderer->type() == "RuleRenderer" )
1219  {
1220  return dynamic_cast<QgsRuleBasedRendererV2*>( renderer->clone() );
1221  }
1222 
1223  if ( renderer->type() == "singleSymbol" )
1224  {
1225  const QgsSingleSymbolRendererV2* singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRendererV2*>( renderer );
1226  if ( !singleSymbolRenderer )
1227  return nullptr;
1228 
1229  QgsSymbolV2* origSymbol = singleSymbolRenderer->symbol()->clone();
1230  convertToDataDefinedSymbology( origSymbol, singleSymbolRenderer->sizeScaleField() );
1231  return new QgsRuleBasedRendererV2( origSymbol );
1232  }
1233 
1234  if ( renderer->type() == "categorizedSymbol" )
1235  {
1236  const QgsCategorizedSymbolRendererV2* categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRendererV2*>( renderer );
1237  if ( !categorizedRenderer )
1238  return nullptr;
1239 
1240  QString attr = categorizedRenderer->classAttribute();
1241  // categorizedAttr could be either an attribute name or an expression.
1242  // the only way to differentiate is to test it as an expression...
1243  QgsExpression testExpr( attr );
1244  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1245  {
1246  //not an expression, so need to quote column name
1247  attr = QgsExpression::quotedColumnRef( attr );
1248  }
1249 
1251 
1252  QString expression;
1253  QString value;
1254  QgsRendererCategoryV2 category;
1255  for ( int i = 0; i < categorizedRenderer->categories().size(); ++i )
1256  {
1257  category = categorizedRenderer->categories().value( i );
1259 
1260  rule->setLabel( category.label() );
1261 
1262  //We first define the rule corresponding to the category
1263  //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1264  if ( QVariant( category.value() ).convert( QVariant::Double ) )
1265  {
1266  value = category.value().toString();
1267  }
1268  else
1269  {
1270  value = QgsExpression::quotedString( category.value().toString() );
1271  }
1272 
1273  //An empty category is equivalent to the ELSE keyword
1274  if ( value == "''" )
1275  {
1276  expression = "ELSE";
1277  }
1278  else
1279  {
1280  expression = QString( "%1 = %2" ).arg( attr, value );
1281  }
1282  rule->setFilterExpression( expression );
1283 
1284  //Then we construct an equivalent symbol.
1285  //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1286  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1287 
1288  QgsSymbolV2* origSymbol = category.symbol()->clone();
1289  convertToDataDefinedSymbology( origSymbol, categorizedRenderer->sizeScaleField() );
1290  rule->setSymbol( origSymbol );
1291 
1292  rootrule->appendChild( rule );
1293  }
1294 
1295  return new QgsRuleBasedRendererV2( rootrule );
1296  }
1297 
1298  if ( renderer->type() == "graduatedSymbol" )
1299  {
1300  const QgsGraduatedSymbolRendererV2* graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRendererV2*>( renderer );
1301  if ( !graduatedRenderer )
1302  return nullptr;
1303 
1304  QString attr = graduatedRenderer->classAttribute();
1305  // categorizedAttr could be either an attribute name or an expression.
1306  // the only way to differentiate is to test it as an expression...
1307  QgsExpression testExpr( attr );
1308  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1309  {
1310  //not an expression, so need to quote column name
1311  attr = QgsExpression::quotedColumnRef( attr );
1312  }
1313  else if ( !testExpr.isField() )
1314  {
1315  //otherwise wrap expression in brackets
1316  attr = QString( "(%1)" ).arg( attr );
1317  }
1318 
1320 
1321  QString expression;
1322  QgsRendererRangeV2 range;
1323  for ( int i = 0; i < graduatedRenderer->ranges().size();++i )
1324  {
1325  range = graduatedRenderer->ranges().value( i );
1327  rule->setLabel( range.label() );
1328  if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1329  {
1330  expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1331  attr + " <= " + QString::number( range.upperValue(), 'f' );
1332  }
1333  else
1334  {
1335  expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1336  attr + " <= " + QString::number( range.upperValue(), 'f' );
1337  }
1338  rule->setFilterExpression( expression );
1339 
1340  //Then we construct an equivalent symbol.
1341  //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1342  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1343 
1344  QgsSymbolV2* symbol = range.symbol()->clone();
1345  convertToDataDefinedSymbology( symbol, graduatedRenderer->sizeScaleField() );
1346 
1347  rule->setSymbol( symbol );
1348 
1349  rootrule->appendChild( rule );
1350  }
1351 
1352  return new QgsRuleBasedRendererV2( rootrule );
1353  }
1354 
1355  if ( renderer->type() == "pointDisplacement" )
1356  {
1357  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1358  if ( pointDisplacementRenderer )
1359  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1360  }
1361  if ( renderer->type() == "invertedPolygonRenderer" )
1362  {
1363  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1364  if ( invertedPolygonRenderer )
1365  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1366  }
1367 
1368  return nullptr;
1369 }
1370 
1372 {
1373  QString sizeExpression;
1374  switch ( symbol->type() )
1375  {
1376  case QgsSymbolV2::Marker:
1377  for ( int j = 0; j < symbol->symbolLayerCount();++j )
1378  {
1379  QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( symbol->symbolLayer( j ) );
1380  if ( ! sizeScaleField.isEmpty() )
1381  {
1382  sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1383  msl->setDataDefinedProperty( "size", new QgsDataDefined( sizeExpression ) );
1384  }
1385  if ( ! rotationField.isEmpty() )
1386  {
1387  msl->setDataDefinedProperty( "angle", new QgsDataDefined( true, false, QString(), rotationField ) );
1388  }
1389  }
1390  break;
1391  case QgsSymbolV2::Line:
1392  if ( ! sizeScaleField.isEmpty() )
1393  {
1394  for ( int j = 0; j < symbol->symbolLayerCount();++j )
1395  {
1396  if ( symbol->symbolLayer( j )->layerType() == "SimpleLine" )
1397  {
1398  QgsLineSymbolLayerV2* lsl = static_cast<QgsLineSymbolLayerV2*>( symbol->symbolLayer( j ) );
1399  sizeExpression = QString( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1400  lsl->setDataDefinedProperty( "width", new QgsDataDefined( sizeExpression ) );
1401  }
1402  if ( symbol->symbolLayer( j )->layerType() == "MarkerLine" )
1403  {
1404  QgsSymbolV2* marker = symbol->symbolLayer( j )->subSymbol();
1405  for ( int k = 0; k < marker->symbolLayerCount();++k )
1406  {
1407  QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( marker->symbolLayer( k ) );
1408  sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1409  msl->setDataDefinedProperty( "size", new QgsDataDefined( sizeExpression ) );
1410  }
1411  }
1412  }
1413  }
1414  break;
1415  default:
1416  break;
1417  }
1418 }
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:111
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:104
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
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)
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
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:187
void removeChild(Rule *rule)
delete child rule
Line symbol.
Definition: qgssymbolv2.h:79
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:384
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
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:78
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:83
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
void startRender(QgsRenderContext &context, const QgsFields *fields=nullptr)
int toInt(bool *ok) const
QList< T > values() 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)
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:131
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
RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra...
QgsFeatureRendererV2 * embeddedRenderer() const
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.
const QgsFeatureRendererV2 * embeddedRenderer() const
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
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
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.