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() ); } );
71 QString defaultPalette = settings.
value( QStringLiteral(
"Raster/defaultPalette" ),
"" ).toString();
72 btnColorRamp->setColorRampFromName( defaultPalette );
84 mNumberOfEntriesSpinBox->setValue( 5 );
86 mClassificationModeComboBox_currentIndexChanged( 0 );
88 resetClassifyButton();
105 mRasterDataProvider = dp;
106 mLoadFromBandButton->setVisible(
bool( mRasterDataProvider ) );
122 colorRampShader.
setColorRampType( static_cast< QgsColorRampShader::Type >( mColorInterpolationComboBox->currentData().toInt() ) );
123 colorRampShader.
setClassificationMode( static_cast< QgsColorRampShader::ClassificationMode >( mClassificationModeComboBox->currentData().toInt() ) );
124 colorRampShader.
setClip( mClipCheckBox->isChecked() );
127 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
128 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
129 QTreeWidgetItem *currentItem =
nullptr;
130 for (
int i = 0; i < topLevelItemCount; ++i )
132 currentItem = mColormapTreeWidget->topLevelItem( i );
138 newColorRampItem.
value = currentItem->text( ValueColumn ).toDouble();
139 newColorRampItem.
color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
140 newColorRampItem.
label = currentItem->text( LabelColumn );
141 colorRampItems.append( newColorRampItem );
144 std::sort( colorRampItems.begin(), colorRampItems.end() );
147 if ( !btnColorRamp->isNull() )
151 return colorRampShader;
154 void QgsColorRampShaderWidget::autoLabel()
158 QString unit = mUnitLineEdit->text();
160 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
161 QTreeWidgetItem *currentItem =
nullptr;
162 for (
int i = 0; i < topLevelItemCount; ++i )
164 currentItem = mColormapTreeWidget->topLevelItem( i );
166 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
175 label =
"<= " + currentItem->text( ValueColumn ) + unit;
177 else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
179 label =
"> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) + unit;
183 label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) +
" - " + currentItem->text( ValueColumn ) + unit;
188 label = currentItem->text( ValueColumn ) + unit;
191 if ( currentItem->text( LabelColumn ).isEmpty() || currentItem->text( LabelColumn ) == label || currentItem->foreground( LabelColumn ).color() == QColor( Qt::gray ) )
193 currentItem->setText( LabelColumn, label );
194 currentItem->setForeground( LabelColumn, QBrush( QColor( Qt::gray ) ) );
199 void QgsColorRampShaderWidget::setUnitFromLabels()
203 QStringList allSuffixes;
205 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
206 QTreeWidgetItem *currentItem =
nullptr;
207 for (
int i = 0; i < topLevelItemCount; ++i )
209 currentItem = mColormapTreeWidget->topLevelItem( i );
211 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
220 label =
"<= " + currentItem->text( ValueColumn );
222 else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
224 label =
"> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn );
228 label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) +
" - " + currentItem->text( ValueColumn );
233 label = currentItem->text( ValueColumn );
236 if ( currentItem->text( LabelColumn ).startsWith( label ) )
238 allSuffixes.append( currentItem->text( LabelColumn ).mid( label.length() ) );
242 QStringList suffixes = QStringList( allSuffixes );
243 suffixes.removeDuplicates();
246 for (
int i = 0; i < suffixes.count(); ++i )
248 int n = allSuffixes.count( suffixes[i] );
258 mUnitLineEdit->setText( unit );
263 void QgsColorRampShaderWidget::mAddEntryButton_clicked()
266 newItem->setText( ValueColumn, QStringLiteral(
"0" ) );
267 newItem->
setData( ColorColumn, Qt::EditRole, QColor( Qt::magenta ) );
268 newItem->setText( LabelColumn, QString() );
269 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
271 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
272 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
279 void QgsColorRampShaderWidget::mDeleteEntryButton_clicked()
281 QList<QTreeWidgetItem *> itemList;
282 itemList = mColormapTreeWidget->selectedItems();
283 if ( itemList.isEmpty() )
288 const auto constItemList = itemList;
289 for ( QTreeWidgetItem *item : constItemList )
300 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
301 if ( !ramp || std::isnan( mMin ) || std::isnan( mMax ) )
310 static_cast< QgsColorRampShader::ClassificationMode >( mClassificationModeComboBox->currentData().toInt() ) )
314 colorRampShader->classifyColorRamp( mNumberOfEntriesSpinBox->value(),
317 mRasterDataProvider );
318 colorRampShader->setClip( mClipCheckBox->isChecked() );
320 mColormapTreeWidget->clear();
322 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
323 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
324 for ( ; it != colorRampItemList.end(); ++it )
327 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
328 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
329 newItem->setText( LabelColumn, QString() );
330 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
332 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
334 mClipCheckBox->setChecked( colorRampShader->clip() );
340 void QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged(
int index )
348 void QgsColorRampShaderWidget::applyColorRamp()
350 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
356 if ( !btnColorRamp->colorRampName().isEmpty() )
360 settings.
setValue( QStringLiteral(
"Raster/defaultPalette" ), btnColorRamp->colorRampName() );
363 bool enableContinuous = ( ramp->count() > 0 );
364 mClassificationModeComboBox->setEnabled( enableContinuous );
365 if ( !enableContinuous )
370 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
371 if ( topLevelItemCount > 0 )
374 QTreeWidgetItem *currentItem =
nullptr;
375 for (
int i = 0; i < topLevelItemCount; ++i )
377 currentItem = mColormapTreeWidget->topLevelItem( i );
383 double value = currentItem->text( ValueColumn ).toDouble();
384 double position = ( value - mMin ) / ( mMax - mMin );
385 currentItem->setData( ColorColumn, Qt::EditRole, ramp->color( position ) );
398 mColormapTreeWidget->clear();
399 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
400 for ( ; it != colorRampItems.constEnd(); ++it )
403 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
404 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
405 newItem->setText( LabelColumn, it->label );
406 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
408 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
414 void QgsColorRampShaderWidget::mLoadFromBandButton_clicked()
416 if ( !mRasterDataProvider )
419 QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterDataProvider->
colorTable( mBand );
420 if ( !colorRampList.isEmpty() )
427 QMessageBox::warning(
this, tr(
"Load Color Map" ), tr(
"The color map for band %1 has no entries." ).arg( mBand ) );
433 void QgsColorRampShaderWidget::mLoadFromFileButton_clicked()
436 bool importError =
false;
439 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
440 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Map from File" ), lastDir, tr(
"Textfile (*.txt)" ) );
441 QFile inputFile( fileName );
442 if ( inputFile.open( QFile::ReadOnly ) )
445 mColormapTreeWidget->clear();
447 QTextStream inputStream( &inputFile );
449 QStringList inputStringComponents;
450 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
453 while ( !inputStream.atEnd() )
456 inputLine = inputStream.readLine();
457 if ( !inputLine.isEmpty() )
459 if ( !inputLine.simplified().startsWith(
'#' ) )
461 if ( inputLine.contains( QLatin1String(
"INTERPOLATION" ), Qt::CaseInsensitive ) )
463 inputStringComponents = inputLine.split(
':' );
464 if ( inputStringComponents.size() == 2 )
466 if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"INTERPOLATED" ), Qt::CaseInsensitive ) == 0 )
470 else if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"DISCRETE" ), Qt::CaseInsensitive ) == 0 )
482 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
487 inputStringComponents = inputLine.split(
',' );
488 if ( inputStringComponents.size() == 6 )
491 QColor::fromRgb( inputStringComponents[1].toInt(), inputStringComponents[2].toInt(),
492 inputStringComponents[3].toInt(), inputStringComponents[4].toInt() ),
493 inputStringComponents[5] );
494 colorRampItems.push_back( currentItem );
499 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
508 QFileInfo fileInfo( fileName );
509 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
513 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"The following lines contained errors\n\n" ) + badLines );
516 else if ( !fileName.isEmpty() )
518 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"Read access denied. Adjust the file permissions and try again.\n\n" ) );
525 void QgsColorRampShaderWidget::mExportToFileButton_clicked()
528 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
529 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Map as File" ), lastDir, tr(
"Textfile (*.txt)" ) );
530 if ( !fileName.isEmpty() )
532 if ( !fileName.endsWith( QLatin1String(
".txt" ), Qt::CaseInsensitive ) )
534 fileName = fileName +
".txt";
537 QFile outputFile( fileName );
538 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
540 QTextStream outputStream( &outputFile );
541 outputStream <<
"# " << tr(
"QGIS Generated Color Map Export File" ) <<
'\n';
542 outputStream <<
"INTERPOLATION:";
544 switch ( interpolation )
547 outputStream <<
"INTERPOLATED\n";
550 outputStream <<
"DISCRETE\n";
553 outputStream <<
"EXACT\n";
557 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
558 QTreeWidgetItem *currentItem =
nullptr;
560 for (
int i = 0; i < topLevelItemCount; ++i )
562 currentItem = mColormapTreeWidget->topLevelItem( i );
567 color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
568 outputStream << currentItem->text( ValueColumn ).toDouble() <<
',';
569 outputStream << color.red() <<
',' << color.green() <<
',' << color.blue() <<
',' << color.alpha() <<
',';
570 if ( currentItem->text( LabelColumn ).isEmpty() )
572 outputStream <<
"Color entry " << i + 1 <<
'\n';
576 outputStream << currentItem->text( LabelColumn ) <<
'\n';
579 outputStream.flush();
582 QFileInfo fileInfo( fileName );
583 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
587 QMessageBox::warning(
this, tr(
"Save Color Map as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
592 void QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked( QTreeWidgetItem *item,
int column )
599 if ( column == LabelColumn )
602 item->setForeground( LabelColumn, QBrush() );
606 void QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited( QTreeWidgetItem *item,
int column )
614 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
649 QString defaultPalette = settings.
value( QStringLiteral(
"/Raster/defaultPalette" ),
"Spectral" ).toString();
650 btnColorRamp->setColorRampFromName( defaultPalette );
653 mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( colorRampShader.
colorRampType() ) );
655 mColormapTreeWidget->clear();
656 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader.
colorRampItemList();
657 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
658 for ( ; it != colorRampItemList.end(); ++it )
661 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
662 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
663 newItem->setText( LabelColumn, it->label );
664 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
666 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
670 mClipCheckBox->setChecked( colorRampShader.
clip() );
671 mClassificationModeComboBox->setCurrentIndex( mClassificationModeComboBox->findData( colorRampShader.
classificationMode() ) );
672 mNumberOfEntriesSpinBox->setValue( colorRampShader.
colorRampItemList().count() );
675 void QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged(
int index )
682 QString valueToolTip;
683 switch ( interpolation )
686 valueLabel = tr(
"Value" );
687 valueToolTip = tr(
"Value for color stop" );
690 valueLabel = tr(
"Value <=" );
691 valueToolTip = tr(
"Maximum value for class" );
694 valueLabel = tr(
"Value =" );
695 valueToolTip = tr(
"Value for color" );
699 QTreeWidgetItem *header = mColormapTreeWidget->headerItem();
700 header->setText( ValueColumn, valueLabel );
701 header->setToolTip( ValueColumn, valueToolTip );
720 resetClassifyButton();
737 QTreeWidgetItem *item = mColormapTreeWidget->topLevelItem( 0 );
743 double min = item->text( ValueColumn ).toDouble();
744 item = mColormapTreeWidget->topLevelItem( mColormapTreeWidget->topLevelItemCount() - 1 );
745 double max = item->text( ValueColumn ).toDouble();
755 void QgsColorRampShaderWidget::resetClassifyButton()
757 mClassifyButton->setEnabled(
true );
758 if ( std::isnan( mMin ) || std::isnan( mMax ) || mMin >= mMax )
760 mClassifyButton->setEnabled(
false );
764 void QgsColorRampShaderWidget::changeColor()
766 QList<QTreeWidgetItem *> itemList;
767 itemList = mColormapTreeWidget->selectedItems();
768 if ( itemList.isEmpty() )
772 QTreeWidgetItem *firstItem = itemList.first();
774 QColor currentColor = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
776 if ( panel && panel->dockMode() )
783 for ( QTreeWidgetItem *item : qgis::as_const( itemList ) )
785 item->setData( ColorColumn, Qt::EditRole, newColor );
791 panel->openPanel( colorWidget );
797 if ( newColor.isValid() )
799 for ( QTreeWidgetItem *item : qgis::as_const( itemList ) )
801 item->setData( ColorColumn, Qt::EditRole, newColor );
810 void QgsColorRampShaderWidget::changeOpacity()
812 QList<QTreeWidgetItem *> itemList;
813 itemList = mColormapTreeWidget->selectedItems();
814 if ( itemList.isEmpty() )
818 QTreeWidgetItem *firstItem = itemList.first();
821 double oldOpacity = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>().alpha() / 255 * 100;
822 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
825 int newOpacity =
static_cast<int>( opacity / 100 * 255 );
826 const auto constItemList = itemList;
827 for ( QTreeWidgetItem *item : constItemList )
829 QColor newColor = item->data( ColorColumn, Qt::EditRole ).value<QColor>();
830 newColor.setAlpha( newOpacity );
831 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
Returns the source color ramp.
A delegate for showing a color swatch in a list.
Base class for raster data providers.