QGIS API Documentation  2.4.0-Chugiak
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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"
24 
25 #include <QSet>
26 
27 #include <QDomDocument>
28 #include <QDomElement>
29 
30 
31 
32 QgsRuleBasedRendererV2::Rule::Rule( QgsSymbolV2* symbol, int scaleMinDenom, int scaleMaxDenom, QString filterExp, QString label, QString description , bool elseRule )
33  : mParent( NULL ), mSymbol( symbol )
34  , mScaleMinDenom( scaleMinDenom ), mScaleMaxDenom( scaleMaxDenom )
35  , mFilterExp( filterExp ), mLabel( label ), mDescription( description )
36  , mElseRule( elseRule ), mFilter( NULL )
37 {
38  initFilter();
39 }
40 
42 {
43  delete mSymbol;
44  delete mFilter;
45  qDeleteAll( mChildren );
46  // do NOT delete parent
47 }
48 
50 {
51  if ( mElseRule || mFilterExp.compare( "ELSE", Qt::CaseInsensitive ) == 0 )
52  {
53  mElseRule = true;
54  mFilter = NULL;
55  }
56  else if ( !mFilterExp.isEmpty() )
57  {
58  delete mFilter;
59  mFilter = new QgsExpression( mFilterExp );
60  }
61  else
62  {
63  mFilter = NULL;
64  }
65 }
66 
68 {
69  mChildren.append( rule );
70  rule->mParent = this;
71  updateElseRules();
72 }
73 
75 {
76  mChildren.insert( i, rule );
77  rule->mParent = this;
78  updateElseRules();
79 }
80 
82 {
83  mChildren.removeAll( rule );
84  delete rule;
85  updateElseRules();
86 }
87 
89 {
90  Rule* rule = mChildren[i];
91  mChildren.removeAt( i );
92  delete rule;
93  updateElseRules();
94 }
95 
97 {
98  mChildren.removeAll( rule );
99  rule->mParent = NULL;
100  updateElseRules();
101 }
102 
104 {
105  Rule* rule = mChildren.takeAt( i );
106  rule->mParent = NULL;
107  return rule;
108  // updateElseRules();
109 }
110 
112 {
113  mElseRules.clear();
114  foreach ( Rule* rule, mChildren )
115  {
116  if ( rule->isElse() )
117  mElseRules << rule;
118  }
119 }
120 
121 
122 QString QgsRuleBasedRendererV2::Rule::dump( int offset ) const
123 {
124  QString off;
125  off.fill( QChar( ' ' ), offset );
126  QString symbolDump = ( mSymbol ? mSymbol->dump() : QString( "[]" ) );
127  QString msg = off + QString( "RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
128  .arg( mLabel ).arg( mScaleMinDenom ).arg( mScaleMaxDenom )
129  .arg( mFilterExp ).arg( symbolDump );
130 
131  QStringList lst;
132  foreach ( Rule* rule, mChildren )
133  {
134  lst.append( rule->dump( offset + 2 ) );
135  }
136  msg += lst.join( "\n" );
137  return msg;
138 }
139 
141 {
142  // attributes needed by this rule
143  QSet<QString> attrs;
144  if ( mFilter )
145  attrs.unite( mFilter->referencedColumns().toSet() );
146  if ( mSymbol )
147  attrs.unite( mSymbol->usedAttributes() );
148 
149  // attributes needed by child rules
150  for ( RuleList::iterator it = mChildren.begin(); it != mChildren.end(); ++it )
151  {
152  Rule* rule = *it;
153  attrs.unite( rule->usedAttributes() );
154  }
155  return attrs;
156 }
157 
159 {
160  QgsSymbolV2List lst;
161  if ( mSymbol )
162  lst.append( mSymbol );
163 
164  for ( RuleList::iterator it = mChildren.begin(); it != mChildren.end(); ++it )
165  {
166  Rule* rule = *it;
167  lst += rule->symbols();
168  }
169  return lst;
170 }
171 
173 {
174  delete mSymbol;
175  mSymbol = sym;
176 }
177 
178 QgsLegendSymbolList QgsRuleBasedRendererV2::Rule::legendSymbolItems( double scaleDenominator, QString ruleFilter )
179 {
181  if ( mSymbol && ( ruleFilter.isEmpty() || mLabel == ruleFilter ) )
182  lst << qMakePair( mLabel, mSymbol );
183 
184  for ( RuleList::iterator it = mChildren.begin(); it != mChildren.end(); ++it )
185  {
186  Rule* rule = *it;
187  if ( scaleDenominator == -1 || rule->isScaleOK( scaleDenominator ) )
188  {
189  lst << rule->legendSymbolItems( scaleDenominator, ruleFilter );
190  }
191  }
192  return lst;
193 }
194 
195 
197 {
198  if ( ! mFilter || mElseRule )
199  return true;
200 
201  QVariant res = mFilter->evaluate( &f );
202  return res.toInt() != 0;
203 }
204 
205 bool QgsRuleBasedRendererV2::Rule::isScaleOK( double scale ) const
206 {
207  if ( scale == 0 ) // so that we can count features in classes without scale context
208  return true;
209  if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 )
210  return true;
211  if ( mScaleMinDenom != 0 && mScaleMinDenom > scale )
212  return false;
213  if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale )
214  return false;
215  return true;
216 }
217 
219 {
220  QgsSymbolV2* sym = mSymbol ? mSymbol->clone() : NULL;
221  Rule* newrule = new Rule( sym, mScaleMinDenom, mScaleMaxDenom, mFilterExp, mLabel, mDescription );
222  // clone children
223  foreach ( Rule* rule, mChildren )
224  newrule->appendChild( rule->clone() );
225  return newrule;
226 }
227 
228 QDomElement QgsRuleBasedRendererV2::Rule::save( QDomDocument& doc, QgsSymbolV2Map& symbolMap )
229 {
230  QDomElement ruleElem = doc.createElement( "rule" );
231 
232  if ( mSymbol )
233  {
234  int symbolIndex = symbolMap.size();
235  symbolMap[QString::number( symbolIndex )] = mSymbol;
236  ruleElem.setAttribute( "symbol", symbolIndex );
237  }
238  if ( !mFilterExp.isEmpty() )
239  ruleElem.setAttribute( "filter", mFilterExp );
240  if ( mScaleMinDenom != 0 )
241  ruleElem.setAttribute( "scalemindenom", mScaleMinDenom );
242  if ( mScaleMaxDenom != 0 )
243  ruleElem.setAttribute( "scalemaxdenom", mScaleMaxDenom );
244  if ( !mLabel.isEmpty() )
245  ruleElem.setAttribute( "label", mLabel );
246  if ( !mDescription.isEmpty() )
247  ruleElem.setAttribute( "description", mDescription );
248 
249  for ( RuleList::iterator it = mChildren.begin(); it != mChildren.end(); ++it )
250  {
251  Rule* rule = *it;
252  ruleElem.appendChild( rule->save( doc, symbolMap ) );
253  }
254  return ruleElem;
255 }
256 
257 void QgsRuleBasedRendererV2::Rule::toSld( QDomDocument& doc, QDomElement &element, QgsStringMap props )
258 {
259  // do not convert this rule if there are no symbols
260  if ( symbols().isEmpty() )
261  return;
262 
263  if ( !mFilterExp.isEmpty() )
264  {
265  if ( !props.value( "filter", "" ).isEmpty() )
266  props[ "filter" ] += " AND ";
267  props[ "filter" ] += mFilterExp;
268  }
269 
270  if ( mScaleMinDenom != 0 )
271  {
272  bool ok;
273  int parentScaleMinDenom = props.value( "scaleMinDenom", "0" ).toInt( &ok );
274  if ( !ok || parentScaleMinDenom <= 0 )
275  props[ "scaleMinDenom" ] = QString::number( mScaleMinDenom );
276  else
277  props[ "scaleMinDenom" ] = QString::number( qMax( parentScaleMinDenom, mScaleMinDenom ) );
278  }
279 
280  if ( mScaleMaxDenom != 0 )
281  {
282  bool ok;
283  int parentScaleMaxDenom = props.value( "scaleMaxDenom", "0" ).toInt( &ok );
284  if ( !ok || parentScaleMaxDenom <= 0 )
285  props[ "scaleMaxDenom" ] = QString::number( mScaleMaxDenom );
286  else
287  props[ "scaleMaxDenom" ] = QString::number( qMin( parentScaleMaxDenom, mScaleMaxDenom ) );
288  }
289 
290  if ( mSymbol )
291  {
292  QDomElement ruleElem = doc.createElement( "se:Rule" );
293  element.appendChild( ruleElem );
294 
295  //XXX: <se:Name> is the rule identifier, but our the Rule objects
296  // have no properties could be used as identifier. Use the label.
297  QDomElement nameElem = doc.createElement( "se:Name" );
298  nameElem.appendChild( doc.createTextNode( mLabel ) );
299  ruleElem.appendChild( nameElem );
300 
301  if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
302  {
303  QDomElement descrElem = doc.createElement( "se:Description" );
304  if ( !mLabel.isEmpty() )
305  {
306  QDomElement titleElem = doc.createElement( "se:Title" );
307  titleElem.appendChild( doc.createTextNode( mLabel ) );
308  descrElem.appendChild( titleElem );
309  }
310  if ( !mDescription.isEmpty() )
311  {
312  QDomElement abstractElem = doc.createElement( "se:Abstract" );
313  abstractElem.appendChild( doc.createTextNode( mDescription ) );
314  descrElem.appendChild( abstractElem );
315  }
316  ruleElem.appendChild( descrElem );
317  }
318 
319  if ( !props.value( "filter", "" ).isEmpty() )
320  {
321  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, props.value( "filter", "" ) );
322  }
323 
324  if ( !props.value( "scaleMinDenom", "" ).isEmpty() )
325  {
326  QDomElement scaleMinDenomElem = doc.createElement( "se:MinScaleDenominator" );
327  scaleMinDenomElem.appendChild( doc.createTextNode( props.value( "scaleMinDenom", "" ) ) );
328  ruleElem.appendChild( scaleMinDenomElem );
329  }
330 
331  if ( !props.value( "scaleMaxDenom", "" ).isEmpty() )
332  {
333  QDomElement scaleMaxDenomElem = doc.createElement( "se:MaxScaleDenominator" );
334  scaleMaxDenomElem.appendChild( doc.createTextNode( props.value( "scaleMaxDenom", "" ) ) );
335  ruleElem.appendChild( scaleMaxDenomElem );
336  }
337 
338  mSymbol->toSld( doc, ruleElem, props );
339  }
340 
341  // loop into childern rule list
342  for ( RuleList::iterator it = mChildren.begin(); it != mChildren.end(); ++it )
343  {
344  ( *it )->toSld( doc, element, props );
345  }
346 }
347 
349 {
350  mActiveChildren.clear();
351 
352  // filter out rules which are not compatible with this scale
353  if ( !isScaleOK( context.rendererScale() ) )
354  return false;
355 
356  // init this rule
357  if ( mFilter )
358  mFilter->prepare( fields );
359  if ( mSymbol )
360  mSymbol->startRender( context, &fields );
361 
362  // init children
363  // build temporary list of active rules (usable with this scale)
364  for ( RuleList::iterator it = mChildren.begin(); it != mChildren.end(); ++it )
365  {
366  Rule* rule = *it;
367  if ( rule->startRender( context, fields ) )
368  {
369  // only add those which are active with current scale
370  mActiveChildren.append( rule );
371  }
372  }
373  return true;
374 }
375 
377 {
378  QSet<int> symbolZLevelsSet;
379 
380  // process this rule
381  if ( mSymbol )
382  {
383  // find out which Z-levels are used
384  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
385  {
386  symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
387  }
388  }
389 
390  // process children
391  QList<Rule*>::iterator it;
392  for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
393  {
394  Rule* rule = *it;
395  symbolZLevelsSet.unite( rule->collectZLevels() );
396  }
397  return symbolZLevelsSet;
398 }
399 
400 void QgsRuleBasedRendererV2::Rule::setNormZLevels( const QMap<int, int>& zLevelsToNormLevels )
401 {
402  if ( mSymbol )
403  {
404  for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
405  {
406  int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
407  mSymbolNormZLevels.append( normLevel );
408  }
409  }
410 
411  // prepare list of normalized levels for each rule
412  for ( QList<Rule*>::iterator it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
413  {
414  Rule* rule = *it;
415  rule->setNormZLevels( zLevelsToNormLevels );
416  }
417 }
418 
419 
421 {
422  if ( !isFilterOK( featToRender.feat ) )
423  return false;
424 
425  bool rendered = false;
426 
427  // create job for this feature and this symbol, add to list of jobs
428  if ( mSymbol )
429  {
430  // add job to the queue: each symbol's zLevel must be added
431  foreach ( int normZLevel, mSymbolNormZLevels )
432  {
433  //QgsDebugMsg(QString("add job at level %1").arg(normZLevel));
434  renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol ) );
435  }
436  rendered = true;
437  }
438 
439  bool willrendersomething = false;
440 
441  // process children
442  for ( QList<Rule*>::iterator it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
443  {
444  Rule* rule = *it;
445  if ( rule->isElse() )
446  {
447  // Don't process else rules yet
448  continue;
449  }
450  willrendersomething |= rule->renderFeature( featToRender, context, renderQueue );
451  rendered |= willrendersomething;
452  }
453 
454  // If none of the rules passed then we jump into the else rules and process them.
455  if ( !willrendersomething )
456  {
457  foreach ( Rule* rule, mElseRules )
458  {
459  rendered |= rule->renderFeature( featToRender, context, renderQueue );
460  }
461  }
462 
463  return rendered;
464 }
465 
467 {
468  if ( !isFilterOK( feat ) )
469  return false;
470  if ( mSymbol )
471  return true;
472 
473  for ( QList<Rule*>::iterator it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
474  {
475  Rule* rule = *it;
476  if ( rule->willRenderFeature( feat ) )
477  return true;
478  }
479  return false;
480 }
481 
483 {
484  QgsSymbolV2List lst;
485  if ( !isFilterOK( feat ) )
486  return lst;
487  if ( mSymbol )
488  lst.append( mSymbol );
489 
490  for ( QList<Rule*>::iterator it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
491  {
492  Rule* rule = *it;
493  lst += rule->symbolsForFeature( feat );
494  }
495  return lst;
496 }
497 
499 {
500  RuleList lst;
501  if ( !isFilterOK( feat ) )
502  return lst;
503 
504  if ( mSymbol )
505  lst.append( this );
506 
507  for ( QList<Rule*>::iterator it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
508  {
509  Rule* rule = *it;
510  lst += rule->rulesForFeature( feat );
511  }
512  return lst;
513 }
514 
516 {
517  if ( mSymbol )
518  mSymbol->stopRender( context );
519 
520  for ( QList<Rule*>::iterator it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
521  {
522  Rule* rule = *it;
523  rule->stopRender( context );
524  }
525 
526  mActiveChildren.clear();
527  mSymbolNormZLevels.clear();
528 }
529 
531 {
532  QString symbolIdx = ruleElem.attribute( "symbol" );
533  QgsSymbolV2* symbol = NULL;
534  if ( !symbolIdx.isEmpty() )
535  {
536  if ( symbolMap.contains( symbolIdx ) )
537  {
538  symbol = symbolMap.take( symbolIdx );
539  }
540  else
541  {
542  QgsDebugMsg( "symbol for rule " + symbolIdx + " not found!" );
543  }
544  }
545 
546  QString filterExp = ruleElem.attribute( "filter" );
547  QString label = ruleElem.attribute( "label" );
548  QString description = ruleElem.attribute( "description" );
549  int scaleMinDenom = ruleElem.attribute( "scalemindenom", "0" ).toInt();
550  int scaleMaxDenom = ruleElem.attribute( "scalemaxdenom", "0" ).toInt();
551  Rule* rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
552 
553  QDomElement childRuleElem = ruleElem.firstChildElement( "rule" );
554  while ( !childRuleElem.isNull() )
555  {
556  Rule* childRule = create( childRuleElem, symbolMap );
557  if ( childRule )
558  {
559  rule->appendChild( childRule );
560  }
561  else
562  {
563  QgsDebugMsg( "failed to init a child rule!" );
564  }
565  childRuleElem = childRuleElem.nextSiblingElement( "rule" );
566  }
567 
568  return rule;
569 }
570 
572 {
573  if ( ruleElem.localName() != "Rule" )
574  {
575  QgsDebugMsg( QString( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
576  return NULL;
577  }
578 
579  QString label, description, filterExp;
580  int scaleMinDenom = 0, scaleMaxDenom = 0;
581  QgsSymbolLayerV2List layers;
582 
583  // retrieve the Rule element child nodes
584  QDomElement childElem = ruleElem.firstChildElement();
585  while ( !childElem.isNull() )
586  {
587  if ( childElem.localName() == "Name" )
588  {
589  // <se:Name> tag contains the rule identifier,
590  // so prefer title tag for the label property value
591  if ( label.isEmpty() )
592  label = childElem.firstChild().nodeValue();
593  }
594  else if ( childElem.localName() == "Description" )
595  {
596  // <se:Description> can contains a title and an abstract
597  QDomElement titleElem = childElem.firstChildElement( "Title" );
598  if ( !titleElem.isNull() )
599  {
600  label = titleElem.firstChild().nodeValue();
601  }
602 
603  QDomElement abstractElem = childElem.firstChildElement( "Abstract" );
604  if ( !abstractElem.isNull() )
605  {
606  description = abstractElem.firstChild().nodeValue();
607  }
608  }
609  else if ( childElem.localName() == "Abstract" )
610  {
611  // <sld:Abstract> (v1.0)
612  description = childElem.firstChild().nodeValue();
613  }
614  else if ( childElem.localName() == "Title" )
615  {
616  // <sld:Title> (v1.0)
617  label = childElem.firstChild().nodeValue();
618  }
619  else if ( childElem.localName() == "Filter" )
620  {
622  if ( filter )
623  {
624  if ( filter->hasParserError() )
625  {
626  QgsDebugMsg( "parser error: " + filter->parserErrorString() );
627  }
628  else
629  {
630  filterExp = filter->expression();
631  }
632  delete filter;
633  }
634  }
635  else if ( childElem.localName() == "MinScaleDenominator" )
636  {
637  bool ok;
638  int v = childElem.firstChild().nodeValue().toInt( &ok );
639  if ( ok )
640  scaleMinDenom = v;
641  }
642  else if ( childElem.localName() == "MaxScaleDenominator" )
643  {
644  bool ok;
645  int v = childElem.firstChild().nodeValue().toInt( &ok );
646  if ( ok )
647  scaleMaxDenom = v;
648  }
649  else if ( childElem.localName().endsWith( "Symbolizer" ) )
650  {
651  // create symbol layers for this symbolizer
652  QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
653  }
654 
655  childElem = childElem.nextSiblingElement();
656  }
657 
658  // now create the symbol
659  QgsSymbolV2 *symbol = 0;
660  if ( layers.size() > 0 )
661  {
662  switch ( geomType )
663  {
664  case QGis::Line:
665  symbol = new QgsLineSymbolV2( layers );
666  break;
667 
668  case QGis::Polygon:
669  symbol = new QgsFillSymbolV2( layers );
670  break;
671 
672  case QGis::Point:
673  symbol = new QgsMarkerSymbolV2( layers );
674  break;
675 
676  default:
677  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
678  return NULL;
679  }
680  }
681 
682  // and then create and return the new rule
683  return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
684 }
685 
686 
688 
690  : QgsFeatureRendererV2( "RuleRenderer" ), mRootRule( root )
691 {
692 }
693 
695  : QgsFeatureRendererV2( "RuleRenderer" )
696 {
697  mRootRule = new Rule( NULL ); // root has no symbol, no filter etc - just a container
698  mRootRule->appendChild( new Rule( defaultSymbol ) );
699 }
700 
702 {
703  delete mRootRule;
704 }
705 
706 
708 {
709  // not used at all
710  return 0;
711 }
712 
714  QgsRenderContext& context,
715  int layer,
716  bool selected,
717  bool drawVertexMarker )
718 {
719  Q_UNUSED( layer );
720 
721  int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
722  mCurrentFeatures.append( FeatureToRender( feature, flags ) );
723 
724  // check each active rule
725  return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue );
726 }
727 
728 
730 {
731  // prepare active children
732  mRootRule->startRender( context, fields );
733 
734  QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
735  QList<int> symbolZLevels = symbolZLevelsSet.toList();
736  qSort( symbolZLevels );
737 
738  // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
739  // and prepare rendering queue
740  QMap<int, int> zLevelsToNormLevels;
741  int maxNormLevel = -1;
742  foreach ( int zLevel, symbolZLevels )
743  {
744  zLevelsToNormLevels[zLevel] = ++maxNormLevel;
745  mRenderQueue.append( RenderLevel( zLevel ) );
746  QgsDebugMsg( QString( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ) );
747  }
748 
749  mRootRule->setNormZLevels( zLevelsToNormLevels );
750 }
751 
753 {
754  //
755  // do the actual rendering
756  //
757 
758  // go through all levels
759  foreach ( const RenderLevel& level, mRenderQueue )
760  {
761  //QgsDebugMsg(QString("level %1").arg(level.zIndex));
762  // go through all jobs at the level
763  foreach ( const RenderJob* job, level.jobs )
764  {
765  //QgsDebugMsg(QString("job fid %1").arg(job->f->id()));
766  // render feature - but only with symbol layers with specified zIndex
767  QgsSymbolV2* s = job->symbol;
768  int count = s->symbolLayerCount();
769  for ( int i = 0; i < count; i++ )
770  {
771  // TODO: better solution for this
772  // renderFeatureWithSymbol asks which symbol layer to draw
773  // but there are multiple transforms going on!
774  if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
775  {
776  int flags = job->ftr.flags;
777  renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
778  }
779  }
780  }
781  }
782 
783  // clean current features
784  mCurrentFeatures.clear();
785 
786  // clean render queue
787  mRenderQueue.clear();
788 
789  // clean up rules from temporary stuff
790  mRootRule->stopRender( context );
791 }
792 
794 {
795  QSet<QString> attrs = mRootRule->usedAttributes();
796  return attrs.values();
797 }
798 
800 {
802 
805  return r;
806 }
807 
808 void QgsRuleBasedRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
809 {
810  mRootRule->toSld( doc, element, QgsStringMap() );
811 }
812 
813 // TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
815 {
816  return mRootRule->symbols();
817 }
818 
819 QDomElement QgsRuleBasedRendererV2::save( QDomDocument& doc )
820 {
821  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
822  rendererElem.setAttribute( "type", "RuleRenderer" );
823  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
824 
826 
827  QDomElement rulesElem = mRootRule->save( doc, symbols );
828  rulesElem.setTagName( "rules" ); // instead of just "rule"
829  rendererElem.appendChild( rulesElem );
830 
831  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
832  rendererElem.appendChild( symbolsElem );
833 
834  return rendererElem;
835 }
836 
838 {
841  for ( QgsLegendSymbolList::iterator it = items.begin(); it != items.end(); ++it )
842  {
843  QPair<QString, QgsSymbolV2*> pair = *it;
844  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( pair.second, iconSize );
845  lst << qMakePair( pair.first, pix );
846  }
847  return lst;
848 }
849 
850 QgsLegendSymbolList QgsRuleBasedRendererV2::legendSymbolItems( double scaleDenominator, QString rule )
851 {
852  return mRootRule->legendSymbolItems( scaleDenominator, rule );
853 }
854 
855 
857 {
858  // load symbols
859  QDomElement symbolsElem = element.firstChildElement( "symbols" );
860  if ( symbolsElem.isNull() )
861  return NULL;
862 
863  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
864 
865  QDomElement rulesElem = element.firstChildElement( "rules" );
866 
867  Rule* root = Rule::create( rulesElem, symbolMap );
868  if ( root == NULL )
869  return NULL;
870 
872 
873  // delete symbols if there are any more
875 
876  return r;
877 }
878 
880 {
881  // retrieve child rules
882  Rule* root = 0;
883 
884  QDomElement ruleElem = element.firstChildElement( "Rule" );
885  while ( !ruleElem.isNull() )
886  {
887  Rule *child = Rule::createFromSld( ruleElem, geomType );
888  if ( child )
889  {
890  // create the root rule if not done before
891  if ( !root )
892  root = new Rule( 0 );
893 
894  root->appendChild( child );
895  }
896 
897  ruleElem = ruleElem.nextSiblingElement( "Rule" );
898  }
899 
900  if ( !root )
901  {
902  // no valid rules was found
903  return NULL;
904  }
905 
906  // create and return the new renderer
907  return new QgsRuleBasedRendererV2( root );
908 }
909 
912 
914 {
915  foreach ( const QgsRendererCategoryV2& cat, r->categories() )
916  {
917  QString attr = QgsExpression::quotedColumnRef( r->classAttribute() );
918  QString value;
919  // not quoting numbers saves a type cast
920  if ( cat.value().type() == QVariant::Int )
921  value = cat.value().toString();
922  else if ( cat.value().type() == QVariant::Double )
923  // we loose precision here - so we may miss some categories :-(
924  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
925  value = QString::number( cat.value().toDouble(), 'f', 4 );
926  else
927  value = QgsExpression::quotedString( cat.value().toString() );
928  QString filter = QString( "%1 = %2" ).arg( attr ).arg( value );
929  QString label = filter;
930  initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
931  }
932 }
933 
935 {
936  foreach ( const QgsRendererRangeV2& rng, r->ranges() )
937  {
938  // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
939  // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
940  QString attr = QgsExpression::quotedColumnRef( r->classAttribute() );
941  QString filter = QString( "%1 >= %2 AND %1 <= %3" ).arg( attr )
942  .arg( QString::number( rng.lowerValue(), 'f', 4 ) )
943  .arg( QString::number( rng.upperValue(), 'f', 4 ) );
944  QString label = filter;
945  initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
946  }
947 }
948 
950 {
951  qSort( scales ); // make sure the scales are in ascending order
952  int oldScale = initialRule->scaleMinDenom();
953  int maxDenom = initialRule->scaleMaxDenom();
954  QgsSymbolV2* symbol = initialRule->symbol();
955  foreach ( int scale, scales )
956  {
957  if ( initialRule->scaleMinDenom() >= scale )
958  continue; // jump over the first scales out of the interval
959  if ( maxDenom != 0 && maxDenom <= scale )
960  break; // ignore the latter scales out of the interval
961  initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
962  oldScale = scale;
963  }
964  // last rule
965  initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QString( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
966 }
967 
969 {
970  QString msg( "Rule-based renderer:\n" );
971  msg += mRootRule->dump();
972  return msg;
973 }
974 
976 {
977  return mRootRule->willRenderFeature( feat );
978 }
979 
981 {
982  return mRootRule->symbolsForFeature( feat );
983 }
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props)
QMap< QString, QgsSymbolV2 * > QgsSymbolV2Map
Definition: qgsrendererv2.h:38
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:89
QgsSymbolV2List symbolsForFeature(QgsFeature &feat)
tell which symbols will be used to render the feature
static QgsSymbolV2Map loadSymbols(QDomElement &element)
void setNormZLevels(const QMap< int, int > &zLevelsToNormLevels)
assign normalized z-levels [0..N-1] for this rule's symbol for quick access during rendering ...
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:43
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.h:96
virtual QgsSymbolV2List symbolsForFeature(QgsFeature &feat)
return list of symbols used for rendering the feature.
virtual QString dump() const
for debugging
const QgsCategoryList & categories() const
GeometryType
Definition: qgis.h:155
static QString quotedColumnRef(QString name)
return quoted column reference (in double quotes)
QList< QgsSymbolV2 * > QgsSymbolV2List
Definition: qgsrendererv2.h:37
QSet< int > collectZLevels()
get all used z-levels from this rule and children
const QString expression() const
Alias for dump()
static QgsFeatureRendererV2 * create(QDomElement &element)
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
double rendererScale() const
virtual QgsSymbolV2 * clone() const =0
static QgsFeatureRendererV2 * createFromSld(QDomElement &element, QGis::GeometryType geomType)
virtual void stopRender(QgsRenderContext &context)
bool isScaleOK(double scale) const
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize)
return a list of symbology items for the legend
bool startRender(QgsRenderContext &context, const QgsFields &fields)
prepare the rule for rendering and its children (build active children array)
Rule * clone() const
clone this rule, return new instance
RuleList rulesForFeature(QgsFeature &feat)
tell which rules will be used to render the feature
Container of fields for a vector layer.
Definition: qgsfield.h:161
void removeChild(Rule *rule)
delete child rule
virtual QDomElement save(QDomDocument &doc)
store renderer info to XML element
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:113
QMap< QString, QString > QgsStringMap
Definition: qgis.h:416
void removeChildAt(int i)
delete child rule
void renderFeatureWithSymbol(QgsFeature &feature, QgsSymbolV2 *symbol, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker)
static bool createSymbolLayerV2ListFromSld(QDomElement &element, QGis::GeometryType geomType, QgsSymbolLayerV2List &layers)
void stopRender(QgsRenderContext &context)
Rule(QgsSymbolV2 *symbol, int scaleMinDenom=0, int scaleMaxDenom=0, QString filterExp=QString(), QString label=QString(), QString description=QString(), bool elseRule=false)
Constructor takes ownership of the symbol.
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 ...
void takeChild(Rule *rule)
take child rule out, set parent as null
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, QString rule="")
return a list of item text / symbol
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, QString function)
void appendChild(Rule *rule)
add child rule, take ownership, sets this as parent
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, QString tagName, QDomDocument &doc)
static Rule * create(QDomElement &ruleElem, QgsSymbolV2Map &symbolMap)
QgsSymbolV2 * symbol() const
QDomElement save(QDomDocument &doc, QgsSymbolV2Map &symbolMap)
bool isFilterOK(QgsFeature &f) const
int symbolLayerCount()
Definition: qgssymbolv2.h:85
This class keeps data about a rules for rule-based renderer.
virtual bool renderFeature(QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false)
virtual QgsFeatureRendererV2 * clone()
QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, QString rule="")
Rule * mRootRule
the root node with hierarchical list of rules
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 ...
QString dump(int offset=0) const
Rule * takeChildAt(int i)
take child rule out, set parent as null
bool renderFeature(FeatureToRender &featToRender, QgsRenderContext &context, RenderQueue &renderQueue)
QList< QPair< QString, QPixmap > > QgsLegendSymbologyList
int renderingPass() const
virtual bool willRenderFeature(QgsFeature &feat)
return whether the renderer will render a feature or not.
QgsRuleBasedRendererV2(QgsRuleBasedRendererV2::Rule *root)
Constructs the renderer from given tree of rules (takes ownership)
void setUsingSymbolLevels(bool usingSymbolLevels)
Contains information about the context of a rendering operation.
QList< QgsSymbolLayerV2 * > QgsSymbolLayerV2List
Definition: qgssymbolv2.h:39
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)
bool usingSymbolLevels() const
static QgsExpression * expressionFromOgcFilter(const QDomElement &element)
Parse XML with OGC filter into QGIS expression.
static QString quotedString(QString text)
return quoted string (in single quotes)
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size)
static void clearSymbolMap(QgsSymbolV2Map &symbols)
void setSymbol(QgsSymbolV2 *sym)
set a new symbol (or NULL). Deletes old symbol.
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature)
return symbol for current feature. Should not be used individually: there could be more symbols for a...
QList< RenderLevel > RenderQueue
QgsSymbolLayerV2 * symbolLayer(int layer)
void insertChild(int i, Rule *rule)
add child rule, take ownership, sets this as parent
bool willRenderFeature(QgsFeature &feat)
only tell whether a feature will be rendered without actually rendering it
QList< QPair< QString, QgsSymbolV2 * > > QgsLegendSymbolList
Definition: qgsrendererv2.h:41
QString parserErrorString() const
Returns parser error.
Definition: qgsexpression.h:98
virtual QList< QString > usedAttributes()
virtual void toSld(QDomDocument &doc, QDomElement &element) const
used from subclasses to create SLD Rule elements following SLD v1.1 specs
QList< FeatureToRender > mCurrentFeatures
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...
virtual QgsSymbolV2List symbols()
for symbol levels