Quantum GIS API Documentation
1.8
|
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 }