Quantum GIS API Documentation  1.7.4
src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
Go to the documentation of this file.
00001 
00002 #include "qgscategorizedsymbolrendererv2.h"
00003 
00004 #include "qgssymbolv2.h"
00005 #include "qgssymbollayerv2utils.h"
00006 #include "qgsvectorcolorrampv2.h"
00007 
00008 #include "qgsfeature.h"
00009 #include "qgsvectorlayer.h"
00010 #include "qgslogger.h"
00011 
00012 #include <QDomDocument>
00013 #include <QDomElement>
00014 #include <QSettings> // for legend
00015 
00016 QgsRendererCategoryV2::QgsRendererCategoryV2( QVariant value, QgsSymbolV2* symbol, QString label )
00017     : mValue( value ), mSymbol( symbol ), mLabel( label )
00018 {
00019 }
00020 
00021 QgsRendererCategoryV2::QgsRendererCategoryV2( const QgsRendererCategoryV2& cat )
00022     : mValue( cat.mValue ), mLabel( cat.mLabel )
00023 {
00024   mSymbol = cat.mSymbol->clone();
00025 }
00026 
00027 
00028 QgsRendererCategoryV2::~QgsRendererCategoryV2()
00029 {
00030   delete mSymbol;
00031 }
00032 
00033 QVariant QgsRendererCategoryV2::value() const
00034 {
00035   return mValue;
00036 }
00037 
00038 QgsSymbolV2* QgsRendererCategoryV2::symbol() const
00039 {
00040   return mSymbol;
00041 }
00042 
00043 QString QgsRendererCategoryV2::label() const
00044 {
00045   return mLabel;
00046 }
00047 
00048 void QgsRendererCategoryV2::setValue( const QVariant &value )
00049 {
00050   mValue = value;
00051 }
00052 
00053 void QgsRendererCategoryV2::setSymbol( QgsSymbolV2* s )
00054 {
00055   if ( mSymbol == s )
00056     return;
00057   delete mSymbol;
00058   mSymbol = s;
00059 }
00060 
00061 void QgsRendererCategoryV2::setLabel( const QString &label )
00062 {
00063   mLabel = label;
00064 }
00065 
00066 QString QgsRendererCategoryV2::dump()
00067 {
00068   return QString( "%1::%2::%3\n" ).arg( mValue.toString() ).arg( mLabel ).arg( mSymbol->dump() );
00069 }
00070 
00072 
00073 QgsCategorizedSymbolRendererV2::QgsCategorizedSymbolRendererV2( QString attrName, QgsCategoryList categories )
00074     : QgsFeatureRendererV2( "categorizedSymbol" ),
00075     mAttrName( attrName ),
00076     mCategories( categories ),
00077     mSourceSymbol( NULL ),
00078     mSourceColorRamp( NULL ),
00079     mRotationFieldIdx( -1 ),
00080     mSizeScaleFieldIdx( -1 )
00081 {
00082   for ( int i = 0; i < mCategories.count(); ++i )
00083   {
00084     QgsRendererCategoryV2& cat = mCategories[i];
00085     if ( cat.symbol() == NULL )
00086     {
00087       QgsDebugMsg( "invalid symbol in a category! ignoring..." );
00088       mCategories.removeAt( i-- );
00089     }
00090     //mCategories.insert(cat.value().toString(), cat);
00091   }
00092 }
00093 
00094 QgsCategorizedSymbolRendererV2::~QgsCategorizedSymbolRendererV2()
00095 {
00096   mCategories.clear(); // this should also call destructors of symbols
00097   delete mSourceSymbol;
00098   delete mSourceColorRamp;
00099 }
00100 
00101 void QgsCategorizedSymbolRendererV2::rebuildHash()
00102 {
00103   mSymbolHash.clear();
00104 
00105   for ( int i = 0; i < mCategories.count(); ++i )
00106   {
00107     QgsRendererCategoryV2& cat = mCategories[i];
00108     mSymbolHash.insert( cat.value().toString(), cat.symbol() );
00109   }
00110 }
00111 
00112 QgsSymbolV2* QgsCategorizedSymbolRendererV2::symbolForValue( QVariant value )
00113 {
00114   // TODO: special case for int, double
00115 
00116   QHash<QString, QgsSymbolV2*>::iterator it = mSymbolHash.find( value.toString() );
00117   if ( it == mSymbolHash.end() )
00118   {
00119     if ( mSymbolHash.count() == 0 )
00120       QgsDebugMsg( "there are no hashed symbols!!!" );
00121     else
00122       QgsDebugMsg( "attribute value not found: " + value.toString() );
00123     return NULL;
00124   }
00125 
00126   return *it;
00127 }
00128 
00129 QgsSymbolV2* QgsCategorizedSymbolRendererV2::symbolForFeature( QgsFeature& feature )
00130 {
00131   const QgsAttributeMap& attrMap = feature.attributeMap();
00132   QgsAttributeMap::const_iterator ita = attrMap.find( mAttrNum );
00133   if ( ita == attrMap.end() )
00134   {
00135     QgsDebugMsg( "attribute '" + mAttrName + "' (index " + QString::number( mAttrNum ) + ") required by renderer not found" );
00136     return NULL;
00137   }
00138 
00139   // find the right symbol for the category
00140   QgsSymbolV2* symbol = symbolForValue( *ita );
00141   if ( symbol == NULL )
00142   {
00143     // if no symbol found use default one
00144     return symbolForValue( QVariant( "" ) );
00145   }
00146 
00147   if ( mRotationFieldIdx == -1 && mSizeScaleFieldIdx == -1 )
00148     return symbol; // no data-defined rotation/scaling - just return the symbol
00149 
00150   // find out rotation, size scale
00151   double rotation = 0;
00152   double sizeScale = 1;
00153   if ( mRotationFieldIdx != -1 )
00154     rotation = attrMap[mRotationFieldIdx].toDouble();
00155   if ( mSizeScaleFieldIdx != -1 )
00156     sizeScale = attrMap[mSizeScaleFieldIdx].toDouble();
00157 
00158   // take a temporary symbol (or create it if doesn't exist)
00159   QgsSymbolV2* tempSymbol = mTempSymbols[ita->toString()];
00160 
00161   // modify the temporary symbol and return it
00162   if ( tempSymbol->type() == QgsSymbolV2::Marker )
00163   {
00164     QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
00165     if ( mRotationFieldIdx != -1 )
00166       markerSymbol->setAngle( rotation );
00167     if ( mSizeScaleFieldIdx != -1 )
00168       markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
00169   }
00170   else if ( tempSymbol->type() == QgsSymbolV2::Line )
00171   {
00172     QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
00173     if ( mSizeScaleFieldIdx != -1 )
00174       lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
00175   }
00176 
00177   return tempSymbol;
00178 }
00179 
00180 int QgsCategorizedSymbolRendererV2::categoryIndexForValue( QVariant val )
00181 {
00182   for ( int i = 0; i < mCategories.count(); i++ )
00183   {
00184     if ( mCategories[i].value() == val )
00185       return i;
00186   }
00187   return -1;
00188 }
00189 
00190 bool QgsCategorizedSymbolRendererV2::updateCategoryValue( int catIndex, const QVariant &value )
00191 {
00192   if ( catIndex < 0 || catIndex >= mCategories.size() )
00193     return false;
00194   mCategories[catIndex].setValue( value );
00195   return true;
00196 }
00197 
00198 bool QgsCategorizedSymbolRendererV2::updateCategorySymbol( int catIndex, QgsSymbolV2* symbol )
00199 {
00200   if ( catIndex < 0 || catIndex >= mCategories.size() )
00201     return false;
00202   mCategories[catIndex].setSymbol( symbol );
00203   return true;
00204 }
00205 
00206 bool QgsCategorizedSymbolRendererV2::updateCategoryLabel( int catIndex, QString label )
00207 {
00208   if ( catIndex < 0 || catIndex >= mCategories.size() )
00209     return false;
00210   mCategories[catIndex].setLabel( label );
00211   return true;
00212 }
00213 
00214 void QgsCategorizedSymbolRendererV2::addCategory( const QgsRendererCategoryV2 &cat )
00215 {
00216   if ( cat.symbol() == NULL )
00217   {
00218     QgsDebugMsg( "invalid symbol in a category! ignoring..." );
00219   }
00220   else
00221   {
00222     mCategories.append( cat );
00223   }
00224 }
00225 
00226 bool QgsCategorizedSymbolRendererV2::deleteCategory( int catIndex )
00227 {
00228   if ( catIndex < 0 || catIndex >= mCategories.size() )
00229     return false;
00230 
00231   mCategories.removeAt( catIndex );
00232   return true;
00233 }
00234 
00235 void QgsCategorizedSymbolRendererV2::deleteAllCategories()
00236 {
00237   mCategories.clear();
00238 }
00239 
00240 void QgsCategorizedSymbolRendererV2::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer )
00241 {
00242   // make sure that the hash table is up to date
00243   rebuildHash();
00244 
00245   // find out classification attribute index from name
00246   mAttrNum = vlayer ? vlayer->fieldNameIndex( mAttrName ) : -1;
00247 
00248   mRotationFieldIdx  = ( mRotationField.isEmpty()  ? -1 : vlayer->fieldNameIndex( mRotationField ) );
00249   mSizeScaleFieldIdx = ( mSizeScaleField.isEmpty() ? -1 : vlayer->fieldNameIndex( mSizeScaleField ) );
00250 
00251   QgsCategoryList::iterator it = mCategories.begin();
00252   for ( ; it != mCategories.end(); ++it )
00253   {
00254     it->symbol()->startRender( context );
00255 
00256     if ( mRotationFieldIdx != -1 || mSizeScaleFieldIdx != -1 )
00257     {
00258       QgsSymbolV2* tempSymbol = it->symbol()->clone();
00259       tempSymbol->setRenderHints(( mRotationFieldIdx != -1 ? QgsSymbolV2::DataDefinedRotation : 0 ) |
00260                                  ( mSizeScaleFieldIdx != -1 ? QgsSymbolV2::DataDefinedSizeScale : 0 ) );
00261       tempSymbol->startRender( context );
00262       mTempSymbols[ it->value().toString()] = tempSymbol;
00263     }
00264   }
00265 
00266 }
00267 
00268 void QgsCategorizedSymbolRendererV2::stopRender( QgsRenderContext& context )
00269 {
00270   QgsCategoryList::iterator it = mCategories.begin();
00271   for ( ; it != mCategories.end(); ++it )
00272     it->symbol()->stopRender( context );
00273 
00274   // cleanup mTempSymbols
00275   QHash<QString, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
00276   for ( ; it2 != mTempSymbols.end(); ++it2 )
00277   {
00278     it2.value()->stopRender( context );
00279     delete it2.value();
00280   }
00281   mTempSymbols.clear();
00282 }
00283 
00284 QList<QString> QgsCategorizedSymbolRendererV2::usedAttributes()
00285 {
00286   QList<QString> lst;
00287   lst.append( mAttrName );
00288   if ( !mRotationField.isEmpty() )
00289     lst.append( mRotationField );
00290   if ( !mSizeScaleField.isEmpty() )
00291     lst.append( mSizeScaleField );
00292   return lst;
00293 }
00294 
00295 QString QgsCategorizedSymbolRendererV2::dump()
00296 {
00297   QString s = QString( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
00298   for ( int i = 0; i < mCategories.count(); i++ )
00299     s += mCategories[i].dump();
00300   return s;
00301 }
00302 
00303 QgsFeatureRendererV2* QgsCategorizedSymbolRendererV2::clone()
00304 {
00305   QgsCategorizedSymbolRendererV2* r = new QgsCategorizedSymbolRendererV2( mAttrName, mCategories );
00306   if ( mSourceSymbol )
00307     r->setSourceSymbol( mSourceSymbol->clone() );
00308   if ( mSourceColorRamp )
00309     r->setSourceColorRamp( mSourceColorRamp->clone() );
00310   r->setUsingSymbolLevels( usingSymbolLevels() );
00311   r->setRotationField( rotationField() );
00312   r->setSizeScaleField( sizeScaleField() );
00313   return r;
00314 }
00315 
00316 QgsSymbolV2List QgsCategorizedSymbolRendererV2::symbols()
00317 {
00318   QgsSymbolV2List lst;
00319   for ( int i = 0; i < mCategories.count(); i++ )
00320     lst.append( mCategories[i].symbol() );
00321   return lst;
00322 }
00323 
00324 QgsFeatureRendererV2* QgsCategorizedSymbolRendererV2::create( QDomElement& element )
00325 {
00326   QDomElement symbolsElem = element.firstChildElement( "symbols" );
00327   if ( symbolsElem.isNull() )
00328     return NULL;
00329 
00330   QDomElement catsElem = element.firstChildElement( "categories" );
00331   if ( catsElem.isNull() )
00332     return NULL;
00333 
00334   QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
00335   QgsCategoryList cats;
00336 
00337   QDomElement catElem = catsElem.firstChildElement();
00338   while ( !catElem.isNull() )
00339   {
00340     if ( catElem.tagName() == "category" )
00341     {
00342       QVariant value = QVariant( catElem.attribute( "value" ) );
00343       QString symbolName = catElem.attribute( "symbol" );
00344       QString label = catElem.attribute( "label" );
00345       if ( symbolMap.contains( symbolName ) )
00346       {
00347         QgsSymbolV2* symbol = symbolMap.take( symbolName );
00348         cats.append( QgsRendererCategoryV2( value, symbol, label ) );
00349       }
00350     }
00351     catElem = catElem.nextSiblingElement();
00352   }
00353 
00354   QString attrName = element.attribute( "attr" );
00355 
00356   QgsCategorizedSymbolRendererV2* r = new QgsCategorizedSymbolRendererV2( attrName, cats );
00357 
00358   // delete symbols if there are any more
00359   QgsSymbolLayerV2Utils::clearSymbolMap( symbolMap );
00360 
00361   // try to load source symbol (optional)
00362   QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
00363   if ( !sourceSymbolElem.isNull() )
00364   {
00365     QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
00366     if ( sourceSymbolMap.contains( "0" ) )
00367     {
00368       r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
00369     }
00370     QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
00371   }
00372 
00373   // try to load color ramp (optional)
00374   QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
00375   if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
00376   {
00377     r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
00378   }
00379 
00380   QDomElement rotationElem = element.firstChildElement( "rotation" );
00381   if ( !rotationElem.isNull() )
00382     r->setRotationField( rotationElem.attribute( "field" ) );
00383 
00384   QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
00385   if ( !sizeScaleElem.isNull() )
00386     r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
00387 
00388   // TODO: symbol levels
00389   return r;
00390 }
00391 
00392 QDomElement QgsCategorizedSymbolRendererV2::save( QDomDocument& doc )
00393 {
00394   QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
00395   rendererElem.setAttribute( "type", "categorizedSymbol" );
00396   rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
00397   rendererElem.setAttribute( "attr", mAttrName );
00398 
00399   // categories
00400   int i = 0;
00401   QgsSymbolV2Map symbols;
00402   QDomElement catsElem = doc.createElement( "categories" );
00403   QgsCategoryList::const_iterator it = mCategories.constBegin();
00404   for ( ; it != mCategories.end(); it++ )
00405   {
00406     const QgsRendererCategoryV2& cat = *it;
00407     QString symbolName = QString::number( i );
00408     symbols.insert( symbolName, cat.symbol() );
00409 
00410     QDomElement catElem = doc.createElement( "category" );
00411     catElem.setAttribute( "value", cat.value().toString() );
00412     catElem.setAttribute( "symbol", symbolName );
00413     catElem.setAttribute( "label", cat.label() );
00414     catsElem.appendChild( catElem );
00415     i++;
00416   }
00417 
00418   rendererElem.appendChild( catsElem );
00419 
00420   // save symbols
00421   QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
00422   rendererElem.appendChild( symbolsElem );
00423 
00424   // save source symbol
00425   if ( mSourceSymbol )
00426   {
00427     QgsSymbolV2Map sourceSymbols;
00428     sourceSymbols.insert( "0", mSourceSymbol );
00429     QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
00430     rendererElem.appendChild( sourceSymbolElem );
00431   }
00432 
00433   // save source color ramp
00434   if ( mSourceColorRamp )
00435   {
00436     QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp, doc );
00437     rendererElem.appendChild( colorRampElem );
00438   }
00439 
00440   QDomElement rotationElem = doc.createElement( "rotation" );
00441   rotationElem.setAttribute( "field", mRotationField );
00442   rendererElem.appendChild( rotationElem );
00443 
00444   QDomElement sizeScaleElem = doc.createElement( "sizescale" );
00445   sizeScaleElem.setAttribute( "field", mSizeScaleField );
00446   rendererElem.appendChild( sizeScaleElem );
00447 
00448   return rendererElem;
00449 }
00450 
00451 QgsLegendSymbologyList QgsCategorizedSymbolRendererV2::legendSymbologyItems( QSize iconSize )
00452 {
00453   QSettings settings;
00454   bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
00455 
00456   QgsLegendSymbologyList lst;
00457   if ( showClassifiers )
00458   {
00459     lst << qMakePair( classAttribute(), QPixmap() );
00460   }
00461 
00462   int count = categories().count();
00463   for ( int i = 0; i < count; i++ )
00464   {
00465     const QgsRendererCategoryV2& cat = categories()[i];
00466     QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( cat.symbol(), iconSize );
00467     lst << qMakePair( cat.label(), pix );
00468   }
00469   return lst;
00470 }
00471 
00472 QgsLegendSymbolList QgsCategorizedSymbolRendererV2::legendSymbolItems()
00473 {
00474   QSettings settings;
00475   bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
00476 
00477   QgsLegendSymbolList lst;
00478   if ( showClassifiers )
00479   {
00480     lst << qMakePair( classAttribute(), ( QgsSymbolV2* )0 );
00481   }
00482 
00483   QgsCategoryList::const_iterator catIt = mCategories.constBegin();
00484   for ( ; catIt != mCategories.constEnd(); ++catIt )
00485   {
00486     lst << qMakePair( catIt->label(), catIt->symbol() );
00487   }
00488   return lst;
00489 }
00490 
00491 
00492 QgsSymbolV2* QgsCategorizedSymbolRendererV2::sourceSymbol()
00493 {
00494   return mSourceSymbol;
00495 }
00496 void QgsCategorizedSymbolRendererV2::setSourceSymbol( QgsSymbolV2* sym )
00497 {
00498   delete mSourceSymbol;
00499   mSourceSymbol = sym;
00500 }
00501 
00502 QgsVectorColorRampV2* QgsCategorizedSymbolRendererV2::sourceColorRamp()
00503 {
00504   return mSourceColorRamp;
00505 }
00506 void QgsCategorizedSymbolRendererV2::setSourceColorRamp( QgsVectorColorRampV2* ramp )
00507 {
00508   delete mSourceColorRamp;
00509   mSourceColorRamp = ramp;
00510 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines