QGIS API Documentation 3.29.0-Master (006c3c0232)
qgscolorrampimpl.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscolorrampimpl.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 "qgscolorrampimpl.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
34static QColor _interpolateRgb( const QColor &c1, const QColor &c2, const double value, const Qgis::AngularDirection )
35{
36 if ( std::isnan( value ) )
37 return c2;
38
39 const qreal red1 = c1.redF();
40 const qreal red2 = c2.redF();
41 const qreal red = ( red1 + value * ( red2 - red1 ) );
42
43 const qreal green1 = c1.greenF();
44 const qreal green2 = c2.greenF();
45 const qreal green = ( green1 + value * ( green2 - green1 ) );
46
47 const qreal blue1 = c1.blueF();
48 const qreal blue2 = c2.blueF();
49 const qreal blue = ( blue1 + value * ( blue2 - blue1 ) );
50
51 const qreal alpha1 = c1.alphaF();
52 const qreal alpha2 = c2.alphaF();
53 const qreal alpha = ( alpha1 + value * ( alpha2 - alpha1 ) );
54
55 return QColor::fromRgbF( red, green, blue, alpha );
56}
57
58static QColor _interpolateHsv( const QColor &c1, const QColor &c2, const double value, const Qgis::AngularDirection direction )
59{
60 if ( std::isnan( value ) )
61 return c2;
62
63 qreal hue1 = c1.hsvHueF();
64 qreal hue2 = c2.hsvHueF();
65 qreal hue = 0;
66 if ( hue1 == -1 )
67 hue = hue2;
68 else if ( hue2 == -1 )
69 hue = hue1;
70 else
71 {
72 switch ( direction )
73 {
75 {
76 if ( hue1 < hue2 )
77 hue1 += 1;
78
79 hue = hue1 - value * ( hue1 - hue2 );
80 if ( hue < 0 )
81 hue += 1;
82 if ( hue > 1 )
83 hue -= 1;
84 break;
85 }
86
88 {
89 if ( hue2 < hue1 )
90 hue2 += 1;
91
92 hue = hue1 + value * ( hue2 - hue1 );
93 if ( hue > 1 )
94 hue -= 1;
95 break;
96 }
97 }
98 }
99
100 const qreal saturation1 = c1.hsvSaturationF();
101 const qreal saturation2 = c2.hsvSaturationF();
102 const qreal saturation = ( saturation1 + value * ( saturation2 - saturation1 ) );
103
104 const qreal value1 = c1.valueF();
105 const qreal value2 = c2.valueF();
106 const qreal valueOut = ( value1 + value * ( value2 - value1 ) );
107
108 const qreal alpha1 = c1.alphaF();
109 const qreal alpha2 = c2.alphaF();
110 const qreal alpha = ( alpha1 + value * ( alpha2 - alpha1 ) );
111
112 return QColor::fromHsvF( hue > 1 ? hue - 1 : hue, saturation, valueOut, alpha );
113}
114
115static QColor _interpolateHsl( const QColor &c1, const QColor &c2, const double value, const Qgis::AngularDirection direction )
116{
117 if ( std::isnan( value ) )
118 return c2;
119
120 qreal hue1 = c1.hslHueF();
121 qreal hue2 = c2.hslHueF();
122 qreal hue = 0;
123 if ( hue1 == -1 )
124 hue = hue2;
125 else if ( hue2 == -1 )
126 hue = hue1;
127 else
128 {
129 switch ( direction )
130 {
132 {
133 if ( hue1 < hue2 )
134 hue1 += 1;
135
136 hue = hue1 - value * ( hue1 - hue2 );
137 if ( hue < 0 )
138 hue += 1;
139 if ( hue > 1 )
140 hue -= 1;
141 break;
142 }
143
145 {
146 if ( hue2 < hue1 )
147 hue2 += 1;
148
149 hue = hue1 + value * ( hue2 - hue1 );
150 if ( hue > 1 )
151 hue -= 1;
152 break;
153 }
154 }
155 }
156
157 const qreal saturation1 = c1.hslSaturationF();
158 const qreal saturation2 = c2.hslSaturationF();
159 const qreal saturation = ( saturation1 + value * ( saturation2 - saturation1 ) );
160
161 const qreal lightness1 = c1.lightnessF();
162 const qreal lightness2 = c2.lightnessF();
163 const qreal lightness = ( lightness1 + value * ( lightness2 - lightness1 ) );
164
165 const qreal alpha1 = c1.alphaF();
166 const qreal alpha2 = c2.alphaF();
167 const qreal alpha = ( alpha1 + value * ( alpha2 - alpha1 ) );
168
169 return QColor::fromHslF( hue > 1 ? hue - 1 : hue, saturation, lightness, alpha );
170}
171
173
174
175QgsGradientStop::QgsGradientStop( double offset, const QColor &color )
176 : offset( offset )
177 , color( color )
178 , mFunc( _interpolateRgb )
179{
180
181}
182
183void QgsGradientStop::setColorSpec( QColor::Spec spec )
184{
185 mColorSpec = spec;
186
187 switch ( mColorSpec )
188 {
189 case QColor::Rgb:
190 case QColor::Invalid:
191 case QColor::ExtendedRgb:
192 case QColor::Cmyk:
193 mFunc = _interpolateRgb;
194 break;
195 case QColor::Hsv:
196 mFunc = _interpolateHsv;
197 break;
198 case QColor::Hsl:
199 mFunc = _interpolateHsl;
200 break;
201 }
202}
203
204QgsGradientColorRamp::QgsGradientColorRamp( const QColor &color1, const QColor &color2,
205 bool discrete, const QgsGradientStopsList &stops )
206 : mColor1( color1 )
207 , mColor2( color2 )
208 , mDiscrete( discrete )
209 , mStops( stops )
210 , mFunc( _interpolateRgb )
211{
212}
213
214QgsColorRamp *QgsGradientColorRamp::create( const QVariantMap &props )
215{
216 // color1 and color2
219 if ( props.contains( QStringLiteral( "color1" ) ) )
220 color1 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color1" )].toString() );
221 if ( props.contains( QStringLiteral( "color2" ) ) )
222 color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color2" )].toString() );
223
224 //stops
226 if ( props.contains( QStringLiteral( "stops" ) ) )
227 {
228 const auto constSplit = props[QStringLiteral( "stops" )].toString().split( ':' );
229 for ( const QString &stop : constSplit )
230 {
231 const QStringList parts = stop.split( ';' );
232 if ( parts.size() != 2 && parts.size() != 4 )
233 continue;
234
235 QColor c = QgsSymbolLayerUtils::decodeColor( parts.at( 1 ) );
236 stops.append( QgsGradientStop( parts.at( 0 ).toDouble(), c ) );
237
238 if ( parts.size() == 4 )
239 {
240 if ( parts.at( 2 ).compare( QLatin1String( "rgb" ) ) == 0 )
241 stops.last().setColorSpec( QColor::Spec::Rgb );
242 else if ( parts.at( 2 ).compare( QLatin1String( "hsv" ) ) == 0 )
243 stops.last().setColorSpec( QColor::Spec::Hsv );
244 else if ( parts.at( 2 ).compare( QLatin1String( "hsl" ) ) == 0 )
245 stops.last().setColorSpec( QColor::Spec::Hsl );
246
247 if ( parts.at( 3 ).compare( QLatin1String( "cw" ) ) == 0 )
248 stops.last().setDirection( Qgis::AngularDirection::Clockwise );
249 else if ( parts.at( 3 ).compare( QLatin1String( "ccw" ) ) == 0 )
250 stops.last().setDirection( Qgis::AngularDirection::CounterClockwise );
251 }
252 }
253 }
254
255 // discrete vs. continuous
256 bool discrete = false;
257 if ( props.contains( QStringLiteral( "discrete" ) ) )
258 {
259 if ( props[QStringLiteral( "discrete" )] == QLatin1String( "1" ) )
260 discrete = true;
261 }
262
263 // search for information keys starting with "info_"
265 for ( QVariantMap::const_iterator it = props.constBegin();
266 it != props.constEnd(); ++it )
267 {
268 if ( it.key().startsWith( QLatin1String( "info_" ) ) )
269 info[ it.key().mid( 5 )] = it.value().toString();
270 }
271
273 r->setInfo( info );
274
275 if ( props.contains( QStringLiteral( "spec" ) ) )
276 {
277 const QString spec = props.value( QStringLiteral( "spec" ) ).toString().trimmed();
278 if ( spec.compare( QLatin1String( "rgb" ) ) == 0 )
279 r->setColorSpec( QColor::Spec::Rgb );
280 else if ( spec.compare( QLatin1String( "hsv" ) ) == 0 )
281 r->setColorSpec( QColor::Spec::Hsv );
282 else if ( spec.compare( QLatin1String( "hsl" ) ) == 0 )
283 r->setColorSpec( QColor::Spec::Hsl );
284 }
285
286 if ( props.contains( QStringLiteral( "direction" ) ) )
287 {
288 const QString direction = props.value( QStringLiteral( "direction" ) ).toString().trimmed();
289 if ( direction.compare( QLatin1String( "ccw" ) ) == 0 )
291 else if ( direction.compare( QLatin1String( "cw" ) ) == 0 )
293 }
294
295 return r;
296}
297
298double QgsGradientColorRamp::value( int index ) const
299{
300 if ( index <= 0 )
301 {
302 return 0;
303 }
304 else if ( index >= mStops.size() + 1 )
305 {
306 return 1;
307 }
308 else
309 {
310 return mStops[index - 1].offset;
311 }
312}
313
314QColor QgsGradientColorRamp::color( double value ) const
315{
316 if ( qgsDoubleNear( value, 0.0 ) || value < 0.0 )
317 {
318 return mColor1;
319 }
320 else if ( qgsDoubleNear( value, 1.0 ) || value > 1.0 )
321 {
322 return mColor2;
323 }
324 else if ( mStops.isEmpty() )
325 {
326 if ( mDiscrete )
327 return mColor1;
328
329 return mFunc( mColor1, mColor2, value, mDirection );
330 }
331 else
332 {
333 double lower = 0, upper = 0;
334 QColor c1 = mColor1, c2;
335 for ( QgsGradientStopsList::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
336 {
337 if ( it->offset > value )
338 {
339 if ( mDiscrete )
340 return c1;
341
342 upper = it->offset;
343 c2 = it->color;
344
345 return qgsDoubleNear( upper, lower ) ? c1 : it->mFunc( c1, c2, ( value - lower ) / ( upper - lower ), it->mDirection );
346 }
347 lower = it->offset;
348 c1 = it->color;
349 }
350
351 if ( mDiscrete )
352 return c1;
353
354 upper = 1;
355 c2 = mColor2;
356 return qgsDoubleNear( upper, lower ) ? c1 : mFunc( c1, c2, ( value - lower ) / ( upper - lower ), mDirection );
357 }
358}
359
361{
363}
364
366{
367 QgsGradientStopsList newStops;
368 newStops.reserve( mStops.size() );
369
370 if ( mDiscrete )
371 {
373 mColor1 = mStops.at( mStops.size() - 1 ).color;
374 for ( int k = mStops.size() - 1; k >= 1; k-- )
375 {
376 newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k - 1 ).color );
377 }
378 newStops << QgsGradientStop( 1 - mStops.at( 0 ).offset, mColor2 );
379 }
380 else
381 {
382 QColor tmpColor = mColor2;
384 mColor1 = tmpColor;
385 for ( int k = mStops.size() - 1; k >= 0; k-- )
386 {
387 newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k ).color );
388 }
389 }
390
391 // transfer color spec, invert directions
392 if ( mStops.empty() )
393 {
394 // reverse direction
396 }
397 else
398 {
399 newStops[0].setColorSpec( mColorSpec );
401 for ( int i = 1, j = mStops.size() - 1; i < mStops.size(); ++i, --j )
402 {
403 newStops[i].setColorSpec( mStops.at( j ).colorSpec() );
405 }
406 mColorSpec = mStops.at( 0 ).colorSpec();
408 }
409
410 mStops = newStops;
411}
412
414{
416 mDiscrete, mStops );
417 r->setInfo( mInfo );
420 return r;
421}
422
424{
425 QVariantMap map;
426 map[QStringLiteral( "color1" )] = QgsSymbolLayerUtils::encodeColor( mColor1 );
427 map[QStringLiteral( "color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
428 if ( !mStops.isEmpty() )
429 {
430 QStringList lst;
431 lst.reserve( mStops.size() );
432 for ( const QgsGradientStop &stop : mStops )
433 {
434 lst.append( QStringLiteral( "%1;%2;%3;%4" ).arg( stop.offset ).arg( QgsSymbolLayerUtils::encodeColor( stop.color ),
435 stop.colorSpec() == QColor::Rgb ? QStringLiteral( "rgb" )
436 : stop.colorSpec() == QColor::Hsv ? QStringLiteral( "hsv" )
437 : stop.colorSpec() == QColor::Hsl ? QStringLiteral( "hsl" ) : QString(),
438 stop.direction() == Qgis::AngularDirection::CounterClockwise ? QStringLiteral( "ccw" ) : QStringLiteral( "cw" ) ) );
439 }
440 map[QStringLiteral( "stops" )] = lst.join( QLatin1Char( ':' ) );
441 }
442
443 map[QStringLiteral( "discrete" )] = mDiscrete ? "1" : "0";
444
445 for ( QgsStringMap::const_iterator it = mInfo.constBegin();
446 it != mInfo.constEnd(); ++it )
447 {
448 map["info_" + it.key()] = it.value();
449 }
450
451 switch ( mColorSpec )
452 {
453 case QColor::Rgb:
454 map[QStringLiteral( "spec" ) ] = QStringLiteral( "rgb" );
455 break;
456 case QColor::Hsv:
457 map[QStringLiteral( "spec" ) ] = QStringLiteral( "hsv" );
458 break;
459 case QColor::Hsl:
460 map[QStringLiteral( "spec" ) ] = QStringLiteral( "hsl" );
461 break;
462 case QColor::Cmyk:
463 case QColor::Invalid:
464 case QColor::ExtendedRgb:
465 break;
466 }
467
468 switch ( mDirection )
469 {
471 map[QStringLiteral( "direction" ) ] = QStringLiteral( "cw" );
472 break;
474 map[QStringLiteral( "direction" ) ] = QStringLiteral( "ccw" );
475 break;
476 }
477
478 map[QStringLiteral( "rampType" )] = type();
479 return map;
480}
482{
483 if ( discrete == mDiscrete )
484 return;
485
486 // if going to/from Discrete, re-arrange stops
487 // this will only work when stops are equally-spaced
488 QgsGradientStopsList newStops;
489 if ( discrete )
490 {
491 // re-arrange stops offset
492 int numStops = mStops.count() + 2;
493 int i = 1;
494 for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
495 it != mStops.constEnd(); ++it )
496 {
497 newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, it->color ) );
498 if ( i == numStops - 1 )
499 break;
500 i++;
501 }
502 // replicate last color
503 newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, mColor2 ) );
504 }
505 else
506 {
507 // re-arrange stops offset, remove duplicate last color
508 int numStops = mStops.count() + 2;
509 int i = 1;
510 for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
511 it != mStops.constEnd(); ++it )
512 {
513 newStops.append( QgsGradientStop( static_cast< double >( i ) / ( numStops - 2 ), it->color ) );
514 if ( i == numStops - 3 )
515 break;
516 i++;
517 }
518 }
519 mStops = newStops;
520 mDiscrete = discrete;
521}
522
523bool stopLessThan( const QgsGradientStop &s1, const QgsGradientStop &s2 )
524{
525 return s1.offset < s2.offset;
526}
527
529{
530 mStops = stops;
531
532 //sort stops by offset
533 std::sort( mStops.begin(), mStops.end(), stopLessThan );
534}
535
536void QgsGradientColorRamp::addStopsToGradient( QGradient *gradient, double opacity ) const
537{
538 //copy color ramp stops to a QGradient
539 QColor color1 = mColor1;
540 QColor color2 = mColor2;
541 if ( opacity < 1 )
542 {
543 color1.setAlpha( color1.alpha() * opacity );
544 color2.setAlpha( color2.alpha() * opacity );
545 }
546 gradient->setColorAt( 0, color1 );
547 gradient->setColorAt( 1, color2 );
548
549 double lastOffset = 0;
550 for ( const QgsGradientStop &stop : mStops )
551 {
552
553 QColor rampColor = stop.color;
554 if ( opacity < 1 )
555 {
556 rampColor.setAlpha( rampColor.alpha() * opacity );
557 }
558 gradient->setColorAt( stop.offset, rampColor );
559
560 if ( stop.colorSpec() != QColor::Rgb )
561 {
562 // QGradient only supports RGB interpolation. For other color specs we have
563 // to "fake" things by populating the gradient with additional stops
564 for ( double offset = lastOffset + 0.05; offset < stop.offset; offset += 0.05 )
565 {
566 QColor midColor = color( offset );
567 if ( opacity < 1 )
568 {
569 midColor.setAlpha( midColor.alpha() * opacity );
570 }
571 gradient->setColorAt( offset, midColor );
572 }
573 }
574 lastOffset = stop.offset;
575 }
576
577 if ( mColorSpec != QColor::Rgb )
578 {
579 for ( double offset = lastOffset + 0.05; offset < 1; offset += 0.05 )
580 {
581 QColor midColor = color( offset );
582 if ( opacity < 1 )
583 {
584 midColor.setAlpha( midColor.alpha() * opacity );
585 }
586 gradient->setColorAt( offset, midColor );
587 }
588 }
589}
590
591void QgsGradientColorRamp::setColorSpec( QColor::Spec spec )
592{
593 mColorSpec = spec;
594 switch ( mColorSpec )
595 {
596 case QColor::Rgb:
597 case QColor::Invalid:
598 case QColor::ExtendedRgb:
599 case QColor::Cmyk:
600 mFunc = _interpolateRgb;
601 break;
602 case QColor::Hsv:
603 mFunc = _interpolateHsv;
604 break;
605 case QColor::Hsl:
606 mFunc = _interpolateHsl;
607 break;
608 }
609}
610
611
613
614
616 int satMin, int satMax, int valMin, int valMax )
617 : mCount( count )
618 , mHueMin( hueMin ), mHueMax( hueMax )
619 , mSatMin( satMin ), mSatMax( satMax )
620 , mValMin( valMin ), mValMax( valMax )
621{
622 updateColors();
623}
624
626{
631
632 if ( props.contains( QStringLiteral( "count" ) ) ) count = props[QStringLiteral( "count" )].toInt();
633 if ( props.contains( QStringLiteral( "hueMin" ) ) ) hueMin = props[QStringLiteral( "hueMin" )].toInt();
634 if ( props.contains( QStringLiteral( "hueMax" ) ) ) hueMax = props[QStringLiteral( "hueMax" )].toInt();
635 if ( props.contains( QStringLiteral( "satMin" ) ) ) satMin = props[QStringLiteral( "satMin" )].toInt();
636 if ( props.contains( QStringLiteral( "satMax" ) ) ) satMax = props[QStringLiteral( "satMax" )].toInt();
637 if ( props.contains( QStringLiteral( "valMin" ) ) ) valMin = props[QStringLiteral( "valMin" )].toInt();
638 if ( props.contains( QStringLiteral( "valMax" ) ) ) valMax = props[QStringLiteral( "valMax" )].toInt();
639
641}
642
643double QgsLimitedRandomColorRamp::value( int index ) const
644{
645 if ( mColors.empty() )
646 return 0;
647 return static_cast< double >( index ) / ( mColors.size() - 1 );
648}
649
650QColor QgsLimitedRandomColorRamp::color( double value ) const
651{
652 if ( value < 0 || value > 1 )
653 return QColor();
654
655 int colorCnt = mColors.count();
656 int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
657
658 if ( colorIdx >= 0 && colorIdx < colorCnt )
659 return mColors.at( colorIdx );
660
661 return QColor();
662}
663
665{
667}
668
670{
672}
673
675{
676 QVariantMap map;
677 map[QStringLiteral( "count" )] = QString::number( mCount );
678 map[QStringLiteral( "hueMin" )] = QString::number( mHueMin );
679 map[QStringLiteral( "hueMax" )] = QString::number( mHueMax );
680 map[QStringLiteral( "satMin" )] = QString::number( mSatMin );
681 map[QStringLiteral( "satMax" )] = QString::number( mSatMax );
682 map[QStringLiteral( "valMin" )] = QString::number( mValMin );
683 map[QStringLiteral( "valMax" )] = QString::number( mValMax );
684 map[QStringLiteral( "rampType" )] = type();
685 return map;
686}
687
689 int hueMax, int hueMin, int satMax, int satMin, int valMax, int valMin )
690{
691 int h, s, v;
692 QList<QColor> colors;
693
694 //normalize values
695 int safeHueMax = std::max( hueMin, hueMax );
696 int safeHueMin = std::min( hueMin, hueMax );
697 int safeSatMax = std::max( satMin, satMax );
698 int safeSatMin = std::min( satMin, satMax );
699 int safeValMax = std::max( valMin, valMax );
700 int safeValMin = std::min( valMin, valMax );
701
702 //start hue at random angle
703 double currentHueAngle = 360.0 * static_cast< double >( std::rand() ) / RAND_MAX;
704
705 colors.reserve( count );
706 for ( int i = 0; i < count; ++i )
707 {
708 //increment hue by golden ratio (approx 137.507 degrees)
709 //as this minimizes hue nearness as count increases
710 //see http://basecase.org/env/on-rainbows for more details
711 currentHueAngle += 137.50776;
712 //scale hue to between hueMax and hueMin
713 h = std::clamp( std::round( ( std::fmod( currentHueAngle, 360.0 ) / 360.0 ) * ( safeHueMax - safeHueMin ) + safeHueMin ), 0.0, 359.0 );
714 s = std::clamp( ( static_cast<int>( std::rand() ) % ( safeSatMax - safeSatMin + 1 ) ) + safeSatMin, 0, 255 );
715 v = std::clamp( ( static_cast<int>( std::rand() ) % ( safeValMax - safeValMin + 1 ) ) + safeValMin, 0, 255 );
716 colors.append( QColor::fromHsv( h, s, v ) );
717 }
718 return colors;
719}
720
722{
724}
725
727
729{
730 return -1;
731}
732
733double QgsRandomColorRamp::value( int index ) const
734{
735 Q_UNUSED( index )
736 return 0.0;
737}
738
739QColor QgsRandomColorRamp::color( double value ) const
740{
741 int minVal = 130;
742 int maxVal = 255;
743
744 //if value is nan, then use last precalculated color
745 if ( std::isnan( value ) )
746 {
747 value = 1.0;
748 }
749 // Caller has converted an index into a value in [0.0, 1.0]
750 // by doing "index / (mTotalColorCount - 1)"; retrieve the original index.
751 int colorIndex = std::round( value * ( mTotalColorCount - 1 ) );
752 if ( mTotalColorCount >= 1 && mPrecalculatedColors.length() > colorIndex )
753 {
754 //use precalculated hue
755 return mPrecalculatedColors.at( colorIndex );
756 }
757
758 //can't use precalculated hues, use a totally random hue
759 int h = static_cast< int >( 360.0 * std::rand() / ( RAND_MAX + 1.0 ) );
760 int s = ( std::rand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
761 int v = ( std::rand() % ( maxVal - minVal + 1 ) ) + minVal;
762 return QColor::fromHsv( h, s, v );
763}
764
765void QgsRandomColorRamp::setTotalColorCount( const int colorCount )
766{
767 //calculate colors in advance, so that we can ensure they are more visually distinct than pure random colors
768 mPrecalculatedColors.clear();
769 mTotalColorCount = colorCount;
770
771 //This works OK for low color counts, but for > 10 or so colors there's still a good chance of
772 //similar colors being picked. TODO - investigate alternative "n-visually distinct color" routines
773
774 //random offsets
775 double hueOffset = ( 360.0 * std::rand() / ( RAND_MAX + 1.0 ) );
776
777 //try to maximise difference between hues. this is not an ideal implementation, as constant steps
778 //through the hue wheel are not visually perceived as constant changes in hue
779 //(for instance, we are much more likely to get green hues than yellow hues)
780 double hueStep = 359.0 / colorCount;
781 double currentHue = hueOffset;
782
783 //build up a list of colors
784 for ( int idx = 0; idx < colorCount; ++ idx )
785 {
786 int h = static_cast< int >( std::round( currentHue ) ) % 360;
787 int s = ( std::rand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
788 int v = ( std::rand() % ( DEFAULT_RANDOM_VAL_MAX - DEFAULT_RANDOM_VAL_MIN + 1 ) ) + DEFAULT_RANDOM_VAL_MIN;
789 mPrecalculatedColors << QColor::fromHsv( h, s, v );
790 currentHue += hueStep;
791 }
792
793 //lastly, shuffle color list
794 std::random_device rd;
795 std::mt19937 g( rd() );
796 std::shuffle( mPrecalculatedColors.begin(), mPrecalculatedColors.end(), g );
797}
798
800{
802}
803
805{
806 return new QgsRandomColorRamp();
807}
808
810{
811 return QVariantMap();
812}
813
815
816QgsColorBrewerColorRamp::QgsColorBrewerColorRamp( const QString &schemeName, int colors, bool inverted )
817 : mSchemeName( schemeName )
818 , mColors( colors )
819 , mInverted( inverted )
820{
821 loadPalette();
822}
823
825{
828 bool inverted = false;
829
830 if ( props.contains( QStringLiteral( "schemeName" ) ) )
831 schemeName = props[QStringLiteral( "schemeName" )].toString();
832 if ( props.contains( QStringLiteral( "colors" ) ) )
833 colors = props[QStringLiteral( "colors" )].toInt();
834 if ( props.contains( QStringLiteral( "inverted" ) ) )
835 inverted = props[QStringLiteral( "inverted" )].toInt();
836
837 return new QgsColorBrewerColorRamp( schemeName, colors, inverted );
838}
839
841{
843
844 if ( mInverted )
845 {
846 QList<QColor> tmpPalette;
847
848 for ( int k = mPalette.size() - 1; k >= 0; k-- )
849 {
850 tmpPalette << mPalette.at( k );
851 }
852 mPalette = tmpPalette;
853 }
854}
855
857{
859}
860
861QList<int> QgsColorBrewerColorRamp::listSchemeVariants( const QString &schemeName )
862{
864}
865
866double QgsColorBrewerColorRamp::value( int index ) const
867{
868 if ( mPalette.empty() )
869 return 0;
870 return static_cast< double >( index ) / ( mPalette.size() - 1 );
871}
872
873QColor QgsColorBrewerColorRamp::color( double value ) const
874{
875 if ( mPalette.isEmpty() || value < 0 || value > 1 || std::isnan( value ) )
876 return QColor();
877
878 int paletteEntry = static_cast< int >( value * mPalette.count() );
879 if ( paletteEntry >= mPalette.count() )
880 paletteEntry = mPalette.count() - 1;
881 return mPalette.at( paletteEntry );
882}
883
885{
887 loadPalette();
888}
889
891{
893}
894
896{
897 QVariantMap map;
898 map[QStringLiteral( "schemeName" )] = mSchemeName;
899 map[QStringLiteral( "colors" )] = QString::number( mColors );
900 map[QStringLiteral( "inverted" )] = QString::number( mInverted );
901 map[QStringLiteral( "rampType" )] = type();
902 return map;
903}
904
905
907
908
909QgsCptCityColorRamp::QgsCptCityColorRamp( const QString &schemeName, const QString &variantName,
910 bool inverted, bool doLoadFile )
912 , mSchemeName( schemeName )
913 , mVariantName( variantName )
914 , mInverted( inverted )
915{
916 // TODO replace this with hard-coded data in the default case
917 // don't load file if variant is missing
918 if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
919 loadFile();
920}
921
922QgsCptCityColorRamp::QgsCptCityColorRamp( const QString &schemeName, const QStringList &variantList,
923 const QString &variantName, bool inverted, bool doLoadFile )
925 , mSchemeName( schemeName )
926 , mVariantName( variantName )
927 , mVariantList( variantList )
928 , mInverted( inverted )
929{
931
932 // TODO replace this with hard-coded data in the default case
933 // don't load file if variant is missing
934 if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
935 loadFile();
936}
937
938QgsColorRamp *QgsCptCityColorRamp::create( const QVariantMap &props )
939{
942 bool inverted = false;
943
944 if ( props.contains( QStringLiteral( "schemeName" ) ) )
945 schemeName = props[QStringLiteral( "schemeName" )].toString();
946 if ( props.contains( QStringLiteral( "variantName" ) ) )
947 variantName = props[QStringLiteral( "variantName" )].toString();
948 if ( props.contains( QStringLiteral( "inverted" ) ) )
949 inverted = props[QStringLiteral( "inverted" )].toInt();
950
951 return new QgsCptCityColorRamp( schemeName, variantName, inverted );
952}
953
955{
957}
958
960{
963}
964
966{
967 QgsCptCityColorRamp *ramp = new QgsCptCityColorRamp( QString(), QString(), mInverted, false );
968 ramp->copy( this );
969 return ramp;
970}
971
973{
974 if ( ! other )
975 return;
976 mColor1 = other->color1();
977 mColor2 = other->color2();
978 mDiscrete = other->isDiscrete();
979 mStops = other->stops();
980 mSchemeName = other->mSchemeName;
981 mVariantName = other->mVariantName;
982 mVariantList = other->mVariantList;
983 mFileLoaded = other->mFileLoaded;
984 mInverted = other->mInverted;
985}
986
988{
991 // add author and copyright information
992 // TODO also add COPYING.xml file/link?
994 info[QStringLiteral( "cpt-city-gradient" )] = "<cpt-city>/" + mSchemeName + mVariantName + ".svg";
995 QString copyingFilename = copyingFileName();
996 copyingFilename.remove( QgsCptCityArchive::defaultBaseDir() );
997 info[QStringLiteral( "cpt-city-license" )] = "<cpt-city>" + copyingFilename;
998 ramp->setInfo( info );
999 return ramp;
1000}
1001
1002
1004{
1005 QVariantMap map;
1006 map[QStringLiteral( "schemeName" )] = mSchemeName;
1007 map[QStringLiteral( "variantName" )] = mVariantName;
1008 map[QStringLiteral( "inverted" )] = QString::number( mInverted );
1009 map[QStringLiteral( "rampType" )] = type();
1010 return map;
1011}
1012
1013QString QgsCptCityColorRamp::fileNameForVariant( const QString &schema, const QString &variant )
1014{
1015 return QgsCptCityArchive::defaultBaseDir() + QDir::separator() + schema + variant + ".svg";
1016}
1017
1019{
1020 if ( mSchemeName.isEmpty() )
1021 return QString();
1022 else
1023 {
1024 return QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mSchemeName + mVariantName + ".svg";
1025 }
1026}
1027
1029{
1030 return QgsCptCityArchive::findFileName( QStringLiteral( "COPYING.xml" ), QFileInfo( fileName() ).dir().path(),
1032}
1033
1035{
1036 return QgsCptCityArchive::findFileName( QStringLiteral( "DESC.xml" ), QFileInfo( fileName() ).dir().path(),
1038}
1039
1041{
1043}
1044
1046{
1047 if ( mFileLoaded )
1048 {
1049 QgsDebugMsgLevel( "File already loaded for " + mSchemeName + mVariantName, 2 );
1050 return true;
1051 }
1052
1053 // get filename
1054 QString filename = fileName();
1055 if ( filename.isNull() )
1056 {
1057 return false;
1058 }
1059
1060 QgsDebugMsgLevel( QStringLiteral( "filename= %1 loaded=%2" ).arg( filename ).arg( mFileLoaded ), 2 );
1061
1062 // get color ramp from svg file
1063 QMap< double, QPair<QColor, QColor> > colorMap =
1065
1066 // add colors to palette
1067 mFileLoaded = false;
1068 mStops.clear();
1069 QMap<double, QPair<QColor, QColor> >::const_iterator it, prev;
1070 // first detect if file is gradient is continuous or discrete
1071 // discrete: stop contains 2 colors and first color is identical to previous second
1072 // multi: stop contains 2 colors and no relation with previous stop
1073 mDiscrete = false;
1074 mMultiStops = false;
1075 it = prev = colorMap.constBegin();
1076 while ( it != colorMap.constEnd() )
1077 {
1078 // look for stops that contain multiple values
1079 if ( it != colorMap.constBegin() && ( it.value().first != it.value().second ) )
1080 {
1081 if ( it.value().first == prev.value().second )
1082 {
1083 mDiscrete = true;
1084 break;
1085 }
1086 else
1087 {
1088 mMultiStops = true;
1089 break;
1090 }
1091 }
1092 prev = it;
1093 ++it;
1094 }
1095
1096 // fill all stops
1097 it = prev = colorMap.constBegin();
1098 while ( it != colorMap.constEnd() )
1099 {
1100 if ( mDiscrete )
1101 {
1102 // mPalette << qMakePair( it.key(), it.value().second );
1103 mStops.append( QgsGradientStop( it.key(), it.value().second ) );
1104 }
1105 else
1106 {
1107 // mPalette << qMakePair( it.key(), it.value().first );
1108 mStops.append( QgsGradientStop( it.key(), it.value().first ) );
1109 if ( ( mMultiStops ) &&
1110 ( it.key() != 0.0 && it.key() != 1.0 ) )
1111 {
1112 mStops.append( QgsGradientStop( it.key(), it.value().second ) );
1113 }
1114 }
1115 prev = it;
1116 ++it;
1117 }
1118
1119 // remove first and last items (mColor1 and mColor2)
1120 if ( ! mStops.isEmpty() && mStops.at( 0 ).offset == 0.0 )
1121 mColor1 = mStops.takeFirst().color;
1122 if ( ! mStops.isEmpty() && mStops.last().offset == 1.0 )
1123 mColor2 = mStops.takeLast().color;
1124
1125 if ( mInverted )
1126 {
1128 }
1129
1130 mFileLoaded = true;
1131 return true;
1132}
1133
1134
1135//
1136// QgsPresetColorRamp
1137//
1138
1140{
1141 const auto constColors = colors;
1142 for ( const QColor &color : constColors )
1143 {
1144 mColors << qMakePair( color, color.name() );
1145 }
1146 // need at least one color
1147 if ( mColors.isEmpty() )
1148 mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
1149}
1150
1152 : mColors( colors )
1153{
1154 // need at least one color
1155 if ( mColors.isEmpty() )
1156 mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
1157}
1158
1159QgsColorRamp *QgsPresetSchemeColorRamp::create( const QVariantMap &properties )
1160{
1162
1163 int i = 0;
1164 QString colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() ).toString();
1165 QString colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() ).toString();
1166 while ( !colorString.isEmpty() )
1167 {
1168 colors << qMakePair( QgsSymbolLayerUtils::decodeColor( colorString ), colorName );
1169 i++;
1170 colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() ).toString();
1171 colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() ).toString();
1172 }
1173
1174 return new QgsPresetSchemeColorRamp( colors );
1175}
1176
1178{
1179 QList< QColor > l;
1180 l.reserve( mColors.count() );
1181 for ( int i = 0; i < mColors.count(); ++i )
1182 {
1183 l << mColors.at( i ).first;
1184 }
1185 return l;
1186}
1187
1188double QgsPresetSchemeColorRamp::value( int index ) const
1189{
1190 if ( mColors.empty() )
1191 return 0;
1192 return static_cast< double >( index ) / ( mColors.size() - 1 );
1193}
1194
1195QColor QgsPresetSchemeColorRamp::color( double value ) const
1196{
1197 if ( value < 0 || value > 1 )
1198 return QColor();
1199
1200 int colorCnt = mColors.count();
1201 int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
1202
1203 if ( colorIdx >= 0 && colorIdx < colorCnt )
1204 return mColors.at( colorIdx ).first;
1205
1206 return QColor();
1207}
1208
1210{
1212}
1213
1215{
1216 QgsNamedColorList tmpColors;
1217
1218 for ( int k = mColors.size() - 1; k >= 0; k-- )
1219 {
1220 tmpColors << mColors.at( k );
1221 }
1222 mColors = tmpColors;
1223}
1224
1226{
1227 return new QgsPresetSchemeColorRamp( *this );
1228}
1229
1231{
1232 QVariantMap props;
1233 for ( int i = 0; i < mColors.count(); ++i )
1234 {
1235 props.insert( QStringLiteral( "preset_color_%1" ).arg( i ), QgsSymbolLayerUtils::encodeColor( mColors.at( i ).first ) );
1236 props.insert( QStringLiteral( "preset_color_name_%1" ).arg( i ), mColors.at( i ).second );
1237 }
1238 props[QStringLiteral( "rampType" )] = type();
1239 return props;
1240}
1241
1243{
1244 return mColors.count();
1245}
1246
1248{
1249 return mColors;
1250}
AngularDirection
Angular directions.
Definition: qgis.h:2023
@ CounterClockwise
Counter-clockwise direction.
@ Clockwise
Clockwise direction.
Color ramp utilising "Color Brewer" preset color schemes.
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 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.
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.
int colors() const
Returns the number of colors in the ramp.
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:30
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
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
QString fileName() const
void copy(const QgsCptCityColorRamp *other)
static QString fileNameForVariant(const QString &schema, const QString &variant)
Returns the source file name for a CPT schema and variant.
static QString typeString()
Returns the string identifier for QgsCptCityColorRamp.
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
QString variantName() const
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
QgsGradientStopsList mStops
void setInfo(const QgsStringMap &info)
Sets additional info to attach to the gradient ramp (e.g., authorship notes)
bool isDiscrete() const
Returns true if the gradient is using discrete interpolation, rather than smoothly interpolating betw...
void setColorSpec(QColor::Spec spec)
Sets the color specification in which the color component interpolation will occur.
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)
void convertToDiscrete(bool discrete)
Converts a gradient with existing color stops to or from discrete interpolation.
Qgis::AngularDirection mDirection
QColor color(double value) const override
Returns the color corresponding to a specified value.
static QString typeString()
Returns the string identifier for QgsGradientColorRamp.
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.
void setDirection(Qgis::AngularDirection direction)
Sets the direction to traverse the color wheel using when interpolating hue-based color specification...
Qgis::AngularDirection direction() const
Returns the direction to traverse the color wheel using when interpolating hue-based color specificat...
void invert() override
Inverts the ordering of the color ramp.
void addStopsToGradient(QGradient *gradient, double opacity=1) const
Copy color ramp stops to a QGradient.
QgsGradientStopsList stops() const
Returns the list of intermediate gradient stops for the ramp.
double value(int index) const override
Returns relative value between [0,1] of color at specified index.
QgsGradientColorRamp * clone() const override
Creates a clone of the color ramp.
InterpolateColorFunc mFunc
QColor color2() const
Returns the gradient end color.
Represents a color stop within a QgsGradientColorRamp color ramp.
void setColorSpec(QColor::Spec spec)
Sets the color specification in which the color component interpolation will occur.
double offset
Relative positional offset, between 0 and 1.
QgsGradientStop(double offset, const QColor &color)
Constructor for QgsGradientStop.
Constrained random color ramp, which returns random colors based on preset parameters.
static QString typeString()
Returns the string identifier for QgsLimitedRandomColorRamp.
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.
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.
QString type() const override
Returns a string representing the color ramp type.
int satMax() const
Returns the maximum saturation for generated colors.
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.
int hueMin() const
Returns the minimum hue for generated colors.
int valMin() const
Returns the minimum value for generated colors.
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.
A scheme based color ramp consisting of a list of predefined colors.
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.
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.
double value(int index) const override
Returns relative value between [0,1] of color at specified index.
QList< QColor > mPrecalculatedColors
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.
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:2915
QMap< QString, QString > QgsStringMap
Definition: qgis.h:3460
bool stopLessThan(const QgsGradientStop &s1, const QgsGradientStop &s2)
#define DEFAULT_COLORBREWER_COLORS
#define DEFAULT_COLORBREWER_SCHEMENAME
#define DEFAULT_RANDOM_HUE_MAX
#define DEFAULT_CPTCITY_SCHEMENAME
#define DEFAULT_RANDOM_HUE_MIN
#define DEFAULT_RANDOM_COUNT
#define DEFAULT_RANDOM_SAT_MAX
#define DEFAULT_RANDOM_SAT_MIN
#define DEFAULT_CPTCITY_VARIANTNAME
#define DEFAULT_GRADIENT_COLOR1
#define DEFAULT_RANDOM_VAL_MIN
QList< QgsGradientStop > QgsGradientStopsList
List of gradient stops.
#define DEFAULT_GRADIENT_COLOR2
#define DEFAULT_RANDOM_VAL_MAX
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39