Quantum GIS API Documentation
1.7.4
|
00001 /*************************************************************************** 00002 00003 qgsgraduatedsymbolrendererv2.cpp - Graduated Symbol Renderer Version 2 00004 00005 *************************************************************************** 00006 * * 00007 * This program is free software; you can redistribute it and/or modify * 00008 * it under the terms of the GNU General Public License as published by * 00009 * the Free Software Foundation; either version 2 of the License, or * 00010 * (at your option) any later version. * 00011 * * 00012 ***************************************************************************/ 00013 00014 #include "qgsgraduatedsymbolrendererv2.h" 00015 00016 #include "qgssymbolv2.h" 00017 #include "qgssymbollayerv2utils.h" 00018 #include "qgsvectorcolorrampv2.h" 00019 00020 #include "qgsfeature.h" 00021 #include "qgsvectorlayer.h" 00022 #include "qgslogger.h" 00023 #include "qgsvectordataprovider.h" 00024 00025 #include <QDomDocument> 00026 #include <QDomElement> 00027 #include <QSettings> // for legend 00028 #include <limits> // for jenks classification 00029 #include <cmath> // for pretty classification 00030 #include <ctime> 00031 00032 QgsRendererRangeV2::QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol, QString label ) 00033 : mLowerValue( lowerValue ) 00034 , mUpperValue( upperValue ) 00035 , mSymbol( symbol ) 00036 , mLabel( label ) 00037 { 00038 } 00039 00040 QgsRendererRangeV2::QgsRendererRangeV2( const QgsRendererRangeV2& range ) 00041 : mLowerValue( range.mLowerValue ) 00042 , mUpperValue( range.mUpperValue ) 00043 , mLabel( range.mLabel ) 00044 { 00045 mSymbol = range.mSymbol->clone(); 00046 } 00047 00048 QgsRendererRangeV2::~QgsRendererRangeV2() 00049 { 00050 delete mSymbol; 00051 } 00052 00053 double QgsRendererRangeV2::lowerValue() const 00054 { 00055 return mLowerValue; 00056 } 00057 00058 double QgsRendererRangeV2::upperValue() const 00059 { 00060 return mUpperValue; 00061 } 00062 00063 QgsSymbolV2* QgsRendererRangeV2::symbol() const 00064 { 00065 return mSymbol; 00066 } 00067 00068 QString QgsRendererRangeV2::label() const 00069 { 00070 return mLabel; 00071 } 00072 00073 void QgsRendererRangeV2::setSymbol( QgsSymbolV2* s ) 00074 { 00075 if ( mSymbol == s ) 00076 return; 00077 delete mSymbol; 00078 mSymbol = s; 00079 } 00080 00081 void QgsRendererRangeV2::setLabel( QString label ) 00082 { 00083 mLabel = label; 00084 } 00085 00086 void QgsRendererRangeV2::setUpperValue( double upperValue ) 00087 { 00088 mUpperValue = upperValue; 00089 } 00090 00091 void QgsRendererRangeV2::setLowerValue( double lowerValue ) 00092 { 00093 mLowerValue = lowerValue; 00094 } 00095 00096 QString QgsRendererRangeV2::dump() 00097 { 00098 return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel ).arg( mSymbol->dump() ); 00099 } 00100 00102 00103 QgsGraduatedSymbolRendererV2::QgsGraduatedSymbolRendererV2( QString attrName, QgsRangeList ranges ) 00104 : QgsFeatureRendererV2( "graduatedSymbol" ), 00105 mAttrName( attrName ), 00106 mRanges( ranges ), 00107 mMode( Custom ), 00108 mSourceSymbol( NULL ), 00109 mSourceColorRamp( NULL ), 00110 mRotationFieldIdx( -1 ), 00111 mSizeScaleFieldIdx( -1 ) 00112 { 00113 // TODO: check ranges for sanity (NULL symbols, invalid ranges) 00114 } 00115 00116 QgsGraduatedSymbolRendererV2::~QgsGraduatedSymbolRendererV2() 00117 { 00118 mRanges.clear(); // should delete all the symbols 00119 delete mSourceSymbol; 00120 delete mSourceColorRamp; 00121 } 00122 00123 QgsSymbolV2* QgsGraduatedSymbolRendererV2::symbolForValue( double value ) 00124 { 00125 for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it ) 00126 { 00127 if ( it->lowerValue() <= value && it->upperValue() >= value ) 00128 return it->symbol(); 00129 } 00130 // the value is out of the range: return NULL instead of symbol 00131 return NULL; 00132 } 00133 00134 QgsSymbolV2* QgsGraduatedSymbolRendererV2::symbolForFeature( QgsFeature& feature ) 00135 { 00136 const QgsAttributeMap& attrMap = feature.attributeMap(); 00137 QgsAttributeMap::const_iterator ita = attrMap.find( mAttrNum ); 00138 if ( ita == attrMap.end() ) 00139 { 00140 QgsDebugMsg( "attribute required by renderer not found: " + mAttrName + "(index " + QString::number( mAttrNum ) + ")" ); 00141 return NULL; 00142 } 00143 00144 // find the right category 00145 QgsSymbolV2* symbol = symbolForValue( ita->toDouble() ); 00146 if ( symbol == NULL ) 00147 return NULL; 00148 00149 if ( mRotationFieldIdx == -1 && mSizeScaleFieldIdx == -1 ) 00150 return symbol; // no data-defined rotation/scaling - just return the symbol 00151 00152 // find out rotation, size scale 00153 double rotation = 0; 00154 double sizeScale = 1; 00155 if ( mRotationFieldIdx != -1 ) 00156 rotation = attrMap[mRotationFieldIdx].toDouble(); 00157 if ( mSizeScaleFieldIdx != -1 ) 00158 sizeScale = attrMap[mSizeScaleFieldIdx].toDouble(); 00159 00160 // take a temporary symbol (or create it if doesn't exist) 00161 QgsSymbolV2* tempSymbol = mTempSymbols[symbol]; 00162 00163 // modify the temporary symbol and return it 00164 if ( tempSymbol->type() == QgsSymbolV2::Marker ) 00165 { 00166 QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol ); 00167 if ( mRotationFieldIdx != -1 ) 00168 markerSymbol->setAngle( rotation ); 00169 if ( mSizeScaleFieldIdx != -1 ) 00170 markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() ); 00171 } 00172 else if ( tempSymbol->type() == QgsSymbolV2::Line ) 00173 { 00174 QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol ); 00175 if ( mSizeScaleFieldIdx != -1 ) 00176 lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() ); 00177 } 00178 return tempSymbol; 00179 } 00180 00181 void QgsGraduatedSymbolRendererV2::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer ) 00182 { 00183 // find out classification attribute index from name 00184 mAttrNum = vlayer ? vlayer->fieldNameIndex( mAttrName ) : -1; 00185 00186 mRotationFieldIdx = ( mRotationField.isEmpty() ? -1 : vlayer->fieldNameIndex( mRotationField ) ); 00187 mSizeScaleFieldIdx = ( mSizeScaleField.isEmpty() ? -1 : vlayer->fieldNameIndex( mSizeScaleField ) ); 00188 00189 QgsRangeList::iterator it = mRanges.begin(); 00190 for ( ; it != mRanges.end(); ++it ) 00191 { 00192 it->symbol()->startRender( context ); 00193 00194 if ( mRotationFieldIdx != -1 || mSizeScaleFieldIdx != -1 ) 00195 { 00196 QgsSymbolV2* tempSymbol = it->symbol()->clone(); 00197 tempSymbol->setRenderHints(( mRotationFieldIdx != -1 ? QgsSymbolV2::DataDefinedRotation : 0 ) | 00198 ( mSizeScaleFieldIdx != -1 ? QgsSymbolV2::DataDefinedSizeScale : 0 ) ); 00199 tempSymbol->startRender( context ); 00200 mTempSymbols[ it->symbol()] = tempSymbol; 00201 } 00202 } 00203 } 00204 00205 void QgsGraduatedSymbolRendererV2::stopRender( QgsRenderContext& context ) 00206 { 00207 QgsRangeList::iterator it = mRanges.begin(); 00208 for ( ; it != mRanges.end(); ++it ) 00209 it->symbol()->stopRender( context ); 00210 00211 // cleanup mTempSymbols 00212 #if QT_VERSION < 0x40600 00213 QMap<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin(); 00214 #else 00215 QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin(); 00216 #endif 00217 for ( ; it2 != mTempSymbols.end(); ++it2 ) 00218 { 00219 it2.value()->stopRender( context ); 00220 delete it2.value(); 00221 } 00222 mTempSymbols.clear(); 00223 } 00224 00225 QList<QString> QgsGraduatedSymbolRendererV2::usedAttributes() 00226 { 00227 QList<QString> lst; 00228 lst.append( mAttrName ); 00229 if ( !mRotationField.isEmpty() ) 00230 lst.append( mRotationField ); 00231 if ( !mSizeScaleField.isEmpty() ) 00232 lst.append( mSizeScaleField ); 00233 return lst; 00234 } 00235 00236 bool QgsGraduatedSymbolRendererV2::updateRangeSymbol( int rangeIndex, QgsSymbolV2* symbol ) 00237 { 00238 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() ) 00239 return false; 00240 mRanges[rangeIndex].setSymbol( symbol ); 00241 return true; 00242 } 00243 00244 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, QString label ) 00245 { 00246 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() ) 00247 return false; 00248 mRanges[rangeIndex].setLabel( label ); 00249 return true; 00250 } 00251 00252 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value ) 00253 { 00254 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() ) 00255 return false; 00256 mRanges[rangeIndex].setUpperValue( value ); 00257 return true; 00258 } 00259 00260 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value ) 00261 { 00262 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() ) 00263 return false; 00264 mRanges[rangeIndex].setLowerValue( value ); 00265 return true; 00266 } 00267 00268 QString QgsGraduatedSymbolRendererV2::dump() 00269 { 00270 QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName ); 00271 for ( int i = 0; i < mRanges.count(); i++ ) 00272 s += mRanges[i].dump(); 00273 return s; 00274 } 00275 00276 QgsFeatureRendererV2* QgsGraduatedSymbolRendererV2::clone() 00277 { 00278 QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( mAttrName, mRanges ); 00279 r->setMode( mMode ); 00280 if ( mSourceSymbol ) 00281 r->setSourceSymbol( mSourceSymbol->clone() ); 00282 if ( mSourceColorRamp ) 00283 r->setSourceColorRamp( mSourceColorRamp->clone() ); 00284 r->setUsingSymbolLevels( usingSymbolLevels() ); 00285 r->setRotationField( rotationField() ); 00286 r->setSizeScaleField( sizeScaleField() ); 00287 return r; 00288 } 00289 00290 QgsSymbolV2List QgsGraduatedSymbolRendererV2::symbols() 00291 { 00292 QgsSymbolV2List lst; 00293 for ( int i = 0; i < mRanges.count(); i++ ) 00294 lst.append( mRanges[i].symbol() ); 00295 return lst; 00296 } 00297 00298 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes ) 00299 { 00300 00301 // Equal interval algorithm 00302 // 00303 // Returns breaks based on dividing the range ('minimum' to 'maximum') 00304 // into 'classes' parts. 00305 00306 double step = ( maximum - minimum ) / classes; 00307 00308 QList<double> breaks; 00309 double value = minimum; 00310 for ( int i = 0; i < classes; i++ ) 00311 { 00312 value += step; 00313 breaks.append( value ); 00314 } 00315 return breaks; 00316 } 00317 00318 static QList<double> _calcQuantileBreaks( QList<double> values, int classes ) 00319 { 00320 00321 // q-th quantile of a data set: 00322 // value where q fraction of data is below and (1-q) fraction is above this value 00323 // Xq = (1 - r) * X_NI1 + r * X_NI2 00324 // NI1 = (int) (q * (n+1)) 00325 // NI2 = NI1 + 1 00326 // r = q * (n+1) - (int) (q * (n+1)) 00327 // (indices of X: 1...n) 00328 00329 // sort the values first 00330 qSort( values ); 00331 00332 QList<double> breaks; 00333 00334 int n = values.count(); 00335 double Xq = n > 0 ? values[0] : 0.0; 00336 00337 for ( int i = 1; i < classes; i++ ) 00338 { 00339 if ( n > 1 ) 00340 { 00341 double q = i / ( double ) classes; 00342 double a = q * ( n - 1 ); 00343 int aa = ( int )( a ); 00344 00345 double r = a - aa; 00346 Xq = ( 1 - r ) * values[aa] + r * values[aa+1]; 00347 } 00348 breaks.append( Xq ); 00349 } 00350 00351 breaks.append( values[ n-1 ] ); 00352 00353 return breaks; 00354 } 00355 00356 static QList<double> _calcPrettyBreaks( double minimum, double maximum, int classes ) 00357 { 00358 00359 // C++ implementation of R's pretty algorithm 00360 // Based on code for determining optimal tick placement for statistical graphics 00361 // from the R statistical programming language. 00362 // Code ported from R implementation from 'labeling' R package 00363 // 00364 // Computes a sequence of about 'classes' equally spaced round values 00365 // which cover the range of values from 'minimum' to 'maximum'. 00366 // The values are chosen so that they are 1, 2 or 5 times a power of 10. 00367 00368 QList<double> breaks; 00369 if ( classes < 1 ) 00370 { 00371 breaks.append( maximum ); 00372 return breaks; 00373 } 00374 00375 int minimumCount = ( int ) classes / 3; 00376 double shrink = 0.75; 00377 double highBias = 1.5; 00378 double adjustBias = 0.5 + 1.5 * highBias; 00379 int divisions = classes; 00380 double h = highBias; 00381 double cell; 00382 int U; 00383 bool small = false; 00384 double dx = maximum - minimum; 00385 00386 if ( dx == 0 && maximum == 0 ) 00387 { 00388 cell = 1.0; 00389 small = true; 00390 U = 1; 00391 } 00392 else 00393 { 00394 cell = qMax( qAbs( minimum ), qAbs( maximum ) ); 00395 if ( adjustBias >= 1.5 * h + 0.5 ) 00396 { 00397 U = 1 + ( 1.0 / ( 1 + h ) ); 00398 } 00399 else 00400 { 00401 U = 1 + ( 1.5 / ( 1 + adjustBias ) ); 00402 } 00403 small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 ); 00404 } 00405 00406 if ( small ) 00407 { 00408 if ( cell > 10 ) 00409 { 00410 cell = 9 + cell / 10; 00411 cell = cell * shrink; 00412 } 00413 if ( minimumCount > 1 ) 00414 { 00415 cell = cell / minimumCount; 00416 } 00417 } 00418 else 00419 { 00420 cell = dx; 00421 if ( divisions > 1 ) 00422 { 00423 cell = cell / divisions; 00424 } 00425 } 00426 if ( cell < 20 * 1e-07 ) 00427 { 00428 cell = 20 * 1e-07; 00429 } 00430 00431 double base = pow( 10.0, floor( log10( cell ) ) ); 00432 double unit = base; 00433 if (( 2 * base ) - cell < h *( cell - unit ) ) 00434 { 00435 unit = 2.0 * base; 00436 if (( 5 * base ) - cell < adjustBias *( cell - unit ) ) 00437 { 00438 unit = 5.0 * base; 00439 if (( 10.0 * base ) - cell < h *( cell - unit ) ) 00440 { 00441 unit = 10.0 * base; 00442 } 00443 } 00444 } 00445 // Maybe used to correct for the epsilon here?? 00446 int start = floor( minimum / unit + 1e-07 ); 00447 int end = ceil( maximum / unit - 1e-07 ); 00448 00449 // Extend the range out beyond the data. Does this ever happen?? 00450 while ( start * unit > minimum + ( 1e-07 * unit ) ) 00451 { 00452 start = start - 1; 00453 } 00454 while ( end * unit < maximum - ( 1e-07 * unit ) ) 00455 { 00456 end = end + 1; 00457 } 00458 QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) ); 00459 00460 // If we don't have quite enough labels, extend the range out 00461 // to make more (these labels are beyond the data :( ) 00462 int k = floor( 0.5 + end - start ); 00463 if ( k < minimumCount ) 00464 { 00465 k = minimumCount - k; 00466 if ( start >= 0 ) 00467 { 00468 end = end + k / 2; 00469 start = start - k / 2 + k % 2; 00470 } 00471 else 00472 { 00473 start = start - k / 2; 00474 end = end + k / 2 + k % 2; 00475 } 00476 divisions = minimumCount; 00477 } 00478 else 00479 { 00480 divisions = k; 00481 } 00482 double minimumBreak = start * unit; 00483 double maximumBreak = end * unit; 00484 int count = ceil( maximumBreak - minimumBreak ) / unit; 00485 00486 for ( int i = 1; i < count + 1; i++ ) 00487 { 00488 breaks.append( minimumBreak + i * unit ); 00489 } 00490 00491 if ( breaks.first() < minimum ) 00492 { 00493 breaks[0] = minimum; 00494 } 00495 if ( breaks.last() > maximum ) 00496 { 00497 breaks[breaks.count()-1] = maximum; 00498 } 00499 return breaks; 00500 } // _calcPrettyBreaks 00501 00502 00503 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<int> &labels ) 00504 { 00505 00506 // C++ implementation of the standard deviation class interval algorithm 00507 // as implemented in the 'classInt' package available for the R statistical 00508 // prgramming language. 00509 00510 // Returns breaks based on '_calcPrettyBreaks' of the centred and scaled 00511 // values of 'values', and may have a number of classes different from 'classes'. 00512 00513 double mean = 0.0; 00514 double stdDev = 0.0; 00515 int n = values.count(); 00516 double minimum = values[0]; 00517 double maximum = values[0]; 00518 00519 for ( int i = 0; i < n; i++ ) 00520 { 00521 mean += values[i]; 00522 minimum = qMin( values[i], minimum ); // could use precomputed max and min 00523 maximum = qMax( values[i], maximum ); // but have to go through entire list anyway 00524 } 00525 mean = mean / ( double ) n; 00526 00527 double sd = 0.0; 00528 for ( int i = 0; i < n; i++ ) 00529 { 00530 sd = values[i] - mean; 00531 stdDev += sd * sd; 00532 } 00533 stdDev = sqrt( stdDev / n ); 00534 00535 QList<double> breaks = _calcPrettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes ); 00536 for ( int i = 0; i < breaks.count(); i++ ) 00537 { 00538 labels.append(( int ) breaks[i] ); 00539 breaks[i] = ( breaks[i] * stdDev ) + mean; 00540 } 00541 00542 return breaks; 00543 } // _calcStdDevBreaks 00544 00545 static QList<double> _calcJenksBreaks( QList<double> values, int classes, 00546 double minimum, double maximum, 00547 int maximumSize = 1000 ) 00548 { 00549 // Jenks Optimal (Natural Breaks) algorithm 00550 // Based on the Jenks algorithm from the 'classInt' package available for 00551 // the R statistical prgramming language, and from Python code from here: 00552 // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/ 00553 // and is based on a JAVA and Fortran code available here: 00554 // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html 00555 00556 // Returns class breaks such that classes are internally homogeneous while 00557 // assuring heterogeneity among classes. 00558 00559 if ( classes < 1 ) 00560 { 00561 return QList<double>() << maximum; 00562 } 00563 00564 if ( classes >= values.size() ) 00565 { 00566 return values; 00567 } 00568 00569 QVector<double> sample; 00570 00571 // if we have lots of values, we need to take a random sample 00572 if ( values.size() > maximumSize ) 00573 { 00574 // for now, sample at least maximumSize values or a 10% sample, whichever 00575 // is larger. This will produce a more representative sample for very large 00576 // layers, but could end up being computationally intensive... 00577 00578 qsrand( time( 0 ) ); 00579 00580 sample.resize( qMax( maximumSize, values.size() / 10 ) ); 00581 00582 QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) ); 00583 QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) ); 00584 00585 sample[ 0 ] = minimum; 00586 sample[ 1 ] = maximum;; 00587 for ( int i = 2; i < sample.size(); i++ ) 00588 { 00589 // pick a random integer from 0 to n 00590 double r = qrand(); 00591 int j = floor( r / RAND_MAX * ( values.size() - 1 ) ); 00592 sample[ i ] = values[ j ]; 00593 } 00594 } 00595 else 00596 { 00597 sample = values.toVector(); 00598 } 00599 00600 int n = sample.size(); 00601 00602 // sort the sample values 00603 qSort( sample ); 00604 00605 QVector< QVector<int> > matrixOne( n + 1 ); 00606 QVector< QVector<double> > matrixTwo( n + 1 ); 00607 00608 for ( int i = 0; i <= n; i++ ) 00609 { 00610 matrixOne[i].resize( classes + 1 ); 00611 matrixTwo[i].resize( classes + 1 ); 00612 } 00613 00614 for ( int i = 1; i <= classes; i++ ) 00615 { 00616 matrixOne[0][i] = 1; 00617 matrixOne[1][i] = 1; 00618 matrixTwo[0][i] = 0.0; 00619 for ( int j = 2; j <= n; j++ ) 00620 { 00621 matrixTwo[j][i] = std::numeric_limits<double>::max(); 00622 } 00623 } 00624 00625 for ( int l = 2; l <= n; l++ ) 00626 { 00627 double s1 = 0.0; 00628 double s2 = 0.0; 00629 int w = 0; 00630 00631 double v = 0.0; 00632 00633 for ( int m = 1; m <= l; m++ ) 00634 { 00635 int i3 = l - m + 1; 00636 00637 double val = sample[ i3 - 1 ]; 00638 00639 s2 += val * val; 00640 s1 += val; 00641 w++; 00642 00643 v = s2 - ( s1 * s1 ) / ( double ) w; 00644 int i4 = i3 - 1; 00645 if ( i4 != 0 ) 00646 { 00647 for ( int j = 2; j <= classes; j++ ) 00648 { 00649 if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] ) 00650 { 00651 matrixOne[l][j] = i4; 00652 matrixTwo[l][j] = v + matrixTwo[i4][j - 1]; 00653 } 00654 } 00655 } 00656 } 00657 matrixOne[l][1] = 1; 00658 matrixTwo[l][1] = v; 00659 } 00660 00661 QVector<double> breaks( classes ); 00662 breaks[classes-1] = sample[n-1]; 00663 00664 for ( int j = classes, k = n; j >= 2; j-- ) 00665 { 00666 int id = matrixOne[k][j] - 1; 00667 breaks[j - 2] = sample[id]; 00668 k = matrixOne[k][j] - 1; 00669 } 00670 00671 return breaks.toList(); 00672 } //_calcJenksBreaks 00673 00674 QgsGraduatedSymbolRendererV2* QgsGraduatedSymbolRendererV2::createRenderer( 00675 QgsVectorLayer* vlayer, 00676 QString attrName, 00677 int classes, 00678 Mode mode, 00679 QgsSymbolV2* symbol, 00680 QgsVectorColorRampV2* ramp ) 00681 { 00682 00683 int attrNum = vlayer->fieldNameIndex( attrName ); 00684 00685 double minimum = vlayer->minimumValue( attrNum ).toDouble(); 00686 double maximum = vlayer->maximumValue( attrNum ).toDouble(); 00687 QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) ); 00688 00689 QList<double> breaks; 00690 QList<int> labels; 00691 if ( mode == EqualInterval ) 00692 { 00693 breaks = _calcEqualIntervalBreaks( minimum, maximum, classes ); 00694 } 00695 else if ( mode == Pretty ) 00696 { 00697 breaks = _calcPrettyBreaks( minimum, maximum, classes ); 00698 } 00699 else if ( mode == Quantile || mode == Jenks || mode == StdDev ) 00700 { 00701 // get values from layer 00702 QList<double> values; 00703 QgsFeature f; 00704 QgsAttributeList lst; 00705 lst.append( attrNum ); 00706 vlayer->select( lst, QgsRectangle(), false ); 00707 while ( vlayer->nextFeature( f ) ) 00708 values.append( f.attributeMap()[attrNum].toDouble() ); 00709 // calculate the breaks 00710 if ( mode == Quantile ) 00711 { 00712 breaks = _calcQuantileBreaks( values, classes ); 00713 } 00714 else if ( mode == Jenks ) 00715 { 00716 breaks = _calcJenksBreaks( values, classes, minimum, maximum ); 00717 } 00718 else if ( mode == StdDev ) 00719 { 00720 breaks = _calcStdDevBreaks( values, classes, labels ); 00721 } 00722 } 00723 else 00724 { 00725 Q_ASSERT( false ); 00726 } 00727 00728 QgsRangeList ranges; 00729 double lower, upper = minimum; 00730 QString label; 00731 00732 // "breaks" list contains all values at class breaks plus maximum as last break 00733 int i = 0; 00734 for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i ) 00735 { 00736 lower = upper; // upper border from last interval 00737 upper = *it; 00738 if ( mode == StdDev ) 00739 { 00740 if ( i == 0 ) 00741 { 00742 label = "< " + QString::number( labels[i], 'i', 0 ) + " Std Dev"; 00743 } 00744 else if ( i == labels.count() - 1 ) 00745 { 00746 label = ">= " + QString::number( labels[i-1], 'i', 0 ) + " Std Dev"; 00747 } 00748 else 00749 { 00750 label = QString::number( labels[i-1], 'i', 0 ) + " Std Dev" + " - " + QString::number( labels[i], 'i', 0 ) + " Std Dev"; 00751 } 00752 } 00753 else 00754 { 00755 label = QString::number( lower, 'f', 4 ) + " - " + QString::number( upper, 'f', 4 ); 00756 } 00757 00758 QgsSymbolV2* newSymbol = symbol->clone(); 00759 newSymbol->setColor( ramp->color(( double ) i / ( breaks.count() - 1 ) ) ); // color from (0 / cl-1) to (cl-1 / cl-1) 00760 00761 ranges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) ); 00762 } 00763 00764 QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges ); 00765 r->setSourceSymbol( symbol->clone() ); 00766 r->setSourceColorRamp( ramp->clone() ); 00767 r->setMode( mode ); 00768 return r; 00769 } 00770 00771 QgsFeatureRendererV2* QgsGraduatedSymbolRendererV2::create( QDomElement& element ) 00772 { 00773 QDomElement symbolsElem = element.firstChildElement( "symbols" ); 00774 if ( symbolsElem.isNull() ) 00775 return NULL; 00776 00777 QDomElement rangesElem = element.firstChildElement( "ranges" ); 00778 if ( rangesElem.isNull() ) 00779 return NULL; 00780 00781 QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem ); 00782 QgsRangeList ranges; 00783 00784 QDomElement rangeElem = rangesElem.firstChildElement(); 00785 while ( !rangeElem.isNull() ) 00786 { 00787 if ( rangeElem.tagName() == "range" ) 00788 { 00789 double lowerValue = rangeElem.attribute( "lower" ).toDouble(); 00790 double upperValue = rangeElem.attribute( "upper" ).toDouble(); 00791 QString symbolName = rangeElem.attribute( "symbol" ); 00792 QString label = rangeElem.attribute( "label" ); 00793 if ( symbolMap.contains( symbolName ) ) 00794 { 00795 QgsSymbolV2* symbol = symbolMap.take( symbolName ); 00796 ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label ) ); 00797 } 00798 } 00799 rangeElem = rangeElem.nextSiblingElement(); 00800 } 00801 00802 QString attrName = element.attribute( "attr" ); 00803 00804 QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges ); 00805 00806 // delete symbols if there are any more 00807 QgsSymbolLayerV2Utils::clearSymbolMap( symbolMap ); 00808 00809 // try to load source symbol (optional) 00810 QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" ); 00811 if ( !sourceSymbolElem.isNull() ) 00812 { 00813 QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem ); 00814 if ( sourceSymbolMap.contains( "0" ) ) 00815 { 00816 r->setSourceSymbol( sourceSymbolMap.take( "0" ) ); 00817 } 00818 QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap ); 00819 } 00820 00821 // try to load color ramp (optional) 00822 QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" ); 00823 if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" ) 00824 { 00825 r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) ); 00826 } 00827 00828 // try to load mode 00829 QDomElement modeElem = element.firstChildElement( "mode" ); 00830 if ( !modeElem.isNull() ) 00831 { 00832 QString modeString = modeElem.attribute( "name" ); 00833 if ( modeString == "equal" ) 00834 r->setMode( EqualInterval ); 00835 else if ( modeString == "quantile" ) 00836 r->setMode( Quantile ); 00837 else if ( modeString == "jenks" ) 00838 r->setMode( Jenks ); 00839 else if ( modeString == "stddev" ) 00840 r->setMode( StdDev ); 00841 else if ( modeString == "pretty" ) 00842 r->setMode( Pretty ); 00843 } 00844 00845 QDomElement rotationElem = element.firstChildElement( "rotation" ); 00846 if ( !rotationElem.isNull() ) 00847 r->setRotationField( rotationElem.attribute( "field" ) ); 00848 00849 QDomElement sizeScaleElem = element.firstChildElement( "sizescale" ); 00850 if ( !sizeScaleElem.isNull() ) 00851 r->setSizeScaleField( sizeScaleElem.attribute( "field" ) ); 00852 00853 // TODO: symbol levels 00854 return r; 00855 } 00856 00857 QDomElement QgsGraduatedSymbolRendererV2::save( QDomDocument& doc ) 00858 { 00859 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME ); 00860 rendererElem.setAttribute( "type", "graduatedSymbol" ); 00861 rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) ); 00862 rendererElem.setAttribute( "attr", mAttrName ); 00863 00864 // ranges 00865 int i = 0; 00866 QgsSymbolV2Map symbols; 00867 QDomElement rangesElem = doc.createElement( "ranges" ); 00868 QgsRangeList::const_iterator it = mRanges.constBegin(); 00869 for ( ; it != mRanges.end(); it++ ) 00870 { 00871 const QgsRendererRangeV2& range = *it; 00872 QString symbolName = QString::number( i ); 00873 symbols.insert( symbolName, range.symbol() ); 00874 00875 QDomElement rangeElem = doc.createElement( "range" ); 00876 rangeElem.setAttribute( "lower", range.lowerValue() ); 00877 rangeElem.setAttribute( "upper", range.upperValue() ); 00878 rangeElem.setAttribute( "symbol", symbolName ); 00879 rangeElem.setAttribute( "label", range.label() ); 00880 rangesElem.appendChild( rangeElem ); 00881 i++; 00882 } 00883 00884 rendererElem.appendChild( rangesElem ); 00885 00886 // save symbols 00887 QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc ); 00888 rendererElem.appendChild( symbolsElem ); 00889 00890 // save source symbol 00891 if ( mSourceSymbol ) 00892 { 00893 QgsSymbolV2Map sourceSymbols; 00894 sourceSymbols.insert( "0", mSourceSymbol ); 00895 QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc ); 00896 rendererElem.appendChild( sourceSymbolElem ); 00897 } 00898 00899 // save source color ramp 00900 if ( mSourceColorRamp ) 00901 { 00902 QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp, doc ); 00903 rendererElem.appendChild( colorRampElem ); 00904 } 00905 00906 // save mode 00907 QString modeString; 00908 if ( mMode == EqualInterval ) 00909 modeString = "equal"; 00910 else if ( mMode == Quantile ) 00911 modeString = "quantile"; 00912 else if ( mMode == Jenks ) 00913 modeString = "jenks"; 00914 else if ( mMode == StdDev ) 00915 modeString = "stddev"; 00916 else if ( mMode == Pretty ) 00917 modeString = "pretty"; 00918 if ( !modeString.isEmpty() ) 00919 { 00920 QDomElement modeElem = doc.createElement( "mode" ); 00921 modeElem.setAttribute( "name", modeString ); 00922 rendererElem.appendChild( modeElem ); 00923 } 00924 00925 QDomElement rotationElem = doc.createElement( "rotation" ); 00926 rotationElem.setAttribute( "field", mRotationField ); 00927 rendererElem.appendChild( rotationElem ); 00928 00929 QDomElement sizeScaleElem = doc.createElement( "sizescale" ); 00930 sizeScaleElem.setAttribute( "field", mSizeScaleField ); 00931 rendererElem.appendChild( sizeScaleElem ); 00932 00933 return rendererElem; 00934 } 00935 00936 QgsLegendSymbologyList QgsGraduatedSymbolRendererV2::legendSymbologyItems( QSize iconSize ) 00937 { 00938 QSettings settings; 00939 bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool(); 00940 00941 QgsLegendSymbologyList lst; 00942 if ( showClassifiers ) 00943 { 00944 lst << qMakePair( classAttribute(), QPixmap() ); 00945 } 00946 00947 int count = ranges().count(); 00948 for ( int i = 0; i < count; i++ ) 00949 { 00950 const QgsRendererRangeV2& range = ranges()[i]; 00951 QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize ); 00952 lst << qMakePair( range.label(), pix ); 00953 } 00954 return lst; 00955 } 00956 00957 QgsLegendSymbolList QgsGraduatedSymbolRendererV2::legendSymbolItems() 00958 { 00959 QSettings settings; 00960 bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool(); 00961 00962 QgsLegendSymbolList lst; 00963 if ( showClassifiers ) 00964 { 00965 lst << qMakePair( classAttribute(), ( QgsSymbolV2* )0 ); 00966 } 00967 00968 QgsRangeList::const_iterator rangeIt = mRanges.constBegin(); 00969 for ( ; rangeIt != mRanges.constEnd(); ++rangeIt ) 00970 { 00971 lst << qMakePair( rangeIt->label(), rangeIt->symbol() ); 00972 } 00973 return lst; 00974 } 00975 00976 QgsSymbolV2* QgsGraduatedSymbolRendererV2::sourceSymbol() 00977 { 00978 return mSourceSymbol; 00979 } 00980 void QgsGraduatedSymbolRendererV2::setSourceSymbol( QgsSymbolV2* sym ) 00981 { 00982 delete mSourceSymbol; 00983 mSourceSymbol = sym; 00984 } 00985 00986 QgsVectorColorRampV2* QgsGraduatedSymbolRendererV2::sourceColorRamp() 00987 { 00988 return mSourceColorRamp; 00989 } 00990 void QgsGraduatedSymbolRendererV2::setSourceColorRamp( QgsVectorColorRampV2* ramp ) 00991 { 00992 delete mSourceColorRamp; 00993 mSourceColorRamp = ramp; 00994 } 00995 00996 void QgsGraduatedSymbolRendererV2::addClass( QgsSymbolV2* symbol ) 00997 { 00998 QgsSymbolV2* newSymbol = symbol->clone(); 00999 QString label = "0.0 - 0.0"; 01000 mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) ); 01001 01002 } 01003 01004 void QgsGraduatedSymbolRendererV2::deleteClass( int idx ) 01005 { 01006 mRanges.removeAt( idx ); 01007 }