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 );
67 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) 68 mColormapTreeWidget->setColumnWidth( ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().width(
'X' ) * 6.6 );
70 mColormapTreeWidget->setColumnWidth( ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance(
'X' ) * 6.6 );
73 mColormapTreeWidget->setContextMenuPolicy( Qt::CustomContextMenu );
74 mColormapTreeWidget->setSelectionMode( QAbstractItemView::ExtendedSelection );
75 connect( mColormapTreeWidget, &QTreeView::customContextMenuRequested,
this, [ = ]( QPoint ) { contextMenu->exec( QCursor::pos() ); } );
77 QString defaultPalette = settings.
value( QStringLiteral(
"Raster/defaultPalette" ),
"" ).toString();
78 btnColorRamp->setColorRampFromName( defaultPalette );
90 mNumberOfEntriesSpinBox->setValue( 5 );
92 mClassificationModeComboBox_currentIndexChanged( 0 );
94 resetClassifyButton();
111 mRasterDataProvider = dp;
112 mLoadFromBandButton->setVisible(
bool( mRasterDataProvider ) );
128 colorRampShader.
setColorRampType( static_cast< QgsColorRampShader::Type >( mColorInterpolationComboBox->currentData().toInt() ) );
129 colorRampShader.
setClassificationMode( static_cast< QgsColorRampShader::ClassificationMode >( mClassificationModeComboBox->currentData().toInt() ) );
130 colorRampShader.
setClip( mClipCheckBox->isChecked() );
133 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
134 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
135 QTreeWidgetItem *currentItem =
nullptr;
136 for (
int i = 0; i < topLevelItemCount; ++i )
138 currentItem = mColormapTreeWidget->topLevelItem( i );
144 newColorRampItem.
value = currentItem->text( ValueColumn ).toDouble();
145 newColorRampItem.
color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
146 newColorRampItem.
label = currentItem->text( LabelColumn );
147 colorRampItems.append( newColorRampItem );
150 std::sort( colorRampItems.begin(), colorRampItems.end() );
153 if ( !btnColorRamp->isNull() )
157 return colorRampShader;
160 void QgsColorRampShaderWidget::autoLabel()
164 QString unit = mUnitLineEdit->text();
166 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
167 QTreeWidgetItem *currentItem =
nullptr;
168 for (
int i = 0; i < topLevelItemCount; ++i )
170 currentItem = mColormapTreeWidget->topLevelItem( i );
172 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
181 label =
"<= " + currentItem->text( ValueColumn ) + unit;
183 else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
185 label =
"> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) + unit;
189 label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) +
" - " + currentItem->text( ValueColumn ) + unit;
194 label = currentItem->text( ValueColumn ) + unit;
197 if ( currentItem->text( LabelColumn ).isEmpty() || currentItem->text( LabelColumn ) == label || currentItem->foreground( LabelColumn ).color() == QColor( Qt::gray ) )
199 currentItem->setText( LabelColumn, label );
200 currentItem->setForeground( LabelColumn, QBrush( QColor( Qt::gray ) ) );
205 void QgsColorRampShaderWidget::setUnitFromLabels()
209 QStringList allSuffixes;
211 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
212 QTreeWidgetItem *currentItem =
nullptr;
213 for (
int i = 0; i < topLevelItemCount; ++i )
215 currentItem = mColormapTreeWidget->topLevelItem( i );
217 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
226 label =
"<= " + currentItem->text( ValueColumn );
228 else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
230 label =
"> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn );
234 label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) +
" - " + currentItem->text( ValueColumn );
239 label = currentItem->text( ValueColumn );
242 if ( currentItem->text( LabelColumn ).startsWith( label ) )
244 allSuffixes.append( currentItem->text( LabelColumn ).mid( label.length() ) );
248 QStringList suffixes = QStringList( allSuffixes );
249 suffixes.removeDuplicates();
252 for (
int i = 0; i < suffixes.count(); ++i )
254 int n = allSuffixes.count( suffixes[i] );
264 mUnitLineEdit->setText( unit );
269 void QgsColorRampShaderWidget::mAddEntryButton_clicked()
272 newItem->setText( ValueColumn, QStringLiteral(
"0" ) );
273 newItem->
setData( ColorColumn, Qt::EditRole, QColor( Qt::magenta ) );
274 newItem->setText( LabelColumn, QString() );
275 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
277 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
278 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
285 void QgsColorRampShaderWidget::mDeleteEntryButton_clicked()
287 QList<QTreeWidgetItem *> itemList;
288 itemList = mColormapTreeWidget->selectedItems();
289 if ( itemList.isEmpty() )
294 const auto constItemList = itemList;
295 for ( QTreeWidgetItem *item : constItemList )
306 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
307 if ( !ramp || std::isnan( mMin ) || std::isnan( mMax ) )
316 static_cast< QgsColorRampShader::ClassificationMode >( mClassificationModeComboBox->currentData().toInt() ) )
320 colorRampShader->classifyColorRamp( mNumberOfEntriesSpinBox->value(),
323 mRasterDataProvider );
324 colorRampShader->setClip( mClipCheckBox->isChecked() );
326 mColormapTreeWidget->clear();
328 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
329 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
330 for ( ; it != colorRampItemList.end(); ++it )
333 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
334 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
335 newItem->setText( LabelColumn, QString() );
336 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
338 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
340 mClipCheckBox->setChecked( colorRampShader->clip() );
346 void QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged(
int index )
354 void QgsColorRampShaderWidget::applyColorRamp()
356 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
362 if ( !btnColorRamp->colorRampName().isEmpty() )
366 settings.
setValue( QStringLiteral(
"Raster/defaultPalette" ), btnColorRamp->colorRampName() );
369 bool enableContinuous = ( ramp->count() > 0 );
370 mClassificationModeComboBox->setEnabled( enableContinuous );
371 if ( !enableContinuous )
376 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
377 if ( topLevelItemCount > 0 )
380 QTreeWidgetItem *currentItem =
nullptr;
381 for (
int i = 0; i < topLevelItemCount; ++i )
383 currentItem = mColormapTreeWidget->topLevelItem( i );
389 double value = currentItem->text( ValueColumn ).toDouble();
390 double position = ( value - mMin ) / ( mMax - mMin );
391 currentItem->setData( ColorColumn, Qt::EditRole, ramp->color( position ) );
404 mColormapTreeWidget->clear();
405 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
406 for ( ; it != colorRampItems.constEnd(); ++it )
409 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
410 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
411 newItem->setText( LabelColumn, it->label );
412 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
414 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
420 void QgsColorRampShaderWidget::mLoadFromBandButton_clicked()
422 if ( !mRasterDataProvider )
425 QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterDataProvider->
colorTable( mBand );
426 if ( !colorRampList.isEmpty() )
433 QMessageBox::warning(
this, tr(
"Load Color Map" ), tr(
"The color map for band %1 has no entries." ).arg( mBand ) );
439 void QgsColorRampShaderWidget::mLoadFromFileButton_clicked()
442 bool importError =
false;
445 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
446 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Map from File" ), lastDir, tr(
"Textfile (*.txt)" ) );
447 QFile inputFile( fileName );
448 if ( inputFile.open( QFile::ReadOnly ) )
451 mColormapTreeWidget->clear();
453 QTextStream inputStream( &inputFile );
455 QStringList inputStringComponents;
456 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
459 while ( !inputStream.atEnd() )
462 inputLine = inputStream.readLine();
463 if ( !inputLine.isEmpty() )
465 if ( !inputLine.simplified().startsWith(
'#' ) )
467 if ( inputLine.contains( QLatin1String(
"INTERPOLATION" ), Qt::CaseInsensitive ) )
469 inputStringComponents = inputLine.split(
':' );
470 if ( inputStringComponents.size() == 2 )
472 if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"INTERPOLATED" ), Qt::CaseInsensitive ) == 0 )
476 else if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"DISCRETE" ), Qt::CaseInsensitive ) == 0 )
488 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
493 inputStringComponents = inputLine.split(
',' );
494 if ( inputStringComponents.size() == 6 )
497 QColor::fromRgb( inputStringComponents[1].toInt(), inputStringComponents[2].toInt(),
498 inputStringComponents[3].toInt(), inputStringComponents[4].toInt() ),
499 inputStringComponents[5] );
500 colorRampItems.push_back( currentItem );
505 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
514 QFileInfo fileInfo( fileName );
515 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
519 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"The following lines contained errors\n\n" ) + badLines );
522 else if ( !fileName.isEmpty() )
524 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"Read access denied. Adjust the file permissions and try again.\n\n" ) );
531 void QgsColorRampShaderWidget::mExportToFileButton_clicked()
534 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
535 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Map as File" ), lastDir, tr(
"Textfile (*.txt)" ) );
536 if ( !fileName.isEmpty() )
538 if ( !fileName.endsWith( QLatin1String(
".txt" ), Qt::CaseInsensitive ) )
540 fileName = fileName +
".txt";
543 QFile outputFile( fileName );
544 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
546 QTextStream outputStream( &outputFile );
547 outputStream <<
"# " << tr(
"QGIS Generated Color Map Export File" ) <<
'\n';
548 outputStream <<
"INTERPOLATION:";
550 switch ( interpolation )
553 outputStream <<
"INTERPOLATED\n";
556 outputStream <<
"DISCRETE\n";
559 outputStream <<
"EXACT\n";
563 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
564 QTreeWidgetItem *currentItem =
nullptr;
566 for (
int i = 0; i < topLevelItemCount; ++i )
568 currentItem = mColormapTreeWidget->topLevelItem( i );
573 color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
574 outputStream << currentItem->text( ValueColumn ).toDouble() <<
',';
575 outputStream << color.red() <<
',' << color.green() <<
',' << color.blue() <<
',' << color.alpha() <<
',';
576 if ( currentItem->text( LabelColumn ).isEmpty() )
578 outputStream <<
"Color entry " << i + 1 <<
'\n';
582 outputStream << currentItem->text( LabelColumn ) <<
'\n';
585 outputStream.flush();
588 QFileInfo fileInfo( fileName );
589 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
593 QMessageBox::warning(
this, tr(
"Save Color Map as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
598 void QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked( QTreeWidgetItem *item,
int column )
605 if ( column == LabelColumn )
608 item->setForeground( LabelColumn, QBrush() );
612 void QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited( QTreeWidgetItem *item,
int column )
620 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
655 QString defaultPalette = settings.
value( QStringLiteral(
"/Raster/defaultPalette" ),
"Spectral" ).toString();
656 btnColorRamp->setColorRampFromName( defaultPalette );
659 mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( colorRampShader.
colorRampType() ) );
661 mColormapTreeWidget->clear();
662 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader.
colorRampItemList();
663 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
664 for ( ; it != colorRampItemList.end(); ++it )
667 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
668 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
669 newItem->setText( LabelColumn, it->label );
670 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
672 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
676 mClipCheckBox->setChecked( colorRampShader.
clip() );
677 mClassificationModeComboBox->setCurrentIndex( mClassificationModeComboBox->findData( colorRampShader.
classificationMode() ) );
678 mNumberOfEntriesSpinBox->setValue( colorRampShader.
colorRampItemList().count() );
681 void QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged(
int index )
688 QString valueToolTip;
689 switch ( interpolation )
692 valueLabel = tr(
"Value" );
693 valueToolTip = tr(
"Value for color stop" );
696 valueLabel = tr(
"Value <=" );
697 valueToolTip = tr(
"Maximum value for class" );
700 valueLabel = tr(
"Value =" );
701 valueToolTip = tr(
"Value for color" );
705 QTreeWidgetItem *header = mColormapTreeWidget->headerItem();
706 header->setText( ValueColumn, valueLabel );
707 header->setToolTip( ValueColumn, valueToolTip );
726 resetClassifyButton();
743 QTreeWidgetItem *item = mColormapTreeWidget->topLevelItem( 0 );
749 double min = item->text( ValueColumn ).toDouble();
750 item = mColormapTreeWidget->topLevelItem( mColormapTreeWidget->topLevelItemCount() - 1 );
751 double max = item->text( ValueColumn ).toDouble();
761 void QgsColorRampShaderWidget::resetClassifyButton()
763 mClassifyButton->setEnabled(
true );
764 if ( std::isnan( mMin ) || std::isnan( mMax ) || mMin >= mMax )
766 mClassifyButton->setEnabled(
false );
770 void QgsColorRampShaderWidget::changeColor()
772 QList<QTreeWidgetItem *> itemList;
773 itemList = mColormapTreeWidget->selectedItems();
774 if ( itemList.isEmpty() )
778 QTreeWidgetItem *firstItem = itemList.first();
780 QColor currentColor = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
782 if ( panel && panel->dockMode() )
789 for ( QTreeWidgetItem *item : qgis::as_const( itemList ) )
791 item->setData( ColorColumn, Qt::EditRole, newColor );
797 panel->openPanel( colorWidget );
803 if ( newColor.isValid() )
805 for ( QTreeWidgetItem *item : qgis::as_const( itemList ) )
807 item->setData( ColorColumn, Qt::EditRole, newColor );
816 void QgsColorRampShaderWidget::changeOpacity()
818 QList<QTreeWidgetItem *> itemList;
819 itemList = mColormapTreeWidget->selectedItems();
820 if ( itemList.isEmpty() )
824 QTreeWidgetItem *firstItem = itemList.first();
827 double oldOpacity = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>().alpha() / 255 * 100;
828 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
831 int newOpacity =
static_cast<int>( opacity / 100 * 255 );
832 const auto constItemList = itemList;
833 for ( QTreeWidgetItem *item : constItemList )
835 QColor newColor = item->data( ColorColumn, Qt::EditRole ).value<QColor>();
836 newColor.setAlpha( newOpacity );
837 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.