Skip to content
Permalink
Browse files

Improved method for selecting random colors for categorised renderer,

which should result in more visually distinct color choices
  • Loading branch information
nyalldawson committed Sep 14, 2014
1 parent 3f8a860 commit b83722346eb7223e28ea1ca100055c6c9c3488a5
@@ -14,6 +14,7 @@ class QgsVectorColorRampV2
%End

public:

virtual ~QgsVectorColorRampV2();

// Number of defined colors
@@ -139,6 +140,14 @@ class QgsRandomColorsV2 : QgsVectorColorRampV2
double value( int index ) const;

QColor color( double value ) const;

/*Sets the desired total number of unique colors for the resultant ramp. Calling
* this method pregenerates a set of visually distinct colors which are returned
* by subsequent calls to color().
* @param colorCount number of unique colors
* @note added in QGIS 2.5
*/
virtual void setTotalColorCount( const int colorCount );

QString type() const;

@@ -22,6 +22,8 @@
#include "qgslogger.h"

#include <stdlib.h> // for random()
#include <algorithm>

#include <QTime>

//////////////
@@ -366,12 +368,53 @@ QColor QgsRandomColorsV2::color( double value ) const
Q_UNUSED( value );
int minVal = 130;
int maxVal = 255;

int colorIndex = value * ( mTotalColorCount - 1 );
if ( mTotalColorCount >= 1 && mPrecalculatedColors.length() > colorIndex )
{
//use precalculated hue
return mPrecalculatedColors.at( colorIndex );
}

//can't use precalculated hues, use a totally random hue
int h = 1 + ( int )( 360.0 * rand() / ( RAND_MAX + 1.0 ) );
int s = ( rand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
int v = ( rand() % ( maxVal - minVal + 1 ) ) + minVal;
return QColor::fromHsv( h, s, v );
}

void QgsRandomColorsV2::setTotalColorCount( const int colorCount )
{
//calculate colors in advance, so that we can ensure they are more visually distinct than pure random colors
mPrecalculatedColors.clear();
mTotalColorCount = colorCount;

//This works ok for low color counts, but for > 10 or so colors there's still a good chance of
//similar colors being picked. TODO - investigate alternative "n-visually distinct color" routines

//random offsets
double hueOffset = ( 360.0 * rand() / ( RAND_MAX + 1.0 ) );

//try to maximise difference between hues. this is not an ideal implementation, as constant steps
//through the hue wheel are not visually perceived as constant changes in hue
//(for instance, we are much more likely to get green hues than yellow hues)
double hueStep = 359.0 / colorCount;
double currentHue = hueOffset;

//build up a list of colors
for ( int idx = 0; idx < colorCount; ++ idx )
{
int h = qRound( currentHue ) % 360;
int s = ( rand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
int v = ( rand() % ( DEFAULT_RANDOM_VAL_MAX - DEFAULT_RANDOM_VAL_MIN + 1 ) ) + DEFAULT_RANDOM_VAL_MIN;
mPrecalculatedColors << QColor::fromHsv( h, s, v );
currentHue += hueStep;
}

//lastly, shuffle color list
std::random_shuffle( mPrecalculatedColors.begin(), mPrecalculatedColors.end() );
}

QString QgsRandomColorsV2::type() const
{
return "randomcolors";
@@ -653,4 +696,3 @@ bool QgsCptCityColorRampV2::loadFile()
mFileLoaded = true;
return true;
}

@@ -25,6 +25,7 @@
class CORE_EXPORT QgsVectorColorRampV2
{
public:

virtual ~QgsVectorColorRampV2() {}

// Number of defined colors
@@ -173,11 +174,25 @@ class CORE_EXPORT QgsRandomColorsV2: public QgsVectorColorRampV2

QColor color( double value ) const;

/*Sets the desired total number of unique colors for the resultant ramp. Calling
* this method pregenerates a set of visually distinct colors which are returned
* by subsequent calls to color().
* @param colorCount number of unique colors
* @note added in QGIS 2.5
*/
virtual void setTotalColorCount( const int colorCount );

QString type() const;

QgsVectorColorRampV2* clone() const;

QgsStringMap properties() const;

protected:

int mTotalColorCount;
QList<QColor> mPrecalculatedColors;

};


@@ -594,6 +594,17 @@ static void _createCategories( QgsCategoryList& cats, QList<QVariant>& values, Q

bool hasNull = false;

QgsRandomColorsV2* randomRamp = dynamic_cast<QgsRandomColorsV2*>( ramp );
if ( randomRamp )
{
//ramp is a random colors ramp, so inform it of the total number of required colors
//this allows the ramp to pregenerate a set of visually distinctive colors

//assume we need an extra color for nulls
int totalColors = num + 1;
randomRamp->setTotalColorCount( totalColors );
}

for ( int i = 0; i < num; i++ )
{
QVariant value = values[i];

0 comments on commit b837223

Please sign in to comment.
You can’t perform that action at this time.