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 Q_FOREACH ( QTreeWidgetItem *item, itemList )
301 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
302 if ( !ramp || std::isnan( mMin ) || std::isnan( mMax ) )
311 static_cast< QgsColorRampShader::ClassificationMode >( mClassificationModeComboBox->currentData().toInt() ) )
315 colorRampShader->classifyColorRamp( mNumberOfEntriesSpinBox->value(),
318 mRasterDataProvider );
319 colorRampShader->setClip( mClipCheckBox->isChecked() );
322 mColormapTreeWidget->clear();
324 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
325 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
326 for ( ; it != colorRampItemList.end(); ++it )
329 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
330 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
331 newItem->setText( LabelColumn, it->label );
332 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
334 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
336 mClipCheckBox->setChecked( colorRampShader->clip() );
343 void QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged(
int index )
351 void QgsColorRampShaderWidget::applyColorRamp()
353 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
359 if ( !btnColorRamp->colorRampName().isEmpty() )
363 settings.
setValue( QStringLiteral(
"Raster/defaultPalette" ), btnColorRamp->colorRampName() );
366 bool enableContinuous = ( ramp->count() > 0 );
367 mClassificationModeComboBox->setEnabled( enableContinuous );
368 if ( !enableContinuous )
378 mColormapTreeWidget->clear();
379 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
380 for ( ; it != colorRampItems.constEnd(); ++it )
383 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
384 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
385 newItem->setText( LabelColumn, it->label );
386 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
388 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
396 void QgsColorRampShaderWidget::mLoadFromBandButton_clicked()
398 if ( !mRasterDataProvider )
401 QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterDataProvider->
colorTable( mBand );
402 if ( !colorRampList.isEmpty() )
409 QMessageBox::warning(
this, tr(
"Load Color Map" ), tr(
"The color map for band %1 has no entries." ).arg( mBand ) );
416 void QgsColorRampShaderWidget::mLoadFromFileButton_clicked()
419 bool importError =
false;
422 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
423 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Map from File" ), lastDir, tr(
"Textfile (*.txt)" ) );
424 QFile inputFile( fileName );
425 if ( inputFile.open( QFile::ReadOnly ) )
428 mColormapTreeWidget->clear();
430 QTextStream inputStream( &inputFile );
432 QStringList inputStringComponents;
433 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
436 while ( !inputStream.atEnd() )
439 inputLine = inputStream.readLine();
440 if ( !inputLine.isEmpty() )
442 if ( !inputLine.simplified().startsWith(
'#' ) )
444 if ( inputLine.contains( QLatin1String(
"INTERPOLATION" ), Qt::CaseInsensitive ) )
446 inputStringComponents = inputLine.split(
':' );
447 if ( inputStringComponents.size() == 2 )
449 if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"INTERPOLATED" ), Qt::CaseInsensitive ) == 0 )
453 else if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"DISCRETE" ), Qt::CaseInsensitive ) == 0 )
465 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
470 inputStringComponents = inputLine.split(
',' );
471 if ( inputStringComponents.size() == 6 )
474 QColor::fromRgb( inputStringComponents[1].toInt(), inputStringComponents[2].toInt(),
475 inputStringComponents[3].toInt(), inputStringComponents[4].toInt() ),
476 inputStringComponents[5] );
477 colorRampItems.push_back( currentItem );
482 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
491 QFileInfo fileInfo( fileName );
492 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
496 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"The following lines contained errors\n\n" ) + badLines );
499 else if ( !fileName.isEmpty() )
501 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"Read access denied. Adjust the file permissions and try again.\n\n" ) );
508 void QgsColorRampShaderWidget::mExportToFileButton_clicked()
511 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
512 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Map as File" ), lastDir, tr(
"Textfile (*.txt)" ) );
513 if ( !fileName.isEmpty() )
515 if ( !fileName.endsWith( QLatin1String(
".txt" ), Qt::CaseInsensitive ) )
517 fileName = fileName +
".txt";
520 QFile outputFile( fileName );
521 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
523 QTextStream outputStream( &outputFile );
524 outputStream <<
"# " << tr(
"QGIS Generated Color Map Export File" ) <<
'\n';
525 outputStream <<
"INTERPOLATION:";
527 switch ( interpolation )
530 outputStream <<
"INTERPOLATED\n";
533 outputStream <<
"DISCRETE\n";
536 outputStream <<
"EXACT\n";
540 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
541 QTreeWidgetItem *currentItem =
nullptr;
543 for (
int i = 0; i < topLevelItemCount; ++i )
545 currentItem = mColormapTreeWidget->topLevelItem( i );
550 color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
551 outputStream << currentItem->text( ValueColumn ).toDouble() <<
',';
552 outputStream << color.red() <<
',' << color.green() <<
',' << color.blue() <<
',' << color.alpha() <<
',';
553 if ( currentItem->text( LabelColumn ).isEmpty() )
555 outputStream <<
"Color entry " << i + 1 <<
'\n';
559 outputStream << currentItem->text( LabelColumn ) <<
'\n';
562 outputStream.flush();
565 QFileInfo fileInfo( fileName );
566 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
570 QMessageBox::warning(
this, tr(
"Save Color Map as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
575 void QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked( QTreeWidgetItem *item,
int column )
582 if ( column == ColorColumn )
584 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
585 QColor newColor =
QgsColorDialog::getColor( item->data( column, Qt::EditRole ).value<QColor>(),
this, QStringLiteral(
"Change Color" ), true );
586 if ( newColor.isValid() )
588 item->setData( ColorColumn, Qt::EditRole, newColor );
595 if ( column == LabelColumn )
598 item->setForeground( LabelColumn, QBrush() );
600 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
604 void QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited( QTreeWidgetItem *item,
int column )
608 if ( column == ValueColumn )
610 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
617 else if ( column == LabelColumn )
634 QString defaultPalette = settings.
value( QStringLiteral(
"/Raster/defaultPalette" ),
"Spectral" ).toString();
635 btnColorRamp->setColorRampFromName( defaultPalette );
638 mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( colorRampShader.
colorRampType() ) );
640 mColormapTreeWidget->clear();
641 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader.
colorRampItemList();
642 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
643 for ( ; it != colorRampItemList.end(); ++it )
646 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
647 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
648 newItem->setText( LabelColumn, it->label );
649 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
651 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
654 mClipCheckBox->setChecked( colorRampShader.
clip() );
655 mClassificationModeComboBox->setCurrentIndex( mClassificationModeComboBox->findData( colorRampShader.
classificationMode() ) );
656 mNumberOfEntriesSpinBox->setValue( colorRampShader.
colorRampItemList().count() );
659 void QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged(
int index )
666 QString valueToolTip;
667 switch ( interpolation )
670 valueLabel = tr(
"Value" );
671 valueToolTip = tr(
"Value for color stop" );
674 valueLabel = tr(
"Value <=" );
675 valueToolTip = tr(
"Maximum value for class" );
678 valueLabel = tr(
"Value =" );
679 valueToolTip = tr(
"Value for color" );
683 QTreeWidgetItem *header = mColormapTreeWidget->headerItem();
684 header->setText( ValueColumn, valueLabel );
685 header->setToolTip( ValueColumn, valueToolTip );
704 resetClassifyButton();
721 QTreeWidgetItem *item = mColormapTreeWidget->topLevelItem( 0 );
727 double min = item->text( ValueColumn ).toDouble();
728 item = mColormapTreeWidget->topLevelItem( mColormapTreeWidget->topLevelItemCount() - 1 );
729 double max = item->text( ValueColumn ).toDouble();
739 void QgsColorRampShaderWidget::resetClassifyButton()
741 mClassifyButton->setEnabled(
true );
742 if ( std::isnan( mMin ) || std::isnan( mMax ) || mMin >= mMax )
744 mClassifyButton->setEnabled(
false );
748 void QgsColorRampShaderWidget::changeColor()
750 QList<QTreeWidgetItem *> itemList;
751 itemList = mColormapTreeWidget->selectedItems();
752 if ( itemList.isEmpty() )
756 QTreeWidgetItem *firstItem = itemList.first();
758 QColor newColor =
QgsColorDialog::getColor( firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>(),
this, QStringLiteral(
"Change Color" ), true );
759 if ( newColor.isValid() )
761 Q_FOREACH ( QTreeWidgetItem *item, itemList )
763 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
764 item->setData( ColorColumn, Qt::EditRole, newColor );
772 void QgsColorRampShaderWidget::changeOpacity()
774 QList<QTreeWidgetItem *> itemList;
775 itemList = mColormapTreeWidget->selectedItems();
776 if ( itemList.isEmpty() )
780 QTreeWidgetItem *firstItem = itemList.first();
783 double oldOpacity = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>().alpha() / 255 * 100;
784 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
787 int newOpacity =
static_cast<int>( opacity / 100 * 255 );
788 Q_FOREACH ( QTreeWidgetItem *item, itemList )
790 QColor newColor = item->data( ColorColumn, Qt::EditRole ).value<QColor>();
791 newColor.setAlpha( newOpacity );
792 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.