40 #include <QPushButton>
41 #include <QInputDialog>
42 #include <QFileDialog>
44 #include <QMessageBox>
45 #include <QTextStream>
55 mLoadFromBandButton->setVisible(
false );
57 connect( mAddEntryButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mAddEntryButton_clicked );
58 connect( mDeleteEntryButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mDeleteEntryButton_clicked );
59 connect( mLoadFromBandButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mLoadFromBandButton_clicked );
60 connect( mLoadFromFileButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mLoadFromFileButton_clicked );
61 connect( mExportToFileButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::mExportToFileButton_clicked );
62 connect( mUnitLineEdit, &QLineEdit::textEdited,
this, &QgsColorRampShaderWidget::mUnitLineEdit_textEdited );
63 connect( mColormapTreeWidget, &QTreeWidget::itemDoubleClicked,
this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked );
64 connect( mColorInterpolationComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged );
65 connect( mClassificationModeComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged );
67 connect( mLegendSettingsButton, &QPushButton::clicked,
this, &QgsColorRampShaderWidget::showLegendSettings );
69 contextMenu =
new QMenu( tr(
"Options" ),
this );
70 contextMenu->addAction( tr(
"Change Color…" ),
this, SLOT( changeColor() ) );
71 contextMenu->addAction( tr(
"Change Opacity…" ),
this, SLOT( changeOpacity() ) );
75 mColormapTreeWidget->setItemDelegateForColumn( ValueColumn, mValueDelegate );
77 mColormapTreeWidget->setColumnWidth( ColorColumn,
Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance(
'X' ) * 6.6 );
79 mColormapTreeWidget->setContextMenuPolicy( Qt::CustomContextMenu );
80 mColormapTreeWidget->setSelectionMode( QAbstractItemView::ExtendedSelection );
81 connect( mColormapTreeWidget, &QTreeView::customContextMenuRequested,
this, [ = ]( QPoint ) { contextMenu->exec( QCursor::pos() ); } );
83 QString defaultPalette = settings.
value( QStringLiteral(
"Raster/defaultPalette" ),
"" ).toString();
84 btnColorRamp->setColorRampFromName( defaultPalette );
96 mNumberOfEntriesSpinBox->setValue( 5 );
98 mClassificationModeComboBox_currentIndexChanged( 0 );
100 resetClassifyButton();
108 connect( mLabelPrecisionSpinBox, qOverload<int>( &QSpinBox::valueChanged ),
this, [ = ](
int )
122 mRasterDataProvider = dp;
123 mLoadFromBandButton->setVisible(
bool( mRasterDataProvider ) );
134 mLabelPrecisionSpinBox->setMaximum( maxDigits );
135 mValueDelegate->setDataType( dataType );
149 colorRampShader.
setClip( mClipCheckBox->isChecked() );
152 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
153 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
154 QTreeWidgetItem *currentItem =
nullptr;
155 for (
int i = 0; i < topLevelItemCount; ++i )
157 currentItem = mColormapTreeWidget->topLevelItem( i );
163 newColorRampItem.
value = currentItem->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toDouble();
164 newColorRampItem.
color = currentItem->data( ColorColumn, Qt::ItemDataRole::EditRole ).value<QColor>();
165 newColorRampItem.
label = currentItem->text( LabelColumn );
166 colorRampItems.append( newColorRampItem );
169 std::sort( colorRampItems.begin(), colorRampItems.end() );
172 if ( !btnColorRamp->isNull() )
178 return colorRampShader;
181 void QgsColorRampShaderWidget::autoLabel()
184 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
190 const QString unit = mUnitLineEdit->text();
191 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
193 QTreeWidgetItem *currentItem =
nullptr;
194 for (
int i = 0; i < topLevelItemCount; ++i )
196 currentItem = mColormapTreeWidget->topLevelItem( i );
198 if ( !currentItem || currentItem->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toString().isEmpty() )
203 const QString lbl = createLabel( currentItem, i, unit );
205 if ( currentItem->text( LabelColumn ).isEmpty() || currentItem->text( LabelColumn ) == lbl || currentItem->foreground( LabelColumn ).color() == QColor( Qt::gray ) )
207 currentItem->setText( LabelColumn, lbl );
208 currentItem->setForeground( LabelColumn, QBrush( QColor( Qt::gray ) ) );
214 void QgsColorRampShaderWidget::setUnitFromLabels()
216 QStringList allSuffixes;
218 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
219 QTreeWidgetItem *currentItem =
nullptr;
220 for (
int i = 0; i < topLevelItemCount; ++i )
222 currentItem = mColormapTreeWidget->topLevelItem( i );
224 if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
229 label = createLabel( currentItem, i, QString() );
231 if ( currentItem->text( LabelColumn ).startsWith( label ) )
233 allSuffixes.append( currentItem->text( LabelColumn ).mid( label.length() ) );
237 QStringList suffixes = QStringList( allSuffixes );
238 suffixes.removeDuplicates();
241 for (
int i = 0; i < suffixes.count(); ++i )
243 int n = allSuffixes.count( suffixes[i] );
253 mUnitLineEdit->setText( unit );
258 void QgsColorRampShaderWidget::dumpClasses()
260 for (
int row = 0; row < mColormapTreeWidget->model()->rowCount(); ++row )
262 const auto labelData { mColormapTreeWidget->model()->itemData( mColormapTreeWidget->model()->index( row, LabelColumn ) ) };
263 const auto valueData { mColormapTreeWidget->model()->itemData( mColormapTreeWidget->model()->index( row, ValueColumn ) ) };
265 .arg( labelData[ Qt::ItemDataRole::DisplayRole ].toString(),
266 valueData[ Qt::ItemDataRole::DisplayRole ].toString() ), 2 );
271 void QgsColorRampShaderWidget::mAddEntryButton_clicked()
274 newItem->
setData( ValueColumn, Qt::ItemDataRole::DisplayRole, 0 );
275 newItem->
setData( ColorColumn, Qt::ItemDataRole::EditRole, QColor( Qt::magenta ) );
276 newItem->setText( LabelColumn, QString() );
277 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
279 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
287 void QgsColorRampShaderWidget::mDeleteEntryButton_clicked()
289 QList<QTreeWidgetItem *> itemList;
290 itemList = mColormapTreeWidget->selectedItems();
291 if ( itemList.isEmpty() )
296 const auto constItemList = itemList;
297 for ( QTreeWidgetItem *item : constItemList )
309 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
310 if ( !ramp || std::isnan( mMin ) || std::isnan( mMax ) )
323 colorRampShader->classifyColorRamp( mNumberOfEntriesSpinBox->value(),
326 mRasterDataProvider );
327 colorRampShader->setClip( mClipCheckBox->isChecked() );
329 mColormapTreeWidget->clear();
331 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
332 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
333 for ( ; it != colorRampItemList.end(); ++it )
336 newItem->
setData( ValueColumn, Qt::ItemDataRole::DisplayRole, it->value );
337 newItem->
setData( ColorColumn, Qt::ItemDataRole::EditRole, it->color );
338 newItem->setText( LabelColumn, QString() );
339 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
341 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
344 mClipCheckBox->setChecked( colorRampShader->clip() );
350 void QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged(
int index )
357 void QgsColorRampShaderWidget::updateColorRamp()
359 std::unique_ptr< QgsColorRamp > ramp(
shader().createColorRamp() );
363 void QgsColorRampShaderWidget::applyColorRamp()
365 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
371 if ( !btnColorRamp->colorRampName().isEmpty() )
375 settings.
setValue( QStringLiteral(
"Raster/defaultPalette" ), btnColorRamp->colorRampName() );
378 bool enableContinuous = ( ramp->count() > 0 );
379 mClassificationModeComboBox->setEnabled( enableContinuous );
380 if ( !enableContinuous )
385 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
386 if ( topLevelItemCount > 0 )
390 if ( std::isnan( mMin ) || std::isnan( mMax ) )
392 colormapMinMax( min, max );
401 QTreeWidgetItem *currentItem =
nullptr;
402 for (
int i = 0; i < topLevelItemCount; ++i )
404 currentItem = mColormapTreeWidget->topLevelItem( i );
410 double value = currentItem->data( ValueColumn, Qt::ItemDataRole::EditRole ).toDouble( );
411 double position = ( value - min ) / ( max - min );
425 mColormapTreeWidget->clear();
426 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
428 for ( ; it != colorRampItems.constEnd(); ++it )
431 newItem->
setData( ValueColumn, Qt::ItemDataRole::DisplayRole, it->value );
432 newItem->
setData( ColorColumn, Qt::ItemDataRole::EditRole, it->color );
433 newItem->setText( LabelColumn, it->label );
434 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
436 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
447 const QString unit = mUnitLineEdit->text();
448 for ( i = 0; i < mColormapTreeWidget->topLevelItemCount(); i++ )
451 QString lbl { createLabel( currentItem, i, unit )};
452 if ( currentItem->text( LabelColumn ).isEmpty() || currentItem->text( LabelColumn ) == lbl || currentItem->foreground( LabelColumn ).color() == QColor( Qt::gray ) )
454 currentItem->setText( LabelColumn, lbl );
455 currentItem->setForeground( LabelColumn, QBrush( QColor( Qt::gray ) ) );
461 void QgsColorRampShaderWidget::mLoadFromBandButton_clicked()
463 if ( !mRasterDataProvider )
466 QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterDataProvider->
colorTable( mBand );
467 if ( !colorRampList.isEmpty() )
474 QMessageBox::warning(
this, tr(
"Load Color Map" ), tr(
"The color map for band %1 has no entries." ).arg( mBand ) );
480 void QgsColorRampShaderWidget::mLoadFromFileButton_clicked()
483 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
484 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Map from File" ), lastDir, tr(
"Textfile (*.txt)" ) );
485 if ( fileName.isEmpty() )
488 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
494 mColormapTreeWidget->clear();
496 mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( type ) );
500 if ( !errors.empty() )
502 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"The following lines contained errors\n\n" ) + errors.join(
'\n' ) );
507 const QString error = tr(
"An error occurred while reading the color map\n\n" ) + errors.join(
'\n' );
508 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), error );
511 QFileInfo fileInfo( fileName );
512 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
519 void QgsColorRampShaderWidget::mExportToFileButton_clicked()
522 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
523 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Map as File" ), lastDir, tr(
"Textfile (*.txt)" ) );
524 if ( fileName.isEmpty() )
529 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
530 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
531 for (
int i = 0; i < topLevelItemCount; ++i )
533 QTreeWidgetItem *currentItem = mColormapTreeWidget->topLevelItem( i );
540 item.
value = currentItem->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toDouble( );
541 item.
color = currentItem->data( ColorColumn, Qt::ItemDataRole::EditRole ).value<QColor>();
542 item.
label = currentItem->text( LabelColumn );
543 colorRampItems << item;
548 QMessageBox::warning(
this, tr(
"Save Color Map as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
551 QFileInfo fileInfo( fileName );
552 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
555 void QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked( QTreeWidgetItem *item,
int column )
562 if ( column == LabelColumn )
565 item->setForeground( LabelColumn, QBrush() );
569 void QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited( QTreeWidgetItem *item,
int column )
607 whileBlocking( mColorInterpolationComboBox )->setCurrentIndex( mColorInterpolationComboBox->findData( colorRampShader.
colorRampType() ) );
608 mColorInterpolationComboBox_currentIndexChanged( mColorInterpolationComboBox->currentIndex() );
610 mClassificationModeComboBox_currentIndexChanged( mClassificationModeComboBox->currentIndex() );
620 QString defaultPalette = settings.
value( QStringLiteral(
"/Raster/defaultPalette" ),
"Spectral" ).toString();
621 btnColorRamp->setColorRampFromName( defaultPalette );
624 mLabelPrecisionSpinBox->setValue( colorRampShader.
labelPrecision() );
634 void QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged(
int index )
641 QString valueToolTip;
642 switch ( interpolation )
645 valueLabel = tr(
"Value" );
646 valueToolTip = tr(
"Value for color stop" );
647 mLegendSettingsButton->setEnabled(
true );
650 valueLabel = tr(
"Value <=" );
651 valueToolTip = tr(
"Maximum value for class" );
652 mLegendSettingsButton->setEnabled(
false );
655 valueLabel = tr(
"Value =" );
656 valueToolTip = tr(
"Value for color" );
657 mLegendSettingsButton->setEnabled(
false );
661 QTreeWidgetItem *header = mColormapTreeWidget->headerItem();
662 header->setText( ValueColumn, valueLabel );
663 header->setToolTip( ValueColumn, valueToolTip );
682 resetClassifyButton();
695 bool QgsColorRampShaderWidget::colormapMinMax(
double &min,
double &max )
const
697 QTreeWidgetItem *item = mColormapTreeWidget->topLevelItem( 0 );
705 if ( ! std::isnan( mMin ) && ! std::isnan( mMax ) &&
static_cast< QgsColorRampShader::Type >( mColorInterpolationComboBox->currentData().toInt() ) == QgsColorRampShader::Type::Discrete )
712 min = item->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toDouble();
713 item = mColormapTreeWidget->topLevelItem( mColormapTreeWidget->topLevelItemCount() - 1 );
714 max = item->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toDouble();
721 double min = 0, max = 0;
722 if ( ! colormapMinMax( min, max ) )
735 void QgsColorRampShaderWidget::resetClassifyButton()
737 mClassifyButton->setEnabled(
true );
738 if ( std::isnan( mMin ) || std::isnan( mMax ) || mMin >= mMax )
740 mClassifyButton->setEnabled(
false );
744 QString QgsColorRampShaderWidget::createLabel( QTreeWidgetItem *currentItem,
int row,
const QString unit )
746 auto applyPrecision = [ = ](
const QString & value )
748 double val { value.toDouble( ) };
762 return QLocale().toString( std::round( val ),
'f', 0 );
767 if ( mLabelPrecisionSpinBox->value() < 0 )
769 const double factor = std::pow( 10, - mLabelPrecisionSpinBox->value() );
770 val =
static_cast<qlonglong
>( val / factor ) * factor;
771 return QLocale().toString( val,
'f', 0 );
773 return QLocale().toString( val,
'f', mLabelPrecisionSpinBox->value() );
779 if ( mLabelPrecisionSpinBox->value() < 0 )
781 const double factor = std::pow( 10, - mLabelPrecisionSpinBox->value() );
782 val =
static_cast<qlonglong
>( val / factor ) * factor;
783 return QLocale().toString( val,
'f', 0 );
785 return QLocale().toString( val,
'f', mLabelPrecisionSpinBox->value() );
799 lbl =
"<= " + applyPrecision( currentItem->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toString() ) + unit;
801 else if ( currentItem->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toDouble( ) == std::numeric_limits<double>::infinity() )
803 lbl =
"> " + applyPrecision( mColormapTreeWidget->topLevelItem( row - 1 )->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toString() ) + unit;
807 lbl = applyPrecision( mColormapTreeWidget->topLevelItem( row - 1 )->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toString() ) +
" - " + applyPrecision( currentItem->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toString() ) + unit;
812 lbl = applyPrecision( currentItem->data( ValueColumn, Qt::ItemDataRole::DisplayRole ).toString() ) + unit;
819 void QgsColorRampShaderWidget::changeColor()
821 QList<QTreeWidgetItem *> itemList;
822 itemList = mColormapTreeWidget->selectedItems();
823 if ( itemList.isEmpty() )
827 QTreeWidgetItem *firstItem = itemList.first();
829 QColor currentColor = firstItem->data( ColorColumn, Qt::ItemDataRole::EditRole ).value<QColor>();
831 if ( panel && panel->dockMode() )
838 for ( QTreeWidgetItem *item : std::as_const( itemList ) )
840 item->setData( ColorColumn, Qt::ItemDataRole::EditRole, newColor );
846 panel->openPanel( colorWidget );
852 if ( newColor.isValid() )
854 for ( QTreeWidgetItem *item : std::as_const( itemList ) )
856 item->setData( ColorColumn, Qt::ItemDataRole::EditRole, newColor );
865 void QgsColorRampShaderWidget::changeOpacity()
867 QList<QTreeWidgetItem *> itemList;
868 itemList = mColormapTreeWidget->selectedItems();
869 if ( itemList.isEmpty() )
873 QTreeWidgetItem *firstItem = itemList.first();
876 double oldOpacity = firstItem->data( ColorColumn, Qt::ItemDataRole::EditRole ).value<QColor>().alpha() / 255 * 100;
877 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
880 int newOpacity =
static_cast<int>( opacity / 100 * 255 );
881 const auto constItemList = itemList;
882 for ( QTreeWidgetItem *item : constItemList )
884 QColor newColor = item->data( ColorColumn, Qt::ItemDataRole::EditRole ).value<QColor>();
885 newColor.setAlpha( newOpacity );
886 item->setData( ColorColumn, Qt::ItemDataRole::EditRole, newColor );
894 void QgsColorRampShaderWidget::showLegendSettings()
904 mLegendSettings = legendPanel->
settings();
912 dialog.setWindowTitle( tr(
"Legend Settings" ) );
915 mLegendSettings = dialog.settings();