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 Q_FOREACH ( QTreeWidgetItem *item, itemList )
299 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
300 if ( !ramp || std::isnan( mMin ) || std::isnan( mMax ) )
309 static_cast< QgsColorRampShader::ClassificationMode >( mClassificationModeComboBox->currentData().toInt() ) )
313 colorRampShader->classifyColorRamp( mNumberOfEntriesSpinBox->value(),
316 mRasterDataProvider );
317 colorRampShader->setClip( mClipCheckBox->isChecked() );
319 mColormapTreeWidget->clear();
321 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
322 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
323 for ( ; it != colorRampItemList.end(); ++it )
326 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
327 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
328 newItem->setText( LabelColumn, QString() );
329 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
331 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
333 mClipCheckBox->setChecked( colorRampShader->clip() );
339 void QgsColorRampShaderWidget::mClassificationModeComboBox_currentIndexChanged(
int index )
347 void QgsColorRampShaderWidget::applyColorRamp()
349 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
355 if ( !btnColorRamp->colorRampName().isEmpty() )
359 settings.
setValue( QStringLiteral(
"Raster/defaultPalette" ), btnColorRamp->colorRampName() );
362 bool enableContinuous = ( ramp->count() > 0 );
363 mClassificationModeComboBox->setEnabled( enableContinuous );
364 if ( !enableContinuous )
369 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
370 if ( topLevelItemCount > 0 )
373 QTreeWidgetItem *currentItem =
nullptr;
374 for (
int i = 0; i < topLevelItemCount; ++i )
376 currentItem = mColormapTreeWidget->topLevelItem( i );
382 double value = currentItem->text( ValueColumn ).toDouble();
383 double position = ( value - mMin ) / ( mMax - mMin );
384 currentItem->setData( ColorColumn, Qt::EditRole, ramp->color( position ) );
397 mColormapTreeWidget->clear();
398 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
399 for ( ; it != colorRampItems.constEnd(); ++it )
402 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
403 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
404 newItem->setText( LabelColumn, it->label );
405 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
407 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
413 void QgsColorRampShaderWidget::mLoadFromBandButton_clicked()
415 if ( !mRasterDataProvider )
418 QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterDataProvider->
colorTable( mBand );
419 if ( !colorRampList.isEmpty() )
426 QMessageBox::warning(
this, tr(
"Load Color Map" ), tr(
"The color map for band %1 has no entries." ).arg( mBand ) );
432 void QgsColorRampShaderWidget::mLoadFromFileButton_clicked()
435 bool importError =
false;
438 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
439 QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Load Color Map from File" ), lastDir, tr(
"Textfile (*.txt)" ) );
440 QFile inputFile( fileName );
441 if ( inputFile.open( QFile::ReadOnly ) )
444 mColormapTreeWidget->clear();
446 QTextStream inputStream( &inputFile );
448 QStringList inputStringComponents;
449 QList<QgsColorRampShader::ColorRampItem> colorRampItems;
452 while ( !inputStream.atEnd() )
455 inputLine = inputStream.readLine();
456 if ( !inputLine.isEmpty() )
458 if ( !inputLine.simplified().startsWith(
'#' ) )
460 if ( inputLine.contains( QLatin1String(
"INTERPOLATION" ), Qt::CaseInsensitive ) )
462 inputStringComponents = inputLine.split(
':' );
463 if ( inputStringComponents.size() == 2 )
465 if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"INTERPOLATED" ), Qt::CaseInsensitive ) == 0 )
469 else if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String(
"DISCRETE" ), Qt::CaseInsensitive ) == 0 )
481 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
486 inputStringComponents = inputLine.split(
',' );
487 if ( inputStringComponents.size() == 6 )
490 QColor::fromRgb( inputStringComponents[1].toInt(), inputStringComponents[2].toInt(),
491 inputStringComponents[3].toInt(), inputStringComponents[4].toInt() ),
492 inputStringComponents[5] );
493 colorRampItems.push_back( currentItem );
498 badLines = badLines + QString::number( lineCounter ) +
":\t[" + inputLine +
"]\n";
507 QFileInfo fileInfo( fileName );
508 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
512 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"The following lines contained errors\n\n" ) + badLines );
515 else if ( !fileName.isEmpty() )
517 QMessageBox::warning(
this, tr(
"Load Color Map from File" ), tr(
"Read access denied. Adjust the file permissions and try again.\n\n" ) );
524 void QgsColorRampShaderWidget::mExportToFileButton_clicked()
527 QString lastDir = settings.
value( QStringLiteral(
"lastColorMapDir" ), QDir::homePath() ).toString();
528 QString fileName = QFileDialog::getSaveFileName(
this, tr(
"Save Color Map as File" ), lastDir, tr(
"Textfile (*.txt)" ) );
529 if ( !fileName.isEmpty() )
531 if ( !fileName.endsWith( QLatin1String(
".txt" ), Qt::CaseInsensitive ) )
533 fileName = fileName +
".txt";
536 QFile outputFile( fileName );
537 if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
539 QTextStream outputStream( &outputFile );
540 outputStream <<
"# " << tr(
"QGIS Generated Color Map Export File" ) <<
'\n';
541 outputStream <<
"INTERPOLATION:";
543 switch ( interpolation )
546 outputStream <<
"INTERPOLATED\n";
549 outputStream <<
"DISCRETE\n";
552 outputStream <<
"EXACT\n";
556 int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
557 QTreeWidgetItem *currentItem =
nullptr;
559 for (
int i = 0; i < topLevelItemCount; ++i )
561 currentItem = mColormapTreeWidget->topLevelItem( i );
566 color = currentItem->data( ColorColumn, Qt::EditRole ).value<QColor>();
567 outputStream << currentItem->text( ValueColumn ).toDouble() <<
',';
568 outputStream << color.red() <<
',' << color.green() <<
',' << color.blue() <<
',' << color.alpha() <<
',';
569 if ( currentItem->text( LabelColumn ).isEmpty() )
571 outputStream <<
"Color entry " << i + 1 <<
'\n';
575 outputStream << currentItem->text( LabelColumn ) <<
'\n';
578 outputStream.flush();
581 QFileInfo fileInfo( fileName );
582 settings.
setValue( QStringLiteral(
"lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
586 QMessageBox::warning(
this, tr(
"Save Color Map as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
591 void QgsColorRampShaderWidget::mColormapTreeWidget_itemDoubleClicked( QTreeWidgetItem *item,
int column )
598 if ( column == ColorColumn )
600 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
601 QColor newColor =
QgsColorDialog::getColor( item->data( column, Qt::EditRole ).value<QColor>(),
this, QStringLiteral(
"Change Color" ), true );
602 if ( newColor.isValid() )
604 item->setData( ColorColumn, Qt::EditRole, newColor );
611 if ( column == LabelColumn )
614 item->setForeground( LabelColumn, QBrush() );
616 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
620 void QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited( QTreeWidgetItem *item,
int column )
624 if ( column == ValueColumn )
626 mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
633 else if ( column == LabelColumn )
650 QString defaultPalette = settings.
value( QStringLiteral(
"/Raster/defaultPalette" ),
"Spectral" ).toString();
651 btnColorRamp->setColorRampFromName( defaultPalette );
654 mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( colorRampShader.
colorRampType() ) );
656 mColormapTreeWidget->clear();
657 const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader.
colorRampItemList();
658 QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
659 for ( ; it != colorRampItemList.end(); ++it )
662 newItem->setText( ValueColumn, QString::number( it->value,
'g', 15 ) );
663 newItem->
setData( ColorColumn, Qt::EditRole, it->color );
664 newItem->setText( LabelColumn, it->label );
665 newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
667 this, &QgsColorRampShaderWidget::mColormapTreeWidget_itemEdited );
671 mClipCheckBox->setChecked( colorRampShader.
clip() );
672 mClassificationModeComboBox->setCurrentIndex( mClassificationModeComboBox->findData( colorRampShader.
classificationMode() ) );
673 mNumberOfEntriesSpinBox->setValue( colorRampShader.
colorRampItemList().count() );
676 void QgsColorRampShaderWidget::mColorInterpolationComboBox_currentIndexChanged(
int index )
683 QString valueToolTip;
684 switch ( interpolation )
687 valueLabel = tr(
"Value" );
688 valueToolTip = tr(
"Value for color stop" );
691 valueLabel = tr(
"Value <=" );
692 valueToolTip = tr(
"Maximum value for class" );
695 valueLabel = tr(
"Value =" );
696 valueToolTip = tr(
"Value for color" );
700 QTreeWidgetItem *header = mColormapTreeWidget->headerItem();
701 header->setText( ValueColumn, valueLabel );
702 header->setToolTip( ValueColumn, valueToolTip );
721 resetClassifyButton();
738 QTreeWidgetItem *item = mColormapTreeWidget->topLevelItem( 0 );
744 double min = item->text( ValueColumn ).toDouble();
745 item = mColormapTreeWidget->topLevelItem( mColormapTreeWidget->topLevelItemCount() - 1 );
746 double max = item->text( ValueColumn ).toDouble();
756 void QgsColorRampShaderWidget::resetClassifyButton()
758 mClassifyButton->setEnabled(
true );
759 if ( std::isnan( mMin ) || std::isnan( mMax ) || mMin >= mMax )
761 mClassifyButton->setEnabled(
false );
765 void QgsColorRampShaderWidget::changeColor()
767 QList<QTreeWidgetItem *> itemList;
768 itemList = mColormapTreeWidget->selectedItems();
769 if ( itemList.isEmpty() )
773 QTreeWidgetItem *firstItem = itemList.first();
775 QColor newColor =
QgsColorDialog::getColor( firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>(),
this, QStringLiteral(
"Change Color" ), true );
776 if ( newColor.isValid() )
778 Q_FOREACH ( QTreeWidgetItem *item, itemList )
780 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
781 item->setData( ColorColumn, Qt::EditRole, newColor );
789 void QgsColorRampShaderWidget::changeOpacity()
791 QList<QTreeWidgetItem *> itemList;
792 itemList = mColormapTreeWidget->selectedItems();
793 if ( itemList.isEmpty() )
797 QTreeWidgetItem *firstItem = itemList.first();
800 double oldOpacity = firstItem->data( ColorColumn, Qt::EditRole ).value<QColor>().alpha() / 255 * 100;
801 double opacity = QInputDialog::getDouble(
this, tr(
"Opacity" ), tr(
"Change color opacity [%]" ), oldOpacity, 0.0, 100.0, 0, &ok );
804 int newOpacity =
static_cast<int>( opacity / 100 * 255 );
805 Q_FOREACH ( QTreeWidgetItem *item, itemList )
807 QColor newColor = item->data( ColorColumn, Qt::EditRole ).value<QColor>();
808 newColor.setAlpha( newOpacity );
809 item->setData( ColorColumn, Qt::EditRole, newColor );
ClassificationMode classificationMode() const
Returns the classification mode.
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.
QgsColorRamp * sourceColorRamp() const
Returns the source color ramp.
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.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setClip(bool clip)
Sets whether the shader should not render values out of range.
Type
Supported methods for color interpolation.
QList< QgsColorRampShader::ColorRampItem > colorRampItemList() const
Returns the custom colormap.
void setColorRampType(QgsColorRampShader::Type colorRampType)
Sets the color ramp type.
bool clip() const
Returns whether the shader will clip values which are out of range.
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.
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.
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
A delegate for showing a color swatch in a list.
Base class for raster data providers.