QGIS API Documentation 3.27.0-Master (9c08adf5ef)
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#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
192 case QColor::ExtendedRgb:
193#endif
194 case QColor::Cmyk:
195 mFunc = _interpolateRgb;
196 break;
197 case QColor::Hsv:
198 mFunc = _interpolateHsv;
199 break;
200 case QColor::Hsl:
201 mFunc = _interpolateHsl;
202 break;
203 }
204}
205
206QgsGradientColorRamp::QgsGradientColorRamp( const QColor &color1, const QColor &color2,
207 bool discrete, const QgsGradientStopsList &stops )
208 : mColor1( color1 )
209 , mColor2( color2 )
210 , mDiscrete( discrete )
211 , mStops( stops )
212 , mFunc( _interpolateRgb )
213{
214}
215
216QgsColorRamp *QgsGradientColorRamp::create( const QVariantMap &props )
217{
218 // color1 and color2
221 if ( props.contains( QStringLiteral( "color1" ) ) )
222 color1 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color1" )].toString() );
223 if ( props.contains( QStringLiteral( "color2" ) ) )
224 color2 = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color2" )].toString() );
225
226 //stops
228 if ( props.contains( QStringLiteral( "stops" ) ) )
229 {
230 const auto constSplit = props[QStringLiteral( "stops" )].toString().split( ':' );
231 for ( const QString &stop : constSplit )
232 {
233 const QStringList parts = stop.split( ';' );
234 if ( parts.size() != 2 && parts.size() != 4 )
235 continue;
236
237 QColor c = QgsSymbolLayerUtils::decodeColor( parts.at( 1 ) );
238 stops.append( QgsGradientStop( parts.at( 0 ).toDouble(), c ) );
239
240 if ( parts.size() == 4 )
241 {
242 if ( parts.at( 2 ).compare( QLatin1String( "rgb" ) ) == 0 )
243 stops.last().setColorSpec( QColor::Spec::Rgb );
244 else if ( parts.at( 2 ).compare( QLatin1String( "hsv" ) ) == 0 )
245 stops.last().setColorSpec( QColor::Spec::Hsv );
246 else if ( parts.at( 2 ).compare( QLatin1String( "hsl" ) ) == 0 )
247 stops.last().setColorSpec( QColor::Spec::Hsl );
248
249 if ( parts.at( 3 ).compare( QLatin1String( "cw" ) ) == 0 )
250 stops.last().setDirection( Qgis::AngularDirection::Clockwise );
251 else if ( parts.at( 3 ).compare( QLatin1String( "ccw" ) ) == 0 )
252 stops.last().setDirection( Qgis::AngularDirection::CounterClockwise );
253 }
254 }
255 }
256
257 // discrete vs. continuous
258 bool discrete = false;
259 if ( props.contains( QStringLiteral( "discrete" ) ) )
260 {
261 if ( props[QStringLiteral( "discrete" )] == QLatin1String( "1" ) )
262 discrete = true;
263 }
264
265 // search for information keys starting with "info_"
267 for ( QVariantMap::const_iterator it = props.constBegin();
268 it != props.constEnd(); ++it )
269 {
270 if ( it.key().startsWith( QLatin1String( "info_" ) ) )
271 info[ it.key().mid( 5 )] = it.value().toString();
272 }
273
275 r->setInfo( info );
276
277 if ( props.contains( QStringLiteral( "spec" ) ) )
278 {
279 const QString spec = props.value( QStringLiteral( "spec" ) ).toString().trimmed();
280 if ( spec.compare( QLatin1String( "rgb" ) ) == 0 )
281 r->setColorSpec( QColor::Spec::Rgb );
282 else if ( spec.compare( QLatin1String( "hsv" ) ) == 0 )
283 r->setColorSpec( QColor::Spec::Hsv );
284 else if ( spec.compare( QLatin1String( "hsl" ) ) == 0 )
285 r->setColorSpec( QColor::Spec::Hsl );
286 }
287
288 if ( props.contains( QStringLiteral( "direction" ) ) )
289 {
290 const QString direction = props.value( QStringLiteral( "direction" ) ).toString().trimmed();
291 if ( direction.compare( QLatin1String( "ccw" ) ) == 0 )
293 else if ( direction.compare( QLatin1String( "cw" ) ) == 0 )
295 }
296
297 return r;
298}
299
300double QgsGradientColorRamp::value( int index ) const
301{
302 if ( index <= 0 )
303 {
304 return 0;
305 }
306 else if ( index >= mStops.size() + 1 )
307 {
308 return 1;
309 }
310 else
311 {
312 return mStops[index - 1].offset;
313 }
314}
315
316QColor QgsGradientColorRamp::color( double value ) const
317{
318 if ( qgsDoubleNear( value, 0.0 ) || value < 0.0 )
319 {
320 return mColor1;
321 }
322 else if ( qgsDoubleNear( value, 1.0 ) || value > 1.0 )
323 {
324 return mColor2;
325 }
326 else if ( mStops.isEmpty() )
327 {
328 if ( mDiscrete )
329 return mColor1;
330
331 return mFunc( mColor1, mColor2, value, mDirection );
332 }
333 else
334 {
335 double lower = 0, upper = 0;
336 QColor c1 = mColor1, c2;
337 for ( QgsGradientStopsList::const_iterator it = mStops.begin(); it != mStops.end(); ++it )
338 {
339 if ( it->offset > value )
340 {
341 if ( mDiscrete )
342 return c1;
343
344 upper = it->offset;
345 c2 = it->color;
346
347 return qgsDoubleNear( upper, lower ) ? c1 : it->mFunc( c1, c2, ( value - lower ) / ( upper - lower ), it->mDirection );
348 }
349 lower = it->offset;
350 c1 = it->color;
351 }
352
353 if ( mDiscrete )
354 return c1;
355
356 upper = 1;
357 c2 = mColor2;
358 return qgsDoubleNear( upper, lower ) ? c1 : mFunc( c1, c2, ( value - lower ) / ( upper - lower ), mDirection );
359 }
360}
361
363{
365}
366
368{
369 QgsGradientStopsList newStops;
370 newStops.reserve( mStops.size() );
371
372 if ( mDiscrete )
373 {
375 mColor1 = mStops.at( mStops.size() - 1 ).color;
376 for ( int k = mStops.size() - 1; k >= 1; k-- )
377 {
378 newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k - 1 ).color );
379 }
380 newStops << QgsGradientStop( 1 - mStops.at( 0 ).offset, mColor2 );
381 }
382 else
383 {
384 QColor tmpColor = mColor2;
386 mColor1 = tmpColor;
387 for ( int k = mStops.size() - 1; k >= 0; k-- )
388 {
389 newStops << QgsGradientStop( 1 - mStops.at( k ).offset, mStops.at( k ).color );
390 }
391 }
392
393 // transfer color spec, invert directions
394 if ( mStops.empty() )
395 {
396 // reverse direction
398 }
399 else
400 {
401 newStops[0].setColorSpec( mColorSpec );
403 for ( int i = 1, j = mStops.size() - 1; i < mStops.size(); ++i, --j )
404 {
405 newStops[i].setColorSpec( mStops.at( j ).colorSpec() );
407 }
408 mColorSpec = mStops.at( 0 ).colorSpec();
410 }
411
412 mStops = newStops;
413}
414
416{
418 mDiscrete, mStops );
419 r->setInfo( mInfo );
422 return r;
423}
424
426{
427 QVariantMap map;
428 map[QStringLiteral( "color1" )] = QgsSymbolLayerUtils::encodeColor( mColor1 );
429 map[QStringLiteral( "color2" )] = QgsSymbolLayerUtils::encodeColor( mColor2 );
430 if ( !mStops.isEmpty() )
431 {
432 QStringList lst;
433 lst.reserve( mStops.size() );
434 for ( const QgsGradientStop &stop : mStops )
435 {
436 lst.append( QStringLiteral( "%1;%2;%3;%4" ).arg( stop.offset ).arg( QgsSymbolLayerUtils::encodeColor( stop.color ),
437 stop.colorSpec() == QColor::Rgb ? QStringLiteral( "rgb" )
438 : stop.colorSpec() == QColor::Hsv ? QStringLiteral( "hsv" )
439 : stop.colorSpec() == QColor::Hsl ? QStringLiteral( "hsl" ) : QString(),
440 stop.direction() == Qgis::AngularDirection::CounterClockwise ? QStringLiteral( "ccw" ) : QStringLiteral( "cw" ) ) );
441 }
442 map[QStringLiteral( "stops" )] = lst.join( QLatin1Char( ':' ) );
443 }
444
445 map[QStringLiteral( "discrete" )] = mDiscrete ? "1" : "0";
446
447 for ( QgsStringMap::const_iterator it = mInfo.constBegin();
448 it != mInfo.constEnd(); ++it )
449 {
450 map["info_" + it.key()] = it.value();
451 }
452
453 switch ( mColorSpec )
454 {
455 case QColor::Rgb:
456 map[QStringLiteral( "spec" ) ] = QStringLiteral( "rgb" );
457 break;
458 case QColor::Hsv:
459 map[QStringLiteral( "spec" ) ] = QStringLiteral( "hsv" );
460 break;
461 case QColor::Hsl:
462 map[QStringLiteral( "spec" ) ] = QStringLiteral( "hsl" );
463 break;
464 case QColor::Cmyk:
465 case QColor::Invalid:
466#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
467 case QColor::ExtendedRgb:
468#endif
469 break;
470 }
471
472 switch ( mDirection )
473 {
475 map[QStringLiteral( "direction" ) ] = QStringLiteral( "cw" );
476 break;
478 map[QStringLiteral( "direction" ) ] = QStringLiteral( "ccw" );
479 break;
480 }
481
482 map[QStringLiteral( "rampType" )] = type();
483 return map;
484}
486{
487 if ( discrete == mDiscrete )
488 return;
489
490 // if going to/from Discrete, re-arrange stops
491 // this will only work when stops are equally-spaced
492 QgsGradientStopsList newStops;
493 if ( discrete )
494 {
495 // re-arrange stops offset
496 int numStops = mStops.count() + 2;
497 int i = 1;
498 for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
499 it != mStops.constEnd(); ++it )
500 {
501 newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, it->color ) );
502 if ( i == numStops - 1 )
503 break;
504 i++;
505 }
506 // replicate last color
507 newStops.append( QgsGradientStop( static_cast< double >( i ) / numStops, mColor2 ) );
508 }
509 else
510 {
511 // re-arrange stops offset, remove duplicate last color
512 int numStops = mStops.count() + 2;
513 int i = 1;
514 for ( QgsGradientStopsList::const_iterator it = mStops.constBegin();
515 it != mStops.constEnd(); ++it )
516 {
517 newStops.append( QgsGradientStop( static_cast< double >( i ) / ( numStops - 2 ), it->color ) );
518 if ( i == numStops - 3 )
519 break;
520 i++;
521 }
522 }
523 mStops = newStops;
524 mDiscrete = discrete;
525}
526
527bool stopLessThan( const QgsGradientStop &s1, const QgsGradientStop &s2 )
528{
529 return s1.offset < s2.offset;
530}
531
533{
534 mStops = stops;
535
536 //sort stops by offset
537 std::sort( mStops.begin(), mStops.end(), stopLessThan );
538}
539
540void QgsGradientColorRamp::addStopsToGradient( QGradient *gradient, double opacity ) const
541{
542 //copy color ramp stops to a QGradient
543 QColor color1 = mColor1;
544 QColor color2 = mColor2;
545 if ( opacity < 1 )
546 {
547 color1.setAlpha( color1.alpha() * opacity );
548 color2.setAlpha( color2.alpha() * opacity );
549 }
550 gradient->setColorAt( 0, color1 );
551 gradient->setColorAt( 1, color2 );
552
553 double lastOffset = 0;
554 for ( const QgsGradientStop &stop : mStops )
555 {
556
557 QColor rampColor = stop.color;
558 if ( opacity < 1 )
559 {
560 rampColor.setAlpha( rampColor.alpha() * opacity );
561 }
562 gradient->setColorAt( stop.offset, rampColor );
563
564 if ( stop.colorSpec() != QColor::Rgb )
565 {
566 // QGradient only supports RGB interpolation. For other color specs we have
567 // to "fake" things by populating the gradient with additional stops
568 for ( double offset = lastOffset + 0.05; offset < stop.offset; offset += 0.05 )
569 {
570 QColor midColor = color( offset );
571 if ( opacity < 1 )
572 {
573 midColor.setAlpha( midColor.alpha() * opacity );
574 }
575 gradient->setColorAt( offset, midColor );
576 }
577 }
578 lastOffset = stop.offset;
579 }
580
581 if ( mColorSpec != QColor::Rgb )
582 {
583 for ( double offset = lastOffset + 0.05; offset < 1; offset += 0.05 )
584 {
585 QColor midColor = color( offset );
586 if ( opacity < 1 )
587 {
588 midColor.setAlpha( midColor.alpha() * opacity );
589 }
590 gradient->setColorAt( offset, midColor );
591 }
592 }
593}
594
595void QgsGradientColorRamp::setColorSpec( QColor::Spec spec )
596{
597 mColorSpec = spec;
598 switch ( mColorSpec )
599 {
600 case QColor::Rgb:
601 case QColor::Invalid:
602#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
603 case QColor::ExtendedRgb:
604#endif
605 case QColor::Cmyk:
606 mFunc = _interpolateRgb;
607 break;
608 case QColor::Hsv:
609 mFunc = _interpolateHsv;
610 break;
611 case QColor::Hsl:
612 mFunc = _interpolateHsl;
613 break;
614 }
615}
616
617
619
620
622 int satMin, int satMax, int valMin, int valMax )
623 : mCount( count )
624 , mHueMin( hueMin ), mHueMax( hueMax )
625 , mSatMin( satMin ), mSatMax( satMax )
626 , mValMin( valMin ), mValMax( valMax )
627{
628 updateColors();
629}
630
632{
637
638 if ( props.contains( QStringLiteral( "count" ) ) ) count = props[QStringLiteral( "count" )].toInt();
639 if ( props.contains( QStringLiteral( "hueMin" ) ) ) hueMin = props[QStringLiteral( "hueMin" )].toInt();
640 if ( props.contains( QStringLiteral( "hueMax" ) ) ) hueMax = props[QStringLiteral( "hueMax" )].toInt();
641 if ( props.contains( QStringLiteral( "satMin" ) ) ) satMin = props[QStringLiteral( "satMin" )].toInt();
642 if ( props.contains( QStringLiteral( "satMax" ) ) ) satMax = props[QStringLiteral( "satMax" )].toInt();
643 if ( props.contains( QStringLiteral( "valMin" ) ) ) valMin = props[QStringLiteral( "valMin" )].toInt();
644 if ( props.contains( QStringLiteral( "valMax" ) ) ) valMax = props[QStringLiteral( "valMax" )].toInt();
645
647}
648
649double QgsLimitedRandomColorRamp::value( int index ) const
650{
651 if ( mColors.empty() )
652 return 0;
653 return static_cast< double >( index ) / ( mColors.size() - 1 );
654}
655
656QColor QgsLimitedRandomColorRamp::color( double value ) const
657{
658 if ( value < 0 || value > 1 )
659 return QColor();
660
661 int colorCnt = mColors.count();
662 int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
663
664 if ( colorIdx >= 0 && colorIdx < colorCnt )
665 return mColors.at( colorIdx );
666
667 return QColor();
668}
669
671{
673}
674
676{
678}
679
681{
682 QVariantMap map;
683 map[QStringLiteral( "count" )] = QString::number( mCount );
684 map[QStringLiteral( "hueMin" )] = QString::number( mHueMin );
685 map[QStringLiteral( "hueMax" )] = QString::number( mHueMax );
686 map[QStringLiteral( "satMin" )] = QString::number( mSatMin );
687 map[QStringLiteral( "satMax" )] = QString::number( mSatMax );
688 map[QStringLiteral( "valMin" )] = QString::number( mValMin );
689 map[QStringLiteral( "valMax" )] = QString::number( mValMax );
690 map[QStringLiteral( "rampType" )] = type();
691 return map;
692}
693
695 int hueMax, int hueMin, int satMax, int satMin, int valMax, int valMin )
696{
697 int h, s, v;
698 QList<QColor> colors;
699
700 //normalize values
701 int safeHueMax = std::max( hueMin, hueMax );
702 int safeHueMin = std::min( hueMin, hueMax );
703 int safeSatMax = std::max( satMin, satMax );
704 int safeSatMin = std::min( satMin, satMax );
705 int safeValMax = std::max( valMin, valMax );
706 int safeValMin = std::min( valMin, valMax );
707
708 //start hue at random angle
709 double currentHueAngle = 360.0 * static_cast< double >( std::rand() ) / RAND_MAX;
710
711 colors.reserve( count );
712 for ( int i = 0; i < count; ++i )
713 {
714 //increment hue by golden ratio (approx 137.507 degrees)
715 //as this minimizes hue nearness as count increases
716 //see http://basecase.org/env/on-rainbows for more details
717 currentHueAngle += 137.50776;
718 //scale hue to between hueMax and hueMin
719 h = std::clamp( std::round( ( std::fmod( currentHueAngle, 360.0 ) / 360.0 ) * ( safeHueMax - safeHueMin ) + safeHueMin ), 0.0, 359.0 );
720 s = std::clamp( ( static_cast<int>( std::rand() ) % ( safeSatMax - safeSatMin + 1 ) ) + safeSatMin, 0, 255 );
721 v = std::clamp( ( static_cast<int>( std::rand() ) % ( safeValMax - safeValMin + 1 ) ) + safeValMin, 0, 255 );
722 colors.append( QColor::fromHsv( h, s, v ) );
723 }
724 return colors;
725}
726
728{
730}
731
733
735{
736 return -1;
737}
738
739double QgsRandomColorRamp::value( int index ) const
740{
741 Q_UNUSED( index )
742 return 0.0;
743}
744
745QColor QgsRandomColorRamp::color( double value ) const
746{
747 int minVal = 130;
748 int maxVal = 255;
749
750 //if value is nan, then use last precalculated color
751 if ( std::isnan( value ) )
752 {
753 value = 1.0;
754 }
755 // Caller has converted an index into a value in [0.0, 1.0]
756 // by doing "index / (mTotalColorCount - 1)"; retrieve the original index.
757 int colorIndex = std::round( value * ( mTotalColorCount - 1 ) );
758 if ( mTotalColorCount >= 1 && mPrecalculatedColors.length() > colorIndex )
759 {
760 //use precalculated hue
761 return mPrecalculatedColors.at( colorIndex );
762 }
763
764 //can't use precalculated hues, use a totally random hue
765 int h = static_cast< int >( 360.0 * std::rand() / ( RAND_MAX + 1.0 ) );
766 int s = ( std::rand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
767 int v = ( std::rand() % ( maxVal - minVal + 1 ) ) + minVal;
768 return QColor::fromHsv( h, s, v );
769}
770
771void QgsRandomColorRamp::setTotalColorCount( const int colorCount )
772{
773 //calculate colors in advance, so that we can ensure they are more visually distinct than pure random colors
774 mPrecalculatedColors.clear();
775 mTotalColorCount = colorCount;
776
777 //This works OK for low color counts, but for > 10 or so colors there's still a good chance of
778 //similar colors being picked. TODO - investigate alternative "n-visually distinct color" routines
779
780 //random offsets
781 double hueOffset = ( 360.0 * std::rand() / ( RAND_MAX + 1.0 ) );
782
783 //try to maximise difference between hues. this is not an ideal implementation, as constant steps
784 //through the hue wheel are not visually perceived as constant changes in hue
785 //(for instance, we are much more likely to get green hues than yellow hues)
786 double hueStep = 359.0 / colorCount;
787 double currentHue = hueOffset;
788
789 //build up a list of colors
790 for ( int idx = 0; idx < colorCount; ++ idx )
791 {
792 int h = static_cast< int >( std::round( currentHue ) ) % 360;
793 int s = ( std::rand() % ( DEFAULT_RANDOM_SAT_MAX - DEFAULT_RANDOM_SAT_MIN + 1 ) ) + DEFAULT_RANDOM_SAT_MIN;
794 int v = ( std::rand() % ( DEFAULT_RANDOM_VAL_MAX - DEFAULT_RANDOM_VAL_MIN + 1 ) ) + DEFAULT_RANDOM_VAL_MIN;
795 mPrecalculatedColors << QColor::fromHsv( h, s, v );
796 currentHue += hueStep;
797 }
798
799 //lastly, shuffle color list
800 std::random_device rd;
801 std::mt19937 g( rd() );
802 std::shuffle( mPrecalculatedColors.begin(), mPrecalculatedColors.end(), g );
803}
804
806{
808}
809
811{
812 return new QgsRandomColorRamp();
813}
814
816{
817 return QVariantMap();
818}
819
821
822QgsColorBrewerColorRamp::QgsColorBrewerColorRamp( const QString &schemeName, int colors, bool inverted )
823 : mSchemeName( schemeName )
824 , mColors( colors )
825 , mInverted( inverted )
826{
827 loadPalette();
828}
829
831{
834 bool inverted = false;
835
836 if ( props.contains( QStringLiteral( "schemeName" ) ) )
837 schemeName = props[QStringLiteral( "schemeName" )].toString();
838 if ( props.contains( QStringLiteral( "colors" ) ) )
839 colors = props[QStringLiteral( "colors" )].toInt();
840 if ( props.contains( QStringLiteral( "inverted" ) ) )
841 inverted = props[QStringLiteral( "inverted" )].toInt();
842
843 return new QgsColorBrewerColorRamp( schemeName, colors, inverted );
844}
845
847{
849
850 if ( mInverted )
851 {
852 QList<QColor> tmpPalette;
853
854 for ( int k = mPalette.size() - 1; k >= 0; k-- )
855 {
856 tmpPalette << mPalette.at( k );
857 }
858 mPalette = tmpPalette;
859 }
860}
861
863{
865}
866
867QList<int> QgsColorBrewerColorRamp::listSchemeVariants( const QString &schemeName )
868{
870}
871
872double QgsColorBrewerColorRamp::value( int index ) const
873{
874 if ( mPalette.empty() )
875 return 0;
876 return static_cast< double >( index ) / ( mPalette.size() - 1 );
877}
878
879QColor QgsColorBrewerColorRamp::color( double value ) const
880{
881 if ( mPalette.isEmpty() || value < 0 || value > 1 || std::isnan( value ) )
882 return QColor();
883
884 int paletteEntry = static_cast< int >( value * mPalette.count() );
885 if ( paletteEntry >= mPalette.count() )
886 paletteEntry = mPalette.count() - 1;
887 return mPalette.at( paletteEntry );
888}
889
891{
893 loadPalette();
894}
895
897{
899}
900
902{
903 QVariantMap map;
904 map[QStringLiteral( "schemeName" )] = mSchemeName;
905 map[QStringLiteral( "colors" )] = QString::number( mColors );
906 map[QStringLiteral( "inverted" )] = QString::number( mInverted );
907 map[QStringLiteral( "rampType" )] = type();
908 return map;
909}
910
911
913
914
915QgsCptCityColorRamp::QgsCptCityColorRamp( const QString &schemeName, const QString &variantName,
916 bool inverted, bool doLoadFile )
918 , mSchemeName( schemeName )
919 , mVariantName( variantName )
920 , mInverted( inverted )
921{
922 // TODO replace this with hard-coded data in the default case
923 // don't load file if variant is missing
924 if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
925 loadFile();
926}
927
928QgsCptCityColorRamp::QgsCptCityColorRamp( const QString &schemeName, const QStringList &variantList,
929 const QString &variantName, bool inverted, bool doLoadFile )
931 , mSchemeName( schemeName )
932 , mVariantName( variantName )
933 , mVariantList( variantList )
934 , mInverted( inverted )
935{
937
938 // TODO replace this with hard-coded data in the default case
939 // don't load file if variant is missing
940 if ( doLoadFile && ( variantName != QString() || mVariantList.isEmpty() ) )
941 loadFile();
942}
943
944QgsColorRamp *QgsCptCityColorRamp::create( const QVariantMap &props )
945{
948 bool inverted = false;
949
950 if ( props.contains( QStringLiteral( "schemeName" ) ) )
951 schemeName = props[QStringLiteral( "schemeName" )].toString();
952 if ( props.contains( QStringLiteral( "variantName" ) ) )
953 variantName = props[QStringLiteral( "variantName" )].toString();
954 if ( props.contains( QStringLiteral( "inverted" ) ) )
955 inverted = props[QStringLiteral( "inverted" )].toInt();
956
957 return new QgsCptCityColorRamp( schemeName, variantName, inverted );
958}
959
961{
963}
964
966{
969}
970
972{
973 QgsCptCityColorRamp *ramp = new QgsCptCityColorRamp( QString(), QString(), mInverted, false );
974 ramp->copy( this );
975 return ramp;
976}
977
979{
980 if ( ! other )
981 return;
982 mColor1 = other->color1();
983 mColor2 = other->color2();
984 mDiscrete = other->isDiscrete();
985 mStops = other->stops();
986 mSchemeName = other->mSchemeName;
987 mVariantName = other->mVariantName;
988 mVariantList = other->mVariantList;
989 mFileLoaded = other->mFileLoaded;
990 mInverted = other->mInverted;
991}
992
994{
997 // add author and copyright information
998 // TODO also add COPYING.xml file/link?
1000 info[QStringLiteral( "cpt-city-gradient" )] = "<cpt-city>/" + mSchemeName + mVariantName + ".svg";
1001 QString copyingFilename = copyingFileName();
1002 copyingFilename.remove( QgsCptCityArchive::defaultBaseDir() );
1003 info[QStringLiteral( "cpt-city-license" )] = "<cpt-city>" + copyingFilename;
1004 ramp->setInfo( info );
1005 return ramp;
1006}
1007
1008
1010{
1011 QVariantMap map;
1012 map[QStringLiteral( "schemeName" )] = mSchemeName;
1013 map[QStringLiteral( "variantName" )] = mVariantName;
1014 map[QStringLiteral( "inverted" )] = QString::number( mInverted );
1015 map[QStringLiteral( "rampType" )] = type();
1016 return map;
1017}
1018
1019QString QgsCptCityColorRamp::fileNameForVariant( const QString &schema, const QString &variant )
1020{
1021 return QgsCptCityArchive::defaultBaseDir() + QDir::separator() + schema + variant + ".svg";
1022}
1023
1025{
1026 if ( mSchemeName.isEmpty() )
1027 return QString();
1028 else
1029 {
1030 return QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mSchemeName + mVariantName + ".svg";
1031 }
1032}
1033
1035{
1036 return QgsCptCityArchive::findFileName( QStringLiteral( "COPYING.xml" ), QFileInfo( fileName() ).dir().path(),
1038}
1039
1041{
1042 return QgsCptCityArchive::findFileName( QStringLiteral( "DESC.xml" ), QFileInfo( fileName() ).dir().path(),
1044}
1045
1047{
1049}
1050
1052{
1053 if ( mFileLoaded )
1054 {
1055 QgsDebugMsgLevel( "File already loaded for " + mSchemeName + mVariantName, 2 );
1056 return true;
1057 }
1058
1059 // get filename
1060 QString filename = fileName();
1061 if ( filename.isNull() )
1062 {
1063 return false;
1064 }
1065
1066 QgsDebugMsgLevel( QStringLiteral( "filename= %1 loaded=%2" ).arg( filename ).arg( mFileLoaded ), 2 );
1067
1068 // get color ramp from svg file
1069 QMap< double, QPair<QColor, QColor> > colorMap =
1071
1072 // add colors to palette
1073 mFileLoaded = false;
1074 mStops.clear();
1075 QMap<double, QPair<QColor, QColor> >::const_iterator it, prev;
1076 // first detect if file is gradient is continuous or discrete
1077 // discrete: stop contains 2 colors and first color is identical to previous second
1078 // multi: stop contains 2 colors and no relation with previous stop
1079 mDiscrete = false;
1080 mMultiStops = false;
1081 it = prev = colorMap.constBegin();
1082 while ( it != colorMap.constEnd() )
1083 {
1084 // look for stops that contain multiple values
1085 if ( it != colorMap.constBegin() && ( it.value().first != it.value().second ) )
1086 {
1087 if ( it.value().first == prev.value().second )
1088 {
1089 mDiscrete = true;
1090 break;
1091 }
1092 else
1093 {
1094 mMultiStops = true;
1095 break;
1096 }
1097 }
1098 prev = it;
1099 ++it;
1100 }
1101
1102 // fill all stops
1103 it = prev = colorMap.constBegin();
1104 while ( it != colorMap.constEnd() )
1105 {
1106 if ( mDiscrete )
1107 {
1108 // mPalette << qMakePair( it.key(), it.value().second );
1109 mStops.append( QgsGradientStop( it.key(), it.value().second ) );
1110 }
1111 else
1112 {
1113 // mPalette << qMakePair( it.key(), it.value().first );
1114 mStops.append( QgsGradientStop( it.key(), it.value().first ) );
1115 if ( ( mMultiStops ) &&
1116 ( it.key() != 0.0 && it.key() != 1.0 ) )
1117 {
1118 mStops.append( QgsGradientStop( it.key(), it.value().second ) );
1119 }
1120 }
1121 prev = it;
1122 ++it;
1123 }
1124
1125 // remove first and last items (mColor1 and mColor2)
1126 if ( ! mStops.isEmpty() && mStops.at( 0 ).offset == 0.0 )
1127 mColor1 = mStops.takeFirst().color;
1128 if ( ! mStops.isEmpty() && mStops.last().offset == 1.0 )
1129 mColor2 = mStops.takeLast().color;
1130
1131 if ( mInverted )
1132 {
1134 }
1135
1136 mFileLoaded = true;
1137 return true;
1138}
1139
1140
1141//
1142// QgsPresetColorRamp
1143//
1144
1146{
1147 const auto constColors = colors;
1148 for ( const QColor &color : constColors )
1149 {
1150 mColors << qMakePair( color, color.name() );
1151 }
1152 // need at least one color
1153 if ( mColors.isEmpty() )
1154 mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
1155}
1156
1158 : mColors( colors )
1159{
1160 // need at least one color
1161 if ( mColors.isEmpty() )
1162 mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral( "#fa4b3c" ) );
1163}
1164
1165QgsColorRamp *QgsPresetSchemeColorRamp::create( const QVariantMap &properties )
1166{
1168
1169 int i = 0;
1170 QString colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() ).toString();
1171 QString colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() ).toString();
1172 while ( !colorString.isEmpty() )
1173 {
1174 colors << qMakePair( QgsSymbolLayerUtils::decodeColor( colorString ), colorName );
1175 i++;
1176 colorString = properties.value( QStringLiteral( "preset_color_%1" ).arg( i ), QString() ).toString();
1177 colorName = properties.value( QStringLiteral( "preset_color_name_%1" ).arg( i ), QString() ).toString();
1178 }
1179
1180 return new QgsPresetSchemeColorRamp( colors );
1181}
1182
1184{
1185 QList< QColor > l;
1186 l.reserve( mColors.count() );
1187 for ( int i = 0; i < mColors.count(); ++i )
1188 {
1189 l << mColors.at( i ).first;
1190 }
1191 return l;
1192}
1193
1194double QgsPresetSchemeColorRamp::value( int index ) const
1195{
1196 if ( mColors.empty() )
1197 return 0;
1198 return static_cast< double >( index ) / ( mColors.size() - 1 );
1199}
1200
1201QColor QgsPresetSchemeColorRamp::color( double value ) const
1202{
1203 if ( value < 0 || value > 1 )
1204 return QColor();
1205
1206 int colorCnt = mColors.count();
1207 int colorIdx = std::min( static_cast< int >( value * colorCnt ), colorCnt - 1 );
1208
1209 if ( colorIdx >= 0 && colorIdx < colorCnt )
1210 return mColors.at( colorIdx ).first;
1211
1212 return QColor();
1213}
1214
1216{
1218}
1219
1221{
1222 QgsNamedColorList tmpColors;
1223
1224 for ( int k = mColors.size() - 1; k >= 0; k-- )
1225 {
1226 tmpColors << mColors.at( k );
1227 }
1228 mColors = tmpColors;
1229}
1230
1232{
1233 return new QgsPresetSchemeColorRamp( *this );
1234}
1235
1237{
1238 QVariantMap props;
1239 for ( int i = 0; i < mColors.count(); ++i )
1240 {
1241 props.insert( QStringLiteral( "preset_color_%1" ).arg( i ), QgsSymbolLayerUtils::encodeColor( mColors.at( i ).first ) );
1242 props.insert( QStringLiteral( "preset_color_name_%1" ).arg( i ), mColors.at( i ).second );
1243 }
1244 props[QStringLiteral( "rampType" )] = type();
1245 return props;
1246}
1247
1249{
1250 return mColors.count();
1251}
1252
1254{
1255 return mColors;
1256}
AngularDirection
Angular directions.
Definition: qgis.h:1696
@ 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:2404
QMap< QString, QString > QgsStringMap
Definition: qgis.h:2920
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