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