34 static QColor _interpolateRgb(
const QColor &c1,
const QColor &c2,
const double value,
const Qgis::AngularDirection )
36 if ( std::isnan( value ) )
39 const qreal red1 = c1.redF();
40 const qreal red2 = c2.redF();
41 const qreal red = ( red1 + value * ( red2 - red1 ) );
43 const qreal green1 = c1.greenF();
44 const qreal green2 = c2.greenF();
45 const qreal green = ( green1 + value * ( green2 - green1 ) );
47 const qreal blue1 = c1.blueF();
48 const qreal blue2 = c2.blueF();
49 const qreal blue = ( blue1 + value * ( blue2 - blue1 ) );
51 const qreal alpha1 = c1.alphaF();
52 const qreal alpha2 = c2.alphaF();
53 const qreal alpha = ( alpha1 + value * ( alpha2 - alpha1 ) );
55 return QColor::fromRgbF( red, green, blue, alpha );
58 static QColor _interpolateHsv(
const QColor &c1,
const QColor &c2,
const double value,
const Qgis::AngularDirection direction )
60 if ( std::isnan( value ) )
63 qreal hue1 = c1.hsvHueF();
64 qreal hue2 = c2.hsvHueF();
68 else if ( hue2 == -1 )
79 hue = hue1 - value * ( hue1 - hue2 );
92 hue = hue1 + value * ( hue2 - hue1 );
100 const qreal saturation1 = c1.hsvSaturationF();
101 const qreal saturation2 = c2.hsvSaturationF();
102 const qreal saturation = ( saturation1 + value * ( saturation2 - saturation1 ) );
104 const qreal value1 = c1.valueF();
105 const qreal value2 = c2.valueF();
106 const qreal valueOut = ( value1 + value * ( value2 - value1 ) );
108 const qreal alpha1 = c1.alphaF();
109 const qreal alpha2 = c2.alphaF();
110 const qreal alpha = ( alpha1 + value * ( alpha2 - alpha1 ) );
112 return QColor::fromHsvF( hue > 1 ? hue - 1 : hue, saturation, valueOut, alpha );
115 static QColor _interpolateHsl(
const QColor &c1,
const QColor &c2,
const double value,
const Qgis::AngularDirection direction )
117 if ( std::isnan( value ) )
120 qreal hue1 = c1.hslHueF();
121 qreal hue2 = c2.hslHueF();
125 else if ( hue2 == -1 )
136 hue = hue1 - value * ( hue1 - hue2 );
149 hue = hue1 + value * ( hue2 - hue1 );
157 const qreal saturation1 = c1.hslSaturationF();
158 const qreal saturation2 = c2.hslSaturationF();
159 const qreal saturation = ( saturation1 + value * ( saturation2 - saturation1 ) );
161 const qreal lightness1 = c1.lightnessF();
162 const qreal lightness2 = c2.lightnessF();
163 const qreal lightness = ( lightness1 + value * ( lightness2 - lightness1 ) );
165 const qreal alpha1 = c1.alphaF();
166 const qreal alpha2 = c2.alphaF();
167 const qreal alpha = ( alpha1 + value * ( alpha2 - alpha1 ) );
169 return QColor::fromHslF( hue > 1 ? hue - 1 : hue, saturation, lightness, alpha );
178 , mFunc( _interpolateRgb )
187 switch ( mColorSpec )
190 case QColor::Invalid:
191 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
192 case QColor::ExtendedRgb:
195 mFunc = _interpolateRgb;
198 mFunc = _interpolateHsv;
201 mFunc = _interpolateHsl;
210 , mDiscrete( discrete )
212 , mFunc( _interpolateRgb )
221 if ( props.contains( QStringLiteral(
"color1" ) ) )
223 if ( props.contains( QStringLiteral(
"color2" ) ) )
228 if ( props.contains( QStringLiteral(
"stops" ) ) )
230 const auto constSplit = props[QStringLiteral(
"stops" )].toString().split(
':' );
231 for (
const QString &stop : constSplit )
233 const QStringList parts = stop.split(
';' );
234 if ( parts.size() != 2 && parts.size() != 4 )
240 if ( parts.size() == 4 )
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 );
249 if ( parts.at( 3 ).compare( QLatin1String(
"cw" ) ) == 0 )
251 else if ( parts.at( 3 ).compare( QLatin1String(
"ccw" ) ) == 0 )
258 bool discrete =
false;
259 if ( props.contains( QStringLiteral(
"discrete" ) ) )
261 if ( props[QStringLiteral(
"discrete" )] == QLatin1String(
"1" ) )
267 for ( QVariantMap::const_iterator it = props.constBegin();
268 it != props.constEnd(); ++it )
270 if ( it.key().startsWith( QLatin1String(
"info_" ) ) )
271 info[ it.key().mid( 5 )] = it.value().toString();
277 if ( props.contains( QStringLiteral(
"spec" ) ) )
279 const QString spec = props.value( QStringLiteral(
"spec" ) ).toString().trimmed();
280 if ( spec.compare( QLatin1String(
"rgb" ) ) == 0 )
282 else if ( spec.compare( QLatin1String(
"hsv" ) ) == 0 )
284 else if ( spec.compare( QLatin1String(
"hsl" ) ) == 0 )
288 if ( props.contains( QStringLiteral(
"direction" ) ) )
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 )
306 else if ( index >=
mStops.size() + 1 )
312 return mStops[index - 1].offset;
326 else if (
mStops.isEmpty() )
335 double lower = 0, upper = 0;
337 for ( QgsGradientStopsList::const_iterator it =
mStops.begin(); it !=
mStops.end(); ++it )
339 if ( it->offset >
value )
370 newStops.reserve(
mStops.size() );
376 for (
int k =
mStops.size() - 1; k >= 1; k-- )
387 for (
int k =
mStops.size() - 1; k >= 0; k-- )
403 for (
int i = 1, j =
mStops.size() - 1; i <
mStops.size(); ++i, --j )
405 newStops[i].setColorSpec(
mStops.at( j ).colorSpec() );
433 lst.reserve(
mStops.size() );
437 stop.colorSpec() == QColor::Rgb ? QStringLiteral(
"rgb" )
438 : stop.colorSpec() == QColor::Hsv ? QStringLiteral(
"hsv" )
439 : stop.colorSpec() == QColor::Hsl ? QStringLiteral(
"hsl" ) : QString(),
442 map[QStringLiteral(
"stops" )] = lst.join( QLatin1Char(
':' ) );
445 map[QStringLiteral(
"discrete" )] =
mDiscrete ?
"1" :
"0";
447 for ( QgsStringMap::const_iterator it =
mInfo.constBegin();
448 it !=
mInfo.constEnd(); ++it )
450 map[
"info_" + it.key()] = it.value();
456 map[QStringLiteral(
"spec" ) ] = QStringLiteral(
"rgb" );
459 map[QStringLiteral(
"spec" ) ] = QStringLiteral(
"hsv" );
462 map[QStringLiteral(
"spec" ) ] = QStringLiteral(
"hsl" );
465 case QColor::Invalid:
466 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
467 case QColor::ExtendedRgb:
475 map[QStringLiteral(
"direction" ) ] = QStringLiteral(
"cw" );
478 map[QStringLiteral(
"direction" ) ] = QStringLiteral(
"ccw" );
482 map[QStringLiteral(
"rampType" )] =
type();
496 int numStops =
mStops.count() + 2;
498 for ( QgsGradientStopsList::const_iterator it =
mStops.constBegin();
499 it !=
mStops.constEnd(); ++it )
501 newStops.append(
QgsGradientStop(
static_cast< double >( i ) / numStops, it->color ) );
502 if ( i == numStops - 1 )
512 int numStops =
mStops.count() + 2;
514 for ( QgsGradientStopsList::const_iterator it =
mStops.constBegin();
515 it !=
mStops.constEnd(); ++it )
517 newStops.append(
QgsGradientStop(
static_cast< double >( i ) / ( numStops - 2 ), it->color ) );
518 if ( i == numStops - 3 )
550 gradient->setColorAt( 0,
color1 );
551 gradient->setColorAt( 1,
color2 );
553 double lastOffset = 0;
557 QColor rampColor = stop.color;
560 rampColor.setAlpha( rampColor.alpha() * opacity );
562 gradient->setColorAt( stop.offset, rampColor );
564 if ( stop.colorSpec() != QColor::Rgb )
568 for (
double offset = lastOffset + 0.05; offset < stop.offset; offset += 0.05 )
570 QColor midColor =
color( offset );
573 midColor.setAlpha( midColor.alpha() * opacity );
575 gradient->setColorAt( offset, midColor );
578 lastOffset = stop.offset;
583 for (
double offset = lastOffset + 0.05; offset < 1; offset += 0.05 )
585 QColor midColor =
color( offset );
588 midColor.setAlpha( midColor.alpha() * opacity );
590 gradient->setColorAt( offset, midColor );
601 case QColor::Invalid:
602 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
603 case QColor::ExtendedRgb:
606 mFunc = _interpolateRgb;
609 mFunc = _interpolateHsv;
612 mFunc = _interpolateHsl;
622 int satMin,
int satMax,
int valMin,
int valMax )
624 , mHueMin( hueMin ), mHueMax( hueMax )
625 , mSatMin( satMin ), mSatMax( satMax )
626 , mValMin( valMin ), mValMax( valMax )
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();
653 return static_cast< double >( index ) / (
mColors.size() - 1 );
658 if ( value < 0 || value > 1 )
661 int colorCnt =
mColors.count();
662 int colorIdx = std::min(
static_cast< int >(
value * colorCnt ), colorCnt - 1 );
664 if ( colorIdx >= 0 && colorIdx < colorCnt )
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();
695 int hueMax,
int hueMin,
int satMax,
int satMin,
int valMax,
int valMin )
698 QList<QColor> colors;
709 double currentHueAngle = 360.0 *
static_cast< double >( std::rand() ) / RAND_MAX;
711 colors.reserve(
count );
712 for (
int i = 0; i <
count; ++i )
717 currentHueAngle += 137.50776;
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 ) );
751 if ( std::isnan(
value ) )
765 int h =
static_cast< int >( 360.0 * std::rand() / ( RAND_MAX + 1.0 ) );
767 int v = ( std::rand() % ( maxVal - minVal + 1 ) ) + minVal;
768 return QColor::fromHsv( h, s, v );
781 double hueOffset = ( 360.0 * std::rand() / ( RAND_MAX + 1.0 ) );
786 double hueStep = 359.0 / colorCount;
787 double currentHue = hueOffset;
790 for (
int idx = 0; idx < colorCount; ++ idx )
792 int h =
static_cast< int >( std::round( currentHue ) ) % 360;
796 currentHue += hueStep;
800 std::random_device rd;
801 std::mt19937 g( rd() );
817 return QVariantMap();
823 : mSchemeName( schemeName )
825 , mInverted( inverted )
834 bool inverted =
false;
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();
852 QList<QColor> tmpPalette;
854 for (
int k =
mPalette.size() - 1; k >= 0; k-- )
876 return static_cast< double >( index ) / (
mPalette.size() - 1 );
881 if (
mPalette.isEmpty() || value < 0 || value > 1 || std::isnan(
value ) )
884 int paletteEntry =
static_cast< int >(
value *
mPalette.count() );
885 if ( paletteEntry >=
mPalette.count() )
886 paletteEntry =
mPalette.count() - 1;
905 map[QStringLiteral(
"colors" )] = QString::number(
mColors );
906 map[QStringLiteral(
"inverted" )] = QString::number(
mInverted );
907 map[QStringLiteral(
"rampType" )] =
type();
916 bool inverted,
bool doLoadFile )
918 , mSchemeName( schemeName )
919 , mVariantName( variantName )
920 , mInverted( inverted )
929 const QString &variantName,
bool inverted,
bool doLoadFile )
931 , mSchemeName( schemeName )
932 , mVariantName( variantName )
933 , mVariantList( variantList )
934 , mInverted( inverted )
948 bool inverted =
false;
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();
1003 info[QStringLiteral(
"cpt-city-license" )] =
"<cpt-city>" + copyingFilename;
1012 map[QStringLiteral(
"schemeName" )] =
mSchemeName;
1014 map[QStringLiteral(
"inverted" )] = QString::number(
mInverted );
1015 map[QStringLiteral(
"rampType" )] =
type();
1061 if ( filename.isNull() )
1069 QMap< double, QPair<QColor, QColor> > colorMap =
1075 QMap<double, QPair<QColor, QColor> >::const_iterator it, prev;
1081 it = prev = colorMap.constBegin();
1082 while ( it != colorMap.constEnd() )
1085 if ( it != colorMap.constBegin() && ( it.value().first != it.value().second ) )
1087 if ( it.value().first == prev.value().second )
1103 it = prev = colorMap.constBegin();
1104 while ( it != colorMap.constEnd() )
1116 ( it.key() != 0.0 && it.key() != 1.0 ) )
1126 if ( !
mStops.isEmpty() &&
mStops.at( 0 ).offset == 0.0 )
1128 if ( !
mStops.isEmpty() &&
mStops.last().offset == 1.0 )
1147 const auto constColors =
colors;
1148 for (
const QColor &
color : constColors )
1153 if ( mColors.isEmpty() )
1154 mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral(
"#fa4b3c" ) );
1161 if ( mColors.isEmpty() )
1162 mColors << qMakePair( QColor( 250, 75, 60 ), QStringLiteral(
"#fa4b3c" ) );
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() )
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();
1186 l.reserve( mColors.count() );
1187 for (
int i = 0; i < mColors.count(); ++i )
1189 l << mColors.at( i ).first;
1196 if ( mColors.empty() )
1198 return static_cast< double >( index ) / ( mColors.size() - 1 );
1203 if ( value < 0 || value > 1 )
1206 int colorCnt = mColors.count();
1207 int colorIdx = std::min(
static_cast< int >(
value * colorCnt ), colorCnt - 1 );
1209 if ( colorIdx >= 0 && colorIdx < colorCnt )
1210 return mColors.at( colorIdx ).first;
1224 for (
int k = mColors.size() - 1; k >= 0; k-- )
1226 tmpColors << mColors.at( k );
1228 mColors = tmpColors;
1239 for (
int i = 0; i < mColors.count(); ++i )
1242 props.insert( QStringLiteral(
"preset_color_name_%1" ).arg( i ), mColors.at( i ).second );
1244 props[QStringLiteral(
"rampType" )] =
type();
1250 return mColors.count();