QGIS API Documentation  2.15.0-Master (13f053b)
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" )
826  , mRootRule( root )
827 {
828 }
829 
831  : QgsFeatureRendererV2( "RuleRenderer" )
832 {
833  mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
834  mRootRule->appendChild( new Rule( defaultSymbol ) );
835 }
836 
838 {
839  delete mRootRule;
840 }
841 
842 
844 {
845  // not used at all
846  return nullptr;
847 }
848 
850  QgsRenderContext& context,
851  int layer,
852  bool selected,
853  bool drawVertexMarker )
854 {
855  Q_UNUSED( layer );
856 
857  int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
858  mCurrentFeatures.append( FeatureToRender( feature, flags ) );
859 
860  // check each active rule
861  return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue ) == Rule::Rendered;
862 }
863 
864 
866 {
867  // prepare active children
868  mRootRule->startRender( context, fields, mFilter );
869 
870  QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
871  QList<int> symbolZLevels = symbolZLevelsSet.toList();
872  qSort( symbolZLevels );
873 
874  // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
875  // and prepare rendering queue
876  QMap<int, int> zLevelsToNormLevels;
877  int maxNormLevel = -1;
878  Q_FOREACH ( int zLevel, symbolZLevels )
879  {
880  zLevelsToNormLevels[zLevel] = ++maxNormLevel;
881  mRenderQueue.append( RenderLevel( zLevel ) );
882  QgsDebugMsg( QString( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ) );
883  }
884 
885  mRootRule->setNormZLevels( zLevelsToNormLevels );
886 }
887 
889 {
890  //
891  // do the actual rendering
892  //
893 
894  // go through all levels
895  Q_FOREACH ( const RenderLevel& level, mRenderQueue )
896  {
897  //QgsDebugMsg(QString("level %1").arg(level.zIndex));
898  // go through all jobs at the level
899  Q_FOREACH ( const RenderJob* job, level.jobs )
900  {
901  context.expressionContext().setFeature( job->ftr.feat );
902  //QgsDebugMsg(QString("job fid %1").arg(job->f->id()));
903  // render feature - but only with symbol layers with specified zIndex
904  QgsSymbolV2* s = job->symbol;
905  int count = s->symbolLayerCount();
906  for ( int i = 0; i < count; i++ )
907  {
908  // TODO: better solution for this
909  // renderFeatureWithSymbol asks which symbol layer to draw
910  // but there are multiple transforms going on!
911  if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
912  {
913  int flags = job->ftr.flags;
914  renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
915  }
916  }
917  }
918  }
919 
920  // clean current features
921  mCurrentFeatures.clear();
922 
923  // clean render queue
925 
926  // clean up rules from temporary stuff
927  mRootRule->stopRender( context );
928 }
929 
931 {
932  return mFilter;
933 }
934 
936 {
938  return attrs.values();
939 }
940 
942 {
944 
945  // normally with clone() the individual rules get new keys (UUID), but here we want to keep
946  // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. visibility presets)
947  clonedRoot->setRuleKey( mRootRule->ruleKey() );
948  RuleList origDescendants = mRootRule->descendants();
949  RuleList clonedDescendants = clonedRoot->descendants();
950  Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
951  for ( int i = 0; i < origDescendants.count(); ++i )
952  clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
953 
954  QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( clonedRoot );
955 
957  copyRendererData( r );
958  return r;
959 }
960 
962 {
963  mRootRule->toSld( doc, element, QgsStringMap() );
964 }
965 
966 // TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
968 {
969  return mRootRule->symbols( context );
970 }
971 
973 {
974  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
975  rendererElem.setAttribute( "type", "RuleRenderer" );
976  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
977  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
978 
980 
981  QDomElement rulesElem = mRootRule->save( doc, symbols );
982  rulesElem.setTagName( "rules" ); // instead of just "rule"
983  rendererElem.appendChild( rulesElem );
984 
985  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
986  rendererElem.appendChild( symbolsElem );
987 
989  mPaintEffect->saveProperties( doc, rendererElem );
990 
991  if ( !mOrderBy.isEmpty() )
992  {
993  QDomElement orderBy = doc.createElement( "orderby" );
994  mOrderBy.save( orderBy );
995  rendererElem.appendChild( orderBy );
996  }
997  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
998 
999  return rendererElem;
1000 }
1001 
1003 {
1006  for ( QgsLegendSymbolList::iterator it = items.begin(); it != items.end(); ++it )
1007  {
1008  QPair<QString, QgsSymbolV2*> pair = *it;
1009  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( pair.second, iconSize );
1010  lst << qMakePair( pair.first, pix );
1011  }
1012  return lst;
1013 }
1014 
1016 {
1017  return true;
1018 }
1019 
1021 {
1022  Rule* rule = mRootRule->findRuleByKey( key );
1023  return rule ? rule->active() : true;
1024 }
1025 
1027 {
1028  Rule* rule = mRootRule->findRuleByKey( key );
1029  if ( rule )
1030  rule->setActive( state );
1031 }
1032 
1034 {
1035  Rule* rule = mRootRule->findRuleByKey( key );
1036  if ( rule )
1037  rule->setSymbol( symbol );
1038  else
1039  delete symbol;
1040 }
1041 
1043 {
1044  return mRootRule->legendSymbolItems( scaleDenominator, rule );
1045 }
1046 
1048 {
1049  return mRootRule->legendSymbolItemsV2();
1050 }
1051 
1052 
1054 {
1055  // load symbols
1056  QDomElement symbolsElem = element.firstChildElement( "symbols" );
1057  if ( symbolsElem.isNull() )
1058  return nullptr;
1059 
1060  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1061 
1062  QDomElement rulesElem = element.firstChildElement( "rules" );
1063 
1064  Rule* root = Rule::create( rulesElem, symbolMap );
1065  if ( !root )
1066  return nullptr;
1067 
1069 
1070  // delete symbols if there are any more
1072 
1073  return r;
1074 }
1075 
1077 {
1078  // retrieve child rules
1079  Rule* root = nullptr;
1080 
1081  QDomElement ruleElem = element.firstChildElement( "Rule" );
1082  while ( !ruleElem.isNull() )
1083  {
1084  Rule *child = Rule::createFromSld( ruleElem, geomType );
1085  if ( child )
1086  {
1087  // create the root rule if not done before
1088  if ( !root )
1089  root = new Rule( nullptr );
1090 
1091  root->appendChild( child );
1092  }
1093 
1094  ruleElem = ruleElem.nextSiblingElement( "Rule" );
1095  }
1096 
1097  if ( !root )
1098  {
1099  // no valid rules was found
1100  return nullptr;
1101  }
1102 
1103  // create and return the new renderer
1104  return new QgsRuleBasedRendererV2( root );
1105 }
1106 
1109 
1111 {
1112  QString attr = r->classAttribute();
1113  // categorizedAttr could be either an attribute name or an expression.
1114  // the only way to differentiate is to test it as an expression...
1115  QgsExpression testExpr( attr );
1116  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1117  {
1118  //not an expression, so need to quote column name
1119  attr = QgsExpression::quotedColumnRef( attr );
1120  }
1121 
1122  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->categories() )
1123  {
1124  QString value;
1125  // not quoting numbers saves a type cast
1126  if ( cat.value().type() == QVariant::Int )
1127  value = cat.value().toString();
1128  else if ( cat.value().type() == QVariant::Double )
1129  // we loose precision here - so we may miss some categories :-(
1130  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1131  value = QString::number( cat.value().toDouble(), 'f', 4 );
1132  else
1133  value = QgsExpression::quotedString( cat.value().toString() );
1134  QString filter = QString( "%1 = %2" ).arg( attr, value );
1135  QString label = filter;
1136  initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1137  }
1138 }
1139 
1141 {
1142  QString attr = r->classAttribute();
1143  // categorizedAttr could be either an attribute name or an expression.
1144  // the only way to differentiate is to test it as an expression...
1145  QgsExpression testExpr( attr );
1146  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1147  {
1148  //not an expression, so need to quote column name
1149  attr = QgsExpression::quotedColumnRef( attr );
1150  }
1151  else if ( !testExpr.isField() )
1152  {
1153  //otherwise wrap expression in brackets
1154  attr = QString( "(%1)" ).arg( attr );
1155  }
1156 
1157  bool firstRange = true;
1158  Q_FOREACH ( const QgsRendererRangeV2& rng, r->ranges() )
1159  {
1160  // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1161  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1162  QString filter = QString( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? ">=" : ">",
1163  QString::number( rng.lowerValue(), 'f', 4 ),
1164  QString::number( rng.upperValue(), 'f', 4 ) );
1165  firstRange = false;
1166  QString label = filter;
1167  initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1168  }
1169 }
1170 
1172 {
1173  qSort( scales ); // make sure the scales are in ascending order
1174  int oldScale = initialRule->scaleMinDenom();
1175  int maxDenom = initialRule->scaleMaxDenom();
1176  QgsSymbolV2* symbol = initialRule->symbol();
1177  Q_FOREACH ( int scale, scales )
1178  {
1179  if ( initialRule->scaleMinDenom() >= scale )
1180  continue; // jump over the first scales out of the interval
1181  if ( maxDenom != 0 && maxDenom <= scale )
1182  break; // ignore the latter scales out of the interval
1183  initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1184  oldScale = scale;
1185  }
1186  // last rule
1187  initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1188 }
1189 
1191 {
1192  QString msg( "Rule-based renderer:\n" );
1193  msg += mRootRule->dump();
1194  return msg;
1195 }
1196 
1198 {
1199  return mRootRule->willRenderFeature( feat, &context );
1200 }
1201 
1203 {
1204  return mRootRule->symbolsForFeature( feat, &context );
1205 }
1206 
1208 {
1209  return mRootRule->symbolsForFeature( feat, &context );
1210 }
1211 
1213 {
1214  return mRootRule->legendKeysForFeature( feature, &context );
1215 }
1216 
1218 {
1219  if ( renderer->type() == "RuleRenderer" )
1220  {
1221  return dynamic_cast<QgsRuleBasedRendererV2*>( renderer->clone() );
1222  }
1223 
1224  if ( renderer->type() == "singleSymbol" )
1225  {
1226  const QgsSingleSymbolRendererV2* singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRendererV2*>( renderer );
1227  if ( !singleSymbolRenderer )
1228  return nullptr;
1229 
1230  QgsSymbolV2* origSymbol = singleSymbolRenderer->symbol()->clone();
1231  convertToDataDefinedSymbology( origSymbol, singleSymbolRenderer->sizeScaleField() );
1232  return new QgsRuleBasedRendererV2( origSymbol );
1233  }
1234 
1235  if ( renderer->type() == "categorizedSymbol" )
1236  {
1237  const QgsCategorizedSymbolRendererV2* categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRendererV2*>( renderer );
1238  if ( !categorizedRenderer )
1239  return nullptr;
1240 
1241  QString attr = categorizedRenderer->classAttribute();
1242  // categorizedAttr could be either an attribute name or an expression.
1243  // the only way to differentiate is to test it as an expression...
1244  QgsExpression testExpr( attr );
1245  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1246  {
1247  //not an expression, so need to quote column name
1248  attr = QgsExpression::quotedColumnRef( attr );
1249  }
1250 
1252 
1253  QString expression;
1254  QString value;
1255  QgsRendererCategoryV2 category;
1256  for ( int i = 0; i < categorizedRenderer->categories().size(); ++i )
1257  {
1258  category = categorizedRenderer->categories().value( i );
1260 
1261  rule->setLabel( category.label() );
1262 
1263  //We first define the rule corresponding to the category
1264  //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1265  if ( QVariant( category.value() ).convert( QVariant::Double ) )
1266  {
1267  value = category.value().toString();
1268  }
1269  else
1270  {
1271  value = QgsExpression::quotedString( category.value().toString() );
1272  }
1273 
1274  //An empty category is equivalent to the ELSE keyword
1275  if ( value == "''" )
1276  {
1277  expression = "ELSE";
1278  }
1279  else
1280  {
1281  expression = QString( "%1 = %2" ).arg( attr, value );
1282  }
1283  rule->setFilterExpression( expression );
1284 
1285  //Then we construct an equivalent symbol.
1286  //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1287  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1288 
1289  QgsSymbolV2* origSymbol = category.symbol()->clone();
1290  convertToDataDefinedSymbology( origSymbol, categorizedRenderer->sizeScaleField() );
1291  rule->setSymbol( origSymbol );
1292 
1293  rootrule->appendChild( rule );
1294  }
1295 
1296  return new QgsRuleBasedRendererV2( rootrule );
1297  }
1298 
1299  if ( renderer->type() == "graduatedSymbol" )
1300  {
1301  const QgsGraduatedSymbolRendererV2* graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRendererV2*>( renderer );
1302  if ( !graduatedRenderer )
1303  return nullptr;
1304 
1305  QString attr = graduatedRenderer->classAttribute();
1306  // categorizedAttr could be either an attribute name or an expression.
1307  // the only way to differentiate is to test it as an expression...
1308  QgsExpression testExpr( attr );
1309  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1310  {
1311  //not an expression, so need to quote column name
1312  attr = QgsExpression::quotedColumnRef( attr );
1313  }
1314  else if ( !testExpr.isField() )
1315  {
1316  //otherwise wrap expression in brackets
1317  attr = QString( "(%1)" ).arg( attr );
1318  }
1319 
1321 
1322  QString expression;
1323  QgsRendererRangeV2 range;
1324  for ( int i = 0; i < graduatedRenderer->ranges().size();++i )
1325  {
1326  range = graduatedRenderer->ranges().value( i );
1328  rule->setLabel( range.label() );
1329  if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1330  {
1331  expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1332  attr + " <= " + QString::number( range.upperValue(), 'f' );
1333  }
1334  else
1335  {
1336  expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1337  attr + " <= " + QString::number( range.upperValue(), 'f' );
1338  }
1339  rule->setFilterExpression( expression );
1340 
1341  //Then we construct an equivalent symbol.
1342  //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1343  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1344 
1345  QgsSymbolV2* symbol = range.symbol()->clone();
1346  convertToDataDefinedSymbology( symbol, graduatedRenderer->sizeScaleField() );
1347 
1348  rule->setSymbol( symbol );
1349 
1350  rootrule->appendChild( rule );
1351  }
1352 
1353  return new QgsRuleBasedRendererV2( rootrule );
1354  }
1355 
1356  if ( renderer->type() == "pointDisplacement" )
1357  {
1358  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1359  if ( pointDisplacementRenderer )
1360  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1361  }
1362  if ( renderer->type() == "invertedPolygonRenderer" )
1363  {
1364  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1365  if ( invertedPolygonRenderer )
1366  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1367  }
1368 
1369  return nullptr;
1370 }
1371 
1373 {
1374  QString sizeExpression;
1375  switch ( symbol->type() )
1376  {
1377  case QgsSymbolV2::Marker:
1378  for ( int j = 0; j < symbol->symbolLayerCount();++j )
1379  {
1380  QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( symbol->symbolLayer( j ) );
1381  if ( ! sizeScaleField.isEmpty() )
1382  {
1383  sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1384  msl->setDataDefinedProperty( "size", new QgsDataDefined( sizeExpression ) );
1385  }
1386  if ( ! rotationField.isEmpty() )
1387  {
1388  msl->setDataDefinedProperty( "angle", new QgsDataDefined( true, false, QString(), rotationField ) );
1389  }
1390  }
1391  break;
1392  case QgsSymbolV2::Line:
1393  if ( ! sizeScaleField.isEmpty() )
1394  {
1395  for ( int j = 0; j < symbol->symbolLayerCount();++j )
1396  {
1397  if ( symbol->symbolLayer( j )->layerType() == "SimpleLine" )
1398  {
1399  QgsLineSymbolLayerV2* lsl = static_cast<QgsLineSymbolLayerV2*>( symbol->symbolLayer( j ) );
1400  sizeExpression = QString( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1401  lsl->setDataDefinedProperty( "width", new QgsDataDefined( sizeExpression ) );
1402  }
1403  if ( symbol->symbolLayer( j )->layerType() == "MarkerLine" )
1404  {
1405  QgsSymbolV2* marker = symbol->symbolLayer( j )->subSymbol();
1406  for ( int k = 0; k < marker->symbolLayerCount();++k )
1407  {
1408  QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( marker->symbolLayer( k ) );
1409  sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1410  msl->setDataDefinedProperty( "size", new QgsDataDefined( sizeExpression ) );
1411  }
1412  }
1413  }
1414  }
1415  break;
1416  default:
1417  break;
1418  }
1419 }
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: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:193
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:481
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:352
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:86
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
double size() const
Returns the symbol size.
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
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
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.