QGIS API Documentation  3.17.0-Master (a035f434f4)
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 {
166 }
167 
169 {
170  QgsGradientStopsList newStops;
171 
172  if ( mDiscrete )
173  {
174  mColor2 = mColor1;
175  mColor1 = mStops.at( mStops.size() - 1 ).color;
176  for ( int k = mStops.size() - 1; k >= 1; k-- )
177  {
178  newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k - 1 ).color );
179  }
180  newStops << QgsGradientStop( 1 - mStops.at( 0 ).offset, mColor2 );
181  }
182  else
183  {
184  QColor tmpColor = mColor2;
185  mColor2 = mColor1;
186  mColor1 = tmpColor;
187  for ( int k = mStops.size() - 1; k >= 0; k-- )
188  {
189  newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k ).color );
190  }
191  }
192  mStops = newStops;
193 }
194 
196 {
198  mDiscrete, mStops );
199  r->setInfo( mInfo );
200  return r;
201 }
202 
204 {
205  QgsStringMap map;
206  map[QStringLiteral( "color1" )] = QgsSymbolLayerUtils::encodeColor( mColor1 );
207  map[QStringLiteral( "color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
208  if ( !mStops.isEmpty() )
209  {
210  QStringList lst;
211  for ( QgsGradientStopsList::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
212  {
213  lst.append( QStringLiteral( "%1;%2" ).arg( it->offset ).arg( QgsSymbolLayerUtils::encodeColor( it->color ) ) );
214  }
215  map[QStringLiteral( "stops" )] = lst.join( QLatin1Char( ':' ) );
216  }
217 
218  map[QStringLiteral( "discrete" )] = mDiscrete ? "1" : "0";
219 
220  for ( QgsStringMap::const_iterator it = mInfo.constBegin();
221  it != mInfo.constEnd(); ++it )
222  {
223  map["info_" + it.key()] = it.value();
224  }
225 
226  map[QStringLiteral( "rampType" )] = type();
227  return map;
228 }
230 {
231  if ( discrete == mDiscrete )
232  return;
233 
234  // if going to/from Discrete, re-arrange stops
235  // this will only work when stops are equally-spaced
236  QgsGradientStopsList newStops;
237  if ( discrete )
238  {
239  // re-arrange stops offset
240  int numStops = mStops.count() + 2;
241  int i = 1;
242  for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
243  it != mStops.constEnd(); ++it )
244  {
245  newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, it->color ) );
246  if ( i == numStops - 1 )
247  break;
248  i++;
249  }
250  // replicate last color
251  newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, mColor2 ) );
252  }
253  else
254  {
255  // re-arrange stops offset, remove duplicate last color
256  int numStops = mStops.count() + 2;
257  int i = 1;
258  for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
259  it != mStops.constEnd(); ++it )
260  {
261  newStops.append( QgsGradientStop( static_cast< double >( i ) / ( numStops - 2 ), it->color ) );
262  if ( i == numStops - 3 )
263  break;
264  i++;
265  }
266  }
267  mStops = newStops;
268  mDiscrete = discrete;
269 }
270 
271 bool stopLessThan( const QgsGradientStop &s1, const QgsGradientStop &s2 )
272 {
273  return s1.offset < s2.offset;
274 }
275 
277 {
278  mStops = stops;
279 
280  //sort stops by offset
281  std::sort( mStops.begin(), mStops.end(), stopLessThan );
282 }
283 
284 void QgsGradientColorRamp::addStopsToGradient( QGradient *gradient, double opacity )
285 {
286  //copy color ramp stops to a QGradient
287  QColor color1 = mColor1;
288  QColor color2 = mColor2;
289  if ( opacity < 1 )
290  {
291  color1.setAlpha( color1.alpha() * opacity );
292  color2.setAlpha( color2.alpha() * opacity );
293  }
294  gradient->setColorAt( 0, color1 );
295  gradient->setColorAt( 1, color2 );
296 
297  for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
298  it != mStops.constEnd(); ++it )
299  {
300  QColor rampColor = it->color;
301  if ( opacity < 1 )
302  {
303  rampColor.setAlpha( rampColor.alpha() * opacity );
304  }
305  gradient->setColorAt( it->offset, rampColor );
306  }
307 }
308 
309 
311 
312 
314  int satMin, int satMax, int valMin, int valMax )
315  : mCount( count )
316  , mHueMin( hueMin ), mHueMax( hueMax )
317  , mSatMin( satMin ), mSatMax( satMax )
318  , mValMin( valMin ), mValMax( valMax )
319 {
320  updateColors();
321 }
322 
324 {
329 
330  if ( props.contains( QStringLiteral( "count" ) ) ) count = props[QStringLiteral( "count" )].toInt();
331  if ( props.contains( QStringLiteral( "hueMin" ) ) ) hueMin = props[QStringLiteral( "hueMin" )].toInt();
332  if ( props.contains( QStringLiteral( "hueMax" ) ) ) hueMax = props[QStringLiteral( "hueMax" )].toInt();
333  if ( props.contains( QStringLiteral( "satMin" ) ) ) satMin = props[QStringLiteral( "satMin" )].toInt();
334  if ( props.contains( QStringLiteral( "satMax" ) ) ) satMax = props[QStringLiteral( "satMax" )].toInt();
335  if ( props.contains( QStringLiteral( "valMin" ) ) ) valMin = props[QStringLiteral( "valMin" )].toInt();
336  if ( props.contains( QStringLiteral( "valMax" ) ) ) valMax = props[QStringLiteral( "valMax" )].toInt();
337 
338  return new QgsLimitedRandomColorRamp( count, hueMin, hueMax, satMin, satMax, valMin, valMax );
339 }
340 
341 double QgsLimitedRandomColorRamp::value( int index ) const
342 {
343  if ( mColors.empty() )
344  return 0;
345  return static_cast< double >( index ) / ( mColors.size() - 1 );
346 }
347 
348 QColor QgsLimitedRandomColorRamp::color( double value ) const
349 {
350  if ( value < 0 || value > 1 )
351  return QColor();
352 
353  int colorCnt = mColors.count();
354  int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
355 
356  if ( colorIdx >= 0 && colorIdx < colorCnt )
357  return mColors.at( colorIdx );
358 
359  return QColor();
360 }
361 
363 {
365 }
366 
368 {
370 }
371 
373 {
374  QgsStringMap map;
375  map[QStringLiteral( "count" )] = QString::number( mCount );
376  map[QStringLiteral( "hueMin" )] = QString::number( mHueMin );
377  map[QStringLiteral( "hueMax" )] = QString::number( mHueMax );
378  map[QStringLiteral( "satMin" )] = QString::number( mSatMin );
379  map[QStringLiteral( "satMax" )] = QString::number( mSatMax );
380  map[QStringLiteral( "valMin" )] = QString::number( mValMin );
381  map[QStringLiteral( "valMax" )] = QString::number( mValMax );
382  map[QStringLiteral( "rampType" )] = type();
383  return map;
384 }
385 
387  int hueMax, int hueMin, int satMax, int satMin, int valMax, int valMin )
388 {
389  int h, s, v;
390  QList<QColor> colors;
391 
392  //normalize values
393  int safeHueMax = std::max( hueMin, hueMax );
394  int safeHueMin = std::min( hueMin, hueMax );
395  int safeSatMax = std::max( satMin, satMax );
396  int safeSatMin = std::min( satMin, satMax );
397  int safeValMax = std::max( valMin, valMax );
398  int safeValMin = std::min( valMin, valMax );
399 
400  //start hue at random angle
401  double currentHueAngle = 360.0 * static_cast< double >( qrand() ) / RAND_MAX;
402 
403  colors.reserve( count );
404  for ( int i = 0; i < count; ++i )
405  {
406  //increment hue by golden ratio (approx 137.507 degrees)
407  //as this minimizes hue nearness as count increases
408  //see http://basecase.org/env/on-rainbows for more details
409  currentHueAngle += 137.50776;
410  //scale hue to between hueMax and hueMin
411  h = qBound( 0.0, std::round( ( std::fmod( currentHueAngle, 360.0 ) / 360.0 ) * ( safeHueMax - safeHueMin ) + safeHueMin ), 359.0 );
412  s = qBound( 0, ( qrand() % ( safeSatMax - safeSatMin + 1 ) ) + safeSatMin, 255 );
413  v = qBound( 0, ( qrand() % ( safeValMax - safeValMin + 1 ) ) + safeValMin, 255 );
414  colors.append( QColor::fromHsv( h, s, v ) );
415  }
416  return colors;
417 }
418 
420 {
422 }
423 
425 
427 {
428  return -1;
429 }
430 
431 double QgsRandomColorRamp::value( int index ) const
432 {
433  Q_UNUSED( index )
434  return 0.0;
435 }
436 
437 QColor QgsRandomColorRamp::color( double value ) const
438 {
439  int minVal = 130;
440  int maxVal = 255;
441 
442  //if value is nan, then use last precalculated color
443  int colorIndex = ( !std::isnan( value ) ? value : 1 ) * ( mTotalColorCount - 1 );
444  if ( mTotalColorCount >= 1 && mPrecalculatedColors.length() > colorIndex )
445  {
446  //use precalculated hue
447  return mPrecalculatedColors.at( colorIndex );
448  }
449 
450  //can't use precalculated hues, use a totally random hue
451  int h = static_cast< int >( 360.0 * qrand() / ( RAND_MAX + 1.0 ) );
452  int s = ( qrand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
453  int v = ( qrand() % ( maxVal - minVal + 1 ) ) + minVal;
454  return QColor::fromHsv( h, s, v );
455 }
456 
457 void QgsRandomColorRamp::setTotalColorCount( const int colorCount )
458 {
459  //calculate colors in advance, so that we can ensure they are more visually distinct than pure random colors
460  mPrecalculatedColors.clear();
461  mTotalColorCount = colorCount;
462 
463  //This works OK for low color counts, but for > 10 or so colors there's still a good chance of
464  //similar colors being picked. TODO - investigate alternative "n-visually distinct color" routines
465 
466  //random offsets
467  double hueOffset = ( 360.0 * qrand() / ( RAND_MAX + 1.0 ) );
468 
469  //try to maximise difference between hues. this is not an ideal implementation, as constant steps
470  //through the hue wheel are not visually perceived as constant changes in hue
471  //(for instance, we are much more likely to get green hues than yellow hues)
472  double hueStep = 359.0 / colorCount;
473  double currentHue = hueOffset;
474 
475  //build up a list of colors
476  for ( int idx = 0; idx < colorCount; ++ idx )
477  {
478  int h = static_cast< int >( std::round( currentHue ) ) % 360;
479  int s = ( qrand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
480  int v = ( qrand() % ( DEFAULT_RANDOM_VAL_MAX - DEFAULT_RANDOM_VAL_MIN + 1 ) ) + DEFAULT_RANDOM_VAL_MIN;
481  mPrecalculatedColors << QColor::fromHsv( h, s, v );
482  currentHue += hueStep;
483  }
484 
485  //lastly, shuffle color list
486  std::random_shuffle( mPrecalculatedColors.begin(), mPrecalculatedColors.end() );
487 }
488 
490 {
492 }
493 
495 {
496  return new QgsRandomColorRamp();
497 }
498 
500 {
501  return QgsStringMap();
502 }
503 
505 
506 QgsColorBrewerColorRamp::QgsColorBrewerColorRamp( const QString &schemeName, int colors, bool inverted )
507  : mSchemeName( schemeName )
508  , mColors( colors )
509  , mInverted( inverted )
510 {
511  loadPalette();
512 }
513 
515 {
518  bool inverted = false;
519 
520  if ( props.contains( QStringLiteral( "schemeName" ) ) )
521  schemeName = props[QStringLiteral( "schemeName" )];
522  if ( props.contains( QStringLiteral( "colors" ) ) )
523  colors = props[QStringLiteral( "colors" )].toInt();
524  if ( props.contains( QStringLiteral( "inverted" ) ) )
525  inverted = props[QStringLiteral( "inverted" )].toInt();
526 
527  return new QgsColorBrewerColorRamp( schemeName, colors, inverted );
528 }
529 
531 {
533 
534  if ( mInverted )
535  {
536  QList<QColor> tmpPalette;
537 
538  for ( int k = mPalette.size() - 1; k >= 0; k-- )
539  {
540  tmpPalette << mPalette.at( k );
541  }
542  mPalette = tmpPalette;
543  }
544 }
545 
547 {
549 }
550 
552 {
553  return QgsColorBrewerPalette::listSchemeVariants( schemeName );
554 }
555 
556 double QgsColorBrewerColorRamp::value( int index ) const
557 {
558  if ( mPalette.empty() )
559  return 0;
560  return static_cast< double >( index ) / ( mPalette.size() - 1 );
561 }
562 
563 QColor QgsColorBrewerColorRamp::color( double value ) const
564 {
565  if ( mPalette.isEmpty() || value < 0 || value > 1 || std::isnan( value ) )
566  return QColor();
567 
568  int paletteEntry = static_cast< int >( value * mPalette.count() );
569  if ( paletteEntry >= mPalette.count() )
570  paletteEntry = mPalette.count() - 1;
571  return mPalette.at( paletteEntry );
572 }
573 
575 {
576  mInverted = !mInverted;
577  loadPalette();
578 }
579 
581 {
583 }
584 
586 {
587  QgsStringMap map;
588  map[QStringLiteral( "schemeName" )] = mSchemeName;
589  map[QStringLiteral( "colors" )] = QString::number( mColors );
590  map[QStringLiteral( "inverted" )] = QString::number( mInverted );
591  map[QStringLiteral( "rampType" )] = type();
592  return map;
593 }
594 
595 
597 
598 
599 QgsCptCityColorRamp::QgsCptCityColorRamp( const QString &schemeName, const QString &variantName,
600  bool inverted, bool doLoadFile )
602  , mSchemeName( schemeName )
603  , mVariantName( variantName )
604  , mInverted( inverted )
605 {
606  // TODO replace this with hard-coded data in the default case
607  // don't load file if variant is missing
608  if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
609  loadFile();
610 }
611 
613  const QString &variantName, bool inverted, bool doLoadFile )
615  , mSchemeName( schemeName )
616  , mVariantName( variantName )
617  , mVariantList( variantList )
618  , mInverted( inverted )
619 {
621 
622  // TODO replace this with hard-coded data in the default case
623  // don't load file if variant is missing
624  if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
625  loadFile();
626 }
627 
629 {
632  bool inverted = false;
633 
634  if ( props.contains( QStringLiteral( "schemeName" ) ) )
635  schemeName = props[QStringLiteral( "schemeName" )];
636  if ( props.contains( QStringLiteral( "variantName" ) ) )
637  variantName = props[QStringLiteral( "variantName" )];
638  if ( props.contains( QStringLiteral( "inverted" ) ) )
639  inverted = props[QStringLiteral( "inverted" )].toInt();
640 
641  return new QgsCptCityColorRamp( schemeName, variantName, inverted );
642 }
643 
645 {
647 }
648 
650 {
651  mInverted = !mInverted;
653 }
654 
656 {
657  QgsCptCityColorRamp *ramp = new QgsCptCityColorRamp( QString(), QString(), mInverted, false );
658  ramp->copy( this );
659  return ramp;
660 }
661 
663 {
664  if ( ! other )
665  return;
666  mColor1 = other->color1();
667  mColor2 = other->color2();
668  mDiscrete = other->isDiscrete();
669  mStops = other->stops();
670  mSchemeName = other->mSchemeName;
671  mVariantName = other->mVariantName;
672  mVariantList = other->mVariantList;
673  mFileLoaded = other->mFileLoaded;
674  mInverted = other->mInverted;
675 }
676 
678 {
679  QgsGradientColorRamp *ramp =
681  // add author and copyright information
682  // TODO also add COPYING.xml file/link?
684  info[QStringLiteral( "cpt-city-gradient" )] = "<cpt-city>/" + mSchemeName + mVariantName + ".svg";
685  QString copyingFilename = copyingFileName();
686  copyingFilename.remove( QgsCptCityArchive::defaultBaseDir() );
687  info[QStringLiteral( "cpt-city-license" )] = "<cpt-city>" + copyingFilename;
688  ramp->setInfo( info );
689  return ramp;
690 }
691 
692 
694 {
695  QgsStringMap map;
696  map[QStringLiteral( "schemeName" )] = mSchemeName;
697  map[QStringLiteral( "variantName" )] = mVariantName;
698  map[QStringLiteral( "inverted" )] = QString::number( mInverted );
699  map[QStringLiteral( "rampType" )] = type();
700  return map;
701 }
702 
703 
705 {
706  if ( mSchemeName.isEmpty() )
707  return QString();
708  else
709  {
710  return QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mSchemeName + mVariantName + ".svg";
711  }
712 }
713 
715 {
716  return QgsCptCityArchive::findFileName( QStringLiteral( "COPYING.xml" ), QFileInfo( fileName() ).dir().path(),
718 }
719 
721 {
722  return QgsCptCityArchive::findFileName( QStringLiteral( "DESC.xml" ), QFileInfo( fileName() ).dir().path(),
724 }
725 
727 {
729 }
730 
732 {
733  if ( mFileLoaded )
734  {
735  QgsDebugMsg( "File already loaded for " + mSchemeName + mVariantName );
736  return true;
737  }
738 
739  // get filename
740  QString filename = fileName();
741  if ( filename.isNull() )
742  {
743  QgsDebugMsg( "Couldn't get fileName() for " + mSchemeName + mVariantName );
744  return false;
745  }
746 
747  QgsDebugMsg( QStringLiteral( "filename= %1 loaded=%2" ).arg( filename ).arg( mFileLoaded ) );
748 
749  // get color ramp from svg file
750  QMap< double, QPair<QColor, QColor> > colorMap =
752 
753  // add colors to palette
754  mFileLoaded = false;
755  mStops.clear();
756  QMap<double, QPair<QColor, QColor> >::const_iterator it, prev;
757  // first detect if file is gradient is continuous or discrete
758  // discrete: stop contains 2 colors and first color is identical to previous second
759  // multi: stop contains 2 colors and no relation with previous stop
760  mDiscrete = false;
761  mMultiStops = false;
762  it = prev = colorMap.constBegin();
763  while ( it != colorMap.constEnd() )
764  {
765  // look for stops that contain multiple values
766  if ( it != colorMap.constBegin() && ( it.value().first != it.value().second ) )
767  {
768  if ( it.value().first == prev.value().second )
769  {
770  mDiscrete = true;
771  break;
772  }
773  else
774  {
775  mMultiStops = true;
776  break;
777  }
778  }
779  prev = it;
780  ++it;
781  }
782 
783  // fill all stops
784  it = prev = colorMap.constBegin();
785  while ( it != colorMap.constEnd() )
786  {
787  if ( mDiscrete )
788  {
789  // mPalette << qMakePair( it.key(), it.value().second );
790  mStops.append( QgsGradientStop( it.key(), it.value().second ) );
791  }
792  else
793  {
794  // mPalette << qMakePair( it.key(), it.value().first );
795  mStops.append( QgsGradientStop( it.key(), it.value().first ) );
796  if ( ( mMultiStops ) &&
797  ( it.key() != 0.0 && it.key() != 1.0 ) )
798  {
799  mStops.append( QgsGradientStop( it.key(), it.value().second ) );
800  }
801  }
802  prev = it;
803  ++it;
804  }
805 
806  // remove first and last items (mColor1 and mColor2)
807  if ( ! mStops.isEmpty() && mStops.at( 0 ).offset == 0.0 )
808  mColor1 = mStops.takeFirst().color;
809  if ( ! mStops.isEmpty() && mStops.last().offset == 1.0 )
810  mColor2 = mStops.takeLast().color;
811 
812  if ( mInverted )
813  {
815  }
816 
817  mFileLoaded = true;
818  return true;
819 }
820 
821 
822 //
823 // QgsPresetColorRamp
824 //
825 
827 {
828  const auto constColors = colors;
829  for ( const QColor &color : constColors )
830  {
831  mColors << qMakePair( color, color.name() );
832  }
833  // need at least one color
834  if ( mColors.isEmpty() )
835  mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
836 }
837 
839  : mColors( colors )
840 {
841  // need at least one color
842  if ( mColors.isEmpty() )
843  mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
844 }
845 
847 {
849 
850  int i = 0;
851  QString colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() );
852  QString colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() );
853  while ( !colorString.isEmpty() )
854  {
855  colors << qMakePair( QgsSymbolLayerUtils::decodeColor( colorString ), colorName );
856  i++;
857  colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() );
858  colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() );
859  }
860 
861  return new QgsPresetSchemeColorRamp( colors );
862 }
863 
864 QList<QColor> QgsPresetSchemeColorRamp::colors() const
865 {
866  QList< QColor > l;
867  l.reserve( mColors.count() );
868  for ( int i = 0; i < mColors.count(); ++i )
869  {
870  l << mColors.at( i ).first;
871  }
872  return l;
873 }
874 
875 double QgsPresetSchemeColorRamp::value( int index ) const
876 {
877  if ( mColors.empty() )
878  return 0;
879  return static_cast< double >( index ) / ( mColors.size() - 1 );
880 }
881 
882 QColor QgsPresetSchemeColorRamp::color( double value ) const
883 {
884  if ( value < 0 || value > 1 )
885  return QColor();
886 
887  int colorCnt = mColors.count();
888  int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
889 
890  if ( colorIdx >= 0 && colorIdx < colorCnt )
891  return mColors.at( colorIdx ).first;
892 
893  return QColor();
894 }
895 
897 {
899 }
900 
902 {
903  QgsNamedColorList tmpColors;
904 
905  for ( int k = mColors.size() - 1; k >= 0; k-- )
906  {
907  tmpColors << mColors.at( k );
908  }
909  mColors = tmpColors;
910 }
911 
913 {
914  return new QgsPresetSchemeColorRamp( *this );
915 }
916 
918 {
919  QgsStringMap props;
920  for ( int i = 0; i < mColors.count(); ++i )
921  {
922  props.insert( QStringLiteral( "preset_color_%1" ).arg( i ), QgsSymbolLayerUtils::encodeColor( mColors.at( i ).first ) );
923  props.insert( QStringLiteral( "preset_color_name_%1" ).arg( i ), mColors.at( i ).second );
924  }
925  props[QStringLiteral( "rampType" )] = type();
926  return props;
927 }
928 
930 {
931  return mColors.count();
932 }
933 
935 {
936  return mColors;
937 }
938 
939 QList<QPair<QString, QString> > QgsColorRamp::rampTypes()
940 {
941  return QList<QPair<QString, QString> >
942  {
943  qMakePair( QgsGradientColorRamp::typeString(), QObject::tr( "Gradient" ) ),
944  qMakePair( QgsPresetSchemeColorRamp::typeString(), QObject::tr( "Color Presets" ) ),
945  qMakePair( QgsLimitedRandomColorRamp::typeString(), QObject::tr( "Random" ) ),
946  qMakePair( QgsCptCityColorRamp::typeString(), QObject::tr( "Catalog: cpt-city" ) ),
947  qMakePair( QgsColorBrewerColorRamp::typeString(), QObject::tr( "Catalog: ColorBrewer" ) )
948  };
949 }
QString fileName() const
int satMax() const
Returns the maximum saturation for generated colors.
Definition: qgscolorramp.h:381
static QList< int > listSchemeVariants(const QString &schemeName)
Returns a list of the valid variants (numbers of colors) for a specified color brewer scheme name...
static QString typeString()
Returns the string identifier for QgsLimitedRandomColorRamp.
Definition: qgscolorramp.h:337
#define DEFAULT_RANDOM_SAT_MAX
Definition: qgscolorramp.h:294
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:292
#define DEFAULT_RANDOM_HUE_MAX
Definition: qgscolorramp.h:290
Represents a color stop within a QgsGradientColorRamp color ramp.
Definition: qgscolorramp.h:112
QStringList mVariantList
Definition: qgscolorramp.h:748
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:198
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:316
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:723
static QString typeString()
Returns the string identifier for QgsCptCityColorRamp.
Definition: qgscolorramp.h:711
QgsStringMap properties() const override
Returns a string map containing all the color ramp&#39;s properties.
#define DEFAULT_RANDOM_HUE_MIN
Definition: qgscolorramp.h:289
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< QPair< QString, QString > > rampTypes()
Returns a list of available ramp types, where the first value in each item is the QgsColorRamp::type(...
static QList< int > listSchemeVariants(const QString &schemeName)
QString descFileName() const
#define DEFAULT_COLORBREWER_SCHEMENAME
Definition: qgscolorramp.h:568
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:759
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Definition: qgscolorramp.h:138
QgsStringMap properties() const override
Returns a string map containing all the color ramp&#39;s properties.
QList< QColor > mColors
Definition: qgscolorramp.h:444
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:375
int hueMin() const
Returns the minimum hue for generated colors.
Definition: qgscolorramp.h:363
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.
int colors() const
Returns the number of colors in the ramp.
Definition: qgscolorramp.h:625
Constrained random color ramp, which returns random colors based on preset parameters.
Definition: qgscolorramp.h:302
void invert() override
Inverts the ordering of the color ramp.
#define DEFAULT_RANDOM_VAL_MIN
Definition: qgscolorramp.h:291
static QString typeString()
Returns the string identifier for QgsColorBrewerColorRamp.
Definition: qgscolorramp.h:607
QgsStringMap info() const
Returns any additional info attached to the gradient ramp (e.g., authorship notes) ...
Definition: qgscolorramp.h:260
double offset
Relative positional offset, between 0 and 1.
Definition: qgscolorramp.h:127
QgsPresetSchemeColorRamp * clone() const override
Creates a clone of the color ramp.
QString type() const override
Returns a string representing the color ramp type.
bool stopLessThan(const QgsGradientStop &s1, const QgsGradientStop &s2)
int hueMax() const
Returns the maximum hue for generated colors.
Definition: qgscolorramp.h:369
QgsStringMap properties() const override
Returns a string map containing all the color ramp&#39;s properties.
static QString typeString()
Returns the string identifier for QgsPresetSchemeColorRamp.
Definition: qgscolorramp.h:550
int count() const override
Returns number of defined colors, or -1 if undefined.
Definition: qgscolorramp.h:342
#define DEFAULT_CPTCITY_SCHEMENAME
Definition: qgscolorramp.h:669
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:221
#define DEFAULT_GRADIENT_COLOR1
Definition: qgscolorramp.h:140
A scheme based color ramp consisting of a list of predefined colors.
Definition: qgscolorramp.h:505
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:254
Totally random color ramp.
Definition: qgscolorramp.h:454
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.
static QList< QColor > listSchemeColors(const QString &schemeName, int colors)
QString schemeName() const
Returns the name of the color brewer color scheme.
Definition: qgscolorramp.h:619
static QgsColorRamp * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsColorRamp from a map of properties.
#define DEFAULT_RANDOM_COUNT
Definition: qgscolorramp.h:288
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:609
#define DEFAULT_RANDOM_SAT_MIN
Definition: qgscolorramp.h:293
QList< QColor > mPalette
Definition: qgscolorramp.h:664
QString type() const override
Returns a string representing the color ramp type.
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:267
QgsCptCityColorRamp * clone() const override
Creates a clone of the color ramp.
static QString typeString()
Returns the string identifier for QgsGradientColorRamp.
Definition: qgscolorramp.h:179
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:577
int valMin() const
Returns the minimum value for generated colors.
Definition: qgscolorramp.h:387
#define DEFAULT_GRADIENT_COLOR2
Definition: qgscolorramp.h:141
static QString typeString()
Returns the string identifier for QgsRandomColorRamp.
Definition: qgscolorramp.h:483
QgsGradientStopsList mStops
Definition: qgscolorramp.h:282
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:393
QColor color(double value) const override
Returns the color corresponding to a specified value.
QgsStringMap mInfo
Definition: qgscolorramp.h:283
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:150
QStringList variantList() const
Definition: qgscolorramp.h:725
QgsPresetSchemeColorRamp(const QList< QColor > &colors=QList< QColor >())
Constructor for QgsPresetSchemeColorRamp.
#define DEFAULT_CPTCITY_VARIANTNAME
Definition: qgscolorramp.h:670
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:191
QString variantName() const
Definition: qgscolorramp.h:724
static QString findFileName(const QString &target, const QString &startDir, const QString &baseDir)
#define DEFAULT_COLORBREWER_COLORS
Definition: qgscolorramp.h:569
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:170