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