QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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  const auto constSplit = props["stops"].split( ':' );
68  for ( const QString &stop : constSplit )
69  {
70  int i = stop.indexOf( ';' );
71  if ( i == -1 )
72  continue;
73 
74  QColor c = QgsSymbolLayerUtils::decodeColor( stop.mid( i + 1 ) );
75  stops.append( QgsGradientStop( stop.leftRef( i ).toDouble(), c ) );
76  }
77  }
78 
79  // discrete vs. continuous
80  bool discrete = false;
81  if ( props.contains( QStringLiteral( "discrete" ) ) )
82  {
83  if ( props[QStringLiteral( "discrete" )] == QLatin1String( "1" ) )
84  discrete = true;
85  }
86 
87  // search for information keys starting with "info_"
89  for ( QgsStringMap::const_iterator it = props.constBegin();
90  it != props.constEnd(); ++it )
91  {
92  if ( it.key().startsWith( QLatin1String( "info_" ) ) )
93  info[ it.key().mid( 5 )] = it.value();
94  }
95 
96  QgsGradientColorRamp *r = new QgsGradientColorRamp( color1, color2, discrete, stops );
97  r->setInfo( info );
98  return r;
99 }
100 
101 double QgsGradientColorRamp::value( int index ) const
102 {
103  if ( index <= 0 )
104  {
105  return 0;
106  }
107  else if ( index >= mStops.size() + 1 )
108  {
109  return 1;
110  }
111  else
112  {
113  return mStops[index - 1].offset;
114  }
115 }
116 
117 QColor QgsGradientColorRamp::color( double value ) const
118 {
119  if ( qgsDoubleNear( value, 0.0 ) || value < 0.0 )
120  {
121  return mColor1;
122  }
123  else if ( qgsDoubleNear( value, 1.0 ) || value > 1.0 )
124  {
125  return mColor2;
126  }
127  else if ( mStops.isEmpty() )
128  {
129  if ( mDiscrete )
130  return mColor1;
131 
132  return _interpolate( mColor1, mColor2, value );
133  }
134  else
135  {
136  double lower = 0, upper = 0;
137  QColor c1 = mColor1, c2;
138  for ( QgsGradientStopsList::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
139  {
140  if ( it->offset > value )
141  {
142  if ( mDiscrete )
143  return c1;
144 
145  upper = it->offset;
146  c2 = it->color;
147 
148  return qgsDoubleNear( upper, lower ) ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) );
149  }
150  lower = it->offset;
151  c1 = it->color;
152  }
153 
154  if ( mDiscrete )
155  return c1;
156 
157  upper = 1;
158  c2 = mColor2;
159  return qgsDoubleNear( upper, lower ) ? c1 : _interpolate( c1, c2, ( value - lower ) / ( upper - lower ) );
160  }
161 }
162 
164 {
165  QgsGradientStopsList newStops;
166 
167  if ( mDiscrete )
168  {
169  mColor2 = mColor1;
170  mColor1 = mStops.at( mStops.size() - 1 ).color;
171  for ( int k = mStops.size() - 1; k >= 1; k-- )
172  {
173  newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k - 1 ).color );
174  }
175  newStops << QgsGradientStop( 1 - mStops.at( 0 ).offset, mColor2 );
176  }
177  else
178  {
179  QColor tmpColor = mColor2;
180  mColor2 = mColor1;
181  mColor1 = tmpColor;
182  for ( int k = mStops.size() - 1; k >= 0; k-- )
183  {
184  newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k ).color );
185  }
186  }
187  mStops = newStops;
188 }
189 
191 {
193  mDiscrete, mStops );
194  r->setInfo( mInfo );
195  return r;
196 }
197 
199 {
200  QgsStringMap map;
201  map[QStringLiteral( "color1" )] = QgsSymbolLayerUtils::encodeColor( mColor1 );
202  map[QStringLiteral( "color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
203  if ( !mStops.isEmpty() )
204  {
205  QStringList lst;
206  for ( QgsGradientStopsList::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
207  {
208  lst.append( QStringLiteral( "%1;%2" ).arg( it->offset ).arg( QgsSymbolLayerUtils::encodeColor( it->color ) ) );
209  }
210  map[QStringLiteral( "stops" )] = lst.join( QStringLiteral( ":" ) );
211  }
212 
213  map[QStringLiteral( "discrete" )] = mDiscrete ? "1" : "0";
214 
215  for ( QgsStringMap::const_iterator it = mInfo.constBegin();
216  it != mInfo.constEnd(); ++it )
217  {
218  map["info_" + it.key()] = it.value();
219  }
220 
221  map[QStringLiteral( "rampType" )] = type();
222  return map;
223 }
225 {
226  if ( discrete == mDiscrete )
227  return;
228 
229  // if going to/from Discrete, re-arrange stops
230  // this will only work when stops are equally-spaced
231  QgsGradientStopsList newStops;
232  if ( discrete )
233  {
234  // re-arrange stops offset
235  int numStops = mStops.count() + 2;
236  int i = 1;
237  for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
238  it != mStops.constEnd(); ++it )
239  {
240  newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, it->color ) );
241  if ( i == numStops - 1 )
242  break;
243  i++;
244  }
245  // replicate last color
246  newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, mColor2 ) );
247  }
248  else
249  {
250  // re-arrange stops offset, remove duplicate last color
251  int numStops = mStops.count() + 2;
252  int i = 1;
253  for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
254  it != mStops.constEnd(); ++it )
255  {
256  newStops.append( QgsGradientStop( static_cast< double >( i ) / ( numStops - 2 ), it->color ) );
257  if ( i == numStops - 3 )
258  break;
259  i++;
260  }
261  }
262  mStops = newStops;
263  mDiscrete = discrete;
264 }
265 
266 bool stopLessThan( const QgsGradientStop &s1, const QgsGradientStop &s2 )
267 {
268  return s1.offset < s2.offset;
269 }
270 
272 {
273  mStops = stops;
274 
275  //sort stops by offset
276  std::sort( mStops.begin(), mStops.end(), stopLessThan );
277 }
278 
279 void QgsGradientColorRamp::addStopsToGradient( QGradient *gradient, double opacity )
280 {
281  //copy color ramp stops to a QGradient
282  QColor color1 = mColor1;
283  QColor color2 = mColor2;
284  if ( opacity < 1 )
285  {
286  color1.setAlpha( color1.alpha() * opacity );
287  color2.setAlpha( color2.alpha() * opacity );
288  }
289  gradient->setColorAt( 0, color1 );
290  gradient->setColorAt( 1, color2 );
291 
292  for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
293  it != mStops.constEnd(); ++it )
294  {
295  QColor rampColor = it->color;
296  if ( opacity < 1 )
297  {
298  rampColor.setAlpha( rampColor.alpha() * opacity );
299  }
300  gradient->setColorAt( it->offset, rampColor );
301  }
302 }
303 
304 
306 
307 
309  int satMin, int satMax, int valMin, int valMax )
310  : mCount( count )
311  , mHueMin( hueMin ), mHueMax( hueMax )
312  , mSatMin( satMin ), mSatMax( satMax )
313  , mValMin( valMin ), mValMax( valMax )
314 {
315  updateColors();
316 }
317 
319 {
324 
325  if ( props.contains( QStringLiteral( "count" ) ) ) count = props[QStringLiteral( "count" )].toInt();
326  if ( props.contains( QStringLiteral( "hueMin" ) ) ) hueMin = props[QStringLiteral( "hueMin" )].toInt();
327  if ( props.contains( QStringLiteral( "hueMax" ) ) ) hueMax = props[QStringLiteral( "hueMax" )].toInt();
328  if ( props.contains( QStringLiteral( "satMin" ) ) ) satMin = props[QStringLiteral( "satMin" )].toInt();
329  if ( props.contains( QStringLiteral( "satMax" ) ) ) satMax = props[QStringLiteral( "satMax" )].toInt();
330  if ( props.contains( QStringLiteral( "valMin" ) ) ) valMin = props[QStringLiteral( "valMin" )].toInt();
331  if ( props.contains( QStringLiteral( "valMax" ) ) ) valMax = props[QStringLiteral( "valMax" )].toInt();
332 
333  return new QgsLimitedRandomColorRamp( count, hueMin, hueMax, satMin, satMax, valMin, valMax );
334 }
335 
336 double QgsLimitedRandomColorRamp::value( int index ) const
337 {
338  if ( mColors.empty() )
339  return 0;
340  return static_cast< double >( index ) / ( mColors.size() - 1 );
341 }
342 
343 QColor QgsLimitedRandomColorRamp::color( double value ) const
344 {
345  if ( value < 0 || value > 1 )
346  return QColor();
347 
348  int colorCnt = mColors.count();
349  int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
350 
351  if ( colorIdx >= 0 && colorIdx < colorCnt )
352  return mColors.at( colorIdx );
353 
354  return QColor();
355 }
356 
358 {
360 }
361 
363 {
364  QgsStringMap map;
365  map[QStringLiteral( "count" )] = QString::number( mCount );
366  map[QStringLiteral( "hueMin" )] = QString::number( mHueMin );
367  map[QStringLiteral( "hueMax" )] = QString::number( mHueMax );
368  map[QStringLiteral( "satMin" )] = QString::number( mSatMin );
369  map[QStringLiteral( "satMax" )] = QString::number( mSatMax );
370  map[QStringLiteral( "valMin" )] = QString::number( mValMin );
371  map[QStringLiteral( "valMax" )] = QString::number( mValMax );
372  map[QStringLiteral( "rampType" )] = type();
373  return map;
374 }
375 
377  int hueMax, int hueMin, int satMax, int satMin, int valMax, int valMin )
378 {
379  int h, s, v;
380  QList<QColor> colors;
381 
382  //normalize values
383  int safeHueMax = std::max( hueMin, hueMax );
384  int safeHueMin = std::min( hueMin, hueMax );
385  int safeSatMax = std::max( satMin, satMax );
386  int safeSatMin = std::min( satMin, satMax );
387  int safeValMax = std::max( valMin, valMax );
388  int safeValMin = std::min( valMin, valMax );
389 
390  //start hue at random angle
391  double currentHueAngle = 360.0 * static_cast< double >( qrand() ) / RAND_MAX;
392 
393  colors.reserve( count );
394  for ( int i = 0; i < count; ++i )
395  {
396  //increment hue by golden ratio (approx 137.507 degrees)
397  //as this minimizes hue nearness as count increases
398  //see http://basecase.org/env/on-rainbows for more details
399  currentHueAngle += 137.50776;
400  //scale hue to between hueMax and hueMin
401  h = qBound( 0.0, std::round( ( std::fmod( currentHueAngle, 360.0 ) / 360.0 ) * ( safeHueMax - safeHueMin ) + safeHueMin ), 359.0 );
402  s = qBound( 0, ( qrand() % ( safeSatMax - safeSatMin + 1 ) ) + safeSatMin, 255 );
403  v = qBound( 0, ( qrand() % ( safeValMax - safeValMin + 1 ) ) + safeValMin, 255 );
404  colors.append( QColor::fromHsv( h, s, v ) );
405  }
406  return colors;
407 }
408 
410 {
412 }
413 
415 
417 {
418  return -1;
419 }
420 
421 double QgsRandomColorRamp::value( int index ) const
422 {
423  Q_UNUSED( index )
424  return 0.0;
425 }
426 
427 QColor QgsRandomColorRamp::color( double value ) const
428 {
429  int minVal = 130;
430  int maxVal = 255;
431 
432  //if value is nan, then use last precalculated color
433  int colorIndex = ( !std::isnan( value ) ? value : 1 ) * ( mTotalColorCount - 1 );
434  if ( mTotalColorCount >= 1 && mPrecalculatedColors.length() > colorIndex )
435  {
436  //use precalculated hue
437  return mPrecalculatedColors.at( colorIndex );
438  }
439 
440  //can't use precalculated hues, use a totally random hue
441  int h = static_cast< int >( 360.0 * qrand() / ( RAND_MAX + 1.0 ) );
442  int s = ( qrand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
443  int v = ( qrand() % ( maxVal - minVal + 1 ) ) + minVal;
444  return QColor::fromHsv( h, s, v );
445 }
446 
447 void QgsRandomColorRamp::setTotalColorCount( const int colorCount )
448 {
449  //calculate colors in advance, so that we can ensure they are more visually distinct than pure random colors
450  mPrecalculatedColors.clear();
451  mTotalColorCount = colorCount;
452 
453  //This works OK for low color counts, but for > 10 or so colors there's still a good chance of
454  //similar colors being picked. TODO - investigate alternative "n-visually distinct color" routines
455 
456  //random offsets
457  double hueOffset = ( 360.0 * qrand() / ( RAND_MAX + 1.0 ) );
458 
459  //try to maximise difference between hues. this is not an ideal implementation, as constant steps
460  //through the hue wheel are not visually perceived as constant changes in hue
461  //(for instance, we are much more likely to get green hues than yellow hues)
462  double hueStep = 359.0 / colorCount;
463  double currentHue = hueOffset;
464 
465  //build up a list of colors
466  for ( int idx = 0; idx < colorCount; ++ idx )
467  {
468  int h = static_cast< int >( std::round( currentHue ) ) % 360;
469  int s = ( qrand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
470  int v = ( qrand() % ( DEFAULT_RANDOM_VAL_MAX - DEFAULT_RANDOM_VAL_MIN + 1 ) ) + DEFAULT_RANDOM_VAL_MIN;
471  mPrecalculatedColors << QColor::fromHsv( h, s, v );
472  currentHue += hueStep;
473  }
474 
475  //lastly, shuffle color list
476  std::random_shuffle( mPrecalculatedColors.begin(), mPrecalculatedColors.end() );
477 }
478 
480 {
481  return QStringLiteral( "randomcolors" );
482 }
483 
485 {
486  return new QgsRandomColorRamp();
487 }
488 
490 {
491  return QgsStringMap();
492 }
493 
495 
496 QgsColorBrewerColorRamp::QgsColorBrewerColorRamp( const QString &schemeName, int colors, bool inverted )
497  : mSchemeName( schemeName )
498  , mColors( colors )
499  , mInverted( inverted )
500 {
501  loadPalette();
502 }
503 
505 {
508  bool inverted = false;
509 
510  if ( props.contains( QStringLiteral( "schemeName" ) ) )
511  schemeName = props[QStringLiteral( "schemeName" )];
512  if ( props.contains( QStringLiteral( "colors" ) ) )
513  colors = props[QStringLiteral( "colors" )].toInt();
514  if ( props.contains( QStringLiteral( "inverted" ) ) )
515  inverted = props[QStringLiteral( "inverted" )].toInt();
516 
517  return new QgsColorBrewerColorRamp( schemeName, colors, inverted );
518 }
519 
521 {
523 
524  if ( mInverted )
525  {
526  QList<QColor> tmpPalette;
527 
528  for ( int k = mPalette.size() - 1; k >= 0; k-- )
529  {
530  tmpPalette << mPalette.at( k );
531  }
532  mPalette = tmpPalette;
533  }
534 }
535 
537 {
539 }
540 
542 {
543  return QgsColorBrewerPalette::listSchemeVariants( schemeName );
544 }
545 
546 double QgsColorBrewerColorRamp::value( int index ) const
547 {
548  if ( mPalette.empty() )
549  return 0;
550  return static_cast< double >( index ) / ( mPalette.size() - 1 );
551 }
552 
553 QColor QgsColorBrewerColorRamp::color( double value ) const
554 {
555  if ( mPalette.isEmpty() || value < 0 || value > 1 || std::isnan( value ) )
556  return QColor();
557 
558  int paletteEntry = static_cast< int >( value * mPalette.count() );
559  if ( paletteEntry >= mPalette.count() )
560  paletteEntry = mPalette.count() - 1;
561  return mPalette.at( paletteEntry );
562 }
563 
565 {
566  mInverted = !mInverted;
567  loadPalette();
568 }
569 
571 {
573 }
574 
576 {
577  QgsStringMap map;
578  map[QStringLiteral( "schemeName" )] = mSchemeName;
579  map[QStringLiteral( "colors" )] = QString::number( mColors );
580  map[QStringLiteral( "inverted" )] = QString::number( mInverted );
581  map[QStringLiteral( "rampType" )] = type();
582  return map;
583 }
584 
585 
587 
588 
589 QgsCptCityColorRamp::QgsCptCityColorRamp( const QString &schemeName, const QString &variantName,
590  bool inverted, bool doLoadFile )
592  , mSchemeName( schemeName )
593  , mVariantName( variantName )
594  , mInverted( inverted )
595 {
596  // TODO replace this with hard-coded data in the default case
597  // don't load file if variant is missing
598  if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
599  loadFile();
600 }
601 
603  const QString &variantName, bool inverted, bool doLoadFile )
605  , mSchemeName( schemeName )
606  , mVariantName( variantName )
607  , mVariantList( variantList )
608  , mInverted( inverted )
609 {
611 
612  // TODO replace this with hard-coded data in the default case
613  // don't load file if variant is missing
614  if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
615  loadFile();
616 }
617 
619 {
622  bool inverted = false;
623 
624  if ( props.contains( QStringLiteral( "schemeName" ) ) )
625  schemeName = props[QStringLiteral( "schemeName" )];
626  if ( props.contains( QStringLiteral( "variantName" ) ) )
627  variantName = props[QStringLiteral( "variantName" )];
628  if ( props.contains( QStringLiteral( "inverted" ) ) )
629  inverted = props[QStringLiteral( "inverted" )].toInt();
630 
631  return new QgsCptCityColorRamp( schemeName, variantName, inverted );
632 }
633 
635 {
636  mInverted = !mInverted;
638 }
639 
641 {
642  QgsCptCityColorRamp *ramp = new QgsCptCityColorRamp( QString(), QString(), mInverted, false );
643  ramp->copy( this );
644  return ramp;
645 }
646 
648 {
649  if ( ! other )
650  return;
651  mColor1 = other->color1();
652  mColor2 = other->color2();
653  mDiscrete = other->isDiscrete();
654  mStops = other->stops();
655  mSchemeName = other->mSchemeName;
656  mVariantName = other->mVariantName;
657  mVariantList = other->mVariantList;
658  mFileLoaded = other->mFileLoaded;
659  mInverted = other->mInverted;
660 }
661 
663 {
664  QgsGradientColorRamp *ramp =
666  // add author and copyright information
667  // TODO also add COPYING.xml file/link?
669  info[QStringLiteral( "cpt-city-gradient" )] = "<cpt-city>/" + mSchemeName + mVariantName + ".svg";
670  QString copyingFilename = copyingFileName();
671  copyingFilename.remove( QgsCptCityArchive::defaultBaseDir() );
672  info[QStringLiteral( "cpt-city-license" )] = "<cpt-city>" + copyingFilename;
673  ramp->setInfo( info );
674  return ramp;
675 }
676 
677 
679 {
680  QgsStringMap map;
681  map[QStringLiteral( "schemeName" )] = mSchemeName;
682  map[QStringLiteral( "variantName" )] = mVariantName;
683  map[QStringLiteral( "inverted" )] = QString::number( mInverted );
684  map[QStringLiteral( "rampType" )] = type();
685  return map;
686 }
687 
688 
690 {
691  if ( mSchemeName.isEmpty() )
692  return QString();
693  else
694  {
695  return QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mSchemeName + mVariantName + ".svg";
696  }
697 }
698 
700 {
701  return QgsCptCityArchive::findFileName( QStringLiteral( "COPYING.xml" ), QFileInfo( fileName() ).dir().path(),
703 }
704 
706 {
707  return QgsCptCityArchive::findFileName( QStringLiteral( "DESC.xml" ), QFileInfo( fileName() ).dir().path(),
709 }
710 
712 {
714 }
715 
717 {
718  if ( mFileLoaded )
719  {
720  QgsDebugMsg( "File already loaded for " + mSchemeName + mVariantName );
721  return true;
722  }
723 
724  // get filename
725  QString filename = fileName();
726  if ( filename.isNull() )
727  {
728  QgsDebugMsg( "Couldn't get fileName() for " + mSchemeName + mVariantName );
729  return false;
730  }
731 
732  QgsDebugMsg( QStringLiteral( "filename= %1 loaded=%2" ).arg( filename ).arg( mFileLoaded ) );
733 
734  // get color ramp from svg file
735  QMap< double, QPair<QColor, QColor> > colorMap =
737 
738  // add colors to palette
739  mFileLoaded = false;
740  mStops.clear();
741  QMap<double, QPair<QColor, QColor> >::const_iterator it, prev;
742  // first detect if file is gradient is continuous or dicrete
743  // discrete: stop contains 2 colors and first color is identical to previous second
744  // multi: stop contains 2 colors and no relation with previous stop
745  mDiscrete = false;
746  mMultiStops = false;
747  it = prev = colorMap.constBegin();
748  while ( it != colorMap.constEnd() )
749  {
750  // look for stops that contain multiple values
751  if ( it != colorMap.constBegin() && ( it.value().first != it.value().second ) )
752  {
753  if ( it.value().first == prev.value().second )
754  {
755  mDiscrete = true;
756  break;
757  }
758  else
759  {
760  mMultiStops = true;
761  break;
762  }
763  }
764  prev = it;
765  ++it;
766  }
767 
768  // fill all stops
769  it = prev = colorMap.constBegin();
770  while ( it != colorMap.constEnd() )
771  {
772  if ( mDiscrete )
773  {
774  // mPalette << qMakePair( it.key(), it.value().second );
775  mStops.append( QgsGradientStop( it.key(), it.value().second ) );
776  }
777  else
778  {
779  // mPalette << qMakePair( it.key(), it.value().first );
780  mStops.append( QgsGradientStop( it.key(), it.value().first ) );
781  if ( ( mMultiStops ) &&
782  ( it.key() != 0.0 && it.key() != 1.0 ) )
783  {
784  mStops.append( QgsGradientStop( it.key(), it.value().second ) );
785  }
786  }
787  prev = it;
788  ++it;
789  }
790 
791  // remove first and last items (mColor1 and mColor2)
792  if ( ! mStops.isEmpty() && mStops.at( 0 ).offset == 0.0 )
793  mColor1 = mStops.takeFirst().color;
794  if ( ! mStops.isEmpty() && mStops.last().offset == 1.0 )
795  mColor2 = mStops.takeLast().color;
796 
797  if ( mInverted )
798  {
800  }
801 
802  mFileLoaded = true;
803  return true;
804 }
805 
806 
807 //
808 // QgsPresetColorRamp
809 //
810 
812 {
813  const auto constColors = colors;
814  for ( const QColor &color : constColors )
815  {
816  mColors << qMakePair( color, color.name() );
817  }
818  // need at least one color
819  if ( mColors.isEmpty() )
820  mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
821 }
822 
824  : mColors( colors )
825 {
826  // need at least one color
827  if ( mColors.isEmpty() )
828  mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
829 }
830 
832 {
834 
835  int i = 0;
836  QString colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() );
837  QString colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() );
838  while ( !colorString.isEmpty() )
839  {
840  colors << qMakePair( QgsSymbolLayerUtils::decodeColor( colorString ), colorName );
841  i++;
842  colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() );
843  colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() );
844  }
845 
846  return new QgsPresetSchemeColorRamp( colors );
847 }
848 
849 QList<QColor> QgsPresetSchemeColorRamp::colors() const
850 {
851  QList< QColor > l;
852  l.reserve( mColors.count() );
853  for ( int i = 0; i < mColors.count(); ++i )
854  {
855  l << mColors.at( i ).first;
856  }
857  return l;
858 }
859 
860 double QgsPresetSchemeColorRamp::value( int index ) const
861 {
862  if ( mColors.empty() )
863  return 0;
864  return static_cast< double >( index ) / ( mColors.size() - 1 );
865 }
866 
867 QColor QgsPresetSchemeColorRamp::color( double value ) const
868 {
869  if ( value < 0 || value > 1 )
870  return QColor();
871 
872  int colorCnt = mColors.count();
873  int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
874 
875  if ( colorIdx >= 0 && colorIdx < colorCnt )
876  return mColors.at( colorIdx ).first;
877 
878  return QColor();
879 }
880 
882 {
883  QgsNamedColorList tmpColors;
884 
885  for ( int k = mColors.size() - 1; k >= 0; k-- )
886  {
887  tmpColors << mColors.at( k );
888  }
889  mColors = tmpColors;
890 }
891 
893 {
894  return new QgsPresetSchemeColorRamp( *this );
895 }
896 
898 {
899  QgsStringMap props;
900  for ( int i = 0; i < mColors.count(); ++i )
901  {
902  props.insert( QStringLiteral( "preset_color_%1" ).arg( i ), QgsSymbolLayerUtils::encodeColor( mColors.at( i ).first ) );
903  props.insert( QStringLiteral( "preset_color_name_%1" ).arg( i ), mColors.at( i ).second );
904  }
905  props[QStringLiteral( "rampType" )] = type();
906  return props;
907 }
908 
910 {
911  return mColors.count();
912 }
913 
915 {
916  return mColors;
917 }
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:265
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:587
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