34 #include <QPushButton> 35 #include <QInputDialog> 36 #include <QFileDialog> 38 #include <QMessageBox> 39 #include <QTextStream> 48 mLoadFromBandButton->setVisible(
false );
50 connect( mAddEntryButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mAddEntryButton_clicked );
51 connect( mDeleteEntryButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mDeleteEntryButton_clicked );
52 connect( mLoadFromBandButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mLoadFromBandButton_clicked );
53 connect( mLoadFromFileButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mLoadFromFileButton_clicked );
54 connect( mExportToFileButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mExportToFileButton_clicked );
55 connect( mUnitLineEdit, &QLineEdit::textEdited,
this, &QgsColorRampShaderWidget::mUnitLineEdit_textEdited );
56 connect( mColormapTreeWidget, &QTreeWidget::itemDoubleClicked,
this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked );
57 connect( mColorInterpolationComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged );
58 connect( mClassificationModeComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged );
60 contextMenu =
new QMenu( tr(
"Options" ),
this );
61 contextMenu->addAction( tr(
"Change Color…" ),
this, SLOT( changeColor() ) );
62 contextMenu->addAction( tr(
"Change Opacity…" ),
this, SLOT( changeOpacity() ) );
65 mColormapTreeWidget->setItemDelegateForColumn( ColorColumn, mSwatchDelegate );
66 mColormapTreeWidget->setColumnWidth( ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().width(
'X' ) * 6.6 );
67 mColormapTreeWidget->setContextMenuPolicy( Qt::CustomContextMenu );
68 mColormapTreeWidget->setSelectionMode( QAbstractItemView::ExtendedSelection );
69 connect( mColormapTreeWidget, &QTreeView::customContextMenuRequested,
this, [ = ]( QPoint ) { contextMenu->exec( QCursor::pos() ); }
72 QString defaultPalette = settings.
value( QStringLiteral(
"Raster/defaultPalette" ),
"" ).toString();
73 btnColorRamp->setColorRampFromName( defaultPalette );
85 mNumberOfEntriesSpinBox->setValue( 5 );
87 mClassificationModeComboBox_currentIndexChanged( 0 );
89 resetClassifyButton();
92 connect( mClassifyButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::applyColorRamp );
106 mRasterDataProvider = dp;
107 mLoadFromBandButton->setVisible(
bool( mRasterDataProvider ) );
123 colorRampShader.
setColorRampType( static_cast< QgsColorRampShader::Type >( mColorInterpolationComboBox->currentData().toInt() ) );
124 colorRampShader.
setClassificationMode( static_cast< QgsColorRampShader::ClassificationMode >( mClassificationModeComboBox->currentData().toInt() ) );
125 colorRampShader.
setClip( mClipCheckBox->isChecked() );
128 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
129 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
130 QTreeWidgetItem *currentItem =
nullptr;
131 for (
int i = 0; i < topLevelItemCount; ++i )
133 currentItem = mColormapTreeWidget->topLevelItem( i );
139 newColorRampItem.
value = currentItem->text( ValueColumn ).toDouble();
140 newColorRampItem.
color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
141 newColorRampItem.
label = currentItem->text( LabelColumn );
142 colorRampItems.append( newColorRampItem );
145 std::sort( colorRampItems.begin(), colorRampItems.end() );
148 if ( !btnColorRamp->isNull() )
152 return colorRampShader;
155 void QgsColorRampShaderWidget::autoLabel()
159 QString unit = mUnitLineEdit->text();
161 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
162 QTreeWidgetItem *currentItem =
nullptr;
163 for (
int i = 0; i < topLevelItemCount; ++i )
165 currentItem = mColormapTreeWidget->topLevelItem( i );
167 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
176 label =
"<= " + currentItem->text( ValueColumn ) + unit;
178 else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
180 label =
"> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) + unit;
184 label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) +
" - " + currentItem->text( ValueColumn ) + unit;
189 label = currentItem->text( ValueColumn ) + unit;
192 if ( currentItem->text( LabelColumn ).isEmpty() || currentItem->text( LabelColumn ) == label || currentItem->foreground( LabelColumn ).color() == QColor( Qt::gray ) )
194 currentItem->setText( LabelColumn, label );
195 currentItem->setForeground( LabelColumn, QBrush( QColor( Qt::gray ) ) );
200 void QgsColorRampShaderWidget::setUnitFromLabels()
204 QStringList allSuffixes;
206 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
207 QTreeWidgetItem *currentItem =
nullptr;
208 for (
int i = 0; i < topLevelItemCount; ++i )
210 currentItem = mColormapTreeWidget->topLevelItem( i );
212 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
221 label =
"<= " + currentItem->text( ValueColumn );
223 else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
225 label =
"> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn );
229 label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) +
" - " + currentItem->text( ValueColumn );
234 label = currentItem->text( ValueColumn );
237 if ( currentItem->text( LabelColumn ).startsWith( label ) )
239 allSuffixes.append( currentItem->text( LabelColumn ).mid( label.length() ) );
243 QStringList suffixes = QStringList( allSuffixes );
244 suffixes.removeDuplicates();
247 for (
int i = 0; i < suffixes.count(); ++i )
249 int n = allSuffixes.count( suffixes[i] );
259 mUnitLineEdit->setText( unit );
265 void QgsColorRampShaderWidget::mAddEntryButton_clicked()
268 newItem->setText( ValueColumn, QStringLiteral(
"0" ) );
269 newItem->
setData( ColorColumn, Qt::EditRole, QColor( Qt::magenta ) );
270 newItem->setText( LabelColumn, QString() );
271 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
273 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
274 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
281 void QgsColorRampShaderWidget::mDeleteEntryButton_clicked()
283 QList<QTreeWidgetItem *> itemList;
284 itemList = mColormapTreeWidget->selectedItems();
285 if ( itemList.isEmpty() )
290 const auto constItemList = itemList;
291 for ( QTreeWidgetItem *item : constItemList )
302 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
303 if ( !ramp || std::isnan( mMin ) || std::isnan( mMax ) )
312 static_cast< QgsColorRampShader::ClassificationMode >( mClassificationModeComboBox->currentData().toInt() ) )
316 colorRampShader->classifyColorRamp( mNumberOfEntriesSpinBox->value(),
319 mRasterDataProvider );
320 colorRampShader->setClip( mClipCheckBox->isChecked() );
323 mColormapTreeWidget->clear();
325 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
326 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
327 for ( ; it != colorRampItemList.end(); ++it )
330 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
331 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
332 newItem->setText( LabelColumn, it->label );
333 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
335 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
337 mClipCheckBox->setChecked( colorRampShader->clip() );
344 void QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged(
int index )
352 void QgsColorRampShaderWidget::applyColorRamp()
354 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
360 if ( !btnColorRamp->colorRampName().isEmpty() )
364 settings.
setValue( QStringLiteral(
"Raster/defaultPalette" ), btnColorRamp->colorRampName() );
367 bool enableContinuous = ( ramp->count() > 0 );
368 mClassificationModeComboBox->setEnabled( enableContinuous );
369 if ( !enableContinuous )
379 mColormapTreeWidget->clear();
380 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
381 for ( ; it != colorRampItems.constEnd(); ++it )
384 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
385 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
386 newItem->setText( LabelColumn, it->label );
387 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
389 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
397 void QgsColorRampShaderWidget::mLoadFromBandButton_clicked()
399 if ( !mRasterDataProvider )
402 QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterDataProvider->
colorTable( mBand );
403 if ( !colorRampList.isEmpty() )
410 QMessageBox::warning(
this, tr(
"Load Color Map" ), tr(
"The color map for band %1 has no entries." ).arg( mBand ) );
417 void QgsColorRampShaderWidget::mLoadFromFileButton_clicked()
420 bool importError =
false;
423 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
424 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Map from File" ), lastDir, tr(
"Textfile (*.txt)" ) );
425 QFile inputFile( fileName );
426 if ( inputFile.open( QFile::ReadOnly ) )
429 mColormapTreeWidget->clear();
431 QTextStream inputStream( &inputFile );
433 QStringList inputStringComponents;
434 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
437 while ( !inputStream.atEnd() )
440 inputLine = inputStream.readLine();
441 if ( !inputLine.isEmpty() )
443 if ( !inputLine.simplified().startsWith(
'#' ) )
445 if ( inputLine.contains( QLatin1String(
"INTERPOLATION" ), Qt::CaseInsensitive ) )
447 inputStringComponents = inputLine.split(
':' );
448 if ( inputStringComponents.size() == 2 )
450 if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"INTERPOLATED" ), Qt::CaseInsensitive ) == 0 )
454 else if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"DISCRETE" ), Qt::CaseInsensitive ) == 0 )
466 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
471 inputStringComponents = inputLine.split(
',' );
472 if ( inputStringComponents.size() == 6 )
475 QColor::fromRgb( inputStringComponents[1].toInt(), inputStringComponents[2].toInt(),
476 inputStringComponents[3].toInt(), inputStringComponents[4].toInt() ),
477 inputStringComponents[5] );
478 colorRampItems.push_back( currentItem );
483 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
492 QFileInfo fileInfo( fileName );
493 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
497 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"The following lines contained errors\n\n" ) + badLines );
500 else if ( !fileName.isEmpty() )
502 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"Read access denied. Adjust the file permissions and try again.\n\n" ) );
509 void QgsColorRampShaderWidget::mExportToFileButton_clicked()
512 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
513 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Map as File" ), lastDir, tr(
"Textfile (*.txt)" ) );
514 if ( !fileName.isEmpty() )
516 if ( !fileName.endsWith( QLatin1String(
".txt" ), Qt::CaseInsensitive ) )
518 fileName = fileName +
".txt";
521 QFile outputFile( fileName );
522 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
524 QTextStream outputStream( &outputFile );
525 outputStream <<
"# " << tr(
"QGIS Generated Color Map Export File" ) <<
'\n';
526 outputStream <<
"INTERPOLATION:";
528 switch ( interpolation )
531 outputStream <<
"INTERPOLATED\n";
534 outputStream <<
"DISCRETE\n";
537 outputStream <<
"EXACT\n";
541 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
542 QTreeWidgetItem *currentItem =
nullptr;
544 for (
int i = 0; i < topLevelItemCount; ++i )
546 currentItem = mColormapTreeWidget->topLevelItem( i );
551 color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
552 outputStream << currentItem->text( ValueColumn ).toDouble() <<
',';
553 outputStream << color.red() <<
',' << color.green() <<
',' << color.blue() <<
',' << color.alpha() <<
',';
554 if ( currentItem->text( LabelColumn ).isEmpty() )
556 outputStream <<
"Color entry " << i + 1 <<
'\n';
560 outputStream << currentItem->text( LabelColumn ) <<
'\n';
563 outputStream.flush();
566 QFileInfo fileInfo( fileName );
567 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
571 QMessageBox::warning(
this, tr(
"Save Color Map as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
576 void QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked( QTreeWidgetItem *item,
int column )
583 if ( column == ColorColumn )
585 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
586 QColor newColor =
QgsColorDialog::getColor( item->data( column, Qt::EditRole ).value<QColor>(),
this, QStringLiteral(
"Change Color" ), true );
587 if ( newColor.isValid() )
589 item->setData( ColorColumn, Qt::EditRole, newColor );
596 if ( column == LabelColumn )
599 item->setForeground( LabelColumn, QBrush() );
601 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
605 void QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited( QTreeWidgetItem *item,
int column )
609 if ( column == ValueColumn )
611 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
618 else if ( column == LabelColumn )
635 QString defaultPalette = settings.
value( QStringLiteral(
"/Raster/defaultPalette" ),
"Spectral" ).toString();
636 btnColorRamp->setColorRampFromName( defaultPalette );
639 mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( colorRampShader.
colorRampType() ) );
641 mColormapTreeWidget->clear();
642 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader.
colorRampItemList();
643 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
644 for ( ; it != colorRampItemList.end(); ++it )
647 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
648 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
649 newItem->setText( LabelColumn, it->label );
650 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
652 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
655 mClipCheckBox->setChecked( colorRampShader.
clip() );
656 mClassificationModeComboBox->setCurrentIndex( mClassificationModeComboBox->findData( colorRampShader.
classificationMode() ) );
657 mNumberOfEntriesSpinBox->setValue( colorRampShader.
colorRampItemList().count() );
660 void QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged(
int index )
667 QString valueToolTip;
668 switch ( interpolation )
671 valueLabel = tr(
"Value" );
672 valueToolTip = tr(
"Value for color stop" );
675 valueLabel = tr(
"Value <=" );
676 valueToolTip = tr(
"Maximum value for class" );
679 valueLabel = tr(
"Value =" );
680 valueToolTip = tr(
"Value for color" );
684 QTreeWidgetItem *header = mColormapTreeWidget->headerItem();
685 header->setText( ValueColumn, valueLabel );
686 header->setToolTip( ValueColumn, valueToolTip );
705 resetClassifyButton();
722 QTreeWidgetItem *item = mColormapTreeWidget->topLevelItem( 0 );
728 double min = item->text( ValueColumn ).toDouble();
729 item = mColormapTreeWidget->topLevelItem( mColormapTreeWidget->topLevelItemCount() - 1 );
730 double max = item->text( ValueColumn ).toDouble();
740 void QgsColorRampShaderWidget::resetClassifyButton()
742 mClassifyButton->setEnabled(
true );
743 if ( std::isnan( mMin ) || std::isnan( mMax ) || mMin >= mMax )
745 mClassifyButton->setEnabled(
false );
749 void QgsColorRampShaderWidget::changeColor()
751 QList<QTreeWidgetItem *> itemList;
752 itemList = mColormapTreeWidget->selectedItems();
753 if ( itemList.isEmpty() )
757 QTreeWidgetItem *firstItem = itemList.first();
759 QColor newColor =
QgsColorDialog::getColor( firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>(),
this, QStringLiteral(
"Change Color" ), true );
760 if ( newColor.isValid() )
762 const auto constItemList = itemList;
763 for ( QTreeWidgetItem *item : constItemList )
765 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
766 item->setData( ColorColumn, Qt::EditRole, newColor );
774 void QgsColorRampShaderWidget::changeOpacity()
776 QList<QTreeWidgetItem *> itemList;
777 itemList = mColormapTreeWidget->selectedItems();
778 if ( itemList.isEmpty() )
782 QTreeWidgetItem *firstItem = itemList.first();
785 double oldOpacity = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>().alpha() / 255 * 100;
786 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
789 int newOpacity =
static_cast<int>( opacity / 100 * 255 );
790 const auto constItemList = itemList;
791 for ( QTreeWidgetItem *item : constItemList )
793 QColor newColor = item->data( ColorColumn, Qt::EditRole ).value<QColor>();
794 newColor.setAlpha( newOpacity );
795 item->setData( ColorColumn, Qt::EditRole, newColor );
static QColor getColor(const QColor &initialColor, QWidget *parent, const QString &title=QString(), bool allowOpacity=false)
Returns a color selection from a color dialog.
A rectangle specified with double values.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &list)
Sets a custom colormap.
Uses quantile (i.e. equal pixel) count.
static const double UI_SCALE_FACTOR
UI scaling factor.
This class is a composition of two QSettings instances:
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QList< QgsColorRampShader::ColorRampItem > colorRampItemList() const
Returns the custom colormap.
void setClip(bool clip)
Sets whether the shader should not render values out of range.
Type
Supported methods for color interpolation.
void setColorRampType(QgsColorRampShader::Type colorRampType)
Sets the color ramp type.
void setSourceColorRamp(QgsColorRamp *colorramp)
Set the source color ramp.
Type colorRampType() const
Returns the color ramp type.
Assigns the color of the exact matching value in the color ramp item list.
Uses breaks from color palette.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
void setClassificationMode(ClassificationMode classificationMode)
Sets classification mode.
bool clip() const
Returns whether the shader will clip values which are out of range.
Interpolates the color between two class breaks linearly.
ClassificationMode
Classification modes used to create the color ramp shader.
Assigns the color of the higher class for every pixel between two class breaks.
ClassificationMode classificationMode() const
Returns the classification mode.
QgsColorRamp * sourceColorRamp() const
Gets the source color ramp.
A delegate for showing a color swatch in a list.
Base class for raster data providers.