Merge pull request #1587 from ccrook/CategorizedRendererUpdate

Categorized and graduated renderer enhancements
This commit is contained in:
Nathan Woodrow 2014-09-26 20:23:03 +10:00
commit 0c6576c423
15 changed files with 843 additions and 223 deletions

View File

@ -122,6 +122,10 @@ class QgsCategorizedSymbolRendererV2 : QgsFeatureRendererV2
bool invertedColorRamp();
void setInvertedColorRamp( bool inverted );
// Update the color ramp used and all symbols colors.
//! @note added in 2.5
void updateColorRamp( QgsVectorColorRampV2* ramp, bool inverted = false );
//! @note added in 1.6
void setRotationField( QString fieldOrExpression );
//! @note added in 1.6
@ -149,6 +153,10 @@ class QgsCategorizedSymbolRendererV2 : QgsFeatureRendererV2
// @note added in 2.5
virtual void checkLegendSymbolItem( QString key, bool state = true );
//! If supported by the renderer, return classification attribute for the use in legend
//! @note added in 2.6
virtual QString legendClassificationAttribute() const;
//! creates a QgsCategorizedSymbolRendererV2 from an existing renderer.
//! @note added in 2.5
//! @returns a new renderer if the conversion was possible, otherwise 0.

View File

@ -5,7 +5,8 @@ class QgsRendererRangeV2
%End
public:
QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol /Transfer/, QString label, bool render = true );
QgsRendererRangeV2();
QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol, QString label, bool render = true );
QgsRendererRangeV2( const QgsRendererRangeV2& range );
// default dtor is ok
@ -38,6 +39,42 @@ class QgsRendererRangeV2
typedef QList<QgsRendererRangeV2> QgsRangeList;
class QgsRendererRangeV2LabelFormat
{
%TypeHeaderCode
#include <qgsgraduatedsymbolrendererv2.h>
%End
public:
QgsRendererRangeV2LabelFormat();
QgsRendererRangeV2LabelFormat( QString prefix, QString separator, QString suffix, int decimalPlaces=4, bool trimTrailingZeroes=false );
bool operator==( const QgsRendererRangeV2LabelFormat & other ) const;
bool operator!=( const QgsRendererRangeV2LabelFormat & other ) const;
QString prefix() const;
void setPrefix( QString prefix );
QString separator() const;
void setSeparator( QString separator );
QString suffix() const;
void setSuffix( QString suffix );
int decimalPlaces() const;
void setDecimalPlaces( int decimalPlaces );
bool trimTrailingZeroes() const;
void setTrimTrailingZeroes( bool trimTrailingZeroes );
//! @note labelForLowerUpper in python bindings
QString labelForRange( double lower, double upper ) /PyName=labelForLowerUpper/;
QString labelForRange( const QgsRendererRangeV2 &range );
void setFromDomElement( QDomElement &element );
void saveToDomElement( QDomElement &element );
};
class QgsGraduatedSymbolRendererV2 : QgsFeatureRendererV2
{
%TypeHeaderCode
@ -86,6 +123,8 @@ class QgsGraduatedSymbolRendererV2 : QgsFeatureRendererV2
void addClass( QgsSymbolV2* symbol );
//! @note available in python bindings as addClassRange
void addClass( QgsRendererRangeV2 range ) /PyName=addClassRange/;
//! @note available in python bindings as addClassLowerUpper
void addClass( double lower, double upper ) /PyName=addClassLowerUpper/;
void deleteClass( int idx );
void deleteAllClasses();
@ -107,6 +146,30 @@ class QgsGraduatedSymbolRendererV2 : QgsFeatureRendererV2
Mode mode() const;
void setMode( Mode mode );
//! Recalculate classes for a layer
//! @param vlayer The layer being rendered (from which data values are calculated)
//! @param mode The calculation mode
//! @param nclasses The number of classes to calculate (approximate for some modes)
//! @note Added in 2.6
void updateClasses( QgsVectorLayer *vlayer, Mode mode, int nclasses );
//! Evaluates the data expression and returns the list of values from the layer
//! @param vlayer The layer for which to evaluate the expression
//! @note Added in 2.6
QList<double> getDataValues( QgsVectorLayer *vlayer );
//! Return the label format used to generate default classification labels
//! @note Added in 2.6
const QgsRendererRangeV2LabelFormat &labelFormat();
//! Set the label format used to generate default classification labels
//! @param labelFormat The string appended to classification labels
//! @param updateRanges If true then ranges ending with the old unit string are updated to the new.
//! @note Added in 2.6
void setLabelFormat( const QgsRendererRangeV2LabelFormat &labelFormat, bool updateRanges=true );
//! Reset the label decimal places to a numberbased on the minimum class interval
//! @param updateRanges if true then ranges currently using the default label will be updated
//! @note Added in 2.6
void calculateLabelDecimalPlaces( bool updateRanges=true );
static QgsGraduatedSymbolRendererV2* createRenderer(
QgsVectorLayer* vlayer,
@ -115,7 +178,9 @@ class QgsGraduatedSymbolRendererV2 : QgsFeatureRendererV2
Mode mode,
QgsSymbolV2* symbol /Transfer/,
QgsVectorColorRampV2* ramp /Transfer/,
bool inverted = false ) /Factory/;
bool inverted = false,
QgsRendererRangeV2LabelFormat labelFormat=QgsRendererRangeV2LabelFormat()
);
//! create renderer from XML element
static QgsFeatureRendererV2* create( QDomElement& element ) /Factory/;
@ -143,9 +208,9 @@ class QgsGraduatedSymbolRendererV2 : QgsFeatureRendererV2
/** Update the color ramp used. Also updates all symbols colors.
* Doesn't alter current breaks.
*/
void updateColorRamp( QgsVectorColorRampV2* ramp /Transfer/ );
void updateColorRamp( QgsVectorColorRampV2* ramp /Transfer/ = 0, bool inverted = false );
/** Update the all symbols but leave breaks and colors. */
/** Update all the symbols but leave breaks and colors. */
void updateSymbols( QgsSymbolV2* sym /Transfer/ );
//! @note added in 1.6
@ -175,6 +240,10 @@ class QgsGraduatedSymbolRendererV2 : QgsFeatureRendererV2
// @note added in 2.5
virtual void checkLegendSymbolItem( QString key, bool state = true );
//! If supported by the renderer, return classification attribute for the use in legend
//! @note added in 2.6
virtual QString legendClassificationAttribute();
//! creates a QgsGraduatedSymbolRendererV2 from an existing renderer.
//! @note added in 2.5
//! @returns a new renderer if the conversion was possible, otherwise 0.

View File

@ -18,6 +18,7 @@ class QgsCategorizedSymbolRendererV2Widget : QgsRendererV2Widget
void categoriesDoubleClicked( const QModelIndex & idx );
void addCategory();
void addCategories();
void applyColorRamp();
void deleteCategories();
void deleteAllCategories();
@ -49,6 +50,8 @@ class QgsCategorizedSymbolRendererV2Widget : QgsRendererV2Widget
void changeCategorySymbol();
QgsVectorColorRampV2* getColorRamp();
QList<QgsSymbolV2*> selectedSymbols();
QgsCategoryList selectedCategoryList();
void refreshSymbolView();

View File

@ -26,17 +26,24 @@ class QgsGraduatedSymbolRendererV2Widget : QgsRendererV2Widget
void deleteClasses();
/**Removes all classes from the classification*/
void deleteAllClasses();
/**Toggle the link between classes boundaries */
void toggleBoundariesLink( bool linked );
void rotationFieldChanged( QString fldName );
void sizeScaleFieldChanged( QString fldName );
void scaleMethodChanged( QgsSymbolV2::ScaleMethod scaleMethod );
void labelFormatChanged();
void showSymbolLevels();
void rowsMoved();
void modelDataChanged();
protected:
void updateUiFromRenderer();
void updateUiFromRenderer( bool updateCount=true );
void connectUpdateHandlers();
void disconnectUpdateHandlers();
bool rowsOrdered();
void updateGraduatedSymbolIcon();

View File

@ -168,7 +168,7 @@ void QgsCategorizedSymbolRendererV2::rebuildHash()
{
mSymbolHash.clear();
for ( int i = 0; i < mCategories.count(); ++i )
for ( int i = 0; i < mCategories.size(); ++i )
{
QgsRendererCategoryV2& cat = mCategories[i];
mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : &sSkipRender );
@ -181,7 +181,7 @@ QgsSymbolV2* QgsCategorizedSymbolRendererV2::symbolForValue( QVariant value )
QHash<QString, QgsSymbolV2*>::iterator it = mSymbolHash.find( value.toString() );
if ( it == mSymbolHash.end() )
{
if ( mSymbolHash.count() == 0 )
if ( mSymbolHash.size() == 0 )
{
QgsDebugMsg( "there are no hashed symbols!!!" );
}
@ -700,11 +700,27 @@ QgsVectorColorRampV2* QgsCategorizedSymbolRendererV2::sourceColorRamp()
{
return mSourceColorRamp.data();
}
void QgsCategorizedSymbolRendererV2::setSourceColorRamp( QgsVectorColorRampV2* ramp )
{
mSourceColorRamp.reset( ramp );
}
void QgsCategorizedSymbolRendererV2::updateColorRamp( QgsVectorColorRampV2* ramp, bool inverted )
{
setSourceColorRamp(ramp);
setInvertedColorRamp(inverted);
double num=mCategories.count()-1;
double count = 0;
foreach ( const QgsRendererCategoryV2 &cat, mCategories )
{
double value=count/num;
if( mInvertedColorRamp ) value=1.0-value;
cat.symbol()->setColor(mSourceColorRamp->color(value));
count += 1;
}
}
void QgsCategorizedSymbolRendererV2::setRotationField( QString fieldOrExpression )
{
mRotation.reset( QgsSymbolLayerV2Utils::fieldOrExpressionToExpression( fieldOrExpression ) );
@ -788,5 +804,16 @@ QgsCategorizedSymbolRendererV2* QgsCategorizedSymbolRendererV2::convertFromRende
const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
}
return 0;
// If not one of the specifically handled renderers, then just grab the symbol from the renderer
// Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
QgsCategorizedSymbolRendererV2* r =new QgsCategorizedSymbolRendererV2( "", QgsCategoryList() );
QgsSymbolV2List symbols=const_cast<QgsFeatureRendererV2 *>(renderer)->symbols();
if( symbols.size() > 0 )
{
r->setSourceSymbol(symbols.at(0)->clone());
}
return r;
}

View File

@ -152,6 +152,10 @@ class CORE_EXPORT QgsCategorizedSymbolRendererV2 : public QgsFeatureRendererV2
bool invertedColorRamp() { return mInvertedColorRamp; }
void setInvertedColorRamp( bool inverted ) { mInvertedColorRamp = inverted; }
// Update the color ramp used and all symbols colors.
//! @note added in 2.5
void updateColorRamp( QgsVectorColorRampV2* ramp, bool inverted = false );
//! @note added in 1.6
void setRotationField( QString fieldOrExpression );
//! @note added in 1.6

View File

@ -1,3 +1,4 @@
/***************************************************************************
qgsgraduatedsymbolrendererv2.cpp
---------------------
@ -62,6 +63,14 @@ QgsRendererRangeV2& QgsRendererRangeV2::operator=( QgsRendererRangeV2 range )
return *this;
}
bool QgsRendererRangeV2::operator<( const QgsRendererRangeV2 &other ) const
{
return
lowerValue() < other.lowerValue() ||
(lowerValue() == other.lowerValue() && upperValue() < other.upperValue());
}
void QgsRendererRangeV2::swap( QgsRendererRangeV2 & other )
{
qSwap( mLowerValue, other.mLowerValue );
@ -157,6 +166,89 @@ void QgsRendererRangeV2::toSld( QDomDocument &doc, QDomElement &element, QgsStri
///////////
QgsRendererRangeV2LabelFormat::QgsRendererRangeV2LabelFormat():
mPrefix(""),
mSeparator(" - "),
mSuffix(""),
mDecimalPlaces(4),
mTrimTrailingZeroes(false),
mReTrailingZeroes("\\.?0*$")
{
}
QgsRendererRangeV2LabelFormat::QgsRendererRangeV2LabelFormat( QString prefix, QString separator, QString suffix, int decimalPlaces, bool trimTrailingZeroes ):
mReTrailingZeroes("\\.?0*$")
{
setPrefix(prefix);
setSeparator(separator);
setSuffix(suffix);
setDecimalPlaces(decimalPlaces);
setTrimTrailingZeroes(trimTrailingZeroes);
}
bool QgsRendererRangeV2LabelFormat::operator==( const QgsRendererRangeV2LabelFormat &other ) const
{
return
prefix()==other.prefix() &&
separator()==other.separator() &&
suffix()==other.suffix() &&
decimalPlaces()==other.decimalPlaces() &&
trimTrailingZeroes()==other.trimTrailingZeroes();
}
bool QgsRendererRangeV2LabelFormat::operator!=( const QgsRendererRangeV2LabelFormat &other ) const
{
return ! (*this == other);
}
void QgsRendererRangeV2LabelFormat::setDecimalPlaces( int decimalPlaces )
{
// Limit the range of decimal places to a reasonable range
if( decimalPlaces < 0 ) decimalPlaces=0;
if( decimalPlaces > 15 ) decimalPlaces=15;
mDecimalPlaces=decimalPlaces;
}
QString QgsRendererRangeV2LabelFormat::labelForRange( const QgsRendererRangeV2 &range ) const
{
return labelForRange(range.lowerValue(),range.upperValue());
}
QString QgsRendererRangeV2LabelFormat::labelForRange( double lower, double upper) const
{
QString lowerStr=QString::number(lower,'f',mDecimalPlaces);
QString upperStr=QString::number(upper,'f',mDecimalPlaces);
if(mTrimTrailingZeroes)
{
if( lowerStr.contains('.')) lowerStr=lowerStr.replace(mReTrailingZeroes,"");
if( upperStr.contains('.')) upperStr=upperStr.replace(mReTrailingZeroes,"");
}
return mPrefix+lowerStr+ mSeparator+upperStr+mSuffix;
}
void QgsRendererRangeV2LabelFormat::setFromDomElement( QDomElement &element )
{
mPrefix=element.attribute("prefix","");
mSeparator=element.attribute("separator"," - ");
mSuffix=element.attribute("suffix","");
mDecimalPlaces=element.attribute("decimalplaces","4").toInt();
mTrimTrailingZeroes=element.attribute("trimtrailingzeroes","false") == "true";
}
void QgsRendererRangeV2LabelFormat::saveToDomElement( QDomElement &element )
{
element.setAttribute("prefix",mPrefix);
element.setAttribute("separator",mSeparator);
element.setAttribute("suffix",mSuffix);
element.setAttribute("decimalplaces",mDecimalPlaces);
element.setAttribute("trimtrailingzeroes",mTrimTrailingZeroes ? "true" : "false" );
}
///////////
QgsGraduatedSymbolRendererV2::QgsGraduatedSymbolRendererV2( QString attrName, QgsRangeList ranges )
: QgsFeatureRendererV2( "graduatedSymbol" ),
mAttrName( attrName ),
@ -336,7 +428,10 @@ bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double
{
if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
return false;
mRanges[rangeIndex].setUpperValue( value );
QgsRendererRangeV2 &range=mRanges[rangeIndex];
bool isDefaultLabel= range.label() == mLabelFormat.labelForRange(range);
range.setUpperValue( value );
if( isDefaultLabel ) range.setLabel(mLabelFormat.labelForRange(range));
return true;
}
@ -344,7 +439,10 @@ bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double
{
if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
return false;
mRanges[rangeIndex].setLowerValue( value );
QgsRendererRangeV2 &range=mRanges[rangeIndex];
bool isDefaultLabel= range.label() == mLabelFormat.labelForRange(range);
range.setLowerValue( value );
if( isDefaultLabel ) range.setLabel(mLabelFormat.labelForRange(range));
return true;
}
@ -379,6 +477,7 @@ QgsFeatureRendererV2* QgsGraduatedSymbolRendererV2::clone() const
r->setRotationField( rotationField() );
r->setSizeScaleField( sizeScaleField() );
r->setScaleMethod( scaleMethod() );
r->setLabelFormat( labelFormat() );
return r;
}
@ -619,7 +718,7 @@ static QList<double> _calcPrettyBreaks( double minimum, double maximum, int clas
} // _calcPrettyBreaks
static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<int> &labels )
static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels )
{
// C++ implementation of the standard deviation class interval algorithm
@ -658,7 +757,7 @@ static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList
QList<double> breaks = _calcPrettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
for ( int i = 0; i < breaks.count(); i++ )
{
labels.append(( int ) breaks[i] );
labels.append( breaks[i] );
breaks[i] = ( breaks[i] * stdDev ) + mean;
}
@ -805,34 +904,76 @@ QgsGraduatedSymbolRendererV2* QgsGraduatedSymbolRendererV2::createRenderer(
Mode mode,
QgsSymbolV2* symbol,
QgsVectorColorRampV2* ramp,
bool inverted )
bool inverted,
QgsRendererRangeV2LabelFormat labelFormat
)
{
if ( classes < 1 )
return NULL;
int attrNum = vlayer->fieldNameIndex( attrName );
double minimum;
double maximum;
QgsRangeList ranges;
QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
r->setSourceSymbol( symbol->clone() );
r->setSourceColorRamp( ramp->clone() );
r->setInvertedColorRamp( inverted );
r->setMode( mode );
r->setLabelFormat(labelFormat,false);
r->updateClasses(vlayer,mode,classes);
return r;
}
QList<double> QgsGraduatedSymbolRendererV2::getDataValues( QgsVectorLayer *vlayer )
{
QList<double> values;
QScopedPointer<QgsExpression> expression;
int attrNum = vlayer->fieldNameIndex( mAttrName );
if ( attrNum == -1 )
{
// try to use expression
expression.reset( new QgsExpression( attrName ) );
expression.reset( new QgsExpression( mAttrName ) );
if ( expression->hasParserError() || !expression->prepare( vlayer->pendingFields() ) )
return 0; // should have a means to report errors
return values; // should have a means to report errors
}
QList<double> values;
QgsFeatureIterator fit = vlayer->getFeatures();
QgsFeature feature;
while ( fit.nextFeature( feature ) )
{
values << expression->evaluate( feature ).toDouble();
}
QgsFeature f;
QStringList lst;
if ( expression.isNull() )
lst.append( mAttrName );
else
lst = expression->referencedColumns();
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( lst, vlayer->pendingFields() ) );
// create list of non-null attribute values
while ( fit.nextFeature( f ) )
{
QVariant v = expression.isNull() ? f.attribute( attrNum ) : expression->evaluate( f );
if ( !v.isNull() )
values.append( v.toDouble() );
}
return values;
}
void QgsGraduatedSymbolRendererV2::updateClasses( QgsVectorLayer *vlayer, Mode mode, int nclasses )
{
// Custom classes are not recalculated
setMode(mode);
if( mode == Custom ) return;
if ( nclasses < 1 ) nclasses=1;
QList<double> values;
bool valuesLoaded=false;
double minimum;
double maximum;
int attrNum = vlayer->fieldNameIndex( mAttrName );
if ( attrNum == -1 )
{
values=getDataValues( vlayer );
qSort( values );
minimum = values.first();
maximum = values.last();
valuesLoaded=true;
}
else
{
@ -842,48 +983,37 @@ QgsGraduatedSymbolRendererV2* QgsGraduatedSymbolRendererV2::createRenderer(
QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
QList<double> breaks;
QList<int> labels;
QList<double> labels;
if ( mode == EqualInterval )
{
breaks = _calcEqualIntervalBreaks( minimum, maximum, classes );
breaks = _calcEqualIntervalBreaks( minimum, maximum, nclasses );
}
else if ( mode == Pretty )
{
breaks = _calcPrettyBreaks( minimum, maximum, classes );
breaks = _calcPrettyBreaks( minimum, maximum, nclasses );
}
else if ( mode == Quantile || mode == Jenks || mode == StdDev )
{
// get values from layer
QList<double> values;
QgsFeature f;
QStringList lst;
if ( expression.isNull() )
lst.append( attrName );
else
lst = expression->referencedColumns();
QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( lst, vlayer->pendingFields() ) );
// create list of non-null attribute values
while ( fit.nextFeature( f ) )
if( ! valuesLoaded )
{
QVariant v = expression.isNull() ? f.attribute( attrNum ) : expression->evaluate( f );
if ( !v.isNull() )
values.append( v.toDouble() );
values=getDataValues( vlayer );
}
// calculate the breaks
if ( mode == Quantile )
{
breaks = _calcQuantileBreaks( values, classes );
breaks = _calcQuantileBreaks( values, nclasses );
}
else if ( mode == Jenks )
{
breaks = _calcJenksBreaks( values, classes, minimum, maximum );
breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
}
else if ( mode == StdDev )
{
breaks = _calcStdDevBreaks( values, classes, labels );
breaks = _calcStdDevBreaks( values, nclasses, labels );
}
}
else
@ -891,53 +1021,48 @@ QgsGraduatedSymbolRendererV2* QgsGraduatedSymbolRendererV2::createRenderer(
Q_ASSERT( false );
}
QgsRangeList ranges;
double lower, upper = minimum;
QString label;
deleteAllClasses();
// "breaks" list contains all values at class breaks plus maximum as last break
int i = 0;
for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
{
lower = upper; // upper border from last interval
upper = *it;
// Label - either StdDev label or default label for a range
if ( mode == StdDev )
{
if ( i == 0 )
{
label = "< " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
}
else if ( i == labels.count() - 1 )
{
label = ">= " + QString::number( labels[i-1], 'i', 0 ) + " Std Dev";
label = ">= " + QString::number( labels[i-1], 'f', 2 ) + " Std Dev";
}
else
{
label = QString::number( labels[i-1], 'i', 0 ) + " Std Dev" + " - " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
label = QString::number( labels[i-1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
}
}
else
{
label = QString::number( lower, 'f', 4 ) + " - " + QString::number( upper, 'f', 4 );
label=mLabelFormat.labelForRange(lower,upper);
}
QgsSymbolV2* newSymbol = symbol->clone();
double colorValue;
if ( inverted ) colorValue = ( breaks.count() > 1 ? ( double )( breaks.count() - i - 1 ) / ( breaks.count() - 1 ) : 0 );
else colorValue = ( breaks.count() > 1 ? ( double ) i / ( breaks.count() - 1 ) : 0 );
newSymbol->setColor( ramp->color( colorValue ) ); // color from (0 / cl-1) to (cl-1 / cl-1)
ranges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
QgsSymbolV2* newSymbol = mSourceSymbol->clone();
addClass( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
}
updateColorRamp(0,mInvertedColorRamp);
QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
r->setSourceSymbol( symbol->clone() );
r->setSourceColorRamp( ramp->clone() );
r->setInvertedColorRamp( inverted );
r->setMode( mode );
return r;
}
QgsFeatureRendererV2* QgsGraduatedSymbolRendererV2::create( QDomElement& element )
{
QDomElement symbolsElem = element.firstChildElement( "symbols" );
@ -1024,6 +1149,13 @@ QgsFeatureRendererV2* QgsGraduatedSymbolRendererV2::create( QDomElement& element
r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
r->setScaleMethod( QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ) );
QDomElement labelFormatElem = element.firstChildElement( "labelformat" );
if( ! labelFormatElem.isNull() )
{
QgsRendererRangeV2LabelFormat labelFormat;
labelFormat.setFromDomElement( labelFormatElem );
r->setLabelFormat( labelFormat );
}
// TODO: symbol levels
return r;
}
@ -1111,6 +1243,10 @@ QDomElement QgsGraduatedSymbolRendererV2::save( QDomDocument& doc )
sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
rendererElem.appendChild( sizeScaleElem );
QDomElement labelFormatElem=doc.createElement("labelformat");
mLabelFormat.saveToDomElement(labelFormatElem);
rendererElem.appendChild( labelFormatElem );
return rendererElem;
}
@ -1164,18 +1300,26 @@ void QgsGraduatedSymbolRendererV2::setSourceColorRamp( QgsVectorColorRampV2* ram
void QgsGraduatedSymbolRendererV2::updateColorRamp( QgsVectorColorRampV2 *ramp, bool inverted )
{
int i = 0;
foreach ( QgsRendererRangeV2 range, mRanges )
if( ramp )
{
QgsSymbolV2* symbol = range.symbol()->clone();
double colorValue;
if ( inverted ) colorValue = ( mRanges.count() > 1 ? ( double )( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
else colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
symbol->setColor( ramp->color( colorValue ) );
updateRangeSymbol( i, symbol );
++i;
this->setSourceColorRamp( ramp );
this->setInvertedColorRamp( inverted );
}
this->setSourceColorRamp( ramp );
this->setInvertedColorRamp( inverted );
if( mSourceColorRamp )
{
foreach ( QgsRendererRangeV2 range, mRanges )
{
QgsSymbolV2* symbol = range.symbol()->clone();
double colorValue;
if ( inverted ) colorValue = ( mRanges.count() > 1 ? ( double )( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
else colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
symbol->setColor( mSourceColorRamp->color( colorValue ) );
updateRangeSymbol( i, symbol );
++i;
}
}
}
void QgsGraduatedSymbolRendererV2::updateSymbols( QgsSymbolV2 *sym )
@ -1249,7 +1393,13 @@ void QgsGraduatedSymbolRendererV2::addClass( QgsSymbolV2* symbol )
QgsSymbolV2* newSymbol = symbol->clone();
QString label = "0.0 - 0.0";
mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
}
void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
{
QgsSymbolV2* newSymbol = mSourceSymbol->clone();
QString label=mLabelFormat.labelForRange( lower, upper );
mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ));
}
void QgsGraduatedSymbolRendererV2::addClass( QgsRendererRangeV2 range )
@ -1267,6 +1417,45 @@ void QgsGraduatedSymbolRendererV2::deleteAllClasses()
mRanges.clear();
}
void QgsGraduatedSymbolRendererV2::setLabelFormat( const QgsRendererRangeV2LabelFormat &labelFormat, bool updateRanges )
{
if( updateRanges && labelFormat != mLabelFormat)
{
for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
{
it->setLabel( labelFormat.labelForRange(*it));
}
}
mLabelFormat=labelFormat;
}
void QgsGraduatedSymbolRendererV2::calculateLabelDecimalPlaces( bool updateRanges )
{
// Find the minimum size of a class
double minClassRange=0.0;
for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
{
double range = it->upperValue()-it->lowerValue();
if( range <= 0.0 ) continue;
if( minClassRange == 0.0 || range < minClassRange ) minClassRange=range;
}
if( minClassRange <= 0.0 ) return;
// Now set the number of decimal places to ensure no more than 20% error in
// representing this range (up to 10% at upper and lower end)
int ndp=10;
double nextDpMinRange=0.0000000099;
while( ndp > 0 && nextDpMinRange < minClassRange )
{
ndp--;
nextDpMinRange *= 10.0;
}
mLabelFormat.setDecimalPlaces(ndp);
if( updateRanges ) setLabelFormat( mLabelFormat, true );
}
void QgsGraduatedSymbolRendererV2::moveClass( int from, int to )
{
if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() ) return;
@ -1275,7 +1464,7 @@ void QgsGraduatedSymbolRendererV2::moveClass( int from, int to )
bool valueLessThan( const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2 )
{
return r1.lowerValue() < r2.lowerValue();
return r1 < r2;
}
bool valueGreaterThan( const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2 )
@ -1334,5 +1523,16 @@ QgsGraduatedSymbolRendererV2* QgsGraduatedSymbolRendererV2::convertFromRenderer(
const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
}
return 0;
// If not one of the specifically handled renderers, then just grab the symbol from the renderer
// Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
QgsGraduatedSymbolRendererV2* r =new QgsGraduatedSymbolRendererV2( "", QgsRangeList() );
QgsSymbolV2List symbols=const_cast<QgsFeatureRendererV2 *>(renderer)->symbols();
if( symbols.size() > 0 )
{
r->setSourceSymbol(symbols.at(0)->clone());
}
return r;
}

View File

@ -19,6 +19,7 @@
#include "qgsrendererv2.h"
#include "qgsexpression.h"
#include <QScopedPointer>
#include <QRegExp>
class CORE_EXPORT QgsRendererRangeV2
{
@ -30,6 +31,8 @@ class CORE_EXPORT QgsRendererRangeV2
// default dtor is ok
QgsRendererRangeV2& operator=( QgsRendererRangeV2 range );
bool operator<( const QgsRendererRangeV2 &other ) const;
double lowerValue() const;
double upperValue() const;
@ -62,6 +65,48 @@ class CORE_EXPORT QgsRendererRangeV2
typedef QList<QgsRendererRangeV2> QgsRangeList;
// @note added in 2.6
class CORE_EXPORT QgsRendererRangeV2LabelFormat
{
public:
QgsRendererRangeV2LabelFormat();
QgsRendererRangeV2LabelFormat( QString prefix, QString separator, QString suffix, int decimalPlaces=4, bool trimTrailingZeroes=false );
bool operator==( const QgsRendererRangeV2LabelFormat & other ) const;
bool operator!=( const QgsRendererRangeV2LabelFormat & other ) const;
QString prefix() const { return mPrefix; }
void setPrefix( QString prefix ) { mPrefix=prefix; }
QString separator() const { return mSeparator; }
void setSeparator( QString separator ) { mSeparator=separator; }
QString suffix() const { return mSuffix; }
void setSuffix( QString suffix ){ mSuffix=suffix; }
int decimalPlaces() const { return mDecimalPlaces; }
void setDecimalPlaces( int decimalPlaces );
bool trimTrailingZeroes() const { return mTrimTrailingZeroes; }
void setTrimTrailingZeroes( bool trimTrailingZeroes ){ mTrimTrailingZeroes=trimTrailingZeroes; }
//! @note labelForLowerUpper in python bindings
QString labelForRange( double lower, double upper ) const;
QString labelForRange( const QgsRendererRangeV2 &range ) const;
void setFromDomElement( QDomElement &element );
void saveToDomElement( QDomElement &element );
protected:
QString mPrefix;
QString mSeparator;
QString mSuffix;
int mDecimalPlaces;
bool mTrimTrailingZeroes;
QRegExp mReTrailingZeroes;
};
class QgsVectorLayer;
class QgsVectorColorRampV2;
@ -107,10 +152,11 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
//! @note added in 2.5
bool updateRangeRenderState( int rangeIndex, bool render );
void addClass( QgsSymbolV2* symbol );
//! @note available in python bindings as addClassRange
void addClass( QgsRendererRangeV2 range );
//! @note available in python bindings as addClassLowerUpper
void addClass( double lower, double upper );
void deleteClass( int idx );
void deleteAllClasses();
@ -132,6 +178,30 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
Mode mode() const { return mMode; }
void setMode( Mode mode ) { mMode = mode; }
//! Recalculate classes for a layer
//! @param vlayer The layer being rendered (from which data values are calculated)
//! @param mode The calculation mode
//! @param nclasses The number of classes to calculate (approximate for some modes)
//! @note Added in 2.6
void updateClasses( QgsVectorLayer *vlayer, Mode mode, int nclasses );
//! Evaluates the data expression and returns the list of values from the layer
//! @param vlayer The layer for which to evaluate the expression
//! @note Added in 2.6
QList<double> getDataValues( QgsVectorLayer *vlayer );
//! Return the label format used to generate default classification labels
//! @note Added in 2.6
const QgsRendererRangeV2LabelFormat &labelFormat() const { return mLabelFormat; }
//! Set the label format used to generate default classification labels
//! @param labelFormat The string appended to classification labels
//! @param updateRanges If true then ranges ending with the old unit string are updated to the new.
//! @note Added in 2.6
void setLabelFormat( const QgsRendererRangeV2LabelFormat &labelFormat, bool updateRanges=true );
//! Reset the label decimal places to a numberbased on the minimum class interval
//! @param updateRanges if true then ranges currently using the default label will be updated
//! @note Added in 2.6
void calculateLabelDecimalPlaces( bool updateRanges=true );
static QgsGraduatedSymbolRendererV2* createRenderer(
QgsVectorLayer* vlayer,
@ -140,7 +210,9 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
Mode mode,
QgsSymbolV2* symbol,
QgsVectorColorRampV2* ramp,
bool inverted = false );
bool inverted = false,
QgsRendererRangeV2LabelFormat labelFormat=QgsRendererRangeV2LabelFormat()
);
//! create renderer from XML element
static QgsFeatureRendererV2* create( QDomElement& element );
@ -168,7 +240,7 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
/** Update the color ramp used. Also updates all symbols colors.
* Doesn't alter current breaks.
*/
void updateColorRamp( QgsVectorColorRampV2* ramp, bool inverted = false );
void updateColorRamp( QgsVectorColorRampV2* ramp=0, bool inverted = false );
/** Update all the symbols but leave breaks and colors. */
void updateSymbols( QgsSymbolV2* sym );
@ -193,11 +265,11 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
virtual bool legendSymbolItemsCheckable() const;
//! item in symbology was checked
// @note added in 2.5
//! @note added in 2.6
virtual bool legendSymbolItemChecked( QString key );
//! item in symbology was checked
// @note added in 2.5
//! @note added in 2.6
virtual void checkLegendSymbolItem( QString key, bool state = true );
//! If supported by the renderer, return classification attribute for the use in legend
@ -205,7 +277,7 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
virtual QString legendClassificationAttribute() const { return classAttribute(); }
//! creates a QgsGraduatedSymbolRendererV2 from an existing renderer.
//! @note added in 2.5
//! @note added in 2.6
//! @returns a new renderer if the conversion was possible, otherwise 0.
static QgsGraduatedSymbolRendererV2* convertFromRenderer( const QgsFeatureRendererV2 *renderer );
@ -216,6 +288,7 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
QScopedPointer<QgsSymbolV2> mSourceSymbol;
QScopedPointer<QgsVectorColorRampV2> mSourceColorRamp;
bool mInvertedColorRamp;
QgsRendererRangeV2LabelFormat mLabelFormat;
QScopedPointer<QgsExpression> mRotation;
QScopedPointer<QgsExpression> mSizeScale;
QgsSymbolV2::ScaleMethod mScaleMethod;

View File

@ -399,5 +399,11 @@ QgsSingleSymbolRendererV2* QgsSingleSymbolRendererV2::convertFromRenderer( const
return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
}
QgsSymbolV2List symbols=const_cast<QgsFeatureRendererV2 *>(renderer)->symbols();
if( symbols.size() > 0 )
{
return new QgsSingleSymbolRendererV2(symbols.at(0)->clone());
}
return 0;
}

View File

@ -202,4 +202,4 @@ void QgsAttributeTypeLoadDialog::accept()
//store data to output variable
loadDataToValueMap();
QDialog::accept();
}
}

View File

@ -338,6 +338,11 @@ void QgsCategorizedSymbolRendererV2Model::sort( int column, Qt::SortOrder order
QgsDebugMsg( "Done" );
}
void QgsCategorizedSymbolRendererV2Model::updateSymbology()
{
emit dataChanged( createIndex( 0, 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
}
// ------------------------------ View style --------------------------------
QgsCategorizedSymbolRendererV2ViewStyle::QgsCategorizedSymbolRendererV2ViewStyle( QStyle* style )
: QProxyStyle( style )
@ -405,8 +410,14 @@ QgsCategorizedSymbolRendererV2Widget::QgsCategorizedSymbolRendererV2Widget( QgsV
cboCategorizedColorRamp->setCurrentIndex( index );
}
mCategorizedSymbol = QgsSymbolV2::defaultSymbol( mLayer->geometryType() );
mModel = new QgsCategorizedSymbolRendererV2Model( this );
mModel->setRenderer( mRenderer );
// update GUI from renderer
updateUiFromRenderer();
viewCategories->setModel( mModel );
viewCategories->resizeColumnToContents( 0 );
viewCategories->resizeColumnToContents( 1 );
@ -414,8 +425,6 @@ QgsCategorizedSymbolRendererV2Widget::QgsCategorizedSymbolRendererV2Widget( QgsV
viewCategories->setStyle( new QgsCategorizedSymbolRendererV2ViewStyle( viewCategories->style() ) );
mCategorizedSymbol = QgsSymbolV2::defaultSymbol( mLayer->geometryType() );
connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( categoryColumnChanged( QString ) ) );
@ -428,9 +437,8 @@ QgsCategorizedSymbolRendererV2Widget::QgsCategorizedSymbolRendererV2Widget( QgsV
connect( btnDeleteCategories, SIGNAL( clicked() ), this, SLOT( deleteCategories() ) );
connect( btnDeleteAllCategories, SIGNAL( clicked() ), this, SLOT( deleteAllCategories() ) );
connect( btnAddCategory, SIGNAL( clicked() ), this, SLOT( addCategory() ) );
// update GUI from renderer
updateUiFromRenderer();
connect( cbxInvertedColorRamp, SIGNAL( toggled(bool)), this, SLOT( applyColorRamp()));
connect( cboCategorizedColorRamp, SIGNAL(currentIndexChanged(int)), this, SLOT( applyColorRamp()));
// menus for data-defined rotation/size
QMenu* advMenu = new QMenu;
@ -453,16 +461,17 @@ QgsCategorizedSymbolRendererV2Widget::~QgsCategorizedSymbolRendererV2Widget()
void QgsCategorizedSymbolRendererV2Widget::updateUiFromRenderer()
{
// Note: This assumes that the signals for UI element changes have not
// yet been connected, so that the updates to color ramp, symbol, etc
// don't override existing customisations.
updateCategorizedSymbolIcon();
//mModel->setRenderer ( mRenderer ); // necessary?
// set column
disconnect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( categoryColumnChanged( QString ) ) );
QString attrName = mRenderer->classAttribute();
mExpressionWidget->setField( attrName );
connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( categoryColumnChanged( QString ) ) );
// set source symbol
if ( mRenderer->sourceSymbol() )
@ -585,7 +594,7 @@ void QgsCategorizedSymbolRendererV2Widget::changeCategorySymbol()
mRenderer->updateCategorySymbol( catIdx, symbol );
}
static void _createCategories( QgsCategoryList& cats, QList<QVariant>& values, QgsSymbolV2* symbol, QgsVectorColorRampV2* ramp, bool invert )
static void _createCategories( QgsCategoryList& cats, QList<QVariant>& values, QgsSymbolV2* symbol )
{
// sort the categories first
QgsSymbolLayerV2Utils::sortVariantList( values, Qt::AscendingOrder );
@ -612,9 +621,7 @@ static void _createCategories( QgsCategoryList& cats, QList<QVariant>& values, Q
{
hasNull = true;
}
double x = ( invert ? num - i : i ) / ( double ) num;
QgsSymbolV2* newSymbol = symbol->clone();
newSymbol->setColor( ramp->color( x ) );
cats.append( QgsRendererCategoryV2( value, newSymbol, value.toString(), true ) );
}
@ -623,11 +630,24 @@ static void _createCategories( QgsCategoryList& cats, QList<QVariant>& values, Q
if ( !hasNull )
{
QgsSymbolV2* newSymbol = symbol->clone();
newSymbol->setColor( ramp->color( invert ? 0 : 1 ) );
cats.append( QgsRendererCategoryV2( QVariant( "" ), newSymbol, QString(), true ) );
}
}
QgsVectorColorRampV2* QgsCategorizedSymbolRendererV2Widget::getColorRamp()
{
QgsVectorColorRampV2* ramp = cboCategorizedColorRamp->currentColorRamp();
if ( ramp == NULL )
{
if ( cboCategorizedColorRamp->count() == 0 )
QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
else
QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
}
return ramp;
}
void QgsCategorizedSymbolRendererV2Widget::addCategories()
{
QString attrName = mExpressionWidget->currentField();
@ -672,20 +692,8 @@ void QgsCategorizedSymbolRendererV2Widget::addCategories()
return;
#endif
QgsVectorColorRampV2* ramp = cboCategorizedColorRamp->currentColorRamp();
if ( ramp == NULL )
{
if ( cboCategorizedColorRamp->count() == 0 )
QMessageBox::critical( this, tr( "Error" ), tr( "There are no available color ramps. You can add them in Style Manager." ) );
else
QMessageBox::critical( this, tr( "Error" ), tr( "The selected color ramp is not available." ) );
return;
}
QgsCategoryList cats;
_createCategories( cats, unique_vals, mCategorizedSymbol, ramp, cbxInvertedColorRamp->isChecked() );
_createCategories( cats, unique_vals, mCategorizedSymbol );
bool deleteExisting = false;
if ( !mOldClassificationAttribute.isEmpty() &&
@ -706,9 +714,12 @@ void QgsCategorizedSymbolRendererV2Widget::addCategories()
deleteExisting = ( res == QMessageBox::Yes );
}
// First element to apply coloring to
bool keepExistingColors=false;
if ( !deleteExisting )
{
QgsCategoryList prevCats = mRenderer->categories();
keepExistingColors=prevCats.size() > 0;
for ( int i = 0; i < cats.size(); ++i )
{
bool contains = false;
@ -747,11 +758,12 @@ void QgsCategorizedSymbolRendererV2Widget::addCategories()
// recreate renderer
QgsCategorizedSymbolRendererV2 *r = new QgsCategorizedSymbolRendererV2( attrName, cats );
r->setSourceSymbol( mCategorizedSymbol->clone() );
r->setSourceColorRamp( ramp->clone() );
r->setScaleMethod( mRenderer->scaleMethod() );
r->setSizeScaleField( mRenderer->sizeScaleField() );
r->setRotationField( mRenderer->rotationField() );
r->setInvertedColorRamp( cbxInvertedColorRamp->isChecked() );
QgsVectorColorRampV2* ramp = getColorRamp();
if( ramp ) r->setSourceColorRamp(ramp->clone());
if ( mModel )
{
@ -759,6 +771,17 @@ void QgsCategorizedSymbolRendererV2Widget::addCategories()
}
delete mRenderer;
mRenderer = r;
if( ! keepExistingColors && ramp ) applyColorRamp();
}
void QgsCategorizedSymbolRendererV2Widget::applyColorRamp()
{
QgsVectorColorRampV2* ramp = getColorRamp();
if( ramp )
{
mRenderer->updateColorRamp(ramp->clone(),cbxInvertedColorRamp->isChecked());
}
mModel->updateSymbology();
}
int QgsCategorizedSymbolRendererV2Widget::currentCategoryRow()

View File

@ -50,6 +50,7 @@ class GUI_EXPORT QgsCategorizedSymbolRendererV2Model : public QAbstractItemModel
void deleteRows( QList<int> rows );
void removeAllRows( );
void sort( int column, Qt::SortOrder order = Qt::AscendingOrder );
void updateSymbology();
signals:
void rowsMoved();
@ -85,6 +86,7 @@ class GUI_EXPORT QgsCategorizedSymbolRendererV2Widget : public QgsRendererV2Widg
void categoriesDoubleClicked( const QModelIndex & idx );
void addCategory();
void addCategories();
void applyColorRamp();
void deleteCategories();
void deleteAllCategories();
@ -116,6 +118,8 @@ class GUI_EXPORT QgsCategorizedSymbolRendererV2Widget : public QgsRendererV2Widg
void changeCategorySymbol();
QgsVectorColorRampV2* getColorRamp();
QList<QgsSymbolV2*> selectedSymbols();
QgsCategoryList selectedCategoryList();
void refreshSymbolView() { populateCategories(); }

View File

@ -330,6 +330,16 @@ void QgsGraduatedSymbolRendererV2Model::sort( int column, Qt::SortOrder order )
QgsDebugMsg( "Done" );
}
void QgsGraduatedSymbolRendererV2Model::updateSymbology()
{
emit dataChanged( createIndex( 0, 0, 0 ), createIndex( mRenderer->ranges().size(), 0 ) );
}
void QgsGraduatedSymbolRendererV2Model::updateLabels()
{
emit dataChanged( createIndex( 0, 2 ), createIndex( mRenderer->ranges().size(), 2 ) );
}
// ------------------------------ View style --------------------------------
QgsGraduatedSymbolRendererV2ViewStyle::QgsGraduatedSymbolRendererV2ViewStyle( QStyle* style )
: QProxyStyle( style )
@ -391,12 +401,6 @@ QgsGraduatedSymbolRendererV2Widget::QgsGraduatedSymbolRendererV2Widget( QgsVecto
cboGraduatedColorRamp->setCurrentIndex( index );
}
mModel = new QgsGraduatedSymbolRendererV2Model( this );
mModel->setRenderer( mRenderer );
viewGraduated->setModel( mModel );
viewGraduated->resizeColumnToContents( 0 );
viewGraduated->resizeColumnToContents( 1 );
viewGraduated->resizeColumnToContents( 2 );
viewGraduated->setStyle( new QgsGraduatedSymbolRendererV2ViewStyle( viewGraduated->style() ) );
@ -407,8 +411,6 @@ QgsGraduatedSymbolRendererV2Widget::QgsGraduatedSymbolRendererV2Widget( QgsVecto
connect( viewGraduated, SIGNAL( clicked( const QModelIndex & ) ), this, SLOT( rangesClicked( const QModelIndex & ) ) );
connect( viewGraduated, SIGNAL( customContextMenuRequested( const QPoint& ) ), this, SLOT( contextMenuViewCategories( const QPoint& ) ) );
connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
connect( btnGraduatedClassify, SIGNAL( clicked() ), this, SLOT( classifyGraduated() ) );
connect( btnChangeGraduatedSymbol, SIGNAL( clicked() ), this, SLOT( changeGraduatedSymbol() ) );
connect( btnGraduatedDelete, SIGNAL( clicked() ), this, SLOT( deleteClasses() ) );
@ -416,13 +418,11 @@ QgsGraduatedSymbolRendererV2Widget::QgsGraduatedSymbolRendererV2Widget( QgsVecto
connect( btnGraduatedAdd, SIGNAL( clicked() ), this, SLOT( addClass() ) );
connect( cbxLinkBoundaries, SIGNAL( toggled( bool ) ), this, SLOT( toggleBoundariesLink( bool ) ) );
connectUpdateHandlers();
// initialize from previously set renderer
updateUiFromRenderer();
connect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ) , this, SLOT( classifyGraduated() ) );
connect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ) , this, SLOT( classifyGraduated() ) );
connect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ) , this, SLOT( reapplyColorRamp() ) );
// menus for data-defined rotation/size
QMenu* advMenu = new QMenu;
@ -447,22 +447,60 @@ QgsFeatureRendererV2* QgsGraduatedSymbolRendererV2Widget::renderer()
return mRenderer;
}
// Connect/disconnect event handlers which trigger updating renderer
void QgsGraduatedSymbolRendererV2Widget::updateUiFromRenderer()
void QgsGraduatedSymbolRendererV2Widget::connectUpdateHandlers()
{
connect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ) , this, SLOT( classifyGraduated() ) );
connect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ) , this, SLOT( classifyGraduated() ) );
connect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ) , this, SLOT( reapplyColorRamp() ) );
connect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ) , this, SLOT( reapplyColorRamp() ) );
connect( spinDecimalPlaces, SIGNAL(valueChanged(int)), this, SLOT(labelFormatChanged()));
connect( cbxTrimTrailingZeroes, SIGNAL(toggled(bool)),this,SLOT(labelFormatChanged()));
connect( txtPrefix, SIGNAL( textChanged(QString)), this, SLOT(labelFormatChanged()));
connect( txtSeparator, SIGNAL( textChanged(QString)), this, SLOT(labelFormatChanged()));
connect( txtSuffix, SIGNAL( textChanged(QString)), this, SLOT(labelFormatChanged()));
connect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
connect( mModel, SIGNAL( dataChanged(QModelIndex,QModelIndex)), this, SLOT( modelDataChanged( )) );
}
// Connect/disconnect event handlers which trigger updating renderer
void QgsGraduatedSymbolRendererV2Widget::disconnectUpdateHandlers()
{
disconnect( spinGraduatedClasses, SIGNAL( valueChanged( int ) ) , this, SLOT( classifyGraduated() ) );
disconnect( cboGraduatedMode, SIGNAL( currentIndexChanged( int ) ) , this, SLOT( classifyGraduated() ) );
disconnect( cboGraduatedColorRamp, SIGNAL( currentIndexChanged( int ) ) , this, SLOT( reapplyColorRamp() ) );
disconnect( cbxInvertedColorRamp, SIGNAL( toggled( bool ) ) , this, SLOT( reapplyColorRamp() ) );
disconnect( spinDecimalPlaces, SIGNAL(valueChanged(int)), this, SLOT(labelFormatChanged()));
disconnect( cbxTrimTrailingZeroes, SIGNAL(toggled(bool)),this,SLOT(labelFormatChanged()));
disconnect( txtPrefix, SIGNAL( textChanged(QString)), this, SLOT(labelFormatChanged()));
disconnect( txtSeparator, SIGNAL( textChanged(QString)), this, SLOT(labelFormatChanged()));
disconnect( txtSuffix, SIGNAL( textChanged(QString)), this, SLOT(labelFormatChanged()));
disconnect( mModel, SIGNAL( rowsMoved() ), this, SLOT( rowsMoved() ) );
disconnect( mModel, SIGNAL( dataChanged(QModelIndex,QModelIndex)), this, SLOT( modelDataChanged( )) );
}
void QgsGraduatedSymbolRendererV2Widget::updateUiFromRenderer( bool updateCount )
{
disconnectUpdateHandlers();
updateGraduatedSymbolIcon();
// update UI from the graduated renderer (update combo boxes, view)
if ( mRenderer->mode() < cboGraduatedMode->count() )
cboGraduatedMode->setCurrentIndex( mRenderer->mode() );
if ( mRenderer->ranges().count() )
// Only update class count if different - otherwise typing value gets very messy
int nclasses=mRenderer->ranges().count();
if ( nclasses && updateCount )
spinGraduatedClasses->setValue( mRenderer->ranges().count() );
// set column
disconnect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( graduatedColumnChanged( QString ) ) );
QString attrName = mRenderer->classAttribute();
mExpressionWidget->setField( attrName );
connect( mExpressionWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( graduatedColumnChanged( QString ) ) );
// set source symbol
if ( mRenderer->sourceSymbol() )
@ -478,6 +516,22 @@ void QgsGraduatedSymbolRendererV2Widget::updateUiFromRenderer()
cboGraduatedColorRamp->setSourceColorRamp( mRenderer->sourceColorRamp() );
cbxInvertedColorRamp->setChecked( mRenderer->invertedColorRamp() );
}
QgsRendererRangeV2LabelFormat labelFormat=mRenderer->labelFormat();
txtPrefix->setText( labelFormat.prefix());
txtSeparator->setText( labelFormat.separator() );
txtSuffix->setText( labelFormat.suffix() );
spinDecimalPlaces->setValue( labelFormat.decimalPlaces());
cbxTrimTrailingZeroes->setChecked( labelFormat.trimTrailingZeroes());
mModel = new QgsGraduatedSymbolRendererV2Model( this );
mModel->setRenderer( mRenderer );
viewGraduated->setModel( mModel );
viewGraduated->resizeColumnToContents( 0 );
viewGraduated->resizeColumnToContents( 1 );
viewGraduated->resizeColumnToContents( 2 );
connectUpdateHandlers();
}
void QgsGraduatedSymbolRendererV2Widget::graduatedColumnChanged( QString field )
@ -489,7 +543,7 @@ void QgsGraduatedSymbolRendererV2Widget::classifyGraduated()
{
QString attrName = mExpressionWidget->currentField();
int classes = spinGraduatedClasses->value();
int nclasses = spinGraduatedClasses->value();
QgsVectorColorRampV2* ramp = cboGraduatedColorRamp->currentColorRamp();
@ -524,26 +578,19 @@ void QgsGraduatedSymbolRendererV2Widget::classifyGraduated()
}
// create and set new renderer
mRenderer->setClassAttribute(attrName);
mRenderer->setMode(mode);
mRenderer->setSourceColorRamp(ramp->clone());
bool updateUiCount=true;
QApplication::setOverrideCursor( Qt::WaitCursor );
QgsGraduatedSymbolRendererV2* r = QgsGraduatedSymbolRendererV2::createRenderer(
mLayer, attrName, classes, mode, mGraduatedSymbol, ramp, cbxInvertedColorRamp->isChecked() );
mRenderer->updateClasses(mLayer,mode,nclasses);
mRenderer->calculateLabelDecimalPlaces();
QApplication::restoreOverrideCursor();
if ( !r )
{
QMessageBox::critical( this, tr( "Error" ), tr( "Renderer creation has failed." ) );
return;
}
r->setSizeScaleField( mRenderer->sizeScaleField() );
r->setRotationField( mRenderer->rotationField() );
r->setScaleMethod( mRenderer->scaleMethod() );
if ( mModel )
{
mModel->setRenderer( r );
}
delete mRenderer;
mRenderer = r;
// PrettyBreaks and StdDev calculation don't generate exact
// number of classes - leave user interface unchanged for these
updateUiCount=false;
updateUiFromRenderer( updateUiCount );
}
void QgsGraduatedSymbolRendererV2Widget::reapplyColorRamp()
@ -644,8 +691,6 @@ void QgsGraduatedSymbolRendererV2Widget::rangesClicked( const QModelIndex & idx
mRowSelected = idx.row();
}
void QgsGraduatedSymbolRendererV2Widget::changeSelectedSymbols()
{
QItemSelectionModel* m = viewGraduated->selectionModel();
@ -693,64 +738,35 @@ void QgsGraduatedSymbolRendererV2Widget::changeRange( int rangeIdx )
QgsLUDialog dialog( this );
const QgsRendererRangeV2& range = mRenderer->ranges()[rangeIdx];
dialog.setLowerValue( QString::number( range.lowerValue(), 'f', 4 ) );
dialog.setUpperValue( QString::number( range.upperValue(), 'f', 4 ) );
// Add arbitrary 3 to number of decimal places to retain a bit extra accuracy in
// case we want to??
int decimalPlaces=mRenderer->labelFormat().decimalPlaces();
dialog.setLowerValue( QString::number( range.lowerValue(), 'f', decimalPlaces+3 ) );
dialog.setUpperValue( QString::number( range.upperValue(), 'f', decimalPlaces+3 ) );
if ( dialog.exec() == QDialog::Accepted )
{
double lowerValue = dialog.lowerValue().toDouble();
double upperValue = dialog.upperValue().toDouble();
QString label = createLabel( range.lowerValue(), range.upperValue() );
QString newLabel;
mRenderer->updateRangeUpperValue( rangeIdx, upperValue );
mRenderer->updateRangeLowerValue( rangeIdx, lowerValue );
//If the label was the label automatically generated, we generate a new one for the new range
if ( range.label() == label )
{
newLabel = createLabel( lowerValue, upperValue );
mRenderer->updateRangeLabel( rangeIdx, newLabel );
}
//If the boundaries have to stay linked, we update the ranges above and below, as well as their label if needed
if ( cbxLinkBoundaries->isChecked() )
{
if ( rangeIdx > 0 )
{
const QgsRendererRangeV2& rangeLower = mRenderer->ranges()[rangeIdx - 1];
label = createLabel( rangeLower.lowerValue(), rangeLower.upperValue() );
mRenderer->updateRangeUpperValue( rangeIdx - 1, lowerValue );
if ( label == rangeLower.label() )
{
newLabel = createLabel( rangeLower.lowerValue(), lowerValue );
mRenderer->updateRangeLabel( rangeIdx - 1, newLabel );
}
}
if ( rangeIdx < mRenderer->ranges().size() - 1 )
{
const QgsRendererRangeV2& rangeUpper = mRenderer->ranges()[rangeIdx + 1];
label = createLabel( rangeUpper.lowerValue(), rangeUpper.upperValue() );
mRenderer->updateRangeLowerValue( rangeIdx + 1, upperValue );
if ( label == rangeUpper.label() )
{
newLabel = createLabel( upperValue, rangeUpper.upperValue() );
mRenderer->updateRangeLabel( rangeIdx + 1, newLabel );
}
}
}
}
}
QString QgsGraduatedSymbolRendererV2Widget::createLabel( double lowerValue, double upperValue )
{
return QString::number( lowerValue , 'f', 4 ) + " - " + QString::number( upperValue, 'f', 4 );
}
void QgsGraduatedSymbolRendererV2Widget::addClass()
{
mModel->addClass( mGraduatedSymbol );
@ -767,12 +783,46 @@ void QgsGraduatedSymbolRendererV2Widget::deleteAllClasses()
mModel->removeAllRows();
}
bool QgsGraduatedSymbolRendererV2Widget::rowsOrdered()
{
const QgsRangeList &ranges = mRenderer->ranges();
bool ordered=true;
for ( int i = 1;i < ranges.size();++i )
{
if( ranges[i] < ranges[i-1] )
{
ordered=false;
break;
}
}
return ordered;
}
void QgsGraduatedSymbolRendererV2Widget::toggleBoundariesLink( bool linked )
{
//If the checkbox controlling the link between boundaries was unchecked and we check it, we have to link the boundaries
//This is done by updating all lower ranges to the upper value of the range above
if ( linked )
{
// Cannot link ranges if they are not sorted - results will be crazy
// qSort( mRenderer->ranges() );
// Could not get qSort to work with copy/swap idiom on QgsVectorRange
if( ! rowsOrdered() )
{
int result=QMessageBox::warning(
this,
tr("Linked range warning"),
tr("Linking ranges that are not ordered may produced unexpected results. Proceed?"),
QMessageBox::Ok | QMessageBox::Cancel );
if( result != QMessageBox::Ok )
{
cbxLinkBoundaries->setChecked( false );
return;
}
}
// Ok to proceed
for ( int i = 1;i < mRenderer->ranges().size();++i )
{
mRenderer->updateRangeLowerValue( i, mRenderer->ranges()[i-1].upperValue() );
@ -806,6 +856,19 @@ void QgsGraduatedSymbolRendererV2Widget::scaleMethodChanged( QgsSymbolV2::ScaleM
mRenderer->setScaleMethod( scaleMethod );
}
void QgsGraduatedSymbolRendererV2Widget::labelFormatChanged()
{
QgsRendererRangeV2LabelFormat labelFormat=QgsRendererRangeV2LabelFormat(
txtPrefix->text(),
txtSeparator->text(),
txtSuffix->text(),
spinDecimalPlaces->value(),
cbxTrimTrailingZeroes->isChecked());
mRenderer->setLabelFormat(labelFormat,true);
mModel->updateLabels();
}
QList<QgsSymbolV2*> QgsGraduatedSymbolRendererV2Widget::selectedSymbols()
{
QList<QgsSymbolV2*> selectedSymbols;
@ -851,6 +914,10 @@ QgsSymbolV2* QgsGraduatedSymbolRendererV2Widget::findSymbolForRange( double lowe
void QgsGraduatedSymbolRendererV2Widget::refreshSymbolView()
{
if( mModel )
{
mModel->updateSymbology();
}
}
void QgsGraduatedSymbolRendererV2Widget::showSymbolLevels()
@ -863,6 +930,14 @@ void QgsGraduatedSymbolRendererV2Widget::rowsMoved()
viewGraduated->selectionModel()->clear();
}
void QgsGraduatedSymbolRendererV2Widget::modelDataChanged()
{
if( ! rowsOrdered() )
{
cbxLinkBoundaries->setChecked(false);
}
}
void QgsGraduatedSymbolRendererV2Widget::keyPressEvent( QKeyEvent* event )
{
if ( !event )

View File

@ -49,6 +49,8 @@ class GUI_EXPORT QgsGraduatedSymbolRendererV2Model : public QAbstractItemModel
void deleteRows( QList<int> rows );
void removeAllRows( );
void sort( int column, Qt::SortOrder order = Qt::AscendingOrder );
void updateSymbology();
void updateLabels();
signals:
void rowsMoved();
@ -100,13 +102,18 @@ class GUI_EXPORT QgsGraduatedSymbolRendererV2Widget : public QgsRendererV2Widget
void rotationFieldChanged( QString fldName );
void sizeScaleFieldChanged( QString fldName );
void scaleMethodChanged( QgsSymbolV2::ScaleMethod scaleMethod );
void labelFormatChanged();
void showSymbolLevels();
void rowsMoved();
void modelDataChanged();
protected:
void updateUiFromRenderer();
void updateUiFromRenderer( bool updateCount=true );
void connectUpdateHandlers();
void disconnectUpdateHandlers();
bool rowsOrdered();
void updateGraduatedSymbolIcon();
@ -117,8 +124,6 @@ class GUI_EXPORT QgsGraduatedSymbolRendererV2Widget : public QgsRendererV2Widget
void changeRangeSymbol( int rangeIdx );
void changeRange( int rangeIdx );
QString createLabel( double lowerValue, double upperValue );
void changeSelectedSymbols();
QList<QgsSymbolV2*> selectedSymbols();

View File

@ -6,36 +6,20 @@
<rect>
<x>0</x>
<y>0</y>
<width>615</width>
<width>637</width>
<height>339</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<property name="margin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout">
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Color ramp</string>
</property>
<property name="buddy">
<cstring>cboGraduatedColorRamp</cstring>
</property>
</widget>
</item>
<item row="5" column="2">
<property name="rightMargin">
<number>6</number>
</property>
<item row="6" column="2">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Mode</string>
@ -48,7 +32,17 @@
</property>
</widget>
</item>
<item row="5" column="3">
<item row="6" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Color ramp</string>
</property>
<property name="buddy">
<cstring>cboGraduatedColorRamp</cstring>
</property>
</widget>
</item>
<item row="6" column="3">
<widget class="QComboBox" name="cboGraduatedMode">
<item>
<property name="text">
@ -77,7 +71,7 @@
</item>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Symbol</string>
@ -87,7 +81,7 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="5" column="1">
<widget class="QPushButton" name="btnChangeGraduatedSymbol">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
@ -100,7 +94,7 @@
</property>
</widget>
</item>
<item row="4" column="3">
<item row="5" column="3">
<widget class="QSpinBox" name="spinGraduatedClasses">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
@ -119,7 +113,7 @@
</property>
</widget>
</item>
<item row="4" column="2">
<item row="5" column="2">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Classes</string>
@ -132,14 +126,7 @@
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Column</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QgsColorRampComboBox" name="cboGraduatedColorRamp"/>
@ -153,13 +140,26 @@
</item>
</layout>
</item>
<item row="0" column="1" colspan="3">
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Column</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QgsFieldExpressionWidget" name="mExpressionWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>500</width>
@ -183,6 +183,104 @@
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Label </string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>txtPrefix</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="txtPrefix">
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>#.##</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtSeparator">
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_17">
<property name="text">
<string>#.##</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtSuffix">
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="2">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Decimal places</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>spinDecimalPlaces</cstring>
</property>
</widget>
</item>
<item row="3" column="3">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QSpinBox" name="spinDecimalPlaces">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbxTrimTrailingZeroes">
<property name="text">
<string>Trim</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
@ -243,7 +341,7 @@
<item>
<widget class="QCheckBox" name="cbxLinkBoundaries">
<property name="text">
<string>Link classes boundaries</string>
<string>Link class boundaries</string>
</property>
<property name="checked">
<bool>true</bool>
@ -288,14 +386,32 @@
</customwidget>
</customwidgets>
<tabstops>
<tabstop>txtPrefix</tabstop>
<tabstop>txtSeparator</tabstop>
<tabstop>txtSuffix</tabstop>
<tabstop>spinDecimalPlaces</tabstop>
<tabstop>cbxTrimTrailingZeroes</tabstop>
<tabstop>btnChangeGraduatedSymbol</tabstop>
<tabstop>spinGraduatedClasses</tabstop>
<tabstop>cboGraduatedColorRamp</tabstop>
<tabstop>cbxInvertedColorRamp</tabstop>
<tabstop>spinGraduatedClasses</tabstop>
<tabstop>cboGraduatedMode</tabstop>
<tabstop>viewGraduated</tabstop>
<tabstop>btnGraduatedClassify</tabstop>
<tabstop>btnGraduatedAdd</tabstop>
<tabstop>btnGraduatedDelete</tabstop>
<tabstop>btnDeleteAllClasses</tabstop>
<tabstop>cbxLinkBoundaries</tabstop>
<tabstop>btnAdvanced</tabstop>
<tabstop>cboGraduatedMode_2</tabstop>
<tabstop>btnChangeGraduatedSymbol_2</tabstop>
<tabstop>spinGraduatedClasses_2</tabstop>
<tabstop>cboGraduatedColorRamp_2</tabstop>
<tabstop>cbxInvertedColorRamp_2</tabstop>
<tabstop>spinDecimalPlaces_2</tabstop>
<tabstop>txtPrefix_4</tabstop>
<tabstop>txtPrefix_5</tabstop>
<tabstop>txtPrefix_6</tabstop>
</tabstops>
<resources/>
<connections/>