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