Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator 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.sk at gmail.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 "qgssearchtreenode.h"
19 #include "qgssymbollayerv2utils.h"
20 #include "qgsrendercontext.h"
21 #include "qgsvectorlayer.h"
22 #include "qgslogger.h"
23 
24 #include <QSet>
25 
26 #include <QDomDocument>
27 #include <QDomElement>
28 
29 
30 
31 QgsRuleBasedRendererV2::Rule::Rule( QgsSymbolV2* symbol, int scaleMinDenom, int scaleMaxDenom, QString filterExp, QString label, QString description )
32  : mSymbol( symbol ),
33  mScaleMinDenom( scaleMinDenom ), mScaleMaxDenom( scaleMaxDenom ),
34  mFilterExp( filterExp ), mLabel( label ), mDescription( description )
35 {
36  initFilter();
37 }
38 
40  : mSymbol( NULL )
41 {
42  *this = other;
43 }
44 
46 {
47  delete mSymbol;
48 }
49 
51 {
52  if ( !mFilterExp.isEmpty() )
53  {
54  mFilterParsed.setString( mFilterExp );
55  mFilterTree = mFilterParsed.tree(); // may be NULL if there's no input
56  }
57  else
58  {
59  mFilterTree = NULL;
60  }
61 }
62 
64 {
65  return QString( "RULE %1 - scale [%2,%3] - filter %4 - symbol %5" )
66  .arg( mLabel ).arg( mScaleMinDenom ).arg( mScaleMaxDenom )
67  .arg( mFilterExp ).arg( mSymbol->dump() );
68 
69 }
70 
72 {
73  if ( ! mFilterTree )
74  return QStringList();
75 
76  return mFilterTree->referencedColumns();
77 }
78 
80 {
81  if ( ! mFilterTree )
82  return true;
83 
84  bool res = mFilterTree->checkAgainst( fields, f );
85  //print "is_ok", res, feature.id(), feature.attributeMap()
86  return res;
87 }
88 
89 bool QgsRuleBasedRendererV2::Rule::isScaleOK( double scale ) const
90 {
91  if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 )
92  return true;
93  if ( mScaleMinDenom != 0 && mScaleMinDenom > scale )
94  return false;
95  if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale )
96  return false;
97  return true;
98 }
99 
101 {
102  if ( this != &other )
103  {
104  delete mSymbol;
105  mSymbol = other.mSymbol->clone();
106 
107  mScaleMinDenom = other.mScaleMinDenom;
108  mScaleMaxDenom = other.mScaleMaxDenom;
109  mFilterExp = other.mFilterExp;
110  mLabel = other.mLabel;
111  mDescription = other.mDescription;
112  initFilter();
113  }
114  return *this;
115 }
116 
118 
120  : QgsFeatureRendererV2( "RuleRenderer" ), mDefaultSymbol( defaultSymbol ), mCurrentSymbol( 0 )
121 {
122  // add the default rule
123  mRules << Rule( defaultSymbol->clone() );
124 }
125 
126 
128 {
129 
130  if( ! usingFirstRule() )
131  return mCurrentSymbol;
132 
133  for ( QList<Rule*>::iterator it = mCurrentRules.begin(); it != mCurrentRules.end(); ++it )
134  {
135  Rule* rule = *it;
136 
137  if ( rule->isFilterOK( mCurrentFields, feature ) )
138  {
139  return rule->symbol(); //works with levels but takes only first rule
140  }
141  }
142  return mCurrentSymbol;
143 }
144 
146  QgsRenderContext& context,
147  int layer,
148  bool selected,
149  bool drawVertexMarker )
150 {
151  for ( QList<Rule*>::iterator it = mCurrentRules.begin(); it != mCurrentRules.end(); ++it )
152  {
153  Rule* rule = *it;
154  if ( rule->isFilterOK( mCurrentFields, feature ) )
155  {
156  mCurrentSymbol = rule->symbol();
157  // will ask for mCurrentSymbol
158  QgsFeatureRendererV2::renderFeature( feature, context, layer, selected, drawVertexMarker );
159  }
160  }
161 }
162 
163 
165 {
166  double currentScale = context.rendererScale();
167  // filter out rules which are not compatible with this scale
168 
169  mCurrentRules.clear();
170  for ( QList<Rule>::iterator it = mRules.begin(); it != mRules.end(); ++it )
171  {
172  Rule& rule = *it;
173  if ( rule.isScaleOK( currentScale ) )
174  mCurrentRules.append( &rule );
175  }
176 
177  mCurrentFields = vlayer->pendingFields();
178 
179  for ( QList<Rule*>::iterator it = mCurrentRules.begin(); it != mCurrentRules.end(); ++it )
180  {
181  Rule* rule = *it;
182  rule->symbol()->startRender( context );
183  }
184 }
185 
187 {
188  for ( QList<Rule*>::iterator it = mCurrentRules.begin(); it != mCurrentRules.end(); ++it )
189  {
190  Rule* rule = *it;
191  rule->symbol()->stopRender( context );
192  }
193 
194  mCurrentRules.clear();
195  mCurrentFields.clear();
196 }
197 
199 {
200  QSet<QString> attrs;
201  for ( QList<Rule>::iterator it = mRules.begin(); it != mRules.end(); ++it )
202  {
203  Rule& rule = *it;
204  attrs.unite( rule.needsFields().toSet() );
205  }
206  return attrs.values();
207 }
208 
210 {
213  r->mRules = mRules;
218  return r;
219 }
220 
222 {
223  QgsSymbolV2List lst;
224  for ( QList<Rule>::iterator it = mRules.begin(); it != mRules.end(); ++it )
225  {
226  Rule& rule = *it;
227  lst.append( rule.symbol() );
228  }
229 
230  return lst;
231 }
232 
233 QDomElement QgsRuleBasedRendererV2::save( QDomDocument& doc )
234 {
235  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
236  rendererElem.setAttribute( "type", "RuleRenderer" );
237  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
238  rendererElem.setAttribute( "firstrule", ( mUsingFirstRule ? "1" : "0" ) );
239 
240  QDomElement rulesElem = doc.createElement( "rules" );
241 
243  symbols["default"] = mDefaultSymbol;
244 
245  int i = 0;
246  for ( QList<Rule>::iterator it = mRules.begin(); it != mRules.end(); ++i, ++it )
247  {
248  Rule& rule = *it;
249  symbols[QString::number( i )] = rule.symbol();
250  QDomElement ruleElem = doc.createElement( "rule" );
251  ruleElem.setAttribute( "symbol", i );
252  ruleElem.setAttribute( "filter", rule.filterExpression() );
253  ruleElem.setAttribute( "scalemindenom", rule.scaleMinDenom() );
254  ruleElem.setAttribute( "scalemaxdenom", rule.scaleMaxDenom() );
255  ruleElem.setAttribute( "label", rule.label() );
256  ruleElem.setAttribute( "description", rule.description() );
257  rulesElem.appendChild( ruleElem );
258  }
259  rendererElem.appendChild( rulesElem );
260 
261  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
262  rendererElem.appendChild( symbolsElem );
263 
264  return rendererElem;
265 }
266 
267 
269 {
271  for ( QList<Rule>::iterator it = mRules.begin(); it != mRules.end(); ++it )
272  {
273  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( it->symbol(), iconSize );
274  lst << qMakePair( it->label(), pix );
275  }
276  return lst;
277 }
278 
280 {
282  for ( QList<Rule>::iterator it = mRules.begin(); it != mRules.end(); ++it )
283  {
284  lst << qMakePair( it->label(), it->symbol() );
285  }
286  return lst;
287 }
288 
289 
291 {
292  // load symbols
293  QDomElement symbolsElem = element.firstChildElement( "symbols" );
294  if ( symbolsElem.isNull() )
295  return NULL;
296 
297  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
298 
299  if ( !symbolMap.contains( "default" ) )
300  {
301  QgsDebugMsg( "default symbol not found!" );
302  return NULL;
303  }
304 
305  QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( symbolMap.take( "default" ) );
306  r->mRules.clear();
307 
308  QDomElement rulesElem = element.firstChildElement( "rules" );
309  QDomElement ruleElem = rulesElem.firstChildElement( "rule" );
310  while ( !ruleElem.isNull() )
311  {
312  QString symbolIdx = ruleElem.attribute( "symbol" );
313  if ( symbolMap.contains( symbolIdx ) )
314  {
315  QString filterExp = ruleElem.attribute( "filter" );
316  QString label = ruleElem.attribute( "label" );
317  QString description = ruleElem.attribute( "description" );
318  int scaleMinDenom = ruleElem.attribute( "scalemindenom", "0" ).toInt();
319  int scaleMaxDenom = ruleElem.attribute( "scalemaxdenom", "0" ).toInt();
320  r->mRules.append( Rule( symbolMap.take( symbolIdx ), scaleMinDenom, scaleMaxDenom, filterExp, label, description ) );
321  }
322  else
323  {
324  QgsDebugMsg( "symbol for rule " + symbolIdx + " not found! (skipping)" );
325  }
326  ruleElem = ruleElem.nextSiblingElement( "rule" );
327  }
328 
329  // delete symbols if there are any more
331 
332  return r;
333 }
334 
335 
337 {
338  return mRules.count();
339 }
340 
342 {
343  return mRules[index];
344 }
345 
347 {
348  mRules.append( rule );
349 }
350 
352 {
353  mRules.insert( index, rule );
354 }
355 
357 {
358  mRules[index] = rule;
359 }
360 
362 {
363  mRules.removeAt( index );
364 }
365 
366 void QgsRuleBasedRendererV2::swapRules( int index1, int index2 )
367 {
368  mRules.swap( index1, index2 );
369 }
370 
371 
374 
376 {
377  QList<Rule> rules;
378  foreach( const QgsRendererCategoryV2& cat, r->categories() )
379  {
380  QString newfilter = QString( "%1 = '%2'" ).arg( r->classAttribute() ).arg( cat.value().toString() );
381  QString filter = initialRule.filterExpression();
382  QString label = initialRule.label();
383  QString description = initialRule.description();
384  if ( filter.isEmpty() )
385  filter = newfilter;
386  else
387  filter = QString( "(%1) AND (%2)" ).arg( filter ).arg( newfilter );
388  rules.append( Rule( cat.symbol()->clone(), initialRule.scaleMinDenom(), initialRule.scaleMaxDenom(), filter, initialRule.label(), initialRule.description() ) );
389  }
390  return rules;
391 }
392 
394 {
395  QList<Rule> rules;
396  foreach( const QgsRendererRangeV2& rng, r->ranges() )
397  {
398  QString newfilter = QString( "%1 >= '%2' AND %1 <= '%3'" ).arg( r->classAttribute() ).arg( rng.lowerValue() ).arg( rng.upperValue() );
399  QString filter = initialRule.filterExpression();
400  QString label = initialRule.label();
401  QString description = initialRule.description();
402  if ( filter.isEmpty() )
403  filter = newfilter;
404  else
405  filter = QString( "(%1) AND (%2)" ).arg( filter ).arg( newfilter );
406  rules.append( Rule( rng.symbol()->clone(), initialRule.scaleMinDenom(), initialRule.scaleMaxDenom(), filter, initialRule.label(), initialRule.description() ) );
407  }
408  return rules;
409 }
410 
411 QList<QgsRuleBasedRendererV2::Rule> QgsRuleBasedRendererV2::refineRuleScales( QgsRuleBasedRendererV2::Rule& initialRule, QList<int> scales )
412 {
413  qSort( scales ); // make sure the scales are in ascending order
414  QList<Rule> rules;
415  int oldScale = initialRule.scaleMinDenom();
416  int maxDenom = initialRule.scaleMaxDenom();
417  foreach( int scale, scales )
418  {
419  if ( initialRule.scaleMinDenom() >= scale )
420  continue; // jump over the first scales out of the interval
421  if ( maxDenom != 0 && maxDenom <= scale )
422  break; // ignore the latter scales out of the interval
423  rules.append( Rule( initialRule.symbol()->clone(), oldScale, scale, initialRule.filterExpression(), initialRule.label(), initialRule.description() ) );
424  oldScale = scale;
425  }
426  // last rule
427  rules.append( Rule( initialRule.symbol()->clone(), oldScale, maxDenom, initialRule.filterExpression(), initialRule.label(), initialRule.description() ) );
428  return rules;
429 }