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