Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
qgscategorizedsymbolrendererv2.cpp
Go to the documentation of this file.
1 
3 
4 #include "qgssymbolv2.h"
6 #include "qgsvectorcolorrampv2.h"
7 
8 #include "qgsfeature.h"
9 #include "qgsvectorlayer.h"
10 #include "qgslogger.h"
11 
12 #include <QDomDocument>
13 #include <QDomElement>
14 #include <QSettings> // for legend
15 
16 QgsRendererCategoryV2::QgsRendererCategoryV2( QVariant value, QgsSymbolV2* symbol, QString label )
17  : mValue( value ), mSymbol( symbol ), mLabel( label )
18 {
19 }
20 
22  : mValue( cat.mValue ), mLabel( cat.mLabel )
23 {
24  mSymbol = cat.mSymbol->clone();
25 }
26 
27 
29 {
30  delete mSymbol;
31 }
32 
34 {
35  return mValue;
36 }
37 
39 {
40  return mSymbol;
41 }
42 
44 {
45  return mLabel;
46 }
47 
48 void QgsRendererCategoryV2::setValue( const QVariant &value )
49 {
50  mValue = value;
51 }
52 
54 {
55  if ( mSymbol == s )
56  return;
57  delete mSymbol;
58  mSymbol = s;
59 }
60 
61 void QgsRendererCategoryV2::setLabel( const QString &label )
62 {
63  mLabel = label;
64 }
65 
67 {
68  return QString( "%1::%2::%3\n" ).arg( mValue.toString() ).arg( mLabel ).arg( mSymbol->dump() );
69 }
70 
72 
74  : QgsFeatureRendererV2( "categorizedSymbol" ),
75  mAttrName( attrName ),
76  mCategories( categories ),
77  mSourceSymbol( NULL ),
78  mSourceColorRamp( NULL ),
79  mRotationFieldIdx( -1 ),
80  mSizeScaleFieldIdx( -1 )
81 {
82  for ( int i = 0; i < mCategories.count(); ++i )
83  {
85  if ( cat.symbol() == NULL )
86  {
87  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
88  mCategories.removeAt( i-- );
89  }
90  //mCategories.insert(cat.value().toString(), cat);
91  }
92 }
93 
95 {
96  mCategories.clear(); // this should also call destructors of symbols
97  delete mSourceSymbol;
98  delete mSourceColorRamp;
99 }
100 
102 {
103  mSymbolHash.clear();
104 
105  for ( int i = 0; i < mCategories.count(); ++i )
106  {
108  mSymbolHash.insert( cat.value().toString(), cat.symbol() );
109  }
110 }
111 
113 {
114  // TODO: special case for int, double
115 
116  QHash<QString, QgsSymbolV2*>::iterator it = mSymbolHash.find( value.toString() );
117  if ( it == mSymbolHash.end() )
118  {
119  if ( mSymbolHash.count() == 0 )
120  QgsDebugMsg( "there are no hashed symbols!!!" );
121  else
122  QgsDebugMsg( "attribute value not found: " + value.toString() );
123  return NULL;
124  }
125 
126  return *it;
127 }
128 
130 {
131  const QgsAttributeMap& attrMap = feature.attributeMap();
132  QgsAttributeMap::const_iterator ita = attrMap.find( mAttrNum );
133  if ( ita == attrMap.end() )
134  {
135  QgsDebugMsg( "attribute '" + mAttrName + "' (index " + QString::number( mAttrNum ) + ") required by renderer not found" );
136  return NULL;
137  }
138 
139  // find the right symbol for the category
140  QgsSymbolV2* symbol = symbolForValue( *ita );
141  if ( symbol == NULL )
142  {
143  // if no symbol found use default one
144  return symbolForValue( QVariant( "" ) );
145  }
146 
147  if ( mRotationFieldIdx == -1 && mSizeScaleFieldIdx == -1 )
148  return symbol; // no data-defined rotation/scaling - just return the symbol
149 
150  // find out rotation, size scale
151  double rotation = 0;
152  double sizeScale = 1;
153  if ( mRotationFieldIdx != -1 )
154  rotation = attrMap[mRotationFieldIdx].toDouble();
155  if ( mSizeScaleFieldIdx != -1 )
156  sizeScale = attrMap[mSizeScaleFieldIdx].toDouble();
157 
158  // take a temporary symbol (or create it if doesn't exist)
159  QgsSymbolV2* tempSymbol = mTempSymbols[ita->toString()];
160 
161  // modify the temporary symbol and return it
162  if ( tempSymbol->type() == QgsSymbolV2::Marker )
163  {
164  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
165  if ( mRotationFieldIdx != -1 )
166  markerSymbol->setAngle( rotation );
167  if ( mSizeScaleFieldIdx != -1 )
168  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
169  }
170  else if ( tempSymbol->type() == QgsSymbolV2::Line )
171  {
172  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
173  if ( mSizeScaleFieldIdx != -1 )
174  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
175  }
176 
177  return tempSymbol;
178 }
179 
181 {
182  for ( int i = 0; i < mCategories.count(); i++ )
183  {
184  if ( mCategories[i].value() == val )
185  return i;
186  }
187  return -1;
188 }
189 
190 bool QgsCategorizedSymbolRendererV2::updateCategoryValue( int catIndex, const QVariant &value )
191 {
192  if ( catIndex < 0 || catIndex >= mCategories.size() )
193  return false;
194  mCategories[catIndex].setValue( value );
195  return true;
196 }
197 
199 {
200  if ( catIndex < 0 || catIndex >= mCategories.size() )
201  return false;
202  mCategories[catIndex].setSymbol( symbol );
203  return true;
204 }
205 
206 bool QgsCategorizedSymbolRendererV2::updateCategoryLabel( int catIndex, QString label )
207 {
208  if ( catIndex < 0 || catIndex >= mCategories.size() )
209  return false;
210  mCategories[catIndex].setLabel( label );
211  return true;
212 }
213 
215 {
216  if ( cat.symbol() == NULL )
217  {
218  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
219  }
220  else
221  {
222  mCategories.append( cat );
223  }
224 }
225 
227 {
228  if ( catIndex < 0 || catIndex >= mCategories.size() )
229  return false;
230 
231  mCategories.removeAt( catIndex );
232  return true;
233 }
234 
236 {
237  mCategories.clear();
238 }
239 
241 {
242  // make sure that the hash table is up to date
243  rebuildHash();
244 
245  // find out classification attribute index from name
246  mAttrNum = vlayer ? vlayer->fieldNameIndex( mAttrName ) : -1;
247 
248  mRotationFieldIdx = ( mRotationField.isEmpty() ? -1 : vlayer->fieldNameIndex( mRotationField ) );
249  mSizeScaleFieldIdx = ( mSizeScaleField.isEmpty() ? -1 : vlayer->fieldNameIndex( mSizeScaleField ) );
250 
251  QgsCategoryList::iterator it = mCategories.begin();
252  for ( ; it != mCategories.end(); ++it )
253  {
254  it->symbol()->startRender( context );
255 
256  if ( mRotationFieldIdx != -1 || mSizeScaleFieldIdx != -1 )
257  {
258  QgsSymbolV2* tempSymbol = it->symbol()->clone();
261  tempSymbol->startRender( context );
262  mTempSymbols[ it->value().toString()] = tempSymbol;
263  }
264  }
265 
266 }
267 
269 {
270  QgsCategoryList::iterator it = mCategories.begin();
271  for ( ; it != mCategories.end(); ++it )
272  it->symbol()->stopRender( context );
273 
274  // cleanup mTempSymbols
275  QHash<QString, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
276  for ( ; it2 != mTempSymbols.end(); ++it2 )
277  {
278  it2.value()->stopRender( context );
279  delete it2.value();
280  }
281  mTempSymbols.clear();
282 }
283 
285 {
286  QList<QString> lst;
287  lst.append( mAttrName );
288  if ( !mRotationField.isEmpty() )
289  lst.append( mRotationField );
290  if ( !mSizeScaleField.isEmpty() )
291  lst.append( mSizeScaleField );
292  return lst;
293 }
294 
296 {
297  QString s = QString( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
298  for ( int i = 0; i < mCategories.count(); i++ )
299  s += mCategories[i].dump();
300  return s;
301 }
302 
304 {
306  if ( mSourceSymbol )
308  if ( mSourceColorRamp )
313  return r;
314 }
315 
317 {
318  QgsSymbolV2List lst;
319  for ( int i = 0; i < mCategories.count(); i++ )
320  lst.append( mCategories[i].symbol() );
321  return lst;
322 }
323 
325 {
326  QDomElement symbolsElem = element.firstChildElement( "symbols" );
327  if ( symbolsElem.isNull() )
328  return NULL;
329 
330  QDomElement catsElem = element.firstChildElement( "categories" );
331  if ( catsElem.isNull() )
332  return NULL;
333 
334  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
335  QgsCategoryList cats;
336 
337  QDomElement catElem = catsElem.firstChildElement();
338  while ( !catElem.isNull() )
339  {
340  if ( catElem.tagName() == "category" )
341  {
342  QVariant value = QVariant( catElem.attribute( "value" ) );
343  QString symbolName = catElem.attribute( "symbol" );
344  QString label = catElem.attribute( "label" );
345  if ( symbolMap.contains( symbolName ) )
346  {
347  QgsSymbolV2* symbol = symbolMap.take( symbolName );
348  cats.append( QgsRendererCategoryV2( value, symbol, label ) );
349  }
350  }
351  catElem = catElem.nextSiblingElement();
352  }
353 
354  QString attrName = element.attribute( "attr" );
355 
357 
358  // delete symbols if there are any more
360 
361  // try to load source symbol (optional)
362  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
363  if ( !sourceSymbolElem.isNull() )
364  {
365  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
366  if ( sourceSymbolMap.contains( "0" ) )
367  {
368  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
369  }
370  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
371  }
372 
373  // try to load color ramp (optional)
374  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
375  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
376  {
377  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
378  }
379 
380  QDomElement rotationElem = element.firstChildElement( "rotation" );
381  if ( !rotationElem.isNull() )
382  r->setRotationField( rotationElem.attribute( "field" ) );
383 
384  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
385  if ( !sizeScaleElem.isNull() )
386  r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
387 
388  // TODO: symbol levels
389  return r;
390 }
391 
392 QDomElement QgsCategorizedSymbolRendererV2::save( QDomDocument& doc )
393 {
394  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
395  rendererElem.setAttribute( "type", "categorizedSymbol" );
396  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
397  rendererElem.setAttribute( "attr", mAttrName );
398 
399  // categories
400  int i = 0;
402  QDomElement catsElem = doc.createElement( "categories" );
403  QgsCategoryList::const_iterator it = mCategories.constBegin();
404  for ( ; it != mCategories.end(); it++ )
405  {
406  const QgsRendererCategoryV2& cat = *it;
407  QString symbolName = QString::number( i );
408  symbols.insert( symbolName, cat.symbol() );
409 
410  QDomElement catElem = doc.createElement( "category" );
411  catElem.setAttribute( "value", cat.value().toString() );
412  catElem.setAttribute( "symbol", symbolName );
413  catElem.setAttribute( "label", cat.label() );
414  catsElem.appendChild( catElem );
415  i++;
416  }
417 
418  rendererElem.appendChild( catsElem );
419 
420  // save symbols
421  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
422  rendererElem.appendChild( symbolsElem );
423 
424  // save source symbol
425  if ( mSourceSymbol )
426  {
427  QgsSymbolV2Map sourceSymbols;
428  sourceSymbols.insert( "0", mSourceSymbol );
429  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
430  rendererElem.appendChild( sourceSymbolElem );
431  }
432 
433  // save source color ramp
434  if ( mSourceColorRamp )
435  {
436  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp, doc );
437  rendererElem.appendChild( colorRampElem );
438  }
439 
440  QDomElement rotationElem = doc.createElement( "rotation" );
441  rotationElem.setAttribute( "field", mRotationField );
442  rendererElem.appendChild( rotationElem );
443 
444  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
445  sizeScaleElem.setAttribute( "field", mSizeScaleField );
446  rendererElem.appendChild( sizeScaleElem );
447 
448  return rendererElem;
449 }
450 
452 {
453  QSettings settings;
454  bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
455 
457  if ( showClassifiers )
458  {
459  lst << qMakePair( classAttribute(), QPixmap() );
460  }
461 
462  int count = categories().count();
463  for ( int i = 0; i < count; i++ )
464  {
465  const QgsRendererCategoryV2& cat = categories()[i];
466  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( cat.symbol(), iconSize );
467  lst << qMakePair( cat.label(), pix );
468  }
469  return lst;
470 }
471 
473 {
474  QSettings settings;
475  bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
476 
478  if ( showClassifiers )
479  {
480  lst << qMakePair( classAttribute(), ( QgsSymbolV2* )0 );
481  }
482 
483  QgsCategoryList::const_iterator catIt = mCategories.constBegin();
484  for ( ; catIt != mCategories.constEnd(); ++catIt )
485  {
486  lst << qMakePair( catIt->label(), catIt->symbol() );
487  }
488  return lst;
489 }
490 
491 
493 {
494  return mSourceSymbol;
495 }
497 {
498  delete mSourceSymbol;
499  mSourceSymbol = sym;
500 }
501 
503 {
504  return mSourceColorRamp;
505 }
507 {
508  delete mSourceColorRamp;
509  mSourceColorRamp = ramp;
510 }