QGIS API Documentation  2.13.0-Master
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 
997  return rendererElem;
998 }
999 
1001 {
1004  for ( QgsLegendSymbolList::iterator it = items.begin(); it != items.end(); ++it )
1005  {
1006  QPair<QString, QgsSymbolV2*> pair = *it;
1007  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( pair.second, iconSize );
1008  lst << qMakePair( pair.first, pix );
1009  }
1010  return lst;
1011 }
1012 
1014 {
1015  return true;
1016 }
1017 
1019 {
1020  Rule* rule = mRootRule->findRuleByKey( key );
1021  return rule ? rule->active() : true;
1022 }
1023 
1025 {
1026  Rule* rule = mRootRule->findRuleByKey( key );
1027  if ( rule )
1028  rule->setActive( state );
1029 }
1030 
1032 {
1033  Rule* rule = mRootRule->findRuleByKey( key );
1034  if ( rule )
1035  rule->setSymbol( symbol );
1036  else
1037  delete symbol;
1038 }
1039 
1041 {
1042  return mRootRule->legendSymbolItems( scaleDenominator, rule );
1043 }
1044 
1046 {
1047  return mRootRule->legendSymbolItemsV2();
1048 }
1049 
1050 
1052 {
1053  // load symbols
1054  QDomElement symbolsElem = element.firstChildElement( "symbols" );
1055  if ( symbolsElem.isNull() )
1056  return nullptr;
1057 
1058  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1059 
1060  QDomElement rulesElem = element.firstChildElement( "rules" );
1061 
1062  Rule* root = Rule::create( rulesElem, symbolMap );
1063  if ( !root )
1064  return nullptr;
1065 
1067 
1068  // delete symbols if there are any more
1070 
1071  return r;
1072 }
1073 
1075 {
1076  // retrieve child rules
1077  Rule* root = nullptr;
1078 
1079  QDomElement ruleElem = element.firstChildElement( "Rule" );
1080  while ( !ruleElem.isNull() )
1081  {
1082  Rule *child = Rule::createFromSld( ruleElem, geomType );
1083  if ( child )
1084  {
1085  // create the root rule if not done before
1086  if ( !root )
1087  root = new Rule( nullptr );
1088 
1089  root->appendChild( child );
1090  }
1091 
1092  ruleElem = ruleElem.nextSiblingElement( "Rule" );
1093  }
1094 
1095  if ( !root )
1096  {
1097  // no valid rules was found
1098  return nullptr;
1099  }
1100 
1101  // create and return the new renderer
1102  return new QgsRuleBasedRendererV2( root );
1103 }
1104 
1107 
1109 {
1110  QString attr = r->classAttribute();
1111  // categorizedAttr could be either an attribute name or an expression.
1112  // the only way to differentiate is to test it as an expression...
1113  QgsExpression testExpr( attr );
1114  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1115  {
1116  //not an expression, so need to quote column name
1117  attr = QgsExpression::quotedColumnRef( attr );
1118  }
1119 
1120  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->categories() )
1121  {
1122  QString value;
1123  // not quoting numbers saves a type cast
1124  if ( cat.value().type() == QVariant::Int )
1125  value = cat.value().toString();
1126  else if ( cat.value().type() == QVariant::Double )
1127  // we loose precision here - so we may miss some categories :-(
1128  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1129  value = QString::number( cat.value().toDouble(), 'f', 4 );
1130  else
1131  value = QgsExpression::quotedString( cat.value().toString() );
1132  QString filter = QString( "%1 = %2" ).arg( attr, value );
1133  QString label = filter;
1134  initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1135  }
1136 }
1137 
1139 {
1140  QString attr = r->classAttribute();
1141  // categorizedAttr could be either an attribute name or an expression.
1142  // the only way to differentiate is to test it as an expression...
1143  QgsExpression testExpr( attr );
1144  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1145  {
1146  //not an expression, so need to quote column name
1147  attr = QgsExpression::quotedColumnRef( attr );
1148  }
1149  else if ( !testExpr.isField() )
1150  {
1151  //otherwise wrap expression in brackets
1152  attr = QString( "(%1)" ).arg( attr );
1153  }
1154 
1155  bool firstRange = true;
1156  Q_FOREACH ( const QgsRendererRangeV2& rng, r->ranges() )
1157  {
1158  // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1159  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1160  QString filter = QString( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? ">=" : ">",
1161  QString::number( rng.lowerValue(), 'f', 4 ),
1162  QString::number( rng.upperValue(), 'f', 4 ) );
1163  firstRange = false;
1164  QString label = filter;
1165  initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1166  }
1167 }
1168 
1170 {
1171  qSort( scales ); // make sure the scales are in ascending order
1172  int oldScale = initialRule->scaleMinDenom();
1173  int maxDenom = initialRule->scaleMaxDenom();
1174  QgsSymbolV2* symbol = initialRule->symbol();
1175  Q_FOREACH ( int scale, scales )
1176  {
1177  if ( initialRule->scaleMinDenom() >= scale )
1178  continue; // jump over the first scales out of the interval
1179  if ( maxDenom != 0 && maxDenom <= scale )
1180  break; // ignore the latter scales out of the interval
1181  initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1182  oldScale = scale;
1183  }
1184  // last rule
1185  initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1186 }
1187 
1189 {
1190  QString msg( "Rule-based renderer:\n" );
1191  msg += mRootRule->dump();
1192  return msg;
1193 }
1194 
1196 {
1197  return mRootRule->willRenderFeature( feat, &context );
1198 }
1199 
1201 {
1202  return mRootRule->symbolsForFeature( feat, &context );
1203 }
1204 
1206 {
1207  return mRootRule->symbolsForFeature( feat, &context );
1208 }
1209 
1211 {
1212  return mRootRule->legendKeysForFeature( feature, &context );
1213 }
1214 
1216 {
1217  if ( renderer->type() == "RuleRenderer" )
1218  {
1219  return dynamic_cast<QgsRuleBasedRendererV2*>( renderer->clone() );
1220  }
1221 
1222  if ( renderer->type() == "singleSymbol" )
1223  {
1224  const QgsSingleSymbolRendererV2* singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRendererV2*>( renderer );
1225  if ( !singleSymbolRenderer )
1226  return nullptr;
1227 
1228  QgsSymbolV2* origSymbol = singleSymbolRenderer->symbol()->clone();
1229  convertToDataDefinedSymbology( origSymbol, singleSymbolRenderer->sizeScaleField() );
1230  return new QgsRuleBasedRendererV2( origSymbol );
1231  }
1232 
1233  if ( renderer->type() == "categorizedSymbol" )
1234  {
1235  const QgsCategorizedSymbolRendererV2* categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRendererV2*>( renderer );
1236  if ( !categorizedRenderer )
1237  return nullptr;
1238 
1239  QString attr = categorizedRenderer->classAttribute();
1240  // categorizedAttr could be either an attribute name or an expression.
1241  // the only way to differentiate is to test it as an expression...
1242  QgsExpression testExpr( attr );
1243  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1244  {
1245  //not an expression, so need to quote column name
1246  attr = QgsExpression::quotedColumnRef( attr );
1247  }
1248 
1250 
1251  QString expression;
1252  QString value;
1253  QgsRendererCategoryV2 category;
1254  for ( int i = 0; i < categorizedRenderer->categories().size(); ++i )
1255  {
1256  category = categorizedRenderer->categories().value( i );
1258 
1259  rule->setLabel( category.label() );
1260 
1261  //We first define the rule corresponding to the category
1262  //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1263  if ( QVariant( category.value() ).convert( QVariant::Double ) )
1264  {
1265  value = category.value().toString();
1266  }
1267  else
1268  {
1269  value = QgsExpression::quotedString( category.value().toString() );
1270  }
1271 
1272  //An empty category is equivalent to the ELSE keyword
1273  if ( value == "''" )
1274  {
1275  expression = "ELSE";
1276  }
1277  else
1278  {
1279  expression = QString( "%1 = %2" ).arg( attr, value );
1280  }
1281  rule->setFilterExpression( expression );
1282 
1283  //Then we construct an equivalent symbol.
1284  //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1285  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1286 
1287  QgsSymbolV2* origSymbol = category.symbol()->clone();
1288  convertToDataDefinedSymbology( origSymbol, categorizedRenderer->sizeScaleField() );
1289  rule->setSymbol( origSymbol );
1290 
1291  rootrule->appendChild( rule );
1292  }
1293 
1294  return new QgsRuleBasedRendererV2( rootrule );
1295  }
1296 
1297  if ( renderer->type() == "graduatedSymbol" )
1298  {
1299  const QgsGraduatedSymbolRendererV2* graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRendererV2*>( renderer );
1300  if ( !graduatedRenderer )
1301  return nullptr;
1302 
1303  QString attr = graduatedRenderer->classAttribute();
1304  // categorizedAttr could be either an attribute name or an expression.
1305  // the only way to differentiate is to test it as an expression...
1306  QgsExpression testExpr( attr );
1307  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1308  {
1309  //not an expression, so need to quote column name
1310  attr = QgsExpression::quotedColumnRef( attr );
1311  }
1312  else if ( !testExpr.isField() )
1313  {
1314  //otherwise wrap expression in brackets
1315  attr = QString( "(%1)" ).arg( attr );
1316  }
1317 
1319 
1320  QString expression;
1321  QgsRendererRangeV2 range;
1322  for ( int i = 0; i < graduatedRenderer->ranges().size();++i )
1323  {
1324  range = graduatedRenderer->ranges().value( i );
1326  rule->setLabel( range.label() );
1327  if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1328  {
1329  expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1330  attr + " <= " + QString::number( range.upperValue(), 'f' );
1331  }
1332  else
1333  {
1334  expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1335  attr + " <= " + QString::number( range.upperValue(), 'f' );
1336  }
1337  rule->setFilterExpression( expression );
1338 
1339  //Then we construct an equivalent symbol.
1340  //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1341  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1342 
1343  QgsSymbolV2* symbol = range.symbol()->clone();
1344  convertToDataDefinedSymbology( symbol, graduatedRenderer->sizeScaleField() );
1345 
1346  rule->setSymbol( symbol );
1347 
1348  rootrule->appendChild( rule );
1349  }
1350 
1351  return new QgsRuleBasedRendererV2( rootrule );
1352  }
1353 
1354  if ( renderer->type() == "pointDisplacement" )
1355  {
1356  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1357  if ( pointDisplacementRenderer )
1358  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1359  }
1360  if ( renderer->type() == "invertedPolygonRenderer" )
1361  {
1362  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1363  if ( invertedPolygonRenderer )
1364  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1365  }
1366 
1367  return nullptr;
1368 }
1369 
1371 {
1372  QString sizeExpression;
1373  switch ( symbol->type() )
1374  {
1375  case QgsSymbolV2::Marker:
1376  for ( int j = 0; j < symbol->symbolLayerCount();++j )
1377  {
1378  QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( symbol->symbolLayer( j ) );
1379  if ( ! sizeScaleField.isEmpty() )
1380  {
1381  sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1382  msl->setDataDefinedProperty( "size", new QgsDataDefined( sizeExpression ) );
1383  }
1384  if ( ! rotationField.isEmpty() )
1385  {
1386  msl->setDataDefinedProperty( "angle", new QgsDataDefined( true, false, QString(), rotationField ) );
1387  }
1388  }
1389  break;
1390  case QgsSymbolV2::Line:
1391  if ( ! sizeScaleField.isEmpty() )
1392  {
1393  for ( int j = 0; j < symbol->symbolLayerCount();++j )
1394  {
1395  if ( symbol->symbolLayer( j )->layerType() == "SimpleLine" )
1396  {
1397  QgsLineSymbolLayerV2* lsl = static_cast<QgsLineSymbolLayerV2*>( symbol->symbolLayer( j ) );
1398  sizeExpression = QString( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1399  lsl->setDataDefinedProperty( "width", new QgsDataDefined( sizeExpression ) );
1400  }
1401  if ( symbol->symbolLayer( j )->layerType() == "MarkerLine" )
1402  {
1403  QgsSymbolV2* marker = symbol->symbolLayer( j )->subSymbol();
1404  for ( int k = 0; k < marker->symbolLayerCount();++k )
1405  {
1406  QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( marker->symbolLayer( k ) );
1407  sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1408  msl->setDataDefinedProperty( "size", new QgsDataDefined( sizeExpression ) );
1409  }
1410  }
1411  }
1412  }
1413  break;
1414  default:
1415  break;
1416  }
1417 }
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:101
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:189
void removeChild(Rule *rule)
delete child rule
Line symbol.
Definition: qgssymbolv2.h:76
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:75
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:128
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.