QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 
25 #include "qgsfeature.h"
26 #include "qgsvectorlayer.h"
27 #include "qgslogger.h"
28 
29 #include <QDomDocument>
30 #include <QDomElement>
31 #include <QSettings> // for legend
32 
34  : mRender( true )
35 {
36 }
37 
38 QgsRendererCategoryV2::QgsRendererCategoryV2( QVariant value, QgsSymbolV2* symbol, QString label, bool render )
39  : mValue( value )
40  , mSymbol( symbol )
41  , mLabel( label )
42  , mRender( render )
43 {
44 }
45 
47  : mValue( cat.mValue )
48  , mSymbol( cat.mSymbol.data() ? cat.mSymbol->clone() : NULL )
49  , mLabel( cat.mLabel )
50  , mRender( cat.mRender )
51 {
52 }
53 
54 // copy+swap idion, the copy is done through the 'pass by value'
56 {
57  swap( cat );
58  return *this;
59 }
60 
62 {
63  qSwap( mValue, cat.mValue );
64  qSwap( mSymbol, cat.mSymbol );
65  qSwap( mLabel, cat.mLabel );
66 }
67 
69 {
70  return mValue;
71 }
72 
74 {
75  return mSymbol.data();
76 }
77 
79 {
80  return mLabel;
81 }
82 
84 {
85  return mRender;
86 }
87 
88 void QgsRendererCategoryV2::setValue( const QVariant &value )
89 {
90  mValue = value;
91 }
92 
94 {
95  if ( mSymbol.data() != s ) mSymbol.reset( s );
96 }
97 
98 void QgsRendererCategoryV2::setLabel( const QString &label )
99 {
100  mLabel = label;
101 }
102 
104 {
105  mRender = render;
106 }
107 
109 {
110  return QString( "%1::%2::%3:%4\n" ).arg( mValue.toString() ).arg( mLabel ).arg( mSymbol->dump() ).arg( mRender );
111 }
112 
113 void QgsRendererCategoryV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
114 {
115  if ( !mSymbol.data() || props.value( "attribute", "" ).isEmpty() )
116  return;
117 
118  QString attrName = props[ "attribute" ];
119 
120  QDomElement ruleElem = doc.createElement( "se:Rule" );
121  element.appendChild( ruleElem );
122 
123  QDomElement nameElem = doc.createElement( "se:Name" );
124  nameElem.appendChild( doc.createTextNode( mLabel ) );
125  ruleElem.appendChild( nameElem );
126 
127  QDomElement descrElem = doc.createElement( "se:Description" );
128  QDomElement titleElem = doc.createElement( "se:Title" );
129  QString descrStr = QString( "%1 is '%2'" ).arg( attrName ).arg( mValue.toString() );
130  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
131  descrElem.appendChild( titleElem );
132  ruleElem.appendChild( descrElem );
133 
134  // create the ogc:Filter for the range
135  QString filterFunc = QString( "%1 = '%2'" )
136  .arg( attrName.replace( "\"", "\"\"" ) )
137  .arg( mValue.toString().replace( "'", "''" ) );
138  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
139 
140  mSymbol->toSld( doc, ruleElem, props );
141 }
142 
144 
146  : QgsFeatureRendererV2( "categorizedSymbol" )
147  , mAttrName( attrName )
148  , mCategories( categories )
149  , mInvertedColorRamp( false )
150  , mScaleMethod( DEFAULT_SCALE_METHOD )
151  , mAttrNum( -1 )
152  , mCounting( false )
153 {
154  for ( int i = 0; i < mCategories.count(); ++i )
155  {
157  if ( cat.symbol() == NULL )
158  {
159  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
160  mCategories.removeAt( i-- );
161  }
162  //mCategories.insert(cat.value().toString(), cat);
163  }
164 }
165 
167 {
168 }
169 
171 {
172  mSymbolHash.clear();
173 
174  for ( int i = 0; i < mCategories.size(); ++i )
175  {
177  mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : &sSkipRender );
178  }
179 }
180 
182 {
183  // TODO: special case for int, double
184  QHash<QString, QgsSymbolV2*>::iterator it = mSymbolHash.find( value.isNull() ? "" : value.toString() );
185  if ( it == mSymbolHash.end() )
186  {
187  if ( mSymbolHash.size() == 0 )
188  {
189  QgsDebugMsg( "there are no hashed symbols!!!" );
190  }
191  else
192  {
193  QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
194  }
195  return NULL;
196  }
197 
198  return *it;
199 }
200 
202 {
203  QgsSymbolV2* symbol = originalSymbolForFeature( feature );
204  if ( !symbol )
205  return 0;
206 
207  if ( !mRotation.data() && !mSizeScale.data() )
208  return symbol; // no data-defined rotation/scaling - just return the symbol
209 
210  // find out rotation, size scale
211  const double rotation = mRotation.data() ? mRotation->evaluate( feature ).toDouble() : 0;
212  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( feature ).toDouble() : 1.;
213 
214  // take a temporary symbol (or create it if doesn't exist)
215  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
216 
217  // modify the temporary symbol and return it
218  if ( tempSymbol->type() == QgsSymbolV2::Marker )
219  {
220  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
221  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
222  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
223  markerSymbol->setScaleMethod( mScaleMethod );
224  }
225  else if ( tempSymbol->type() == QgsSymbolV2::Line )
226  {
227  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
228  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
229  }
230 
231  return tempSymbol;
232 }
233 
234 
236 {
237  const QgsAttributes& attrs = feature.attributes();
238  QVariant value;
239  if ( mAttrNum == -1 )
240  {
241  Q_ASSERT( mExpression.data() );
242  value = mExpression->evaluate( &feature );
243  }
244  else
245  {
246  value = attrs.value( mAttrNum );
247  }
248 
249  // find the right symbol for the category
250  QgsSymbolV2 *symbol = symbolForValue( value );
251  if ( symbol == &sSkipRender )
252  return 0;
253 
254  if ( !symbol )
255  {
256  // if no symbol found use default one
257  return symbolForValue( QVariant( "" ) );
258  }
259 
260  return symbol;
261 }
262 
263 
265 {
266  for ( int i = 0; i < mCategories.count(); i++ )
267  {
268  if ( mCategories[i].value() == val )
269  return i;
270  }
271  return -1;
272 }
273 
275 {
276  int idx = -1;
277  for ( int i = 0; i < mCategories.count(); i++ )
278  {
279  if ( mCategories[i].label() == val )
280  {
281  if ( idx != -1 )
282  return -1;
283  else
284  idx = i;
285  }
286  }
287  return idx;
288 }
289 
290 bool QgsCategorizedSymbolRendererV2::updateCategoryValue( int catIndex, const QVariant &value )
291 {
292  if ( catIndex < 0 || catIndex >= mCategories.size() )
293  return false;
294  mCategories[catIndex].setValue( value );
295  return true;
296 }
297 
299 {
300  if ( catIndex < 0 || catIndex >= mCategories.size() )
301  return false;
302  mCategories[catIndex].setSymbol( symbol );
303  return true;
304 }
305 
306 bool QgsCategorizedSymbolRendererV2::updateCategoryLabel( int catIndex, QString label )
307 {
308  if ( catIndex < 0 || catIndex >= mCategories.size() )
309  return false;
310  mCategories[catIndex].setLabel( label );
311  return true;
312 }
313 
315 {
316  if ( catIndex < 0 || catIndex >= mCategories.size() )
317  return false;
318  mCategories[catIndex].setRenderState( render );
319  return true;
320 }
321 
323 {
324  if ( !cat.symbol() )
325  {
326  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
327  return;
328  }
329 
330  mCategories.append( cat );
331 }
332 
334 {
335  if ( catIndex < 0 || catIndex >= mCategories.size() )
336  return false;
337 
338  mCategories.removeAt( catIndex );
339  return true;
340 }
341 
343 {
344  mCategories.clear();
345 }
346 
348 {
349  if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
350  mCategories.move( from, to );
351 }
352 
354 {
355  return qgsVariantLessThan( c1.value(), c2.value() );
356 }
358 {
359  return qgsVariantGreaterThan( c1.value(), c2.value() );
360 }
361 
363 {
364  if ( order == Qt::AscendingOrder )
365  {
366  qSort( mCategories.begin(), mCategories.end(), valueLessThan );
367  }
368  else
369  {
370  qSort( mCategories.begin(), mCategories.end(), valueGreaterThan );
371  }
372 }
373 
375 {
376  return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
377 }
378 
380 {
381  return !labelLessThan( c1, c2 );
382 }
383 
385 {
386  if ( order == Qt::AscendingOrder )
387  {
388  qSort( mCategories.begin(), mCategories.end(), labelLessThan );
389  }
390  else
391  {
392  qSort( mCategories.begin(), mCategories.end(), labelGreaterThan );
393  }
394 }
395 
397 {
398  mCounting = context.rendererScale() == 0.0;
399 
400  // make sure that the hash table is up to date
401  rebuildHash();
402 
403  // find out classification attribute index from name
404  mAttrNum = fields.fieldNameIndex( mAttrName );
405  if ( mAttrNum == -1 )
406  {
407  mExpression.reset( new QgsExpression( mAttrName ) );
408  mExpression->prepare( fields );
409  }
410 
411  QgsCategoryList::iterator it = mCategories.begin();
412  for ( ; it != mCategories.end(); ++it )
413  {
414  it->symbol()->startRender( context, &fields );
415 
416  if ( mRotation.data() || mSizeScale.data() )
417  {
418  QgsSymbolV2* tempSymbol = it->symbol()->clone();
419  tempSymbol->setRenderHints(( mRotation.data() ? QgsSymbolV2::DataDefinedRotation : 0 ) |
421  tempSymbol->startRender( context, &fields );
422  mTempSymbols[ it->symbol()] = tempSymbol;
423  }
424  }
425 }
426 
428 {
429  QgsCategoryList::iterator it = mCategories.begin();
430  for ( ; it != mCategories.end(); ++it )
431  it->symbol()->stopRender( context );
432 
433  // cleanup mTempSymbols
434  QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
435  for ( ; it2 != mTempSymbols.end(); ++it2 )
436  {
437  it2.value()->stopRender( context );
438  delete it2.value();
439  }
440  mTempSymbols.clear();
441  mExpression.reset();
442 }
443 
445 {
446  QSet<QString> attributes;
447 
448  // mAttrName can contain either attribute name or an expression.
449  // Sometimes it is not possible to distinguish between those two,
450  // e.g. "a - b" can be both a valid attribute name or expression.
451  // Since we do not have access to fields here, try both options.
452  attributes << mAttrName;
453 
454  QgsExpression testExpr( mAttrName );
455  if ( !testExpr.hasParserError() )
456  attributes.unite( testExpr.referencedColumns().toSet() );
457 
458  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
459  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
460 
461  QgsCategoryList::const_iterator catIt = mCategories.constBegin();
462  for ( ; catIt != mCategories.constEnd(); ++catIt )
463  {
464  QgsSymbolV2* catSymbol = catIt->symbol();
465  if ( catSymbol )
466  {
467  attributes.unite( catSymbol->usedAttributes() );
468  }
469  }
470  return attributes.toList();
471 }
472 
474 {
475  QString s = QString( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
476  for ( int i = 0; i < mCategories.count(); i++ )
477  s += mCategories[i].dump();
478  return s;
479 }
480 
482 {
484  if ( mSourceSymbol.data() )
485  r->setSourceSymbol( mSourceSymbol->clone() );
486  if ( mSourceColorRamp.data() )
487  {
488  r->setSourceColorRamp( mSourceColorRamp->clone() );
490  }
494  r->setScaleMethod( scaleMethod() );
495  return r;
496 }
497 
498 void QgsCategorizedSymbolRendererV2::toSld( QDomDocument &doc, QDomElement &element ) const
499 {
500  QgsStringMap props;
501  props[ "attribute" ] = mAttrName;
502  if ( mRotation.data() )
503  props[ "angle" ] = mRotation->expression();
504  if ( mSizeScale.data() )
505  props[ "scale" ] = mSizeScale->expression();
506 
507  // create a Rule for each range
508  for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
509  {
510  QgsStringMap catProps( props );
511  it->toSld( doc, element, catProps );
512  }
513 }
514 
516 {
517  QgsSymbolV2List lst;
518  for ( int i = 0; i < mCategories.count(); i++ )
519  lst.append( mCategories[i].symbol() );
520  return lst;
521 }
522 
524 {
525  QDomElement symbolsElem = element.firstChildElement( "symbols" );
526  if ( symbolsElem.isNull() )
527  return NULL;
528 
529  QDomElement catsElem = element.firstChildElement( "categories" );
530  if ( catsElem.isNull() )
531  return NULL;
532 
533  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
534  QgsCategoryList cats;
535 
536  QDomElement catElem = catsElem.firstChildElement();
537  while ( !catElem.isNull() )
538  {
539  if ( catElem.tagName() == "category" )
540  {
541  QVariant value = QVariant( catElem.attribute( "value" ) );
542  QString symbolName = catElem.attribute( "symbol" );
543  QString label = catElem.attribute( "label" );
544  bool render = catElem.attribute( "render" ) != "false";
545  if ( symbolMap.contains( symbolName ) )
546  {
547  QgsSymbolV2* symbol = symbolMap.take( symbolName );
548  cats.append( QgsRendererCategoryV2( value, symbol, label, render ) );
549  }
550  }
551  catElem = catElem.nextSiblingElement();
552  }
553 
554  QString attrName = element.attribute( "attr" );
555 
557 
558  // delete symbols if there are any more
560 
561  // try to load source symbol (optional)
562  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
563  if ( !sourceSymbolElem.isNull() )
564  {
565  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
566  if ( sourceSymbolMap.contains( "0" ) )
567  {
568  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
569  }
570  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
571  }
572 
573  // try to load color ramp (optional)
574  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
575  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
576  {
577  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
578  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
579  if ( !invertedColorRampElem.isNull() )
580  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
581  }
582 
583  QDomElement rotationElem = element.firstChildElement( "rotation" );
584  if ( !rotationElem.isNull() )
585  r->setRotationField( rotationElem.attribute( "field" ) );
586 
587  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
588  if ( !sizeScaleElem.isNull() )
589  {
590  r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
591  r->setScaleMethod( QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ) );
592  }
593 
594  // TODO: symbol levels
595  return r;
596 }
597 
598 QDomElement QgsCategorizedSymbolRendererV2::save( QDomDocument& doc )
599 {
600  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
601  rendererElem.setAttribute( "type", "categorizedSymbol" );
602  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
603  rendererElem.setAttribute( "attr", mAttrName );
604 
605  // categories
606  int i = 0;
608  QDomElement catsElem = doc.createElement( "categories" );
609  QgsCategoryList::const_iterator it = mCategories.constBegin();
610  for ( ; it != mCategories.end(); ++it )
611  {
612  const QgsRendererCategoryV2& cat = *it;
613  QString symbolName = QString::number( i );
614  symbols.insert( symbolName, cat.symbol() );
615 
616  QDomElement catElem = doc.createElement( "category" );
617  catElem.setAttribute( "value", cat.value().toString() );
618  catElem.setAttribute( "symbol", symbolName );
619  catElem.setAttribute( "label", cat.label() );
620  catElem.setAttribute( "render", cat.renderState() ? "true" : "false" );
621  catsElem.appendChild( catElem );
622  i++;
623  }
624 
625  rendererElem.appendChild( catsElem );
626 
627  // save symbols
628  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
629  rendererElem.appendChild( symbolsElem );
630 
631  // save source symbol
632  if ( mSourceSymbol.data() )
633  {
634  QgsSymbolV2Map sourceSymbols;
635  sourceSymbols.insert( "0", mSourceSymbol.data() );
636  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
637  rendererElem.appendChild( sourceSymbolElem );
638  }
639 
640  // save source color ramp
641  if ( mSourceColorRamp.data() )
642  {
643  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
644  rendererElem.appendChild( colorRampElem );
645  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
646  invertedElem.setAttribute( "value", mInvertedColorRamp );
647  rendererElem.appendChild( invertedElem );
648  }
649 
650  QDomElement rotationElem = doc.createElement( "rotation" );
651  if ( mRotation.data() )
652  rotationElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) );
653  rendererElem.appendChild( rotationElem );
654 
655  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
656  if ( mSizeScale.data() )
657  sizeScaleElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mSizeScale.data() ) );
658  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
659  rendererElem.appendChild( sizeScaleElem );
660 
661  return rendererElem;
662 }
663 
665 {
667  int count = categories().count();
668  for ( int i = 0; i < count; i++ )
669  {
670  const QgsRendererCategoryV2& cat = categories()[i];
671  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( cat.symbol(), iconSize );
672  lst << qMakePair( cat.label(), pix );
673  }
674  return lst;
675 }
676 
678 {
679  Q_UNUSED( scaleDenominator );
681 
682  foreach ( const QgsRendererCategoryV2& cat, mCategories )
683  {
684  if ( rule.isEmpty() || cat.label() == rule )
685  {
686  lst << qMakePair( cat.label(), cat.symbol() );
687  }
688  }
689  return lst;
690 }
691 
692 
694 {
695  return mSourceSymbol.data();
696 }
698 {
699  mSourceSymbol.reset( sym );
700 }
701 
703 {
704  return mSourceColorRamp.data();
705 }
706 
708 {
709  mSourceColorRamp.reset( ramp );
710 }
711 
713 {
714  setSourceColorRamp( ramp );
715  setInvertedColorRamp( inverted );
716  double num = mCategories.count() - 1;
717  double count = 0;
718 
719  QgsRandomColorsV2* randomRamp = dynamic_cast<QgsRandomColorsV2*>( ramp );
720  if ( randomRamp )
721  {
722  //ramp is a random colors ramp, so inform it of the total number of required colors
723  //this allows the ramp to pregenerate a set of visually distinctive colors
724  randomRamp->setTotalColorCount( mCategories.count() );
725  }
726 
727  foreach ( const QgsRendererCategoryV2 &cat, mCategories )
728  {
729  double value = count / num;
730  if ( mInvertedColorRamp ) value = 1.0 - value;
731  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
732  count += 1;
733  }
734 }
735 
736 void QgsCategorizedSymbolRendererV2::setRotationField( QString fieldOrExpression )
737 {
739 }
740 
742 {
743  return mRotation.data() ? QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) : QString();
744 }
745 
746 void QgsCategorizedSymbolRendererV2::setSizeScaleField( QString fieldOrExpression )
747 {
749 }
750 
752 {
754 }
755 
757 {
758  int i = 0;
759  foreach ( QgsRendererCategoryV2 cat, mCategories )
760  {
761  QgsSymbolV2* symbol = sym->clone();
762  symbol->setColor( cat.symbol()->color() );
763  updateCategorySymbol( i, symbol );
764  ++i;
765  }
766 }
767 
769 {
771  QgsCategoryList::const_iterator catIt = mCategories.constBegin();
772  for ( ; catIt != mCategories.constEnd(); ++catIt )
773  {
774  setScaleMethodToSymbol( catIt->symbol(), scaleMethod );
775  }
776 }
777 
779 {
780  return true;
781 }
782 
784 {
785  bool ok;
786  int index = key.toInt( &ok );
787  if ( ok && index >= 0 && index < mCategories.size() )
788  return mCategories[ index ].renderState();
789  else
790  return true;
791 }
792 
794 {
795  bool ok;
796  int index = key.toInt( &ok );
797  if ( ok )
798  updateCategoryRenderState( index, state );
799 }
800 
802 
804 {
805  if ( renderer->type() == "categorizedSymbol" )
806  {
807  return dynamic_cast<QgsCategorizedSymbolRendererV2*>( renderer->clone() );
808  }
809  if ( renderer->type() == "pointDisplacement" )
810  {
811  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
812  if ( pointDisplacementRenderer )
813  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
814  }
815  if ( renderer->type() == "invertedPolygonRenderer" )
816  {
817  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
818  if ( invertedPolygonRenderer )
819  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
820  }
821 
822  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
823  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
824 
826  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols();
827  if ( symbols.size() > 0 )
828  {
829  r->setSourceSymbol( symbols.at( 0 )->clone() );
830  }
831 
832  return r;
833 }