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