QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsrulebasedrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrulebasedrendererv2.cpp - Rule-based renderer (symbology-ng)
3  ---------------------
4  begin : May 2010
5  copyright : (C) 2010 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsrulebasedrendererv2.h"
17 #include "qgssymbollayerv2.h"
18 #include "qgsexpression.h"
19 #include "qgssymbollayerv2utils.h"
20 #include "qgsrendercontext.h"
21 #include "qgsvectorlayer.h"
22 #include "qgslogger.h"
23 #include "qgsogcutils.h"
27 #include "qgspainteffect.h"
28 #include "qgspainteffectregistry.h"
29 #include "qgsdatadefined.h"
30 
31 #include <QSet>
32 
33 #include <QDomDocument>
34 #include <QDomElement>
35 #include <QUuid>
36 
37 
38 QgsRuleBasedRendererV2::Rule::Rule( QgsSymbolV2* symbol, int scaleMinDenom, int scaleMaxDenom, const QString& filterExp, const QString& label, const QString& description, bool elseRule )
39  : mParent( nullptr )
40  , mSymbol( symbol )
41  , mScaleMinDenom( scaleMinDenom )
42  , mScaleMaxDenom( scaleMaxDenom )
43  , mFilterExp( filterExp )
44  , mLabel( label )
45  , mDescription( description )
46  , mElseRule( elseRule )
47  , mIsActive( true )
48  , mFilter( nullptr )
49 {
50  if ( mElseRule )
51  mFilterExp = "ELSE";
52 
54  initFilter();
55 }
56 
58 {
59  delete mSymbol;
60  delete mFilter;
61  qDeleteAll( mChildren );
62  // do NOT delete parent
63 }
64 
66 {
67  if ( mFilterExp.trimmed().compare( "ELSE", Qt::CaseInsensitive ) == 0 )
68  {
69  mElseRule = true;
70  delete mFilter;
71  mFilter = nullptr;
72  }
73  else if ( mFilterExp.trimmed().isEmpty() )
74  {
75  mElseRule = false;
76  delete mFilter;
77  mFilter = nullptr;
78  }
79  else
80  {
81  mElseRule = false;
82  delete mFilter;
84  }
85 }
86 
88 {
89  mChildren.append( rule );
90  rule->mParent = this;
92 }
93 
95 {
96  mChildren.insert( i, rule );
97  rule->mParent = this;
99 }
100 
102 {
103  mChildren.removeAll( rule );
104  delete rule;
105  updateElseRules();
106 }
107 
109 {
110  delete mChildren.takeAt( i );
111  updateElseRules();
112 }
113 
115 {
116  mChildren.removeAll( rule );
117  rule->mParent = nullptr;
118  updateElseRules();
119  return rule;
120 }
121 
123 {
124  Rule* rule = mChildren.takeAt( i );
125  rule->mParent = nullptr;
126  updateElseRules();
127  return rule;
128 }
129 
131 {
132  // we could use a hash / map for search if this will be slow...
133 
134  if ( key == mRuleKey )
135  return this;
136 
137  Q_FOREACH ( Rule* rule, mChildren )
138  {
139  Rule* r = rule->findRuleByKey( key );
140  if ( r )
141  return r;
142  }
143  return nullptr;
144 }
145 
147 {
148  mElseRules.clear();
149  Q_FOREACH ( Rule* rule, mChildren )
150  {
151  if ( rule->isElse() )
152  mElseRules << rule;
153  }
154 }
155 
157 {
158  mFilterExp = "ELSE";
159  mElseRule = iselse;
160  delete mFilter;
161  mFilter = nullptr;
162 }
163 
164 
166 {
167  QString off;
168  off.fill( QChar( ' ' ), indent );
169  QString symbolDump = ( mSymbol ? mSymbol->dump() : QString( "[]" ) );
170  QString msg = off + QString( "RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
172  .arg( mFilterExp, symbolDump );
173 
174  QStringList lst;
175  Q_FOREACH ( Rule* rule, mChildren )
176  {
177  lst.append( rule->dump( indent + 2 ) );
178  }
179  msg += lst.join( "\n" );
180  return msg;
181 }
182 
184 {
185  // attributes needed by this rule
186  QSet<QString> attrs;
187  if ( mFilter )
188  attrs.unite( mFilter->referencedColumns().toSet() );
189  if ( mSymbol )
190  attrs.unite( mSymbol->usedAttributes() );
191 
192  // attributes needed by child rules
193  Q_FOREACH ( Rule* rule, mChildren )
194  {
195  attrs.unite( rule->usedAttributes() );
196  }
197  return attrs;
198 }
199 
201 {
202  if ( mFilter && mFilter->needsGeometry() )
203  return true;
204 
205  Q_FOREACH ( Rule* rule, mChildren )
206  {
207  if ( rule->needsGeometry() )
208  return true;
209  }
210 
211  return false;
212 }
213 
215 {
216  QgsSymbolV2List lst;
217  if ( mSymbol )
218  lst.append( mSymbol );
219 
220  Q_FOREACH ( Rule* rule, mChildren )
221  {
222  lst += rule->symbols( context );
223  }
224  return lst;
225 }
226 
228 {
229  delete mSymbol;
230  mSymbol = sym;
231 }
232 
234 {
235  mFilterExp = filterExp;
236  initFilter();
237 }
238 
239 QgsLegendSymbolList QgsRuleBasedRendererV2::Rule::legendSymbolItems( double scaleDenominator, const QString& ruleFilter ) const
240 {
242  if ( mSymbol && ( ruleFilter.isEmpty() || mLabel == ruleFilter ) )
243  lst << qMakePair( mLabel, mSymbol );
244 
245  Q_FOREACH ( Rule* rule, mChildren )
246  {
247  if ( qgsDoubleNear( scaleDenominator, -1 ) || rule->isScaleOK( scaleDenominator ) )
248  {
249  lst << rule->legendSymbolItems( scaleDenominator, ruleFilter );
250  }
251  }
252  return lst;
253 }
254 
256 {
258  if ( currentLevel != -1 ) // root rule should not be shown
259  {
261  }
262 
263  for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
264  {
265  Rule* rule = *it;
266  lst << rule->legendSymbolItemsV2( currentLevel + 1 );
267  }
268  return lst;
269 }
270 
271 
273 {
274  if ( ! mFilter || mElseRule )
275  return true;
276 
277  context->expressionContext().setFeature( f );
278  QVariant res = mFilter->evaluate( &context->expressionContext() );
279  return res.toInt() != 0;
280 }
281 
282 bool QgsRuleBasedRendererV2::Rule::isScaleOK( double scale ) const
283 {
284  if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
285  return true;
286  if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 )
287  return true;
288  if ( mScaleMinDenom != 0 && mScaleMinDenom > scale )
289  return false;
290  if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale )
291  return false;
292  return true;
293 }
294 
296 {
297  QgsSymbolV2* sym = mSymbol ? mSymbol->clone() : nullptr;
299  newrule->setActive( mIsActive );
300  // clone children
301  Q_FOREACH ( Rule* rule, mChildren )
302  newrule->appendChild( rule->clone() );
303  return newrule;
304 }
305 
307 {
308  QDomElement ruleElem = doc.createElement( "rule" );
309 
310  if ( mSymbol )
311  {
312  int symbolIndex = symbolMap.size();
313  symbolMap[QString::number( symbolIndex )] = mSymbol;
314  ruleElem.setAttribute( "symbol", symbolIndex );
315  }
316  if ( !mFilterExp.isEmpty() )
317  ruleElem.setAttribute( "filter", mFilterExp );
318  if ( mScaleMinDenom != 0 )
319  ruleElem.setAttribute( "scalemindenom", mScaleMinDenom );
320  if ( mScaleMaxDenom != 0 )
321  ruleElem.setAttribute( "scalemaxdenom", mScaleMaxDenom );
322  if ( !mLabel.isEmpty() )
323  ruleElem.setAttribute( "label", mLabel );
324  if ( !mDescription.isEmpty() )
325  ruleElem.setAttribute( "description", mDescription );
326  if ( !mIsActive )
327  ruleElem.setAttribute( "checkstate", 0 );
328  ruleElem.setAttribute( "key", mRuleKey );
329 
330  Q_FOREACH ( Rule* rule, mChildren )
331  {
332  ruleElem.appendChild( rule->save( doc, symbolMap ) );
333  }
334  return ruleElem;
335 }
336 
338 {
339  // do not convert this rule if there are no symbols
340  QgsRenderContext context;
341  if ( symbols( context ).isEmpty() )
342  return;
343 
344  QgsStringMap locProps( props );
345  if ( !mFilterExp.isEmpty() )
346  {
347  if ( !locProps.value( "filter", "" ).isEmpty() )
348  locProps[ "filter" ] += " AND ";
349  locProps[ "filter" ] += mFilterExp;
350  }
351 
353 
354  if ( mSymbol )
355  {
356  QDomElement ruleElem = doc.createElement( "se:Rule" );
357  element.appendChild( ruleElem );
358 
359  //XXX: <se:Name> is the rule identifier, but our the Rule objects
360  // have no properties could be used as identifier. Use the label.
361  QDomElement nameElem = doc.createElement( "se:Name" );
362  nameElem.appendChild( doc.createTextNode( mLabel ) );
363  ruleElem.appendChild( nameElem );
364 
365  if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
366  {
367  QDomElement descrElem = doc.createElement( "se:Description" );
368  if ( !mLabel.isEmpty() )
369  {
370  QDomElement titleElem = doc.createElement( "se:Title" );
371  titleElem.appendChild( doc.createTextNode( mLabel ) );
372  descrElem.appendChild( titleElem );
373  }
374  if ( !mDescription.isEmpty() )
375  {
376  QDomElement abstractElem = doc.createElement( "se:Abstract" );
377  abstractElem.appendChild( doc.createTextNode( mDescription ) );
378  descrElem.appendChild( abstractElem );
379  }
380  ruleElem.appendChild( descrElem );
381  }
382 
383  if ( !locProps.value( "filter", "" ).isEmpty() )
384  {
385  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, locProps.value( "filter", "" ) );
386  }
387 
388  QgsSymbolLayerV2Utils::applyScaleDependency( doc, ruleElem, locProps );
389 
390  mSymbol->toSld( doc, ruleElem, locProps );
391  }
392 
393  // loop into childern rule list
394  Q_FOREACH ( Rule* rule, mChildren )
395  {
396  rule->toSld( doc, element, locProps );
397  }
398 }
399 
401 {
402  QString filter;
403  return startRender( context, fields, filter );
404 }
405 
407 {
409 
410  if ( ! mIsActive )
411  return false;
412 
413  // filter out rules which are not compatible with this scale
414  if ( !isScaleOK( context.rendererScale() ) )
415  return false;
416 
417  // init this rule
418  if ( mFilter )
419  mFilter->prepare( &context.expressionContext() );
420  if ( mSymbol )
421  mSymbol->startRender( context, &fields );
422 
423  // init children
424  // build temporary list of active rules (usable with this scale)
425  QStringList subfilters;
426  Q_FOREACH ( Rule* rule, mChildren )
427  {
428  QString subfilter;
429  if ( rule->startRender( context, fields , subfilter ) )
430  {
431  // only add those which are active with current scale
432  mActiveChildren.append( rule );
433  subfilters.append( subfilter );
434  }
435  }
436 
437  // subfilters (on the same level) are joined with OR
438  // Finally they are joined with their parent (this) with AND
439  QString sf;
440  // If there are subfilters present (and it's not a single empty one), group them and join them with OR
441  if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
442  {
443  if ( subfilters.contains( "TRUE" ) )
444  sf = "TRUE";
445  else
446  sf = subfilters.join( ") OR (" ).prepend( '(' ).append( ')' );
447  }
448 
449  // Now join the subfilters with their parent (this) based on if
450  // * The parent is an else rule
451  // * The existence of parent filter and subfilters
452 
453  // No filter expression: ELSE rule or catchall rule
454  if ( !mFilter )
455  {
456  if ( mSymbol || sf.isEmpty() )
457  filter = "TRUE";
458  else
459  filter = sf;
460  }
461  else if ( mSymbol )
462  filter = mFilterExp;
463  else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
464  filter = QString( "(%1) AND (%2)" ).arg( mFilterExp, sf );
465  else if ( !mFilterExp.trimmed().isEmpty() )
466  filter = mFilterExp;
467  else if ( sf.isEmpty() )
468  filter = "TRUE";
469  else
470  filter = sf;
471 
472  filter = filter.trimmed();
473 
474  return true;
475 }
476 
478 {
479  QSet<int> symbolZLevelsSet;
480 
481  // process this rule
482  if ( mSymbol )
483  {
484  // find out which Z-levels are used
485  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
486  {
487  symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
488  }
489  }
490 
491  // process children
493  for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
494  {
495  Rule* rule = *it;
496  symbolZLevelsSet.unite( rule->collectZLevels() );
497  }
498  return symbolZLevelsSet;
499 }
500 
502 {
503  if ( mSymbol )
504  {
505  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
506  {
507  int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
508  mSymbolNormZLevels.insert( normLevel );
509  }
510  }
511 
512  // prepare list of normalized levels for each rule
513  Q_FOREACH ( Rule* rule, mActiveChildren )
514  {
515  rule->setNormZLevels( zLevelsToNormLevels );
516  }
517 }
518 
519 
521 {
522  if ( !isFilterOK( featToRender.feat, &context ) )
523  return Filtered;
524 
525  bool rendered = false;
526 
527  // create job for this feature and this symbol, add to list of jobs
528  if ( mSymbol && mIsActive )
529  {
530  // add job to the queue: each symbol's zLevel must be added
531  Q_FOREACH ( int normZLevel, mSymbolNormZLevels )
532  {
533  //QgsDebugMsg(QString("add job at level %1").arg(normZLevel));
534  renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol ) );
535  rendered = true;
536  }
537  }
538 
539  bool willrendersomething = false;
540 
541  // process children
542  Q_FOREACH ( Rule* rule, mChildren )
543  {
544  // Don't process else rules yet
545  if ( !rule->isElse() )
546  {
547  RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
548  // consider inactive items as "rendered" so the else rule will ignore them
549  willrendersomething |= ( res == Rendered || res == Inactive );
550  rendered |= ( res == Rendered );
551  }
552  }
553 
554  // If none of the rules passed then we jump into the else rules and process them.
555  if ( !willrendersomething )
556  {
557  Q_FOREACH ( Rule* rule, mElseRules )
558  {
559  rendered |= rule->renderFeature( featToRender, context, renderQueue ) == Rendered;
560  }
561  }
562  if ( !mIsActive || ( mSymbol && !rendered ) )
563  return Inactive;
564  else if ( rendered )
565  return Rendered;
566  else
567  return Filtered;
568 }
569 
571 {
572  if ( !isFilterOK( feat, context ) )
573  return false;
574  if ( mSymbol )
575  return true;
576 
577  Q_FOREACH ( Rule* rule, mActiveChildren )
578  {
579  if ( rule->willRenderFeature( feat, context ) )
580  return true;
581  }
582  return false;
583 }
584 
586 {
587  QgsSymbolV2List lst;
588  if ( !isFilterOK( feat, context ) )
589  return lst;
590  if ( mSymbol )
591  lst.append( mSymbol );
592 
593  Q_FOREACH ( Rule* rule, mActiveChildren )
594  {
595  lst += rule->symbolsForFeature( feat, context );
596  }
597  return lst;
598 }
599 
601 {
602  QSet< QString> lst;
603  if ( !isFilterOK( feat, context ) )
604  return lst;
605  lst.insert( mRuleKey );
606 
607  Q_FOREACH ( Rule* rule, mActiveChildren )
608  {
609  lst.unite( rule->legendKeysForFeature( feat, context ) );
610  }
611  return lst;
612 }
613 
615 {
616  RuleList lst;
617  if ( !isFilterOK( feat, context ) )
618  return lst;
619 
620  if ( mSymbol )
621  lst.append( this );
622 
623  Q_FOREACH ( Rule* rule, mActiveChildren )
624  {
625  lst += rule->rulesForFeature( feat, context );
626  }
627  return lst;
628 }
629 
631 {
632  if ( mSymbol )
633  mSymbol->stopRender( context );
634 
635  Q_FOREACH ( Rule* rule, mActiveChildren )
636  {
637  rule->stopRender( context );
638  }
639 
642 }
643 
645 {
646  QString symbolIdx = ruleElem.attribute( "symbol" );
647  QgsSymbolV2* symbol = nullptr;
648  if ( !symbolIdx.isEmpty() )
649  {
650  if ( symbolMap.contains( symbolIdx ) )
651  {
652  symbol = symbolMap.take( symbolIdx );
653  }
654  else
655  {
656  QgsDebugMsg( "symbol for rule " + symbolIdx + " not found!" );
657  }
658  }
659 
660  QString filterExp = ruleElem.attribute( "filter" );
661  QString label = ruleElem.attribute( "label" );
662  QString description = ruleElem.attribute( "description" );
663  int scaleMinDenom = ruleElem.attribute( "scalemindenom", "0" ).toInt();
664  int scaleMaxDenom = ruleElem.attribute( "scalemaxdenom", "0" ).toInt();
665  QString ruleKey = ruleElem.attribute( "key" );
666  Rule* rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
667 
668  if ( !ruleKey.isEmpty() )
669  rule->mRuleKey = ruleKey;
670 
671  rule->setActive( ruleElem.attribute( "checkstate", "1" ).toInt() );
672 
673  QDomElement childRuleElem = ruleElem.firstChildElement( "rule" );
674  while ( !childRuleElem.isNull() )
675  {
676  Rule* childRule = create( childRuleElem, symbolMap );
677  if ( childRule )
678  {
679  rule->appendChild( childRule );
680  }
681  else
682  {
683  QgsDebugMsg( "failed to init a child rule!" );
684  }
685  childRuleElem = childRuleElem.nextSiblingElement( "rule" );
686  }
687 
688  return rule;
689 }
690 
692 {
693  if ( ruleElem.localName() != "Rule" )
694  {
695  QgsDebugMsg( QString( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
696  return nullptr;
697  }
698 
699  QString label, description, filterExp;
700  int scaleMinDenom = 0, scaleMaxDenom = 0;
701  QgsSymbolLayerV2List layers;
702 
703  // retrieve the Rule element child nodes
704  QDomElement childElem = ruleElem.firstChildElement();
705  while ( !childElem.isNull() )
706  {
707  if ( childElem.localName() == "Name" )
708  {
709  // <se:Name> tag contains the rule identifier,
710  // so prefer title tag for the label property value
711  if ( label.isEmpty() )
712  label = childElem.firstChild().nodeValue();
713  }
714  else if ( childElem.localName() == "Description" )
715  {
716  // <se:Description> can contains a title and an abstract
717  QDomElement titleElem = childElem.firstChildElement( "Title" );
718  if ( !titleElem.isNull() )
719  {
720  label = titleElem.firstChild().nodeValue();
721  }
722 
723  QDomElement abstractElem = childElem.firstChildElement( "Abstract" );
724  if ( !abstractElem.isNull() )
725  {
726  description = abstractElem.firstChild().nodeValue();
727  }
728  }
729  else if ( childElem.localName() == "Abstract" )
730  {
731  // <sld:Abstract> (v1.0)
732  description = childElem.firstChild().nodeValue();
733  }
734  else if ( childElem.localName() == "Title" )
735  {
736  // <sld:Title> (v1.0)
737  label = childElem.firstChild().nodeValue();
738  }
739  else if ( childElem.localName() == "Filter" )
740  {
742  if ( filter )
743  {
744  if ( filter->hasParserError() )
745  {
746  QgsDebugMsg( "parser error: " + filter->parserErrorString() );
747  }
748  else
749  {
750  filterExp = filter->expression();
751  }
752  delete filter;
753  }
754  }
755  else if ( childElem.localName() == "MinScaleDenominator" )
756  {
757  bool ok;
758  int v = childElem.firstChild().nodeValue().toInt( &ok );
759  if ( ok )
760  scaleMinDenom = v;
761  }
762  else if ( childElem.localName() == "MaxScaleDenominator" )
763  {
764  bool ok;
765  int v = childElem.firstChild().nodeValue().toInt( &ok );
766  if ( ok )
767  scaleMaxDenom = v;
768  }
769  else if ( childElem.localName().endsWith( "Symbolizer" ) )
770  {
771  // create symbol layers for this symbolizer
772  QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
773  }
774 
775  childElem = childElem.nextSiblingElement();
776  }
777 
778  // now create the symbol
779  QgsSymbolV2 *symbol = nullptr;
780  if ( !layers.isEmpty() )
781  {
782  switch ( geomType )
783  {
784  case QGis::Line:
785  symbol = new QgsLineSymbolV2( layers );
786  break;
787 
788  case QGis::Polygon:
789  symbol = new QgsFillSymbolV2( layers );
790  break;
791 
792  case QGis::Point:
793  symbol = new QgsMarkerSymbolV2( layers );
794  break;
795 
796  default:
797  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
798  return nullptr;
799  }
800  }
801 
802  // and then create and return the new rule
803  return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
804 }
805 
806 
808 
810  : QgsFeatureRendererV2( "RuleRenderer" )
811  , mRootRule( root )
812 {
813 }
814 
816  : QgsFeatureRendererV2( "RuleRenderer" )
817 {
818  mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
819  mRootRule->appendChild( new Rule( defaultSymbol ) );
820 }
821 
823 {
824  delete mRootRule;
825 }
826 
827 
829 {
830  // not used at all
831  return nullptr;
832 }
833 
835  QgsRenderContext& context,
836  int layer,
837  bool selected,
838  bool drawVertexMarker )
839 {
840  Q_UNUSED( layer );
841 
842  int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
843  mCurrentFeatures.append( FeatureToRender( feature, flags ) );
844 
845  // check each active rule
846  return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue ) == Rule::Rendered;
847 }
848 
849 
851 {
852  // prepare active children
853  mRootRule->startRender( context, fields, mFilter );
854 
855  QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
856  QList<int> symbolZLevels = symbolZLevelsSet.toList();
857  qSort( symbolZLevels );
858 
859  // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
860  // and prepare rendering queue
861  QMap<int, int> zLevelsToNormLevels;
862  int maxNormLevel = -1;
863  Q_FOREACH ( int zLevel, symbolZLevels )
864  {
865  zLevelsToNormLevels[zLevel] = ++maxNormLevel;
866  mRenderQueue.append( RenderLevel( zLevel ) );
867  QgsDebugMsg( QString( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ) );
868  }
869 
870  mRootRule->setNormZLevels( zLevelsToNormLevels );
871 }
872 
874 {
875  //
876  // do the actual rendering
877  //
878 
879  // go through all levels
880  Q_FOREACH ( const RenderLevel& level, mRenderQueue )
881  {
882  //QgsDebugMsg(QString("level %1").arg(level.zIndex));
883  // go through all jobs at the level
884  Q_FOREACH ( const RenderJob* job, level.jobs )
885  {
886  context.expressionContext().setFeature( job->ftr.feat );
887  //QgsDebugMsg(QString("job fid %1").arg(job->f->id()));
888  // render feature - but only with symbol layers with specified zIndex
889  QgsSymbolV2* s = job->symbol;
890  int count = s->symbolLayerCount();
891  for ( int i = 0; i < count; i++ )
892  {
893  // TODO: better solution for this
894  // renderFeatureWithSymbol asks which symbol layer to draw
895  // but there are multiple transforms going on!
896  if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
897  {
898  int flags = job->ftr.flags;
899  renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
900  }
901  }
902  }
903  }
904 
905  // clean current features
906  mCurrentFeatures.clear();
907 
908  // clean render queue
910 
911  // clean up rules from temporary stuff
912  mRootRule->stopRender( context );
913 }
914 
916 {
917  return mFilter;
918 }
919 
921 {
923  return attrs.toList();
924 }
925 
927 {
928  return mRootRule->needsGeometry();
929 }
930 
932 {
934 
935  // normally with clone() the individual rules get new keys (UUID), but here we want to keep
936  // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. visibility presets)
937  clonedRoot->setRuleKey( mRootRule->ruleKey() );
938  RuleList origDescendants = mRootRule->descendants();
939  RuleList clonedDescendants = clonedRoot->descendants();
940  Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
941  for ( int i = 0; i < origDescendants.count(); ++i )
942  clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
943 
944  QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( clonedRoot );
945 
947  copyRendererData( r );
948  return r;
949 }
950 
952 {
953  toSld( doc, element, QgsStringMap() );
954 }
955 
956 void QgsRuleBasedRendererV2::toSld( QDomDocument& doc, QDomElement &element, const QgsStringMap& props ) const
957 {
958  mRootRule->toSld( doc, element, props );
959 }
960 
961 // TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
963 {
964  return mRootRule->symbols( context );
965 }
966 
968 {
969  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
970  rendererElem.setAttribute( "type", "RuleRenderer" );
971  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
972  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
973 
975 
976  QDomElement rulesElem = mRootRule->save( doc, symbols );
977  rulesElem.setTagName( "rules" ); // instead of just "rule"
978  rendererElem.appendChild( rulesElem );
979 
980  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
981  rendererElem.appendChild( symbolsElem );
982 
984  mPaintEffect->saveProperties( doc, rendererElem );
985 
986  if ( !mOrderBy.isEmpty() )
987  {
988  QDomElement orderBy = doc.createElement( "orderby" );
989  mOrderBy.save( orderBy );
990  rendererElem.appendChild( orderBy );
991  }
992  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
993 
994  return rendererElem;
995 }
996 
998 {
1001  for ( QgsLegendSymbolList::iterator it = items.begin(); it != items.end(); ++it )
1002  {
1003  QPair<QString, QgsSymbolV2*> pair = *it;
1004  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( pair.second, iconSize );
1005  lst << qMakePair( pair.first, pix );
1006  }
1007  return lst;
1008 }
1009 
1011 {
1012  return true;
1013 }
1014 
1016 {
1017  Rule* rule = mRootRule->findRuleByKey( key );
1018  return rule ? rule->active() : true;
1019 }
1020 
1022 {
1023  Rule* rule = mRootRule->findRuleByKey( key );
1024  if ( rule )
1025  rule->setActive( state );
1026 }
1027 
1029 {
1030  Rule* rule = mRootRule->findRuleByKey( key );
1031  if ( rule )
1032  rule->setSymbol( symbol );
1033  else
1034  delete symbol;
1035 }
1036 
1038 {
1039  return mRootRule->legendSymbolItems( scaleDenominator, rule );
1040 }
1041 
1043 {
1044  return mRootRule->legendSymbolItemsV2();
1045 }
1046 
1047 
1049 {
1050  // load symbols
1051  QDomElement symbolsElem = element.firstChildElement( "symbols" );
1052  if ( symbolsElem.isNull() )
1053  return nullptr;
1054 
1055  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1056 
1057  QDomElement rulesElem = element.firstChildElement( "rules" );
1058 
1059  Rule* root = Rule::create( rulesElem, symbolMap );
1060  if ( !root )
1061  return nullptr;
1062 
1064 
1065  // delete symbols if there are any more
1067 
1068  return r;
1069 }
1070 
1072 {
1073  // retrieve child rules
1074  Rule* root = nullptr;
1075 
1076  QDomElement ruleElem = element.firstChildElement( "Rule" );
1077  while ( !ruleElem.isNull() )
1078  {
1079  Rule *child = Rule::createFromSld( ruleElem, geomType );
1080  if ( child )
1081  {
1082  // create the root rule if not done before
1083  if ( !root )
1084  root = new Rule( nullptr );
1085 
1086  root->appendChild( child );
1087  }
1088 
1089  ruleElem = ruleElem.nextSiblingElement( "Rule" );
1090  }
1091 
1092  if ( !root )
1093  {
1094  // no valid rules was found
1095  return nullptr;
1096  }
1097 
1098  // create and return the new renderer
1099  return new QgsRuleBasedRendererV2( root );
1100 }
1101 
1104 
1106 {
1107  QString attr = r->classAttribute();
1108  // categorizedAttr could be either an attribute name or an expression.
1109  // the only way to differentiate is to test it as an expression...
1110  QgsExpression testExpr( attr );
1111  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1112  {
1113  //not an expression, so need to quote column name
1114  attr = QgsExpression::quotedColumnRef( attr );
1115  }
1116 
1117  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->categories() )
1118  {
1119  QString value;
1120  // not quoting numbers saves a type cast
1121  if ( cat.value().type() == QVariant::Int )
1122  value = cat.value().toString();
1123  else if ( cat.value().type() == QVariant::Double )
1124  // we loose precision here - so we may miss some categories :-(
1125  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1126  value = QString::number( cat.value().toDouble(), 'f', 4 );
1127  else
1128  value = QgsExpression::quotedString( cat.value().toString() );
1129  QString filter = QString( "%1 = %2" ).arg( attr, value );
1130  QString label = filter;
1131  initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1132  }
1133 }
1134 
1136 {
1137  QString attr = r->classAttribute();
1138  // categorizedAttr could be either an attribute name or an expression.
1139  // the only way to differentiate is to test it as an expression...
1140  QgsExpression testExpr( attr );
1141  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1142  {
1143  //not an expression, so need to quote column name
1144  attr = QgsExpression::quotedColumnRef( attr );
1145  }
1146  else if ( !testExpr.isField() )
1147  {
1148  //otherwise wrap expression in brackets
1149  attr = QString( "(%1)" ).arg( attr );
1150  }
1151 
1152  bool firstRange = true;
1153  Q_FOREACH ( const QgsRendererRangeV2& rng, r->ranges() )
1154  {
1155  // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1156  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1157  QString filter = QString( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? ">=" : ">",
1158  QString::number( rng.lowerValue(), 'f', 4 ),
1159  QString::number( rng.upperValue(), 'f', 4 ) );
1160  firstRange = false;
1161  QString label = filter;
1162  initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1163  }
1164 }
1165 
1167 {
1168  qSort( scales ); // make sure the scales are in ascending order
1169  int oldScale = initialRule->scaleMinDenom();
1170  int maxDenom = initialRule->scaleMaxDenom();
1171  QgsSymbolV2* symbol = initialRule->symbol();
1172  Q_FOREACH ( int scale, scales )
1173  {
1174  if ( initialRule->scaleMinDenom() >= scale )
1175  continue; // jump over the first scales out of the interval
1176  if ( maxDenom != 0 && maxDenom <= scale )
1177  break; // ignore the latter scales out of the interval
1178  initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1179  oldScale = scale;
1180  }
1181  // last rule
1182  initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1183 }
1184 
1186 {
1187  QString msg( "Rule-based renderer:\n" );
1188  msg += mRootRule->dump();
1189  return msg;
1190 }
1191 
1193 {
1194  return mRootRule->willRenderFeature( feat, &context );
1195 }
1196 
1198 {
1199  return mRootRule->symbolsForFeature( feat, &context );
1200 }
1201 
1203 {
1204  return mRootRule->symbolsForFeature( feat, &context );
1205 }
1206 
1208 {
1209  return mRootRule->legendKeysForFeature( feature, &context );
1210 }
1211 
1213 {
1214  QgsRuleBasedRendererV2* r = nullptr;
1215  if ( renderer->type() == "RuleRenderer" )
1216  {
1217  r = dynamic_cast<QgsRuleBasedRendererV2*>( renderer->clone() );
1218  }
1219  else if ( renderer->type() == "singleSymbol" )
1220  {
1221  const QgsSingleSymbolRendererV2* singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRendererV2*>( renderer );
1222  if ( !singleSymbolRenderer )
1223  return nullptr;
1224 
1225  QgsSymbolV2* origSymbol = singleSymbolRenderer->symbol()->clone();
1226  convertToDataDefinedSymbology( origSymbol, singleSymbolRenderer->sizeScaleField() );
1227  r = new QgsRuleBasedRendererV2( origSymbol );
1228  }
1229  else if ( renderer->type() == "categorizedSymbol" )
1230  {
1231  const QgsCategorizedSymbolRendererV2* categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRendererV2*>( renderer );
1232  if ( !categorizedRenderer )
1233  return nullptr;
1234 
1235  QString attr = categorizedRenderer->classAttribute();
1236  // categorizedAttr could be either an attribute name or an expression.
1237  // the only way to differentiate is to test it as an expression...
1238  QgsExpression testExpr( attr );
1239  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1240  {
1241  //not an expression, so need to quote column name
1242  attr = QgsExpression::quotedColumnRef( attr );
1243  }
1244 
1246 
1247  QString expression;
1248  QString value;
1249  QgsRendererCategoryV2 category;
1250  for ( int i = 0; i < categorizedRenderer->categories().size(); ++i )
1251  {
1252  category = categorizedRenderer->categories().value( i );
1254 
1255  rule->setLabel( category.label() );
1256 
1257  //We first define the rule corresponding to the category
1258  //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1259  if ( QVariant( category.value() ).convert( QVariant::Double ) )
1260  {
1261  value = category.value().toString();
1262  }
1263  else
1264  {
1265  value = QgsExpression::quotedString( category.value().toString() );
1266  }
1267 
1268  //An empty category is equivalent to the ELSE keyword
1269  if ( value == "''" )
1270  {
1271  expression = "ELSE";
1272  }
1273  else
1274  {
1275  expression = QString( "%1 = %2" ).arg( attr, value );
1276  }
1277  rule->setFilterExpression( expression );
1278 
1279  //Then we construct an equivalent symbol.
1280  //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1281  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1282 
1283  QgsSymbolV2* origSymbol = category.symbol()->clone();
1284  convertToDataDefinedSymbology( origSymbol, categorizedRenderer->sizeScaleField() );
1285  rule->setSymbol( origSymbol );
1286 
1287  rootrule->appendChild( rule );
1288  }
1289 
1290  r = new QgsRuleBasedRendererV2( rootrule );
1291  }
1292  else if ( renderer->type() == "graduatedSymbol" )
1293  {
1294  const QgsGraduatedSymbolRendererV2* graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRendererV2*>( renderer );
1295  if ( !graduatedRenderer )
1296  return nullptr;
1297 
1298  QString attr = graduatedRenderer->classAttribute();
1299  // categorizedAttr could be either an attribute name or an expression.
1300  // the only way to differentiate is to test it as an expression...
1301  QgsExpression testExpr( attr );
1302  if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1303  {
1304  //not an expression, so need to quote column name
1305  attr = QgsExpression::quotedColumnRef( attr );
1306  }
1307  else if ( !testExpr.isField() )
1308  {
1309  //otherwise wrap expression in brackets
1310  attr = QString( "(%1)" ).arg( attr );
1311  }
1312 
1314 
1315  QString expression;
1316  QgsRendererRangeV2 range;
1317  for ( int i = 0; i < graduatedRenderer->ranges().size();++i )
1318  {
1319  range = graduatedRenderer->ranges().value( i );
1321  rule->setLabel( range.label() );
1322  if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1323  {
1324  expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1325  attr + " <= " + QString::number( range.upperValue(), 'f' );
1326  }
1327  else
1328  {
1329  expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1330  attr + " <= " + QString::number( range.upperValue(), 'f' );
1331  }
1332  rule->setFilterExpression( expression );
1333 
1334  //Then we construct an equivalent symbol.
1335  //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1336  //data dependent area and rotation, so we need to convert these to obtain the same rendering
1337 
1338  QgsSymbolV2* symbol = range.symbol()->clone();
1339  convertToDataDefinedSymbology( symbol, graduatedRenderer->sizeScaleField() );
1340 
1341  rule->setSymbol( symbol );
1342 
1343  rootrule->appendChild( rule );
1344  }
1345 
1346  r = new QgsRuleBasedRendererV2( rootrule );
1347  }
1348  else if ( renderer->type() == "pointDisplacement" )
1349  {
1350  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1351  if ( pointDisplacementRenderer )
1352  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1353  }
1354  else if ( renderer->type() == "invertedPolygonRenderer" )
1355  {
1356  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1357  if ( invertedPolygonRenderer )
1358  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1359  }
1360 
1361  if ( r )
1362  {
1363  r->setOrderBy( renderer->orderBy() );
1364  r->setOrderByEnabled( renderer->orderByEnabled() );
1365  }
1366 
1367  return r;
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 }
const QgsCategoryList & categories() const
bool active() const
Returns if this rule is active.
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
bool needsGeometry() const
Returns true if this rule or one of its chilren needs the geometry to be applied. ...
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsExpression * filter() const
A filter that will check if this rule applies.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
void setLabel(const QString &label)
void clear()
QgsSymbolV2List symbols(const QgsRenderContext &context=QgsRenderContext()) const
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 ...
virtual Q_DECL_DEPRECATED QString rotationField() const
return rotation field name (or empty string if not set or not supported by renderer) ...
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:49
double rendererScale() const
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...
QString dump() const
A container class for data source field mapping or expression.
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.
QString & fill(QChar ch, int size)
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, const QgsStringMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
QSet< int > collectZLevels()
get all used z-levels from this rule and children
QDomNode appendChild(const QDomNode &newChild)
QgsLegendSymbolListV2 legendSymbolItemsV2(int currentLevel=-1) const
static QgsFeatureRendererV2 * create(QDomElement &element)
virtual QSet< QString > legendKeysForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return legend keys matching a specified 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
double size() const
Returns the symbol size.
void setActive(bool state)
Sets if this rule is active.
QStringList referencedColumns() const
Get list of columns referenced by the expression.
QString & prepend(QChar ch)
virtual QgsSymbolV2 * clone() const =0
static QgsFeatureRendererV2 * createFromSld(QDomElement &element, QGis::GeometryType geomType)
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
bool contains(const QString &str, Qt::CaseSensitivity cs) const
virtual bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
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)
QDomElement save(QDomDocument &doc, QgsSymbolV2Map &symbolMap) const
Container of fields for a vector layer.
Definition: qgsfield.h:252
void removeChild(Rule *rule)
delete child rule
SymbolType type() const
Definition: qgssymbolv2.h:107
Line symbol.
Definition: qgssymbolv2.h:82
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
static void mergeScaleDependencies(int mScaleMinDenom, int mScaleMaxDenom, QgsStringMap &props)
Merges the local scale limits, if any, with the ones already in the map, if any.
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.
QString parserErrorString() const
Returns parser error.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:492
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:353
QgsPaintEffect * mPaintEffect
Rule * takeChild(Rule *rule)
take child rule out, set parent as null
void removeChildAt(int i)
delete child rule
Marker symbol.
Definition: qgssymbolv2.h:81
int size() const
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the SLD element following the SLD v1.1 specs.
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
QString description() const
A human readable description for this rule.
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)
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)
int renderingPass() const
virtual double width() const
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 append(const T &value)
static void refineRuleCategories(Rule *initialRule, QgsCategorizedSymbolRendererV2 *r)
take a rule and create a list of new rules based on the categories from categorized symbol renderer ...
QString localName() const
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
void startRender(QgsRenderContext &context, const QgsFields *fields=nullptr)
int toInt(bool *ok) const
virtual void stopRender(QgsRenderContext &context) override
Needs to be called when a render cycle has finished to clean up.
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
const QgsRangeList & ranges() const
QSet< QString > usedAttributes() const
Return the attributes used to evaluate the expression of this rule.
void setAttribute(const QString &name, const QString &value)
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
bool isFilterOK(QgsFeature &f, QgsRenderContext *context=nullptr) const
Check if a given feature shall be rendered by this rule.
int toInt(bool *ok, int base) const
bool isEmpty() const
void appendChild(Rule *rule)
add child rule, take ownership, sets this as parent
QString type() const
Definition: qgsrendererv2.h:92
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...
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
QSet< QString > legendKeysForFeature(QgsFeature &feat, QgsRenderContext *context=nullptr)
Returns which legend keys match the feature.
int symbolLayerCount()
Returns total number of symbol layers contained in the symbol.
Definition: qgssymbolv2.h:134
This class keeps data about a rules for rule-based renderer.
void setRuleKey(const QString &key)
Override the assigned rule key (should be used just internally by rule-based renderer) ...
virtual void toSld(QDomDocument &doc, QDomElement &element) const override
Writes the SLD element following the SLD v1.1 specs.
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
Rule * clone() const
clone this rule, return new instance
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize) override
return a list of symbology items for the legend
RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra...
bool willRenderFeature(QgsFeature &feat, QgsRenderContext *context=nullptr)
only tell whether a feature will be rendered without actually rendering it
bool usingSymbolLevels() const
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.
QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, const QString &rule="") const
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
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.
virtual QString layerType() const =0
Returns a string that represents this layer type.
QString expression() const
Return the original, unmodified expression string.
QgsExpressionContext & expressionContext()
Gets the expression context.
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
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
void copyRendererData(QgsFeatureRendererV2 *destRenderer) const
Clones generic renderer data to another renderer.
Contains information about the context of a rendering operation.
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Creates a DOM element representing the rule in SLD format.
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...
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
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)
QString ruleKey() const
Unique rule identifier (for identification of rule within renderer)
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)
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
static QgsExpression * expressionFromOgcFilter(const QDomElement &element)
Parse XML with OGC filter into QGIS expression.
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
bool isField() const
Checks whether an expression consists only of a single field reference.
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.
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.
QString tagName() const
QgsSymbolLayerV2 * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
const_iterator constEnd() 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
QSet< QString > usedAttributes() const
Return a list of attributes required to render this feature.
Type type() const
int compare(const QString &other) const
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
QString toString() const
iterator begin()
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, const QString &rule="") override
return a list of item text / symbol
T take(const Key &key)
int size() const
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QUuid createUuid()
QString dump(int indent=0) const
Dump for debug purpose.
QList< FeatureToRender > mCurrentFeatures
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.
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
bool isScaleOK(double scale) const
Check if this rule applies for a given scale.