QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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  if ( std::isnan( value ) )
451  {
452  value = 1.0;
453  }
454  // Caller has converted an index into a value in [0.0, 1.0]
455  // by doing "index / (mTotalColorCount - 1)"; retrieve the original index.
456  int colorIndex = std::round( value * ( mTotalColorCount - 1 ) );
457  if ( mTotalColorCount >= 1 && mPrecalculatedColors.length() > colorIndex )
458  {
459  //use precalculated hue
460  return mPrecalculatedColors.at( colorIndex );
461  }
462 
463  //can't use precalculated hues, use a totally random hue
464  int h = static_cast< int >( 360.0 * std::rand() / ( RAND_MAX + 1.0 ) );
465  int s = ( std::rand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
466  int v = ( std::rand() % ( maxVal - minVal + 1 ) ) + minVal;
467  return QColor::fromHsv( h, s, v );
468 }
469 
470 void QgsRandomColorRamp::setTotalColorCount( const int colorCount )
471 {
472  //calculate colors in advance, so that we can ensure they are more visually distinct than pure random colors
473  mPrecalculatedColors.clear();
474  mTotalColorCount = colorCount;
475 
476  //This works OK for low color counts, but for > 10 or so colors there's still a good chance of
477  //similar colors being picked. TODO - investigate alternative "n-visually distinct color" routines
478 
479  //random offsets
480  double hueOffset = ( 360.0 * std::rand() / ( RAND_MAX + 1.0 ) );
481 
482  //try to maximise difference between hues. this is not an ideal implementation, as constant steps
483  //through the hue wheel are not visually perceived as constant changes in hue
484  //(for instance, we are much more likely to get green hues than yellow hues)
485  double hueStep = 359.0 / colorCount;
486  double currentHue = hueOffset;
487 
488  //build up a list of colors
489  for ( int idx = 0; idx < colorCount; ++ idx )
490  {
491  int h = static_cast< int >( std::round( currentHue ) ) % 360;
492  int s = ( std::rand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
493  int v = ( std::rand() % ( DEFAULT_RANDOM_VAL_MAX - DEFAULT_RANDOM_VAL_MIN + 1 ) ) + DEFAULT_RANDOM_VAL_MIN;
494  mPrecalculatedColors << QColor::fromHsv( h, s, v );
495  currentHue += hueStep;
496  }
497 
498  //lastly, shuffle color list
499  std::random_device rd;
500  std::mt19937 g( rd() );
501  std::shuffle( mPrecalculatedColors.begin(), mPrecalculatedColors.end(), g );
502 }
503 
505 {
507 }
508 
510 {
511  return new QgsRandomColorRamp();
512 }
513 
515 {
516  return QVariantMap();
517 }
518 
520 
521 QgsColorBrewerColorRamp::QgsColorBrewerColorRamp( const QString &schemeName, int colors, bool inverted )
522  : mSchemeName( schemeName )
523  , mColors( colors )
524  , mInverted( inverted )
525 {
526  loadPalette();
527 }
528 
529 QgsColorRamp *QgsColorBrewerColorRamp::create( const QVariantMap &props )
530 {
533  bool inverted = false;
534 
535  if ( props.contains( QStringLiteral( "schemeName" ) ) )
536  schemeName = props[QStringLiteral( "schemeName" )].toString();
537  if ( props.contains( QStringLiteral( "colors" ) ) )
538  colors = props[QStringLiteral( "colors" )].toInt();
539  if ( props.contains( QStringLiteral( "inverted" ) ) )
540  inverted = props[QStringLiteral( "inverted" )].toInt();
541 
542  return new QgsColorBrewerColorRamp( schemeName, colors, inverted );
543 }
544 
546 {
548 
549  if ( mInverted )
550  {
551  QList<QColor> tmpPalette;
552 
553  for ( int k = mPalette.size() - 1; k >= 0; k-- )
554  {
555  tmpPalette << mPalette.at( k );
556  }
557  mPalette = tmpPalette;
558  }
559 }
560 
562 {
564 }
565 
566 QList<int> QgsColorBrewerColorRamp::listSchemeVariants( const QString &schemeName )
567 {
569 }
570 
571 double QgsColorBrewerColorRamp::value( int index ) const
572 {
573  if ( mPalette.empty() )
574  return 0;
575  return static_cast< double >( index ) / ( mPalette.size() - 1 );
576 }
577 
578 QColor QgsColorBrewerColorRamp::color( double value ) const
579 {
580  if ( mPalette.isEmpty() || value < 0 || value > 1 || std::isnan( value ) )
581  return QColor();
582 
583  int paletteEntry = static_cast< int >( value * mPalette.count() );
584  if ( paletteEntry >= mPalette.count() )
585  paletteEntry = mPalette.count() - 1;
586  return mPalette.at( paletteEntry );
587 }
588 
590 {
591  mInverted = !mInverted;
592  loadPalette();
593 }
594 
596 {
598 }
599 
601 {
602  QVariantMap map;
603  map[QStringLiteral( "schemeName" )] = mSchemeName;
604  map[QStringLiteral( "colors" )] = QString::number( mColors );
605  map[QStringLiteral( "inverted" )] = QString::number( mInverted );
606  map[QStringLiteral( "rampType" )] = type();
607  return map;
608 }
609 
610 
612 
613 
614 QgsCptCityColorRamp::QgsCptCityColorRamp( const QString &schemeName, const QString &variantName,
615  bool inverted, bool doLoadFile )
617  , mSchemeName( schemeName )
618  , mVariantName( variantName )
619  , mInverted( inverted )
620 {
621  // TODO replace this with hard-coded data in the default case
622  // don't load file if variant is missing
623  if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
624  loadFile();
625 }
626 
627 QgsCptCityColorRamp::QgsCptCityColorRamp( const QString &schemeName, const QStringList &variantList,
628  const QString &variantName, bool inverted, bool doLoadFile )
630  , mSchemeName( schemeName )
631  , mVariantName( variantName )
632  , mVariantList( variantList )
633  , mInverted( inverted )
634 {
636 
637  // TODO replace this with hard-coded data in the default case
638  // don't load file if variant is missing
639  if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
640  loadFile();
641 }
642 
643 QgsColorRamp *QgsCptCityColorRamp::create( const QVariantMap &props )
644 {
647  bool inverted = false;
648 
649  if ( props.contains( QStringLiteral( "schemeName" ) ) )
650  schemeName = props[QStringLiteral( "schemeName" )].toString();
651  if ( props.contains( QStringLiteral( "variantName" ) ) )
652  variantName = props[QStringLiteral( "variantName" )].toString();
653  if ( props.contains( QStringLiteral( "inverted" ) ) )
654  inverted = props[QStringLiteral( "inverted" )].toInt();
655 
656  return new QgsCptCityColorRamp( schemeName, variantName, inverted );
657 }
658 
660 {
662 }
663 
665 {
666  mInverted = !mInverted;
668 }
669 
671 {
672  QgsCptCityColorRamp *ramp = new QgsCptCityColorRamp( QString(), QString(), mInverted, false );
673  ramp->copy( this );
674  return ramp;
675 }
676 
678 {
679  if ( ! other )
680  return;
681  mColor1 = other->color1();
682  mColor2 = other->color2();
683  mDiscrete = other->isDiscrete();
684  mStops = other->stops();
685  mSchemeName = other->mSchemeName;
686  mVariantName = other->mVariantName;
687  mVariantList = other->mVariantList;
688  mFileLoaded = other->mFileLoaded;
689  mInverted = other->mInverted;
690 }
691 
693 {
694  QgsGradientColorRamp *ramp =
696  // add author and copyright information
697  // TODO also add COPYING.xml file/link?
699  info[QStringLiteral( "cpt-city-gradient" )] = "<cpt-city>/" + mSchemeName + mVariantName + ".svg";
700  QString copyingFilename = copyingFileName();
701  copyingFilename.remove( QgsCptCityArchive::defaultBaseDir() );
702  info[QStringLiteral( "cpt-city-license" )] = "<cpt-city>" + copyingFilename;
703  ramp->setInfo( info );
704  return ramp;
705 }
706 
707 
709 {
710  QVariantMap map;
711  map[QStringLiteral( "schemeName" )] = mSchemeName;
712  map[QStringLiteral( "variantName" )] = mVariantName;
713  map[QStringLiteral( "inverted" )] = QString::number( mInverted );
714  map[QStringLiteral( "rampType" )] = type();
715  return map;
716 }
717 
718 
720 {
721  if ( mSchemeName.isEmpty() )
722  return QString();
723  else
724  {
725  return QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mSchemeName + mVariantName + ".svg";
726  }
727 }
728 
730 {
731  return QgsCptCityArchive::findFileName( QStringLiteral( "COPYING.xml" ), QFileInfo( fileName() ).dir().path(),
733 }
734 
736 {
737  return QgsCptCityArchive::findFileName( QStringLiteral( "DESC.xml" ), QFileInfo( fileName() ).dir().path(),
739 }
740 
742 {
744 }
745 
747 {
748  if ( mFileLoaded )
749  {
750  QgsDebugMsg( "File already loaded for " + mSchemeName + mVariantName );
751  return true;
752  }
753 
754  // get filename
755  QString filename = fileName();
756  if ( filename.isNull() )
757  {
758  QgsDebugMsg( "Couldn't get fileName() for " + mSchemeName + mVariantName );
759  return false;
760  }
761 
762  QgsDebugMsg( QStringLiteral( "filename= %1 loaded=%2" ).arg( filename ).arg( mFileLoaded ) );
763 
764  // get color ramp from svg file
765  QMap< double, QPair<QColor, QColor> > colorMap =
767 
768  // add colors to palette
769  mFileLoaded = false;
770  mStops.clear();
771  QMap<double, QPair<QColor, QColor> >::const_iterator it, prev;
772  // first detect if file is gradient is continuous or discrete
773  // discrete: stop contains 2 colors and first color is identical to previous second
774  // multi: stop contains 2 colors and no relation with previous stop
775  mDiscrete = false;
776  mMultiStops = false;
777  it = prev = colorMap.constBegin();
778  while ( it != colorMap.constEnd() )
779  {
780  // look for stops that contain multiple values
781  if ( it != colorMap.constBegin() && ( it.value().first != it.value().second ) )
782  {
783  if ( it.value().first == prev.value().second )
784  {
785  mDiscrete = true;
786  break;
787  }
788  else
789  {
790  mMultiStops = true;
791  break;
792  }
793  }
794  prev = it;
795  ++it;
796  }
797 
798  // fill all stops
799  it = prev = colorMap.constBegin();
800  while ( it != colorMap.constEnd() )
801  {
802  if ( mDiscrete )
803  {
804  // mPalette << qMakePair( it.key(), it.value().second );
805  mStops.append( QgsGradientStop( it.key(), it.value().second ) );
806  }
807  else
808  {
809  // mPalette << qMakePair( it.key(), it.value().first );
810  mStops.append( QgsGradientStop( it.key(), it.value().first ) );
811  if ( ( mMultiStops ) &&
812  ( it.key() != 0.0 && it.key() != 1.0 ) )
813  {
814  mStops.append( QgsGradientStop( it.key(), it.value().second ) );
815  }
816  }
817  prev = it;
818  ++it;
819  }
820 
821  // remove first and last items (mColor1 and mColor2)
822  if ( ! mStops.isEmpty() && mStops.at( 0 ).offset == 0.0 )
823  mColor1 = mStops.takeFirst().color;
824  if ( ! mStops.isEmpty() && mStops.last().offset == 1.0 )
825  mColor2 = mStops.takeLast().color;
826 
827  if ( mInverted )
828  {
830  }
831 
832  mFileLoaded = true;
833  return true;
834 }
835 
836 
837 //
838 // QgsPresetColorRamp
839 //
840 
842 {
843  const auto constColors = colors;
844  for ( const QColor &color : constColors )
845  {
846  mColors << qMakePair( color, color.name() );
847  }
848  // need at least one color
849  if ( mColors.isEmpty() )
850  mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
851 }
852 
854  : mColors( colors )
855 {
856  // need at least one color
857  if ( mColors.isEmpty() )
858  mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
859 }
860 
861 QgsColorRamp *QgsPresetSchemeColorRamp::create( const QVariantMap &properties )
862 {
864 
865  int i = 0;
866  QString colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() ).toString();
867  QString colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() ).toString();
868  while ( !colorString.isEmpty() )
869  {
870  colors << qMakePair( QgsSymbolLayerUtils::decodeColor( colorString ), colorName );
871  i++;
872  colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() ).toString();
873  colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() ).toString();
874  }
875 
876  return new QgsPresetSchemeColorRamp( colors );
877 }
878 
879 QList<QColor> QgsPresetSchemeColorRamp::colors() const
880 {
881  QList< QColor > l;
882  l.reserve( mColors.count() );
883  for ( int i = 0; i < mColors.count(); ++i )
884  {
885  l << mColors.at( i ).first;
886  }
887  return l;
888 }
889 
890 double QgsPresetSchemeColorRamp::value( int index ) const
891 {
892  if ( mColors.empty() )
893  return 0;
894  return static_cast< double >( index ) / ( mColors.size() - 1 );
895 }
896 
897 QColor QgsPresetSchemeColorRamp::color( double value ) const
898 {
899  if ( value < 0 || value > 1 )
900  return QColor();
901 
902  int colorCnt = mColors.count();
903  int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
904 
905  if ( colorIdx >= 0 && colorIdx < colorCnt )
906  return mColors.at( colorIdx ).first;
907 
908  return QColor();
909 }
910 
912 {
914 }
915 
917 {
918  QgsNamedColorList tmpColors;
919 
920  for ( int k = mColors.size() - 1; k >= 0; k-- )
921  {
922  tmpColors << mColors.at( k );
923  }
924  mColors = tmpColors;
925 }
926 
928 {
929  return new QgsPresetSchemeColorRamp( *this );
930 }
931 
933 {
934  QVariantMap props;
935  for ( int i = 0; i < mColors.count(); ++i )
936  {
937  props.insert( QStringLiteral( "preset_color_%1" ).arg( i ), QgsSymbolLayerUtils::encodeColor( mColors.at( i ).first ) );
938  props.insert( QStringLiteral( "preset_color_name_%1" ).arg( i ), mColors.at( i ).second );
939  }
940  props[QStringLiteral( "rampType" )] = type();
941  return props;
942 }
943 
945 {
946  return mColors.count();
947 }
948 
950 {
951  return mColors;
952 }
953 
954 QList<QPair<QString, QString> > QgsColorRamp::rampTypes()
955 {
956  return QList<QPair<QString, QString> >
957  {
958  qMakePair( QgsGradientColorRamp::typeString(), QObject::tr( "Gradient" ) ),
959  qMakePair( QgsPresetSchemeColorRamp::typeString(), QObject::tr( "Color Presets" ) ),
960  qMakePair( QgsLimitedRandomColorRamp::typeString(), QObject::tr( "Random" ) ),
961  qMakePair( QgsCptCityColorRamp::typeString(), QObject::tr( "Catalog: cpt-city" ) ),
962  qMakePair( QgsColorBrewerColorRamp::typeString(), QObject::tr( "Catalog: ColorBrewer" ) )
963  };
964 }
Color ramp utilising "Color Brewer" preset color schemes.
Definition: qgscolorramp.h:580
QList< QColor > mPalette
Definition: qgscolorramp.h:666
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:609
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:611
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:621
int colors() const
Returns the number of colors in the ramp.
Definition: qgscolorramp.h:627
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:751
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:728
QString fileName() const
void copy(const QgsCptCityColorRamp *other)
static QString typeString()
Returns the string identifier for QgsCptCityColorRamp.
Definition: qgscolorramp.h:714
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:726
QString variantName() const
Definition: qgscolorramp.h:727
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:153
QgsGradientStopsList mStops
Definition: qgscolorramp.h:284
void setInfo(const QgsStringMap &info)
Sets additional info to attach to the gradient ramp (e.g., authorship notes)
Definition: qgscolorramp.h:269
QgsStringMap mInfo
Definition: qgscolorramp.h:285
bool isDiscrete() const
Returns true if the gradient is using discrete interpolation, rather than smoothly interpolating betw...
Definition: qgscolorramp.h:223
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:262
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:181
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:193
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:256
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:200
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:305
static QString typeString()
Returns the string identifier for QgsLimitedRandomColorRamp.
Definition: qgscolorramp.h:339
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:344
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:395
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:383
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:371
int hueMin() const
Returns the minimum hue for generated colors.
Definition: qgscolorramp.h:365
QList< QColor > mColors
Definition: qgscolorramp.h:446
int valMin() const
Returns the minimum value for generated colors.
Definition: qgscolorramp.h:389
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:377
A scheme based color ramp consisting of a list of predefined colors.
Definition: qgscolorramp.h:508
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:552
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:457
double value(int index) const override
Returns relative value between [0,1] of color at specified index.
QList< QColor > mPrecalculatedColors
Definition: qgscolorramp.h:496
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:485
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:1246
QMap< QString, QString > QgsStringMap
Definition: qgis.h:1703
bool stopLessThan(const QgsGradientStop &s1, const QgsGradientStop &s2)
#define DEFAULT_COLORBREWER_COLORS
Definition: qgscolorramp.h:571
#define DEFAULT_COLORBREWER_SCHEMENAME
Definition: qgscolorramp.h:570
#define DEFAULT_RANDOM_HUE_MAX
Definition: qgscolorramp.h:292
#define DEFAULT_CPTCITY_SCHEMENAME
Definition: qgscolorramp.h:671
#define DEFAULT_RANDOM_HUE_MIN
Definition: qgscolorramp.h:291
#define DEFAULT_RANDOM_COUNT
Definition: qgscolorramp.h:290
#define DEFAULT_RANDOM_SAT_MAX
Definition: qgscolorramp.h:296
#define DEFAULT_RANDOM_SAT_MIN
Definition: qgscolorramp.h:295
#define DEFAULT_CPTCITY_VARIANTNAME
Definition: qgscolorramp.h:672
#define DEFAULT_GRADIENT_COLOR1
Definition: qgscolorramp.h:142
#define DEFAULT_RANDOM_VAL_MIN
Definition: qgscolorramp.h:293
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
Definition: qgscolorramp.h:138
#define DEFAULT_GRADIENT_COLOR2
Definition: qgscolorramp.h:143
#define DEFAULT_RANDOM_VAL_MAX
Definition: qgscolorramp.h:294
#define QgsDebugMsg(str)
Definition: qgslogger.h:38