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