QGIS API Documentation  2.9.0-Master
qgscategorizedsymbolrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscategorizedsymbolrendererv2.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 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 #include <algorithm>
16 
18 
19 #include "qgssymbolv2.h"
20 #include "qgssymbollayerv2utils.h"
21 #include "qgsvectorcolorrampv2.h"
24 #include "qgspainteffect.h"
25 #include "qgsscaleexpression.h"
26 #include "qgsdatadefined.h"
27 
28 #include "qgsfeature.h"
29 #include "qgsvectorlayer.h"
30 #include "qgslogger.h"
31 
32 #include <QDomDocument>
33 #include <QDomElement>
34 #include <QSettings> // for legend
35 
37  : mRender( true )
38 {
39 }
40 
41 QgsRendererCategoryV2::QgsRendererCategoryV2( QVariant value, QgsSymbolV2* symbol, QString label, bool render )
42  : mValue( value )
43  , mSymbol( symbol )
44  , mLabel( label )
45  , mRender( render )
46 {
47 }
48 
50  : mValue( cat.mValue )
51  , mSymbol( cat.mSymbol.data() ? cat.mSymbol->clone() : NULL )
52  , mLabel( cat.mLabel )
53  , mRender( cat.mRender )
54 {
55 }
56 
57 // copy+swap idion, the copy is done through the 'pass by value'
59 {
60  swap( cat );
61  return *this;
62 }
63 
65 {
66  qSwap( mValue, cat.mValue );
67  qSwap( mSymbol, cat.mSymbol );
68  qSwap( mLabel, cat.mLabel );
69 }
70 
72 {
73  return mValue;
74 }
75 
77 {
78  return mSymbol.data();
79 }
80 
82 {
83  return mLabel;
84 }
85 
87 {
88  return mRender;
89 }
90 
91 void QgsRendererCategoryV2::setValue( const QVariant &value )
92 {
93  mValue = value;
94 }
95 
97 {
98  if ( mSymbol.data() != s ) mSymbol.reset( s );
99 }
100 
101 void QgsRendererCategoryV2::setLabel( const QString &label )
102 {
103  mLabel = label;
104 }
105 
107 {
108  mRender = render;
109 }
110 
112 {
113  return QString( "%1::%2::%3:%4\n" ).arg( mValue.toString() ).arg( mLabel ).arg( mSymbol->dump() ).arg( mRender );
114 }
115 
116 void QgsRendererCategoryV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
117 {
118  if ( !mSymbol.data() || props.value( "attribute", "" ).isEmpty() )
119  return;
120 
121  QString attrName = props[ "attribute" ];
122 
123  QDomElement ruleElem = doc.createElement( "se:Rule" );
124  element.appendChild( ruleElem );
125 
126  QDomElement nameElem = doc.createElement( "se:Name" );
127  nameElem.appendChild( doc.createTextNode( mLabel ) );
128  ruleElem.appendChild( nameElem );
129 
130  QDomElement descrElem = doc.createElement( "se:Description" );
131  QDomElement titleElem = doc.createElement( "se:Title" );
132  QString descrStr = QString( "%1 is '%2'" ).arg( attrName ).arg( mValue.toString() );
133  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
134  descrElem.appendChild( titleElem );
135  ruleElem.appendChild( descrElem );
136 
137  // create the ogc:Filter for the range
138  QString filterFunc = QString( "%1 = '%2'" )
139  .arg( attrName.replace( "\"", "\"\"" ) )
140  .arg( mValue.toString().replace( "'", "''" ) );
141  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
142 
143  mSymbol->toSld( doc, ruleElem, props );
144 }
145 
147 
149  : QgsFeatureRendererV2( "categorizedSymbol" )
150  , mAttrName( attrName )
151  , mCategories( categories )
152  , mInvertedColorRamp( false )
153  , mScaleMethod( DEFAULT_SCALE_METHOD )
154  , mAttrNum( -1 )
155  , mCounting( false )
156 {
157  for ( int i = 0; i < mCategories.count(); ++i )
158  {
160  if ( cat.symbol() == NULL )
161  {
162  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
163  mCategories.removeAt( i-- );
164  }
165  //mCategories.insert(cat.value().toString(), cat);
166  }
167 }
168 
170 {
171 }
172 
174 {
175  mSymbolHash.clear();
176 
177  for ( int i = 0; i < mCategories.size(); ++i )
178  {
180  mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : &sSkipRender );
181  }
182 }
183 
185 {
186  // TODO: special case for int, double
187  QHash<QString, QgsSymbolV2*>::iterator it = mSymbolHash.find( value.isNull() ? "" : value.toString() );
188  if ( it == mSymbolHash.end() )
189  {
190  if ( mSymbolHash.size() == 0 )
191  {
192  QgsDebugMsg( "there are no hashed symbols!!!" );
193  }
194  else
195  {
196  QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
197  }
198  return NULL;
199  }
200 
201  return *it;
202 }
203 
205 {
206  QgsSymbolV2* symbol = originalSymbolForFeature( feature );
207  if ( !symbol )
208  return 0;
209 
210  if ( !mRotation.data() && !mSizeScale.data() )
211  return symbol; // no data-defined rotation/scaling - just return the symbol
212 
213  // find out rotation, size scale
214  const double rotation = mRotation.data() ? mRotation->evaluate( feature ).toDouble() : 0;
215  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( feature ).toDouble() : 1.;
216 
217  // take a temporary symbol (or create it if doesn't exist)
218  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
219 
220  // modify the temporary symbol and return it
221  if ( tempSymbol->type() == QgsSymbolV2::Marker )
222  {
223  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
224  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
225  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
226  markerSymbol->setScaleMethod( mScaleMethod );
227  }
228  else if ( tempSymbol->type() == QgsSymbolV2::Line )
229  {
230  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
231  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
232  }
233 
234  return tempSymbol;
235 }
236 
237 
239 {
240  QgsAttributes attrs = feature.attributes();
241  QVariant value;
242  if ( mAttrNum == -1 )
243  {
244  Q_ASSERT( mExpression.data() );
245  value = mExpression->evaluate( &feature );
246  }
247  else
248  {
249  value = attrs.value( mAttrNum );
250  }
251 
252  // find the right symbol for the category
253  QgsSymbolV2 *symbol = symbolForValue( value );
254  if ( symbol == &sSkipRender )
255  return 0;
256 
257  if ( !symbol )
258  {
259  // if no symbol found use default one
260  return symbolForValue( QVariant( "" ) );
261  }
262 
263  return symbol;
264 }
265 
266 
268 {
269  for ( int i = 0; i < mCategories.count(); i++ )
270  {
271  if ( mCategories[i].value() == val )
272  return i;
273  }
274  return -1;
275 }
276 
278 {
279  int idx = -1;
280  for ( int i = 0; i < mCategories.count(); i++ )
281  {
282  if ( mCategories[i].label() == val )
283  {
284  if ( idx != -1 )
285  return -1;
286  else
287  idx = i;
288  }
289  }
290  return idx;
291 }
292 
293 bool QgsCategorizedSymbolRendererV2::updateCategoryValue( int catIndex, const QVariant &value )
294 {
295  if ( catIndex < 0 || catIndex >= mCategories.size() )
296  return false;
297  mCategories[catIndex].setValue( value );
298  return true;
299 }
300 
302 {
303  if ( catIndex < 0 || catIndex >= mCategories.size() )
304  return false;
305  mCategories[catIndex].setSymbol( symbol );
306  return true;
307 }
308 
309 bool QgsCategorizedSymbolRendererV2::updateCategoryLabel( int catIndex, QString label )
310 {
311  if ( catIndex < 0 || catIndex >= mCategories.size() )
312  return false;
313  mCategories[catIndex].setLabel( label );
314  return true;
315 }
316 
318 {
319  if ( catIndex < 0 || catIndex >= mCategories.size() )
320  return false;
321  mCategories[catIndex].setRenderState( render );
322  return true;
323 }
324 
326 {
327  if ( !cat.symbol() )
328  {
329  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
330  return;
331  }
332 
333  mCategories.append( cat );
334 }
335 
337 {
338  if ( catIndex < 0 || catIndex >= mCategories.size() )
339  return false;
340 
341  mCategories.removeAt( catIndex );
342  return true;
343 }
344 
346 {
347  mCategories.clear();
348 }
349 
351 {
352  if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
353  mCategories.move( from, to );
354 }
355 
357 {
358  return qgsVariantLessThan( c1.value(), c2.value() );
359 }
361 {
362  return qgsVariantGreaterThan( c1.value(), c2.value() );
363 }
364 
366 {
367  if ( order == Qt::AscendingOrder )
368  {
369  qSort( mCategories.begin(), mCategories.end(), valueLessThan );
370  }
371  else
372  {
373  qSort( mCategories.begin(), mCategories.end(), valueGreaterThan );
374  }
375 }
376 
378 {
379  return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
380 }
381 
383 {
384  return !labelLessThan( c1, c2 );
385 }
386 
388 {
389  if ( order == Qt::AscendingOrder )
390  {
391  qSort( mCategories.begin(), mCategories.end(), labelLessThan );
392  }
393  else
394  {
395  qSort( mCategories.begin(), mCategories.end(), labelGreaterThan );
396  }
397 }
398 
400 {
401  mCounting = context.rendererScale() == 0.0;
402 
403  // make sure that the hash table is up to date
404  rebuildHash();
405 
406  // find out classification attribute index from name
407  mAttrNum = fields.fieldNameIndex( mAttrName );
408  if ( mAttrNum == -1 )
409  {
410  mExpression.reset( new QgsExpression( mAttrName ) );
411  mExpression->prepare( fields );
412  }
413 
414  QgsCategoryList::iterator it = mCategories.begin();
415  for ( ; it != mCategories.end(); ++it )
416  {
417  it->symbol()->startRender( context, &fields );
418 
419  if ( mRotation.data() || mSizeScale.data() )
420  {
421  QgsSymbolV2* tempSymbol = it->symbol()->clone();
422  tempSymbol->setRenderHints(( mRotation.data() ? QgsSymbolV2::DataDefinedRotation : 0 ) |
424  tempSymbol->startRender( context, &fields );
425  mTempSymbols[ it->symbol()] = tempSymbol;
426  }
427  }
428 }
429 
431 {
432  QgsCategoryList::iterator it = mCategories.begin();
433  for ( ; it != mCategories.end(); ++it )
434  it->symbol()->stopRender( context );
435 
436  // cleanup mTempSymbols
437  QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
438  for ( ; it2 != mTempSymbols.end(); ++it2 )
439  {
440  it2.value()->stopRender( context );
441  delete it2.value();
442  }
443  mTempSymbols.clear();
444  mExpression.reset();
445 }
446 
448 {
449  QSet<QString> attributes;
450 
451  // mAttrName can contain either attribute name or an expression.
452  // Sometimes it is not possible to distinguish between those two,
453  // e.g. "a - b" can be both a valid attribute name or expression.
454  // Since we do not have access to fields here, try both options.
455  attributes << mAttrName;
456 
457  QgsExpression testExpr( mAttrName );
458  if ( !testExpr.hasParserError() )
459  attributes.unite( testExpr.referencedColumns().toSet() );
460 
461  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
462  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
463 
464  QgsCategoryList::const_iterator catIt = mCategories.constBegin();
465  for ( ; catIt != mCategories.constEnd(); ++catIt )
466  {
467  QgsSymbolV2* catSymbol = catIt->symbol();
468  if ( catSymbol )
469  {
470  attributes.unite( catSymbol->usedAttributes() );
471  }
472  }
473  return attributes.toList();
474 }
475 
477 {
478  QString s = QString( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
479  for ( int i = 0; i < mCategories.count(); i++ )
480  s += mCategories[i].dump();
481  return s;
482 }
483 
485 {
487  if ( mSourceSymbol.data() )
488  r->setSourceSymbol( mSourceSymbol->clone() );
489  if ( mSourceColorRamp.data() )
490  {
491  r->setSourceColorRamp( mSourceColorRamp->clone() );
493  }
497  //r->setScaleMethod( scaleMethod() );
498 
499  copyPaintEffect( r );
500  return r;
501 }
502 
503 void QgsCategorizedSymbolRendererV2::toSld( QDomDocument &doc, QDomElement &element ) const
504 {
505  QgsStringMap props;
506  props[ "attribute" ] = mAttrName;
507  if ( mRotation.data() )
508  props[ "angle" ] = mRotation->expression();
509  if ( mSizeScale.data() )
510  props[ "scale" ] = mSizeScale->expression();
511 
512  // create a Rule for each range
513  for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
514  {
515  QgsStringMap catProps( props );
516  it->toSld( doc, element, catProps );
517  }
518 }
519 
521 {
522  QgsSymbolV2List lst;
523  for ( int i = 0; i < mCategories.count(); i++ )
524  lst.append( mCategories[i].symbol() );
525  return lst;
526 }
527 
529 {
530  QDomElement symbolsElem = element.firstChildElement( "symbols" );
531  if ( symbolsElem.isNull() )
532  return NULL;
533 
534  QDomElement catsElem = element.firstChildElement( "categories" );
535  if ( catsElem.isNull() )
536  return NULL;
537 
538  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
539  QgsCategoryList cats;
540 
541  QDomElement catElem = catsElem.firstChildElement();
542  while ( !catElem.isNull() )
543  {
544  if ( catElem.tagName() == "category" )
545  {
546  QVariant value = QVariant( catElem.attribute( "value" ) );
547  QString symbolName = catElem.attribute( "symbol" );
548  QString label = catElem.attribute( "label" );
549  bool render = catElem.attribute( "render" ) != "false";
550  if ( symbolMap.contains( symbolName ) )
551  {
552  QgsSymbolV2* symbol = symbolMap.take( symbolName );
553  cats.append( QgsRendererCategoryV2( value, symbol, label, render ) );
554  }
555  }
556  catElem = catElem.nextSiblingElement();
557  }
558 
559  QString attrName = element.attribute( "attr" );
560 
562 
563  // delete symbols if there are any more
565 
566  // try to load source symbol (optional)
567  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
568  if ( !sourceSymbolElem.isNull() )
569  {
570  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
571  if ( sourceSymbolMap.contains( "0" ) )
572  {
573  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
574  }
575  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
576  }
577 
578  // try to load color ramp (optional)
579  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
580  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
581  {
582  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
583  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
584  if ( !invertedColorRampElem.isNull() )
585  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
586  }
587 
588  QDomElement rotationElem = element.firstChildElement( "rotation" );
589  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
590  {
591  QgsCategoryList::iterator it = r->mCategories.begin();
592  for ( ; it != r->mCategories.end(); ++it )
593  {
594  convertSymbolRotation( it->symbol(), rotationElem.attribute( "field" ) );
595  }
596  if ( r->mSourceSymbol.data() )
597  {
598  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
599  }
600  }
601 
602  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
603  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
604  {
605  QgsCategoryList::iterator it = r->mCategories.begin();
606  for ( ; it != r->mCategories.end(); ++it )
607  {
608  convertSymbolSizeScale( it->symbol(),
609  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
610  sizeScaleElem.attribute( "field" ) );
611  }
612  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
613  {
615  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
616  sizeScaleElem.attribute( "field" ) );
617  }
618  }
619 
620  // TODO: symbol levels
621  return r;
622 }
623 
624 QDomElement QgsCategorizedSymbolRendererV2::save( QDomDocument& doc )
625 {
626  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
627  rendererElem.setAttribute( "type", "categorizedSymbol" );
628  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
629  rendererElem.setAttribute( "attr", mAttrName );
630 
631  // categories
632  int i = 0;
634  QDomElement catsElem = doc.createElement( "categories" );
635  QgsCategoryList::const_iterator it = mCategories.constBegin();
636  for ( ; it != mCategories.end(); ++it )
637  {
638  const QgsRendererCategoryV2& cat = *it;
639  QString symbolName = QString::number( i );
640  symbols.insert( symbolName, cat.symbol() );
641 
642  QDomElement catElem = doc.createElement( "category" );
643  catElem.setAttribute( "value", cat.value().toString() );
644  catElem.setAttribute( "symbol", symbolName );
645  catElem.setAttribute( "label", cat.label() );
646  catElem.setAttribute( "render", cat.renderState() ? "true" : "false" );
647  catsElem.appendChild( catElem );
648  i++;
649  }
650 
651  rendererElem.appendChild( catsElem );
652 
653  // save symbols
654  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
655  rendererElem.appendChild( symbolsElem );
656 
657  // save source symbol
658  if ( mSourceSymbol.data() )
659  {
660  QgsSymbolV2Map sourceSymbols;
661  sourceSymbols.insert( "0", mSourceSymbol.data() );
662  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
663  rendererElem.appendChild( sourceSymbolElem );
664  }
665 
666  // save source color ramp
667  if ( mSourceColorRamp.data() )
668  {
669  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
670  rendererElem.appendChild( colorRampElem );
671  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
672  invertedElem.setAttribute( "value", mInvertedColorRamp );
673  rendererElem.appendChild( invertedElem );
674  }
675 
676  QDomElement rotationElem = doc.createElement( "rotation" );
677  if ( mRotation.data() )
678  rotationElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) );
679  rendererElem.appendChild( rotationElem );
680 
681  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
682  if ( mSizeScale.data() )
683  sizeScaleElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mSizeScale.data() ) );
684  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
685  rendererElem.appendChild( sizeScaleElem );
686 
687  if ( mPaintEffect )
688  mPaintEffect->saveProperties( doc, rendererElem );
689 
690  return rendererElem;
691 }
692 
694 {
696  int count = categories().count();
697  for ( int i = 0; i < count; i++ )
698  {
699  const QgsRendererCategoryV2& cat = categories()[i];
700  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( cat.symbol(), iconSize );
701  lst << qMakePair( cat.label(), pix );
702  }
703  return lst;
704 }
705 
707 {
708  Q_UNUSED( scaleDenominator );
710 
711  foreach ( const QgsRendererCategoryV2& cat, mCategories )
712  {
713  if ( rule.isEmpty() || cat.label() == rule )
714  {
715  lst << qMakePair( cat.label(), cat.symbol() );
716  }
717  }
718  return lst;
719 }
720 
722 {
724  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
725  {
726  // check that all symbols that have the same size expression
727  QgsDataDefined ddSize;
728  foreach ( QgsRendererCategoryV2 category, mCategories )
729  {
730  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( category.symbol() );
731  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
732  {
733  // no common size expression
735  }
736  else
737  {
738  ddSize = symbol->dataDefinedSize();
739  }
740  }
741 
742  if ( !ddSize.isActive() || !ddSize.useExpression() )
743  {
745  }
746 
747  QgsScaleExpression exp( ddSize.expressionString() );
748  if ( exp.type() != QgsScaleExpression::Unknown )
749  {
750  QgsLegendSymbolItemV2 title( NULL, exp.baseExpression(), "" );
751  lst << title;
752  foreach ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
753  {
754  QgsLegendSymbolItemV2 si( mSourceSymbol.data(), QString::number( v ), "" );
755  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
756  s->setColor( QColor( 0, 0, 0 ) );
757  s->setDataDefinedSize( QgsDataDefined() );
758  s->setSize( exp.size( v ) );
759  lst << si;
760  }
761  // now list the categorized symbols
763  foreach ( QgsLegendSymbolItemV2 item, list2 )
764  lst << item;
765  return lst;
766  }
767  }
768 
770 }
771 
773 {
774  return mSourceSymbol.data();
775 }
777 {
778  mSourceSymbol.reset( sym );
779 }
780 
782 {
783  return mSourceColorRamp.data();
784 }
785 
787 {
788  mSourceColorRamp.reset( ramp );
789 }
790 
792 {
793  setSourceColorRamp( ramp );
794  setInvertedColorRamp( inverted );
795  double num = mCategories.count() - 1;
796  double count = 0;
797 
798  QgsRandomColorsV2* randomRamp = dynamic_cast<QgsRandomColorsV2*>( ramp );
799  if ( randomRamp )
800  {
801  //ramp is a random colors ramp, so inform it of the total number of required colors
802  //this allows the ramp to pregenerate a set of visually distinctive colors
803  randomRamp->setTotalColorCount( mCategories.count() );
804  }
805 
806  foreach ( const QgsRendererCategoryV2 &cat, mCategories )
807  {
808  double value = count / num;
809  if ( mInvertedColorRamp ) value = 1.0 - value;
810  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
811  count += 1;
812  }
813 }
814 
815 void QgsCategorizedSymbolRendererV2::setRotationField( QString fieldOrExpression )
816 {
818 }
819 
821 {
822  return mRotation.data() ? QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) : QString();
823 }
824 
825 void QgsCategorizedSymbolRendererV2::setSizeScaleField( QString fieldOrExpression )
826 {
828 }
829 
831 {
833 }
834 
836 {
837  int i = 0;
838  foreach ( QgsRendererCategoryV2 cat, mCategories )
839  {
840  QgsSymbolV2* symbol = sym->clone();
841  symbol->setColor( cat.symbol()->color() );
842  updateCategorySymbol( i, symbol );
843  ++i;
844  }
845 }
846 
848 {
850  QgsCategoryList::const_iterator catIt = mCategories.constBegin();
851  for ( ; catIt != mCategories.constEnd(); ++catIt )
852  {
853  setScaleMethodToSymbol( catIt->symbol(), scaleMethod );
854  }
855 }
856 
858 {
859  return true;
860 }
861 
863 {
864  bool ok;
865  int index = key.toInt( &ok );
866  if ( ok && index >= 0 && index < mCategories.size() )
867  return mCategories[ index ].renderState();
868  else
869  return true;
870 }
871 
873 {
874  bool ok;
875  int index = key.toInt( &ok );
876  if ( ok )
877  updateCategoryRenderState( index, state );
878 }
879 
881 
883 {
884  if ( renderer->type() == "categorizedSymbol" )
885  {
886  return dynamic_cast<QgsCategorizedSymbolRendererV2*>( renderer->clone() );
887  }
888  if ( renderer->type() == "pointDisplacement" )
889  {
890  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
891  if ( pointDisplacementRenderer )
892  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
893  }
894  if ( renderer->type() == "invertedPolygonRenderer" )
895  {
896  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
897  if ( invertedPolygonRenderer )
898  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
899  }
900 
901  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
902  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
903 
905  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols();
906  if ( symbols.size() > 0 )
907  {
908  r->setSourceSymbol( symbols.at( 0 )->clone() );
909  }
910 
911  return r;
912 }
QMap< QString, QgsSymbolV2 * > QgsSymbolV2Map
Definition: qgsrendererv2.h:40
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:86
static QgsSymbolV2Map loadSymbols(QDomElement &element)
void setValue(const QVariant &value)
void setLabel(const QString &label)
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:48
static unsigned index
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.h:93
virtual void stopRender(QgsRenderContext &context) override
virtual QgsSymbolV2 * originalSymbolForFeature(QgsFeature &feature) override
Return symbol for feature.
QStringList referencedColumns() const
Get list of columns referenced by the expression.
A container class for data source field mapping or expression.
virtual bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
const QgsCategoryList & categories() const
static QList< double > prettyBreaks(double minimum, double maximum, int classes)
Computes a sequence of about 'classes' equally spaced round values which cover the range of values fr...
static QgsVectorColorRampV2 * loadColorRamp(QDomElement &element)
QList< QgsSymbolV2 * > QgsSymbolV2List
Definition: qgsrendererv2.h:39
SymbolType type() const
Definition: qgssymbolv2.h:86
QSet< QString > usedAttributes() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
int fieldNameIndex(const QString &fieldName) const
Look up field's index from name - case insensitive TODO: sort out case sensitive (indexFromName()) vs...
Definition: qgsfield.cpp:350
double rendererScale() const
virtual QgsSymbolV2 * clone() const =0
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
int categoryIndexForLabel(QString val)
return index of category with specified label (-1 if not found or not unique)
QScopedPointer< QgsSymbolV2 > mSourceSymbol
bool updateCategoryRenderState(int catIndex, bool render)
QScopedPointer< QgsExpression > mRotation
Container of fields for a vector layer.
Definition: qgsfield.h:173
virtual void setTotalColorCount(const int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
QString expressionString() const
void moveCategory(int from, int to)
Moves the category at index position from to index position to.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:119
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Definition: qgis.cpp:264
void setSizeScaleField(QString fieldOrExpression)
QScopedPointer< QgsSymbolV2 > mSymbol
QMap< QString, QString > QgsStringMap
Definition: qgis.h:438
QgsPaintEffect * mPaintEffect
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Definition: qgis.cpp:237
void setWidth(double width)
QHash< QString, QgsSymbolV2 * > mSymbolHash
hashtable for faster access to symbols
virtual QDomElement save(QDomDocument &doc) override
store renderer info to XML element
QString rotationField() const override
return rotation field name (or empty string if not set or not supported by renderer) ...
QScopedPointer< QgsExpression > mSizeScale
QString type() const
Definition: qgsrendererv2.h:82
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
void setColor(const QColor &color)
virtual QgsFeatureRendererV2 * clone() const =0
QList< QgsRendererCategoryV2 > QgsCategoryList
static QDomElement saveColorRamp(QString name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
QgsLegendSymbolListV2 legendSymbolItemsV2() const override
virtual void toSld(QDomDocument &doc, QDomElement &element) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
QScopedPointer< QgsVectorColorRampV2 > mSourceColorRamp
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
void startRender(QgsRenderContext &context, const QgsFields *fields=0)
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, QString rule=QString()) override
return a list of item text / symbol
QgsAttributes attributes() const
Returns the feature's attributes.
Definition: qgsfeature.cpp:90
bool labelGreaterThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
#define DEFAULT_SCALE_METHOD
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, QString function)
QgsSymbolV2 * symbolForValue(QVariant value)
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, QString tagName, QDomDocument &doc)
static QgsFeatureRendererV2 * create(QDomElement &element)
create renderer from XML element
int categoryIndexForValue(QVariant val)
return index of category with specified value (-1 if not found)
void setAngle(double angle)
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
void setSize(double size)
virtual void checkLegendSymbolItem(QString key, bool state=true) override
item in symbology was checked
virtual QgsFeatureRendererV2 * clone() const override
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
bool labelLessThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
static QgsCategorizedSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsCategorizedSymbolRendererV2 from an existing renderer.
static void convertSymbolSizeScale(QgsSymbolV2 *symbol, QgsSymbolV2::ScaleMethod method, const QString &field)
QList< QPair< QString, QPixmap > > QgsLegendSymbologyList
QgsFeatureRendererV2 * embeddedRenderer() const
bool useExpression() const
void updateColorRamp(QgsVectorColorRampV2 *ramp, bool inverted=false)
QHash< QgsSymbolV2 *, QgsSymbolV2 * > mTempSymbols
temporary symbols, used for data-defined rotation and scaling
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
virtual bool legendSymbolItemChecked(QString key) override
item in symbology was checked
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature) override
to be overridden
A renderer that automatically displaces points with the same position.
bool updateCategoryLabel(int catIndex, QString label)
void setUsingSymbolLevels(bool usingSymbolLevels)
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
bool valueLessThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
Contains information about the context of a rendering operation.
const QgsFeatureRendererV2 * embeddedRenderer() const
virtual QList< QString > usedAttributes() override
QgsCategorizedSymbolRendererV2(QString attrName=QString(), QgsCategoryList categories=QgsCategoryList())
void copyPaintEffect(QgsFeatureRendererV2 *destRenderer) const
Copies paint effect of this renderer to another renderer.
static QgsExpression * fieldOrExpressionToExpression(const QString &fieldOrExpression)
Return a new valid expression instance for given field or expression string.
QVector< QVariant > QgsAttributes
Definition: qgsfeature.h:106
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const
Return a list of symbology items for the legend.
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
bool updateCategoryValue(int catIndex, const QVariant &value)
bool valueGreaterThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
static QString fieldOrExpressionFromExpression(QgsExpression *expression)
Return a field name if the whole expression is just a name of the field .
bool usingSymbolLevels() const
void setScaleMethodToSymbol(QgsSymbolV2 *symbol, int scaleMethod)
bool updateCategorySymbol(int catIndex, QgsSymbolV2 *symbol)
int mAttrNum
attribute index (derived from attribute name in startRender)
void setRenderHints(int hints)
Definition: qgssymbolv2.h:162
static void clearSymbolMap(QgsSymbolV2Map &symbols)
void setRotationField(QString fieldOrExpression) override
sets rotation field of renderer (if supported by the renderer)
static QgsSymbolV2::ScaleMethod decodeScaleMethod(QString str)
void swap(QgsRendererCategoryV2 &other)
QgsRendererCategoryV2 & operator=(QgsRendererCategoryV2 cat)
QList< QgsLegendSymbolItemV2 > QgsLegendSymbolListV2
bool hasDefaultValues() const
Returns whether the data defined container is set to all the default values, ie, disabled, with empty expression and no assigned field.
QgsSymbolV2::ScaleMethod scaleMethod() const
virtual QgsSymbolV2List symbols() override
for symbol levels
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=0)
void addCategory(const QgsRendererCategoryV2 &category)
double size
Definition: qgssvgcache.cpp:77
QList< QPair< QString, QgsSymbolV2 * > > QgsLegendSymbolList
Definition: qgsrendererv2.h:43
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
bool isActive() const
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize) override
return a list of symbology items for the legend
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
QScopedPointer< QgsExpression > mExpression
static void convertSymbolRotation(QgsSymbolV2 *symbol, const QString &field)
QColor color() const
virtual QString dump() const override
for debugging