QGIS API Documentation  3.2.0-Bonn (bc43194)
qgscolorramp.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscolorramp.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgscolorramp.h"
17 #include "qgscolorbrewerpalette.h"
18 #include "qgscptcityarchive.h"
19 
20 #include "qgssymbollayerutils.h"
21 #include "qgsapplication.h"
22 #include "qgslogger.h"
23 
24 #include <algorithm>
25 
26 #include <QTime>
27 
29 
30 static QColor _interpolate( const QColor &c1, const QColor &c2, const double value )
31 {
32  if ( std::isnan( value ) ) return c2;
33 
34  qreal r = ( c1.redF() + value * ( c2.redF() - c1.redF() ) );
35  qreal g = ( c1.greenF() + value * ( c2.greenF() - c1.greenF() ) );
36  qreal b = ( c1.blueF() + value * ( c2.blueF() - c1.blueF() ) );
37  qreal a = ( c1.alphaF() + value * ( c2.alphaF() - c1.alphaF() ) );
38 
39  return QColor::fromRgbF( r, g, b, a );
40 }
41 
43 
44 QgsGradientColorRamp::QgsGradientColorRamp( const QColor &color1, const QColor &color2,
45  bool discrete, const QgsGradientStopsList &stops )
46  : mColor1( color1 )
47  , mColor2( color2 )
48  , mDiscrete( discrete )
49  , mStops( stops )
50 {
51 }
52 
54 {
55  // color1 and color2
58  if ( props.contains( QStringLiteral( "color1" ) ) )
59  color1 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color1" )] );
60  if ( props.contains( QStringLiteral( "color2" ) ) )
61  color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color2" )] );
62 
63  //stops
65  if ( props.contains( QStringLiteral( "stops" ) ) )
66  {
67  Q_FOREACH ( const QString &stop, props["stops"].split( ':' ) )
68  {
69  int i = stop.indexOf( ';' );
70  if ( i == -1 )
71  continue;
72 
73  QColor c = QgsSymbolLayerUtils::decodeColor( stop.mid( i + 1 ) );
74  stops.append( QgsGradientStop( stop.leftRef( i ).toDouble(), c ) );
75  }
76  }
77 
78  // discrete vs. continuous
79  bool discrete = false;
80  if ( props.contains( QStringLiteral( "discrete" ) ) )
81  {
82  if ( props[QStringLiteral( "discrete" )] == QLatin1String( "1" ) )
83  discrete = true;
84  }
85 
86  // search for information keys starting with "info_"
88  for ( QgsStringMap::const_iterator it = props.constBegin();
89  it != props.constEnd(); ++it )
90  {
91  if ( it.key().startsWith( QLatin1String( "info_" ) ) )
92  info[ it.key().mid( 5 )] = it.value();
93  }
94 
95  QgsGradientColorRamp *r = new QgsGradientColorRamp( color1, color2, discrete, stops );
96  r->setInfo( info );
97  return r;
98 }
99 
100 double QgsGradientColorRamp::value( int index ) const
101 {
102  if ( index <= 0 )
103  {
104  return 0;
105  }
106  else if ( index >= mStops.size() + 1 )
107  {
108  return 1;
109  }
110  else
111  {
112  return mStops[index - 1].offset;
113  }
114 }
115 
116 QColor QgsGradientColorRamp::color( double value ) const
117 {
118  if ( qgsDoubleNear( value, 0.0 ) || value < 0.0 )
119  {
120  return mColor1;
121  }
122  else if ( qgsDoubleNear( value, 1.0 ) || value > 1.0 )
123  {
124  return mColor2;
125  }
126  else if ( mStops.isEmpty() )
127  {
128  if ( mDiscrete )
129  return mColor1;
130 
131  return _interpolate( mColor1, mColor2, value );
132  }
133  else
134  {
135  double lower = 0, upper = 0;
136  QColor c1 = mColor1, c2;
137  for ( QgsGradientStopsList::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
138  {
139  if ( it->offset > value )
140  {
141  if ( mDiscrete )
142  return c1;
143 
144  upper = it->offset;
145  c2 = it->color;
146 
147  return qgsDoubleNear( upper, lower ) ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) );
148  }
149  lower = it->offset;
150  c1 = it->color;
151  }
152 
153  if ( mDiscrete )
154  return c1;
155 
156  upper = 1;
157  c2 = mColor2;
158  return qgsDoubleNear( upper, lower ) ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) );
159  }
160 }
161 
163 {
164  QgsGradientStopsList newStops;
165 
166  if ( mDiscrete )
167  {
168  mColor2 = mColor1;
169  mColor1 = mStops.at( mStops.size() - 1 ).color;
170  for ( int k = mStops.size() - 1; k >= 1; k-- )
171  {
172  newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k - 1 ).color );
173  }
174  newStops << QgsGradientStop( 1 - mStops.at( 0 ).offset, mColor2 );
175  }
176  else
177  {
178  QColor tmpColor = mColor2;
179  mColor2 = mColor1;
180  mColor1 = tmpColor;
181  for ( int k = mStops.size() - 1; k >= 0; k-- )
182  {
183  newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k ).color );
184  }
185  }
186  mStops = newStops;
187 }
188 
190 {
192  mDiscrete, mStops );
193  r->setInfo( mInfo );
194  return r;
195 }
196 
198 {
199  QgsStringMap map;
200  map[QStringLiteral( "color1" )] = QgsSymbolLayerUtils::encodeColor( mColor1 );
201  map[QStringLiteral( "color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
202  if ( !mStops.isEmpty() )
203  {
204  QStringList lst;
205  for ( QgsGradientStopsList::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
206  {
207  lst.append( QStringLiteral( "%1;%2" ).arg( it->offset ).arg( QgsSymbolLayerUtils::encodeColor( it->color ) ) );
208  }
209  map[QStringLiteral( "stops" )] = lst.join( QStringLiteral( ":" ) );
210  }
211 
212  map[QStringLiteral( "discrete" )] = mDiscrete ? "1" : "0";
213 
214  for ( QgsStringMap::const_iterator it = mInfo.constBegin();
215  it != mInfo.constEnd(); ++it )
216  {
217  map["info_" + it.key()] = it.value();
218  }
219 
220  map[QStringLiteral( "rampType" )] = type();
221  return map;
222 }
224 {
225  if ( discrete == mDiscrete )
226  return;
227 
228  // if going to/from Discrete, re-arrange stops
229  // this will only work when stops are equally-spaced
230  QgsGradientStopsList newStops;
231  if ( discrete )
232  {
233  // re-arrange stops offset
234  int numStops = mStops.count() + 2;
235  int i = 1;
236  for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
237  it != mStops.constEnd(); ++it )
238  {
239  newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, it->color ) );
240  if ( i == numStops - 1 )
241  break;
242  i++;
243  }
244  // replicate last color
245  newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, mColor2 ) );
246  }
247  else
248  {
249  // re-arrange stops offset, remove duplicate last color
250  int numStops = mStops.count() + 2;
251  int i = 1;
252  for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
253  it != mStops.constEnd(); ++it )
254  {
255  newStops.append( QgsGradientStop( static_cast< double >( i ) / ( numStops - 2 ), it->color ) );
256  if ( i == numStops - 3 )
257  break;
258  i++;
259  }
260  }
261  mStops = newStops;
262  mDiscrete = discrete;
263 }
264 
265 bool stopLessThan( const QgsGradientStop &s1, const QgsGradientStop &s2 )
266 {
267  return s1.offset < s2.offset;
268 }
269 
271 {
272  mStops = stops;
273 
274  //sort stops by offset
275  std::sort( mStops.begin(), mStops.end(), stopLessThan );
276 }
277 
278 void QgsGradientColorRamp::addStopsToGradient( QGradient *gradient, double opacity )
279 {
280  //copy color ramp stops to a QGradient
281  QColor color1 = mColor1;
282  QColor color2 = mColor2;
283  if ( opacity < 1 )
284  {
285  color1.setAlpha( color1.alpha() * opacity );
286  color2.setAlpha( color2.alpha() * opacity );
287  }
288  gradient->setColorAt( 0, color1 );
289  gradient->setColorAt( 1, color2 );
290 
291  for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
292  it != mStops.constEnd(); ++it )
293  {
294  QColor rampColor = it->color;
295  if ( opacity < 1 )
296  {
297  rampColor.setAlpha( rampColor.alpha() * opacity );
298  }
299  gradient->setColorAt( it->offset, rampColor );
300  }
301 }
302 
303 
305 
306 
308  int satMin, int satMax, int valMin, int valMax )
309  : mCount( count )
310  , mHueMin( hueMin ), mHueMax( hueMax )
311  , mSatMin( satMin ), mSatMax( satMax )
312  , mValMin( valMin ), mValMax( valMax )
313 {
314  updateColors();
315 }
316 
318 {
323 
324  if ( props.contains( QStringLiteral( "count" ) ) ) count = props[QStringLiteral( "count" )].toInt();
325  if ( props.contains( QStringLiteral( "hueMin" ) ) ) hueMin = props[QStringLiteral( "hueMin" )].toInt();
326  if ( props.contains( QStringLiteral( "hueMax" ) ) ) hueMax = props[QStringLiteral( "hueMax" )].toInt();
327  if ( props.contains( QStringLiteral( "satMin" ) ) ) satMin = props[QStringLiteral( "satMin" )].toInt();
328  if ( props.contains( QStringLiteral( "satMax" ) ) ) satMax = props[QStringLiteral( "satMax" )].toInt();
329  if ( props.contains( QStringLiteral( "valMin" ) ) ) valMin = props[QStringLiteral( "valMin" )].toInt();
330  if ( props.contains( QStringLiteral( "valMax" ) ) ) valMax = props[QStringLiteral( "valMax" )].toInt();
331 
332  return new QgsLimitedRandomColorRamp( count, hueMin, hueMax, satMin, satMax, valMin, valMax );
333 }
334 
335 double QgsLimitedRandomColorRamp::value( int index ) const
336 {
337  if ( mColors.empty() )
338  return 0;
339  return static_cast< double >( index ) / ( mColors.size() - 1 );
340 }
341 
342 QColor QgsLimitedRandomColorRamp::color( double value ) const
343 {
344  if ( value < 0 || value > 1 )
345  return QColor();
346 
347  int colorCnt = mColors.count();
348  int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
349 
350  if ( colorIdx >= 0 && colorIdx < colorCnt )
351  return mColors.at( colorIdx );
352 
353  return QColor();
354 }
355 
357 {
359 }
360 
362 {
363  QgsStringMap map;
364  map[QStringLiteral( "count" )] = QString::number( mCount );
365  map[QStringLiteral( "hueMin" )] = QString::number( mHueMin );
366  map[QStringLiteral( "hueMax" )] = QString::number( mHueMax );
367  map[QStringLiteral( "satMin" )] = QString::number( mSatMin );
368  map[QStringLiteral( "satMax" )] = QString::number( mSatMax );
369  map[QStringLiteral( "valMin" )] = QString::number( mValMin );
370  map[QStringLiteral( "valMax" )] = QString::number( mValMax );
371  map[QStringLiteral( "rampType" )] = type();
372  return map;
373 }
374 
376  int hueMax, int hueMin, int satMax, int satMin, int valMax, int valMin )
377 {
378  int h, s, v;
379  QList<QColor> colors;
380 
381  //normalize values
382  int safeHueMax = std::max( hueMin, hueMax );
383  int safeHueMin = std::min( hueMin, hueMax );
384  int safeSatMax = std::max( satMin, satMax );
385  int safeSatMin = std::min( satMin, satMax );
386  int safeValMax = std::max( valMin, valMax );
387  int safeValMin = std::min( valMin, valMax );
388 
389  //start hue at random angle
390  double currentHueAngle = 360.0 * static_cast< double >( qrand() ) / RAND_MAX;
391 
392  colors.reserve( count );
393  for ( int i = 0; i < count; ++i )
394  {
395  //increment hue by golden ratio (approx 137.507 degrees)
396  //as this minimizes hue nearness as count increases
397  //see http://basecase.org/env/on-rainbows for more details
398  currentHueAngle += 137.50776;
399  //scale hue to between hueMax and hueMin
400  h = qBound( 0.0, std::round( ( std::fmod( currentHueAngle, 360.0 ) / 360.0 ) * ( safeHueMax - safeHueMin ) + safeHueMin ), 359.0 );
401  s = qBound( 0, ( qrand() % ( safeSatMax - safeSatMin + 1 ) ) + safeSatMin, 255 );
402  v = qBound( 0, ( qrand() % ( safeValMax - safeValMin + 1 ) ) + safeValMin, 255 );
403  colors.append( QColor::fromHsv( h, s, v ) );
404  }
405  return colors;
406 }
407 
409 {
411 }
412 
414 
416 {
417  return -1;
418 }
419 
420 double QgsRandomColorRamp::value( int index ) const
421 {
422  Q_UNUSED( index );
423  return 0.0;
424 }
425 
426 QColor QgsRandomColorRamp::color( double value ) const
427 {
428  int minVal = 130;
429  int maxVal = 255;
430 
431  //if value is nan, then use last precalculated color
432  int colorIndex = ( !std::isnan( value ) ? value : 1 ) * ( mTotalColorCount - 1 );
433  if ( mTotalColorCount >= 1 && mPrecalculatedColors.length() > colorIndex )
434  {
435  //use precalculated hue
436  return mPrecalculatedColors.at( colorIndex );
437  }
438 
439  //can't use precalculated hues, use a totally random hue
440  int h = static_cast< int >( 360.0 * qrand() / ( RAND_MAX + 1.0 ) );
441  int s = ( qrand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
442  int v = ( qrand() % ( maxVal - minVal + 1 ) ) + minVal;
443  return QColor::fromHsv( h, s, v );
444 }
445 
446 void QgsRandomColorRamp::setTotalColorCount( const int colorCount )
447 {
448  //calculate colors in advance, so that we can ensure they are more visually distinct than pure random colors
449  mPrecalculatedColors.clear();
450  mTotalColorCount = colorCount;
451 
452  //This works OK for low color counts, but for > 10 or so colors there's still a good chance of
453  //similar colors being picked. TODO - investigate alternative "n-visually distinct color" routines
454 
455  //random offsets
456  double hueOffset = ( 360.0 * qrand() / ( RAND_MAX + 1.0 ) );
457 
458  //try to maximise difference between hues. this is not an ideal implementation, as constant steps
459  //through the hue wheel are not visually perceived as constant changes in hue
460  //(for instance, we are much more likely to get green hues than yellow hues)
461  double hueStep = 359.0 / colorCount;
462  double currentHue = hueOffset;
463 
464  //build up a list of colors
465  for ( int idx = 0; idx < colorCount; ++ idx )
466  {
467  int h = static_cast< int >( std::round( currentHue ) ) % 360;
468  int s = ( qrand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
469  int v = ( qrand() % ( DEFAULT_RANDOM_VAL_MAX - DEFAULT_RANDOM_VAL_MIN + 1 ) ) + DEFAULT_RANDOM_VAL_MIN;
470  mPrecalculatedColors << QColor::fromHsv( h, s, v );
471  currentHue += hueStep;
472  }
473 
474  //lastly, shuffle color list
475  std::random_shuffle( mPrecalculatedColors.begin(), mPrecalculatedColors.end() );
476 }
477 
479 {
480  return QStringLiteral( "randomcolors" );
481 }
482 
484 {
485  return new QgsRandomColorRamp();
486 }
487 
489 {
490  return QgsStringMap();
491 }
492 
494 
495 QgsColorBrewerColorRamp::QgsColorBrewerColorRamp( const QString &schemeName, int colors, bool inverted )
496  : mSchemeName( schemeName )
497  , mColors( colors )
498  , mInverted( inverted )
499 {
500  loadPalette();
501 }
502 
504 {
507  bool inverted = false;
508 
509  if ( props.contains( QStringLiteral( "schemeName" ) ) )
510  schemeName = props[QStringLiteral( "schemeName" )];
511  if ( props.contains( QStringLiteral( "colors" ) ) )
512  colors = props[QStringLiteral( "colors" )].toInt();
513  if ( props.contains( QStringLiteral( "inverted" ) ) )
514  inverted = props[QStringLiteral( "inverted" )].toInt();
515 
516  return new QgsColorBrewerColorRamp( schemeName, colors, inverted );
517 }
518 
520 {
522 
523  if ( mInverted )
524  {
525  QList<QColor> tmpPalette;
526 
527  for ( int k = mPalette.size() - 1; k >= 0; k-- )
528  {
529  tmpPalette << mPalette.at( k );
530  }
531  mPalette = tmpPalette;
532  }
533 }
534 
536 {
538 }
539 
541 {
542  return QgsColorBrewerPalette::listSchemeVariants( schemeName );
543 }
544 
545 double QgsColorBrewerColorRamp::value( int index ) const
546 {
547  if ( mPalette.empty() )
548  return 0;
549  return static_cast< double >( index ) / ( mPalette.size() - 1 );
550 }
551 
552 QColor QgsColorBrewerColorRamp::color( double value ) const
553 {
554  if ( mPalette.isEmpty() || value < 0 || value > 1 )
555  return QColor();
556 
557  int paletteEntry = static_cast< int >( value * mPalette.count() );
558  if ( paletteEntry >= mPalette.count() )
559  paletteEntry = mPalette.count() - 1;
560  return mPalette.at( paletteEntry );
561 }
562 
564 {
565  mInverted = !mInverted;
566  loadPalette();
567 }
568 
570 {
572 }
573 
575 {
576  QgsStringMap map;
577  map[QStringLiteral( "schemeName" )] = mSchemeName;
578  map[QStringLiteral( "colors" )] = QString::number( mColors );
579  map[QStringLiteral( "inverted" )] = QString::number( mInverted );
580  map[QStringLiteral( "rampType" )] = type();
581  return map;
582 }
583 
584 
586 
587 
588 QgsCptCityColorRamp::QgsCptCityColorRamp( const QString &schemeName, const QString &variantName,
589  bool inverted, bool doLoadFile )
591  , mSchemeName( schemeName )
592  , mVariantName( variantName )
593  , mInverted( inverted )
594 {
595  // TODO replace this with hard-coded data in the default case
596  // don't load file if variant is missing
597  if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
598  loadFile();
599 }
600 
602  const QString &variantName, bool inverted, bool doLoadFile )
604  , mSchemeName( schemeName )
605  , mVariantName( variantName )
606  , mVariantList( variantList )
607  , mInverted( inverted )
608 {
610 
611  // TODO replace this with hard-coded data in the default case
612  // don't load file if variant is missing
613  if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
614  loadFile();
615 }
616 
618 {
621  bool inverted = false;
622 
623  if ( props.contains( QStringLiteral( "schemeName" ) ) )
624  schemeName = props[QStringLiteral( "schemeName" )];
625  if ( props.contains( QStringLiteral( "variantName" ) ) )
626  variantName = props[QStringLiteral( "variantName" )];
627  if ( props.contains( QStringLiteral( "inverted" ) ) )
628  inverted = props[QStringLiteral( "inverted" )].toInt();
629 
630  return new QgsCptCityColorRamp( schemeName, variantName, inverted );
631 }
632 
634 {
635  mInverted = !mInverted;
637 }
638 
640 {
641  QgsCptCityColorRamp *ramp = new QgsCptCityColorRamp( QLatin1String( "" ), QLatin1String( "" ), mInverted, false );
642  ramp->copy( this );
643  return ramp;
644 }
645 
647 {
648  if ( ! other )
649  return;
650  mColor1 = other->color1();
651  mColor2 = other->color2();
652  mDiscrete = other->isDiscrete();
653  mStops = other->stops();
654  mSchemeName = other->mSchemeName;
655  mVariantName = other->mVariantName;
656  mVariantList = other->mVariantList;
657  mFileLoaded = other->mFileLoaded;
658  mInverted = other->mInverted;
659 }
660 
662 {
663  QgsGradientColorRamp *ramp =
665  // add author and copyright information
666  // TODO also add COPYING.xml file/link?
668  info[QStringLiteral( "cpt-city-gradient" )] = "<cpt-city>/" + mSchemeName + mVariantName + ".svg";
669  QString copyingFilename = copyingFileName();
670  copyingFilename.remove( QgsCptCityArchive::defaultBaseDir() );
671  info[QStringLiteral( "cpt-city-license" )] = "<cpt-city>" + copyingFilename;
672  ramp->setInfo( info );
673  return ramp;
674 }
675 
676 
678 {
679  QgsStringMap map;
680  map[QStringLiteral( "schemeName" )] = mSchemeName;
681  map[QStringLiteral( "variantName" )] = mVariantName;
682  map[QStringLiteral( "inverted" )] = QString::number( mInverted );
683  map[QStringLiteral( "rampType" )] = type();
684  return map;
685 }
686 
687 
689 {
690  if ( mSchemeName.isEmpty() )
691  return QString();
692  else
693  {
694  return QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mSchemeName + mVariantName + ".svg";
695  }
696 }
697 
699 {
700  return QgsCptCityArchive::findFileName( QStringLiteral( "COPYING.xml" ), QFileInfo( fileName() ).dir().path(),
702 }
703 
705 {
706  return QgsCptCityArchive::findFileName( QStringLiteral( "DESC.xml" ), QFileInfo( fileName() ).dir().path(),
708 }
709 
711 {
713 }
714 
716 {
717  if ( mFileLoaded )
718  {
719  QgsDebugMsg( "File already loaded for " + mSchemeName + mVariantName );
720  return true;
721  }
722 
723  // get filename
724  QString filename = fileName();
725  if ( filename.isNull() )
726  {
727  QgsDebugMsg( "Couldn't get fileName() for " + mSchemeName + mVariantName );
728  return false;
729  }
730 
731  QgsDebugMsg( QString( "filename= %1 loaded=%2" ).arg( filename ).arg( mFileLoaded ) );
732 
733  // get color ramp from svg file
734  QMap< double, QPair<QColor, QColor> > colorMap =
736 
737  // add colors to palette
738  mFileLoaded = false;
739  mStops.clear();
740  QMap<double, QPair<QColor, QColor> >::const_iterator it, prev;
741  // first detect if file is gradient is continuous or dicrete
742  // discrete: stop contains 2 colors and first color is identical to previous second
743  // multi: stop contains 2 colors and no relation with previous stop
744  mDiscrete = false;
745  mMultiStops = false;
746  it = prev = colorMap.constBegin();
747  while ( it != colorMap.constEnd() )
748  {
749  // look for stops that contain multiple values
750  if ( it != colorMap.constBegin() && ( it.value().first != it.value().second ) )
751  {
752  if ( it.value().first == prev.value().second )
753  {
754  mDiscrete = true;
755  break;
756  }
757  else
758  {
759  mMultiStops = true;
760  break;
761  }
762  }
763  prev = it;
764  ++it;
765  }
766 
767  // fill all stops
768  it = prev = colorMap.constBegin();
769  while ( it != colorMap.constEnd() )
770  {
771  if ( mDiscrete )
772  {
773  // mPalette << qMakePair( it.key(), it.value().second );
774  mStops.append( QgsGradientStop( it.key(), it.value().second ) );
775  }
776  else
777  {
778  // mPalette << qMakePair( it.key(), it.value().first );
779  mStops.append( QgsGradientStop( it.key(), it.value().first ) );
780  if ( ( mMultiStops ) &&
781  ( it.key() != 0.0 && it.key() != 1.0 ) )
782  {
783  mStops.append( QgsGradientStop( it.key(), it.value().second ) );
784  }
785  }
786  prev = it;
787  ++it;
788  }
789 
790  // remove first and last items (mColor1 and mColor2)
791  if ( ! mStops.isEmpty() && mStops.at( 0 ).offset == 0.0 )
792  mColor1 = mStops.takeFirst().color;
793  if ( ! mStops.isEmpty() && mStops.last().offset == 1.0 )
794  mColor2 = mStops.takeLast().color;
795 
796  if ( mInverted )
797  {
799  }
800 
801  mFileLoaded = true;
802  return true;
803 }
804 
805 
806 //
807 // QgsPresetColorRamp
808 //
809 
811 {
812  Q_FOREACH ( const QColor &color, colors )
813  {
814  mColors << qMakePair( color, color.name() );
815  }
816  // need at least one color
817  if ( mColors.isEmpty() )
818  mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
819 }
820 
822  : mColors( colors )
823 {
824  // need at least one color
825  if ( mColors.isEmpty() )
826  mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
827 }
828 
830 {
832 
833  int i = 0;
834  QString colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() );
835  QString colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() );
836  while ( !colorString.isEmpty() )
837  {
838  colors << qMakePair( QgsSymbolLayerUtils::decodeColor( colorString ), colorName );
839  i++;
840  colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() );
841  colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() );
842  }
843 
844  return new QgsPresetSchemeColorRamp( colors );
845 }
846 
847 QList<QColor> QgsPresetSchemeColorRamp::colors() const
848 {
849  QList< QColor > l;
850  l.reserve( mColors.count() );
851  for ( int i = 0; i < mColors.count(); ++i )
852  {
853  l << mColors.at( i ).first;
854  }
855  return l;
856 }
857 
858 double QgsPresetSchemeColorRamp::value( int index ) const
859 {
860  if ( mColors.empty() )
861  return 0;
862  return static_cast< double >( index ) / ( mColors.size() - 1 );
863 }
864 
865 QColor QgsPresetSchemeColorRamp::color( double value ) const
866 {
867  if ( value < 0 || value > 1 )
868  return QColor();
869 
870  int colorCnt = mColors.count();
871  int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
872 
873  if ( colorIdx >= 0 && colorIdx < colorCnt )
874  return mColors.at( colorIdx ).first;
875 
876  return QColor();
877 }
878 
880 {
881  QgsNamedColorList tmpColors;
882 
883  for ( int k = mColors.size() - 1; k >= 0; k-- )
884  {
885  tmpColors << mColors.at( k );
886  }
887  mColors = tmpColors;
888 }
889 
891 {
892  return new QgsPresetSchemeColorRamp( *this );
893 }
894 
896 {
897  QgsStringMap props;
898  for ( int i = 0; i < mColors.count(); ++i )
899  {
900  props.insert( QStringLiteral( "preset_color_%1" ).arg( i ), QgsSymbolLayerUtils::encodeColor( mColors.at( i ).first ) );
901  props.insert( QStringLiteral( "preset_color_name_%1" ).arg( i ), mColors.at( i ).second );
902  }
903  props[QStringLiteral( "rampType" )] = type();
904  return props;
905 }
906 
908 {
909  return mColors.count();
910 }
911 
913 {
914  return mColors;
915 }
QString fileName() const
int satMax() const
Returns the maximum saturation for generated colors.
Definition: qgscolorramp.h:354
static QList< int > listSchemeVariants(const QString &schemeName)
Returns a list of the valid variants (numbers of colors) for a specified color brewer scheme name...
#define DEFAULT_RANDOM_SAT_MAX
Definition: qgscolorramp.h:275
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
Returns a new QgsColorBrewerColorRamp color ramp created using the properties encoded in a string map...
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
Returns a new QgsPresetSchemeColorRamp color ramp created using the properties encoded in a string ma...
#define DEFAULT_RANDOM_VAL_MAX
Definition: qgscolorramp.h:273
#define DEFAULT_RANDOM_HUE_MAX
Definition: qgscolorramp.h:271
Represents a color stop within a QgsGradientColorRamp color ramp.
Definition: qgscolorramp.h:101
QStringList mVariantList
Definition: qgscolorramp.h:691
void invert() override
Inverts the ordering of the color ramp.
QgsColorBrewerColorRamp * clone() const override
Creates a clone of the color ramp.
int count() const override
Returns number of defined colors, or -1 if undefined.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QColor color2() const
Returns the gradient end color.
Definition: qgscolorramp.h:179
static QString defaultBaseDir()
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:251
double value(int index) const override
Returns relative value between [0,1] of color at specified index.
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
QString schemeName() const
Definition: qgscolorramp.h:666
QgsStringMap properties() const override
Returns a string map containing all the color ramp&#39;s properties.
#define DEFAULT_RANDOM_HUE_MIN
Definition: qgscolorramp.h:270
QColor color(double value) const override
Returns the color corresponding to a specified value.
void invert() override
Inverts the ordering of the color ramp.
void convertToDiscrete(bool discrete)
Converts a gradient with existing color stops to or from discrete interpolation.
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
static QList< int > listSchemeVariants(const QString &schemeName)
QString descFileName() const
#define DEFAULT_COLORBREWER_SCHEMENAME
Definition: qgscolorramp.h:526
QString copyingFileName() const
double value(int index) const override
Returns relative value between [0,1] of color at specified index.
QList< QColor > colors() const
Returns the list of colors used by the ramp.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:501
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Definition: qgscolorramp.h:127
QgsStringMap properties() const override
Returns a string map containing all the color ramp&#39;s properties.
QList< QColor > mColors
Definition: qgscolorramp.h:417
static QString encodeColor(const QColor &color)
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
void copy(const QgsCptCityColorRamp *other)
QgsGradientColorRamp * clone() const override
Creates a clone of the color ramp.
int satMin() const
Returns the minimum saturation for generated colors.
Definition: qgscolorramp.h:348
int hueMin() const
Returns the minimum hue for generated colors.
Definition: qgscolorramp.h:336
double value(int index) const override
Returns relative value between [0,1] of color at specified index.
QgsCptCityColorRamp(const QString &schemeName=DEFAULT_CPTCITY_SCHEMENAME, const QString &variantName=DEFAULT_CPTCITY_VARIANTNAME, bool inverted=false, bool doLoadFile=true)
Constructor for QgsCptCityColorRamp.
QString type() const override
Returns a string representing the color ramp type.
Definition: qgscolorramp.h:162
int colors() const
Returns the number of colors in the ramp.
Definition: qgscolorramp.h:575
Constrained random color ramp, which returns random colors based on preset parameters.
Definition: qgscolorramp.h:283
void invert() override
Inverts the ordering of the color ramp.
#define DEFAULT_RANDOM_VAL_MIN
Definition: qgscolorramp.h:272
QgsStringMap info() const
Returns any additional info attached to the gradient ramp (e.g., authorship notes) ...
Definition: qgscolorramp.h:241
double offset
Relative positional offset, between 0 and 1.
Definition: qgscolorramp.h:116
QgsPresetSchemeColorRamp * clone() const override
Creates a clone of the color ramp.
QString type() const override
Returns a string representing the color ramp type.
Definition: qgscolorramp.h:510
bool stopLessThan(const QgsGradientStop &s1, const QgsGradientStop &s2)
int hueMax() const
Returns the maximum hue for generated colors.
Definition: qgscolorramp.h:342
QgsStringMap properties() const override
Returns a string map containing all the color ramp&#39;s properties.
int count() const override
Returns number of defined colors, or -1 if undefined.
Definition: qgscolorramp.h:315
#define DEFAULT_CPTCITY_SCHEMENAME
Definition: qgscolorramp.h:619
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
static QMap< QString, QString > copyingInfo(const QString &fileName)
QgsGradientColorRamp(const QColor &color1=DEFAULT_GRADIENT_COLOR1, const QColor &color2=DEFAULT_GRADIENT_COLOR2, bool discrete=false, const QgsGradientStopsList &stops=QgsGradientStopsList())
Constructor for QgsGradientColorRamp.
static QList< QColor > randomColors(int count, int hueMax=DEFAULT_RANDOM_HUE_MAX, int hueMin=DEFAULT_RANDOM_HUE_MIN, int satMax=DEFAULT_RANDOM_SAT_MAX, int satMin=DEFAULT_RANDOM_SAT_MIN, int valMax=DEFAULT_RANDOM_VAL_MAX, int valMin=DEFAULT_RANDOM_VAL_MIN)
Gets a list of random colors.
QgsStringMap properties() const override
Returns a string map containing all the color ramp&#39;s properties.
void setStops(const QgsGradientStopsList &stops)
Sets the list of intermediate gradient stops for the ramp.
bool isDiscrete() const
Returns true if the gradient is using discrete interpolation, rather than smoothly interpolating betw...
Definition: qgscolorramp.h:202
#define DEFAULT_GRADIENT_COLOR1
Definition: qgscolorramp.h:129
A scheme based color ramp consisting of a list of predefined colors.
Definition: qgscolorramp.h:471
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
QgsGradientStopsList stops() const
Returns the list of intermediate gradient stops for the ramp.
Definition: qgscolorramp.h:235
Totally random color ramp.
Definition: qgscolorramp.h:427
QgsGradientColorRamp * cloneGradientRamp() const
int count() const override
Returns number of defined colors, or -1 if undefined.
QgsLimitedRandomColorRamp * clone() const override
Creates a clone of the color ramp.
double value(int index) const override
Returns relative value between [0,1] of color at specified index.
QgsStringMap copyingInfo() const
static QStringList listSchemes()
QString type() const override
Returns a string representing the color ramp type.
Definition: qgscolorramp.h:312
static QList< QColor > listSchemeColors(const QString &schemeName, int colors)
QString schemeName() const
Returns the name of the color brewer color scheme.
Definition: qgscolorramp.h:569
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsColorRamp from a map of properties.
#define DEFAULT_RANDOM_COUNT
Definition: qgscolorramp.h:269
QgsNamedColorList fetchColors(const QString &context=QString(), const QColor &baseColor=QColor()) override
Gets a list of colors from the scheme.
QString type() const override
Returns a string representing the color ramp type.
Definition: qgscolorramp.h:559
#define DEFAULT_RANDOM_SAT_MIN
Definition: qgscolorramp.h:274
QList< QColor > mPalette
Definition: qgscolorramp.h:614
QString type() const override
Returns a string representing the color ramp type.
Definition: qgscolorramp.h:656
void invert() override
Inverts the ordering of the color ramp.
void setInfo(const QgsStringMap &info)
Sets additional info to attach to the gradient ramp (e.g., authorship notes)
Definition: qgscolorramp.h:248
QgsCptCityColorRamp * clone() const override
Creates a clone of the color ramp.
double value(int index) const override
Returns relative value between [0,1] of color at specified index.
static QStringList listSchemeNames()
Returns a list of all valid color brewer scheme names.
QString type() const override
Returns a string representing the color ramp type.
QgsStringMap properties() const override
Returns a string map containing all the color ramp&#39;s properties.
Color ramp utilising "Color Brewer" preset color schemes.
Definition: qgscolorramp.h:535
int valMin() const
Returns the minimum value for generated colors.
Definition: qgscolorramp.h:360
#define DEFAULT_GRADIENT_COLOR2
Definition: qgscolorramp.h:130
QgsGradientStopsList mStops
Definition: qgscolorramp.h:263
QColor color(double value) const override
Returns the color corresponding to a specified value.
int valMax() const
Returns the maximum value for generated colors.
Definition: qgscolorramp.h:366
QColor color(double value) const override
Returns the color corresponding to a specified value.
QgsStringMap mInfo
Definition: qgscolorramp.h:264
static QMap< double, QPair< QColor, QColor > > gradientColorMap(const QString &fileName)
QgsLimitedRandomColorRamp(int count=DEFAULT_RANDOM_COUNT, int hueMin=DEFAULT_RANDOM_HUE_MIN, int hueMax=DEFAULT_RANDOM_HUE_MAX, int satMin=DEFAULT_RANDOM_SAT_MIN, int satMax=DEFAULT_RANDOM_SAT_MAX, int valMin=DEFAULT_RANDOM_VAL_MIN, int valMax=DEFAULT_RANDOM_VAL_MAX)
Constructor for QgsLimitedRandomColorRamp.
QgsColorBrewerColorRamp(const QString &schemeName=DEFAULT_COLORBREWER_SCHEMENAME, int colors=DEFAULT_COLORBREWER_COLORS, bool inverted=false)
Constructor for QgsColorBrewerColorRamp.
QgsRandomColorRamp * clone() const override
Creates a clone of the color ramp.
int ANALYSIS_EXPORT lower(int n, int i)
Lower function.
Definition: MathUtils.cpp:407
void loadPalette()
Generates the scheme using the current name and number of colors.
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
Returns a new QgsLimitedRandomColorRamp color ramp created using the properties encoded in a string m...
void addStopsToGradient(QGradient *gradient, double opacity=1)
Copy color ramp stops to a QGradient.
void updateColors()
Must be called after changing the properties of the color ramp to regenerate the list of random color...
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:139
QStringList variantList() const
Definition: qgscolorramp.h:668
QgsPresetSchemeColorRamp(const QList< QColor > &colors=QList< QColor >())
Constructor for QgsPresetSchemeColorRamp.
#define DEFAULT_CPTCITY_VARIANTNAME
Definition: qgscolorramp.h:620
QgsStringMap properties() const override
Returns a string map containing all the color ramp&#39;s properties.
QColor color1() const
Returns the gradient start color.
Definition: qgscolorramp.h:172
QString variantName() const
Definition: qgscolorramp.h:667
static QString findFileName(const QString &target, const QString &startDir, const QString &baseDir)
#define DEFAULT_COLORBREWER_COLORS
Definition: qgscolorramp.h:527
static QColor decodeColor(const QString &str)
QColor color(double value) const override
Returns the color corresponding to a specified value.
QColor color(double value) const override
Returns the color corresponding to a specified value.
int count() const override
Returns number of defined colors, or -1 if undefined.
Definition: qgscolorramp.h:159