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