QGIS API Documentation  2.99.0-Master (37c43df)
qgssinglebandpseudocolorrendererwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssinglebandpseudocolorrendererwidget.cpp
3  ------------------------------------------
4  begin : February 2012
5  copyright : (C) 2012 by Marco Hugentobler
6  email : marco at sourcepole dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
20 #include "qgsrasterlayer.h"
21 #include "qgsrasterdataprovider.h"
22 #include "qgsrastershader.h"
23 #include "qgsrasterminmaxwidget.h"
24 #include "qgstreewidgetitem.h"
25 
26 // for color ramps - todo add rasterStyle and refactor raster vs. vector ramps
27 #include "qgsstyle.h"
28 #include "qgscolorramp.h"
29 #include "qgscolorrampbutton.h"
30 #include "qgscolordialog.h"
31 
32 #include <QPushButton>
33 #include <QFileDialog>
34 #include <QMessageBox>
35 #include <QSettings>
36 #include <QTextStream>
37 
39  : QgsRasterRendererWidget( layer, extent )
40  , mMinMaxWidget( nullptr )
41  , mMinMaxOrigin( 0 )
42 {
43  QSettings settings;
44 
45  setupUi( this );
46 
47  mColormapTreeWidget->setColumnWidth( ColorColumn, 50 );
48 
49  QString defaultPalette = settings.value( QStringLiteral( "/Raster/defaultPalette" ), "" ).toString();
50  btnColorRamp->setColorRampFromName( defaultPalette );
51 
52  if ( !mRasterLayer )
53  {
54  return;
55  }
56 
58  if ( !provider )
59  {
60  return;
61  }
62 
63  // Must be before adding items to mBandComboBox (signal)
64  mMinLineEdit->setValidator( new QDoubleValidator( mMinLineEdit ) );
65  mMaxLineEdit->setValidator( new QDoubleValidator( mMaxLineEdit ) );
66 
67  mMinMaxWidget = new QgsRasterMinMaxWidget( layer, this );
68  mMinMaxWidget->setExtent( extent );
69  mMinMaxWidget->setMapCanvas( mCanvas );
70 
71  QHBoxLayout *layout = new QHBoxLayout();
72  layout->setContentsMargins( 0, 0, 0, 0 );
73  mMinMaxContainerWidget->setLayout( layout );
74  layout->addWidget( mMinMaxWidget );
75  connect( mMinMaxWidget, SIGNAL( load( int, double, double, int ) ),
76  this, SLOT( loadMinMax( int, double, double, int ) ) );
77 
78 
79  //fill available bands into combo box
80  int nBands = provider->bandCount();
81  for ( int i = 1; i <= nBands; ++i ) //band numbering seem to start at 1
82  {
83  mBandComboBox->addItem( displayBandName( i ), i );
84  }
85 
86  mColorInterpolationComboBox->addItem( tr( "Discrete" ), QgsColorRampShader::DISCRETE );
87  mColorInterpolationComboBox->addItem( tr( "Linear" ), QgsColorRampShader::INTERPOLATED );
88  mColorInterpolationComboBox->addItem( tr( "Exact" ), QgsColorRampShader::EXACT );
89  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( QgsColorRampShader::INTERPOLATED ) );
90 
91  mClassificationModeComboBox->addItem( tr( "Continuous" ), Continuous );
92  mClassificationModeComboBox->addItem( tr( "Equal interval" ), EqualInterval );
93  mClassificationModeComboBox->addItem( tr( "Quantile" ), Quantile );
94 
95  mNumberOfEntriesSpinBox->setValue( 5 ); // some default
96 
97  setFromRenderer( layer->renderer() );
98 
99  // If there is currently no min/max, load default with user current default options
100  if ( mMinLineEdit->text().isEmpty() || mMaxLineEdit->text().isEmpty() )
101  {
102  mMinMaxWidget->load();
103  }
104 
105  on_mClassificationModeComboBox_currentIndexChanged( 0 );
106 
107  resetClassifyButton();
108 
109  connect( mClassificationModeComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classify() ) );
110  connect( mMinLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( classify() ) );
111  connect( mMaxLineEdit, SIGNAL( textChanged( QString ) ), this, SLOT( classify() ) );
112  connect( mClassifyButton, &QPushButton::clicked, this, &QgsSingleBandPseudoColorRendererWidget::applyColorRamp );
113  connect( btnColorRamp, &QgsColorRampButton::colorRampChanged, this, &QgsSingleBandPseudoColorRendererWidget::applyColorRamp );
114  connect( mNumberOfEntriesSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( classify() ) );
115  connect( mBandComboBox, SIGNAL( currentIndexChanged( int ) ), this, SLOT( classify() ) );
116  connect( mClipCheckBox, SIGNAL( toggled( bool ) ), this, SIGNAL( widgetChanged() ) );
117 }
118 
120 {
121 }
122 
124 {
125  QgsRasterShader* rasterShader = new QgsRasterShader();
126  QgsColorRampShader* colorRampShader = new QgsColorRampShader();
127  colorRampShader->setClip( mClipCheckBox->isChecked() );
128 
129  //iterate through mColormapTreeWidget and set colormap info of layer
130  QList<QgsColorRampShader::ColorRampItem> colorRampItems;
131  int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
132  QTreeWidgetItem* currentItem;
133  for ( int i = 0; i < topLevelItemCount; ++i )
134  {
135  currentItem = mColormapTreeWidget->topLevelItem( i );
136  if ( !currentItem )
137  {
138  continue;
139  }
140  QgsColorRampShader::ColorRampItem newColorRampItem;
141  newColorRampItem.value = currentItem->text( ValueColumn ).toDouble();
142  newColorRampItem.color = currentItem->background( ColorColumn ).color();
143  newColorRampItem.label = currentItem->text( LabelColumn );
144  colorRampItems.append( newColorRampItem );
145  }
146  // sort the shader items
147  qSort( colorRampItems );
148  colorRampShader->setColorRampItemList( colorRampItems );
149 
150  QgsColorRampShader::ColorRamp_TYPE interpolation = static_cast< QgsColorRampShader::ColorRamp_TYPE >( mColorInterpolationComboBox->currentData().toInt() );
151  colorRampShader->setColorRampType( interpolation );
152 
153 
154  if ( !btnColorRamp->isNull() )
155  {
156  colorRampShader->setSourceColorRamp( btnColorRamp->colorRamp() );
157  }
158 
159  rasterShader->setRasterShaderFunction( colorRampShader );
160 
161  int bandNumber = mBandComboBox->currentData().toInt();
163 
164  renderer->setClassificationMin( lineEditValue( mMinLineEdit ) );
165  renderer->setClassificationMax( lineEditValue( mMaxLineEdit ) );
166  renderer->setClassificationMinMaxOrigin( mMinMaxOrigin );
167  return renderer;
168 }
169 
171 {
173  mMinMaxWidget->setMapCanvas( canvas );
174 }
175 
180 void QgsSingleBandPseudoColorRendererWidget::autoLabel()
181 {
182  QgsColorRampShader::ColorRamp_TYPE interpolation = static_cast< QgsColorRampShader::ColorRamp_TYPE >( mColorInterpolationComboBox->currentData().toInt() );
183  bool discrete = interpolation == QgsColorRampShader::DISCRETE;
184  QString unit = mUnitLineEdit->text();
185  QString label;
186  int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
187  QTreeWidgetItem* currentItem;
188  for ( int i = 0; i < topLevelItemCount; ++i )
189  {
190  currentItem = mColormapTreeWidget->topLevelItem( i );
191  //If the item is null or does not have a pixel values set, skip
192  if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
193  {
194  continue;
195  }
196 
197  if ( discrete )
198  {
199  if ( i == 0 )
200  {
201  label = "<= " + currentItem->text( ValueColumn ) + unit;
202  }
203  else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
204  {
205  label = "> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) + unit;
206  }
207  else
208  {
209  label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) + " - " + currentItem->text( ValueColumn ) + unit;
210  }
211  }
212  else
213  {
214  label = currentItem->text( ValueColumn ) + unit;
215  }
216 
217  if ( currentItem->text( LabelColumn ).isEmpty() || currentItem->text( LabelColumn ) == label || currentItem->foreground( LabelColumn ).color() == QColor( Qt::gray ) )
218  {
219  currentItem->setText( LabelColumn, label );
220  currentItem->setForeground( LabelColumn, QBrush( QColor( Qt::gray ) ) );
221  }
222  }
223 }
224 
226 void QgsSingleBandPseudoColorRendererWidget::setUnitFromLabels()
227 {
228  QgsColorRampShader::ColorRamp_TYPE interpolation = static_cast< QgsColorRampShader::ColorRamp_TYPE >( mColorInterpolationComboBox->currentData().toInt() );
229  bool discrete = interpolation == QgsColorRampShader::DISCRETE;
230  QStringList allSuffixes;
231  QString label;
232  int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
233  QTreeWidgetItem* currentItem;
234  for ( int i = 0; i < topLevelItemCount; ++i )
235  {
236  currentItem = mColormapTreeWidget->topLevelItem( i );
237  //If the item is null or does not have a pixel values set, skip
238  if ( !currentItem || currentItem->text( ValueColumn ).isEmpty() )
239  {
240  continue;
241  }
242 
243  if ( discrete )
244  {
245  if ( i == 0 )
246  {
247  label = "<= " + currentItem->text( ValueColumn );
248  }
249  else if ( currentItem->text( ValueColumn ).toDouble() == std::numeric_limits<double>::infinity() )
250  {
251  label = "> " + mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn );
252  }
253  else
254  {
255  label = mColormapTreeWidget->topLevelItem( i - 1 )->text( ValueColumn ) + " - " + currentItem->text( ValueColumn );
256  }
257  }
258  else
259  {
260  label = currentItem->text( ValueColumn );
261  }
262 
263  if ( currentItem->text( LabelColumn ).startsWith( label ) )
264  {
265  allSuffixes.append( currentItem->text( LabelColumn ).mid( label.length() ) );
266  }
267  }
268  // find most common suffix
269  QStringList suffixes = QStringList( allSuffixes );
270  suffixes.removeDuplicates();
271  int max = 0;
272  QString unit;
273  for ( int i = 0; i < suffixes.count(); ++i )
274  {
275  int n = allSuffixes.count( suffixes[i] );
276  if ( n > max )
277  {
278  max = n;
279  unit = suffixes[i];
280  }
281  }
282  // Set this suffix as unit if at least used twice
283  if ( max >= 2 )
284  {
285  mUnitLineEdit->setText( unit );
286  }
287  autoLabel();
288 }
289 
290 void QgsSingleBandPseudoColorRendererWidget::on_mAddEntryButton_clicked()
291 {
292  QgsTreeWidgetItemObject* newItem = new QgsTreeWidgetItemObject( mColormapTreeWidget );
293  newItem->setText( ValueColumn, QStringLiteral( "0" ) );
294  newItem->setBackground( ColorColumn, QBrush( QColor( Qt::magenta ) ) );
295  newItem->setText( LabelColumn, QString() );
296  newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
297  connect( newItem, SIGNAL( itemEdited( QTreeWidgetItem*, int ) ),
298  this, SLOT( mColormapTreeWidget_itemEdited( QTreeWidgetItem*, int ) ) );
299  mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
300  autoLabel();
301  emit widgetChanged();
302 }
303 
304 void QgsSingleBandPseudoColorRendererWidget::on_mDeleteEntryButton_clicked()
305 {
306  QTreeWidgetItem* currentItem = mColormapTreeWidget->currentItem();
307  if ( currentItem )
308  {
309  delete currentItem;
310  }
311  emit widgetChanged();
312 }
313 
315 {
316  int bandComboIndex = mBandComboBox->currentIndex();
317  if ( bandComboIndex == -1 || !mRasterLayer )
318  {
319  return;
320  }
321 
322  //int bandNr = mBandComboBox->itemData( bandComboIndex ).toInt();
323  //QgsRasterBandStats myRasterBandStats = mRasterLayer->dataProvider()->bandStatistics( bandNr );
324  int numberOfEntries;
325 
326  QgsColorRampShader::ColorRamp_TYPE interpolation = static_cast< QgsColorRampShader::ColorRamp_TYPE >( mColorInterpolationComboBox->currentData().toInt() );
327  bool discrete = interpolation == QgsColorRampShader::DISCRETE;
328 
329  QList<double> entryValues;
330  QVector<QColor> entryColors;
331 
332  double min = lineEditValue( mMinLineEdit );
333  double max = lineEditValue( mMaxLineEdit );
334 
335  QScopedPointer< QgsColorRamp > colorRamp( btnColorRamp->colorRamp() );
336 
337  if ( mClassificationModeComboBox->currentData().toInt() == Continuous )
338  {
339  if ( colorRamp.data() )
340  {
341  numberOfEntries = colorRamp->count();
342  entryValues.reserve( numberOfEntries );
343  if ( discrete )
344  {
345  double intervalDiff = max - min;
346 
347  // remove last class when ColorRamp is gradient and discrete, as they are implemented with an extra stop
348  QgsGradientColorRamp* colorGradientRamp = dynamic_cast<QgsGradientColorRamp*>( colorRamp.data() );
349  if ( colorGradientRamp != NULL && colorGradientRamp->isDiscrete() )
350  {
351  numberOfEntries--;
352  }
353  else
354  {
355  // if color ramp is continuous scale values to get equally distributed classes.
356  // Doesn't work perfectly when stops are non equally distributed.
357  intervalDiff *= ( numberOfEntries - 1 ) / ( double )numberOfEntries;
358  }
359 
360  // skip first value (always 0.0)
361  for ( int i = 1; i < numberOfEntries; ++i )
362  {
363  double value = colorRamp->value( i );
364  entryValues.push_back( min + value * intervalDiff );
365  }
366  entryValues.push_back( std::numeric_limits<double>::infinity() );
367  }
368  else
369  {
370  for ( int i = 0; i < numberOfEntries; ++i )
371  {
372  double value = colorRamp->value( i );
373  entryValues.push_back( min + value * ( max - min ) );
374  }
375  }
376  // for continuous mode take original color map colors
377  for ( int i = 0; i < numberOfEntries; ++i )
378  {
379  int idx = i;
380  entryColors.push_back( colorRamp->color( colorRamp->value( idx ) ) );
381  }
382  }
383  }
384  else // for other classification modes interpolate colors linearly
385  {
386  numberOfEntries = mNumberOfEntriesSpinBox->value();
387  if ( numberOfEntries < 2 )
388  return; // < 2 classes is not useful, shouldn't happen, but if it happens save it from crashing
389 
390  if ( mClassificationModeComboBox->currentData().toInt() == Quantile )
391  { // Quantile
392  int bandNr = mBandComboBox->itemData( bandComboIndex ).toInt();
393  //QgsRasterHistogram rasterHistogram = mRasterLayer->dataProvider()->histogram( bandNr );
394 
395  double cut1 = std::numeric_limits<double>::quiet_NaN();
396  double cut2 = std::numeric_limits<double>::quiet_NaN();
397 
398  QgsRectangle extent = mMinMaxWidget->extent();
399  int sampleSize = mMinMaxWidget->sampleSize();
400 
401  // set min and max from histogram, used later to calculate number of decimals to display
402  mRasterLayer->dataProvider()->cumulativeCut( bandNr, 0.0, 1.0, min, max, extent, sampleSize );
403 
404  entryValues.reserve( numberOfEntries );
405  if ( discrete )
406  {
407  double intervalDiff = 1.0 / ( numberOfEntries );
408  for ( int i = 1; i < numberOfEntries; ++i )
409  {
410  mRasterLayer->dataProvider()->cumulativeCut( bandNr, 0.0, i * intervalDiff, cut1, cut2, extent, sampleSize );
411  entryValues.push_back( cut2 );
412  }
413  entryValues.push_back( std::numeric_limits<double>::infinity() );
414  }
415  else
416  {
417  double intervalDiff = 1.0 / ( numberOfEntries - 1 );
418  for ( int i = 0; i < numberOfEntries; ++i )
419  {
420  mRasterLayer->dataProvider()->cumulativeCut( bandNr, 0.0, i * intervalDiff, cut1, cut2, extent, sampleSize );
421  entryValues.push_back( cut2 );
422  }
423  }
424  }
425  else // EqualInterval
426  {
427  entryValues.reserve( numberOfEntries );
428  if ( discrete )
429  {
430  // in discrete mode the lowest value is not an entry and the highest
431  // value is inf, there are ( numberOfEntries ) of which the first
432  // and last are not used.
433  double intervalDiff = ( max - min ) / ( numberOfEntries );
434 
435  for ( int i = 1; i < numberOfEntries; ++i )
436  {
437  entryValues.push_back( min + i * intervalDiff );
438  }
439  entryValues.push_back( std::numeric_limits<double>::infinity() );
440  }
441  else
442  {
443  //because the highest value is also an entry, there are (numberOfEntries - 1) intervals
444  double intervalDiff = ( max - min ) / ( numberOfEntries - 1 );
445 
446  for ( int i = 0; i < numberOfEntries; ++i )
447  {
448  entryValues.push_back( min + i * intervalDiff );
449  }
450  }
451  }
452 
453  if ( !colorRamp.data() )
454  {
455  //hard code color range from blue -> red (previous default)
456  int colorDiff = 0;
457  if ( numberOfEntries != 0 )
458  {
459  colorDiff = ( int )( 255 / numberOfEntries );
460  }
461 
462  entryColors.reserve( numberOfEntries );
463  for ( int i = 0; i < numberOfEntries; ++i )
464  {
465  QColor currentColor;
466  int idx = i;
467  currentColor.setRgb( colorDiff*idx, 0, 255 - colorDiff * idx );
468  entryColors.push_back( currentColor );
469  }
470  }
471  else
472  {
473  entryColors.reserve( numberOfEntries );
474  for ( int i = 0; i < numberOfEntries; ++i )
475  {
476  int idx = i;
477  entryColors.push_back( colorRamp->color((( double ) idx ) / ( numberOfEntries - 1 ) ) );
478  }
479  }
480  }
481 
482  mColormapTreeWidget->clear();
483 
484  QList<double>::const_iterator value_it = entryValues.begin();
485  QVector<QColor>::const_iterator color_it = entryColors.begin();
486 
487  // calculate a reasonable number of decimals to display
488  double maxabs = log10( qMax( qAbs( max ), qAbs( min ) ) );
489  int nDecimals = qRound( qMax( 3.0 + maxabs - log10( max - min ), maxabs <= 15.0 ? maxabs + 0.49 : 0.0 ) );
490 
491  for ( ; value_it != entryValues.end(); ++value_it, ++color_it )
492  {
493  QgsTreeWidgetItemObject* newItem = new QgsTreeWidgetItemObject( mColormapTreeWidget );
494  newItem->setText( ValueColumn, QString::number( *value_it, 'g', nDecimals ) );
495  newItem->setBackground( ColorColumn, QBrush( *color_it ) );
496  newItem->setText( LabelColumn, QString() );
497  newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
498  connect( newItem, SIGNAL( itemEdited( QTreeWidgetItem*, int ) ),
499  this, SLOT( mColormapTreeWidget_itemEdited( QTreeWidgetItem*, int ) ) );
500  }
501  autoLabel();
502  emit widgetChanged();
503 }
504 
505 void QgsSingleBandPseudoColorRendererWidget::on_mClassificationModeComboBox_currentIndexChanged( int index )
506 {
507  Mode mode = static_cast< Mode >( mClassificationModeComboBox->itemData( index ).toInt() );
508  mNumberOfEntriesSpinBox->setEnabled( mode != Continuous );
509  mMinLineEdit->setEnabled( mode != Quantile );
510  mMaxLineEdit->setEnabled( mode != Quantile );
511 }
512 
513 void QgsSingleBandPseudoColorRendererWidget::applyColorRamp()
514 {
515  QScopedPointer< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
516  if ( !ramp )
517  {
518  return;
519  }
520 
521  if ( !btnColorRamp->colorRampName().isEmpty() )
522  {
523  // Remember last used color ramp
524  QSettings settings;
525  settings.setValue( QStringLiteral( "/Raster/defaultPalette" ), btnColorRamp->colorRampName() );
526  }
527 
528  bool enableContinuous = ( ramp->count() > 0 );
529  mClassificationModeComboBox->setEnabled( enableContinuous );
530  if ( !enableContinuous )
531  {
532  mClassificationModeComboBox->setCurrentIndex( mClassificationModeComboBox->findData( EqualInterval ) );
533  }
534 
535  classify();
536 }
537 
538 void QgsSingleBandPseudoColorRendererWidget::populateColormapTreeWidget( const QList<QgsColorRampShader::ColorRampItem>& colorRampItems )
539 {
540  mColormapTreeWidget->clear();
541  QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
542  for ( ; it != colorRampItems.constEnd(); ++it )
543  {
544  QgsTreeWidgetItemObject* newItem = new QgsTreeWidgetItemObject( mColormapTreeWidget );
545  newItem->setText( ValueColumn, QString::number( it->value, 'g', 15 ) );
546  newItem->setBackground( ColorColumn, QBrush( it->color ) );
547  newItem->setText( LabelColumn, it->label );
548  newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
549  connect( newItem, SIGNAL( itemEdited( QTreeWidgetItem*, int ) ),
550  this, SLOT( mColormapTreeWidget_itemEdited( QTreeWidgetItem*, int ) ) );
551  }
552  setUnitFromLabels();
553 }
554 
555 void QgsSingleBandPseudoColorRendererWidget::on_mLoadFromBandButton_clicked()
556 {
557  if ( !mRasterLayer || !mRasterLayer->dataProvider() )
558  {
559  return;
560  }
561 
562  int bandIndex = mBandComboBox->currentData().toInt();
563 
564 
565  QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterLayer->dataProvider()->colorTable( bandIndex );
566  if ( !colorRampList.isEmpty() )
567  {
568  populateColormapTreeWidget( colorRampList );
569  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( QgsColorRampShader::INTERPOLATED ) );
570  }
571  else
572  {
573  QMessageBox::warning( this, tr( "Load Color Map" ), tr( "The color map for band %1 has no entries" ).arg( bandIndex ) );
574  }
575  emit widgetChanged();
576 }
577 
578 void QgsSingleBandPseudoColorRendererWidget::on_mLoadFromFileButton_clicked()
579 {
580  int lineCounter = 0;
581  bool importError = false;
582  QString badLines;
583  QSettings settings;
584  QString lastDir = settings.value( QStringLiteral( "lastColorMapDir" ), QDir::homePath() ).toString();
585  QString fileName = QFileDialog::getOpenFileName( this, tr( "Open file" ), lastDir, tr( "Textfile (*.txt)" ) );
586  QFile inputFile( fileName );
587  if ( inputFile.open( QFile::ReadOnly ) )
588  {
589  //clear the current tree
590  mColormapTreeWidget->clear();
591 
592  QTextStream inputStream( &inputFile );
593  QString inputLine;
594  QStringList inputStringComponents;
595  QList<QgsColorRampShader::ColorRampItem> colorRampItems;
596 
597  //read through the input looking for valid data
598  while ( !inputStream.atEnd() )
599  {
600  lineCounter++;
601  inputLine = inputStream.readLine();
602  if ( !inputLine.isEmpty() )
603  {
604  if ( !inputLine.simplified().startsWith( '#' ) )
605  {
606  if ( inputLine.contains( QLatin1String( "INTERPOLATION" ), Qt::CaseInsensitive ) )
607  {
608  inputStringComponents = inputLine.split( ':' );
609  if ( inputStringComponents.size() == 2 )
610  {
611  if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String( "INTERPOLATED" ), Qt::CaseInsensitive ) == 0 )
612  {
613  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( QgsColorRampShader::INTERPOLATED ) );
614  }
615  else if ( inputStringComponents[1].trimmed().toUpper().compare( QLatin1String( "DISCRETE" ), Qt::CaseInsensitive ) == 0 )
616  {
617  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( QgsColorRampShader::DISCRETE ) );
618  }
619  else
620  {
621  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( QgsColorRampShader::EXACT ) );
622  }
623  }
624  else
625  {
626  importError = true;
627  badLines = badLines + QString::number( lineCounter ) + ":\t[" + inputLine + "]\n";
628  }
629  }
630  else
631  {
632  inputStringComponents = inputLine.split( ',' );
633  if ( inputStringComponents.size() == 6 )
634  {
635  QgsColorRampShader::ColorRampItem currentItem( inputStringComponents[0].toDouble(),
636  QColor::fromRgb( inputStringComponents[1].toInt(), inputStringComponents[2].toInt(),
637  inputStringComponents[3].toInt(), inputStringComponents[4].toInt() ),
638  inputStringComponents[5] );
639  colorRampItems.push_back( currentItem );
640  }
641  else
642  {
643  importError = true;
644  badLines = badLines + QString::number( lineCounter ) + ":\t[" + inputLine + "]\n";
645  }
646  }
647  }
648  }
649  lineCounter++;
650  }
651  populateColormapTreeWidget( colorRampItems );
652 
653  QFileInfo fileInfo( fileName );
654  settings.setValue( QStringLiteral( "lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
655 
656  if ( importError )
657  {
658  QMessageBox::warning( this, tr( "Import Error" ), tr( "The following lines contained errors\n\n" ) + badLines );
659  }
660  }
661  else if ( !fileName.isEmpty() )
662  {
663  QMessageBox::warning( this, tr( "Read access denied" ), tr( "Read access denied. Adjust the file permissions and try again.\n\n" ) );
664  }
665  emit widgetChanged();
666 }
667 
668 void QgsSingleBandPseudoColorRendererWidget::on_mExportToFileButton_clicked()
669 {
670  QSettings settings;
671  QString lastDir = settings.value( QStringLiteral( "lastColorMapDir" ), QDir::homePath() ).toString();
672  QString fileName = QFileDialog::getSaveFileName( this, tr( "Save file" ), lastDir, tr( "Textfile (*.txt)" ) );
673  if ( !fileName.isEmpty() )
674  {
675  if ( !fileName.endsWith( QLatin1String( ".txt" ), Qt::CaseInsensitive ) )
676  {
677  fileName = fileName + ".txt";
678  }
679 
680  QFile outputFile( fileName );
681  if ( outputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
682  {
683  QTextStream outputStream( &outputFile );
684  outputStream << "# " << tr( "QGIS Generated Color Map Export File" ) << '\n';
685  outputStream << "INTERPOLATION:";
686  QgsColorRampShader::ColorRamp_TYPE interpolation = static_cast< QgsColorRampShader::ColorRamp_TYPE >( mColorInterpolationComboBox->currentData().toInt() );
687  switch ( interpolation )
688  {
690  outputStream << "INTERPOLATED\n";
691  break;
693  outputStream << "DISCRETE\n";
694  break;
696  outputStream << "EXACT\n";
697  break;
698  }
699 
700  int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
701  QTreeWidgetItem* currentItem;
702  QColor color;
703  for ( int i = 0; i < topLevelItemCount; ++i )
704  {
705  currentItem = mColormapTreeWidget->topLevelItem( i );
706  if ( !currentItem )
707  {
708  continue;
709  }
710  color = currentItem->background( ColorColumn ).color();
711  outputStream << currentItem->text( ValueColumn ).toDouble() << ',';
712  outputStream << color.red() << ',' << color.green() << ',' << color.blue() << ',' << color.alpha() << ',';
713  if ( currentItem->text( LabelColumn ).isEmpty() )
714  {
715  outputStream << "Color entry " << i + 1 << '\n';
716  }
717  else
718  {
719  outputStream << currentItem->text( LabelColumn ) << '\n';
720  }
721  }
722  outputStream.flush();
723  outputFile.close();
724 
725  QFileInfo fileInfo( fileName );
726  settings.setValue( QStringLiteral( "lastColorMapDir" ), fileInfo.absoluteDir().absolutePath() );
727  }
728  else
729  {
730  QMessageBox::warning( this, tr( "Write access denied" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
731  }
732  }
733 }
734 
735 void QgsSingleBandPseudoColorRendererWidget::on_mColormapTreeWidget_itemDoubleClicked( QTreeWidgetItem* item, int column )
736 {
737  if ( !item )
738  {
739  return;
740  }
741 
742  if ( column == ColorColumn )
743  {
744  item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
745  QColor newColor = QgsColorDialog::getColor( item->background( column ).color(), this, QStringLiteral( "Change color" ), true );
746  if ( newColor.isValid() )
747  {
748  item->setBackground( ColorColumn, QBrush( newColor ) );
749  emit widgetChanged();
750  }
751  }
752  else
753  {
754  if ( column == LabelColumn )
755  {
756  // Set text color to default black, which signifies a manually edited label
757  item->setForeground( LabelColumn, QBrush() );
758  }
759  item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
760  }
761 }
762 
764 void QgsSingleBandPseudoColorRendererWidget::mColormapTreeWidget_itemEdited( QTreeWidgetItem* item, int column )
765 {
766  Q_UNUSED( item );
767 
768  if ( column == ValueColumn )
769  {
770  mColormapTreeWidget->sortItems( ValueColumn, Qt::AscendingOrder );
771  autoLabel();
772  emit widgetChanged();
773  }
774  else if ( column == LabelColumn )
775  {
776  // call autoLabel to fill when empty or gray out when same as autoLabel
777  autoLabel();
778  }
779 }
780 
782 {
783  const QgsSingleBandPseudoColorRenderer* pr = dynamic_cast<const QgsSingleBandPseudoColorRenderer*>( r );
784  if ( pr )
785  {
786  mBandComboBox->setCurrentIndex( mBandComboBox->findData( pr->band() ) );
787 
788  const QgsRasterShader* rasterShader = pr->shader();
789  if ( rasterShader )
790  {
791  const QgsColorRampShader* colorRampShader = dynamic_cast<const QgsColorRampShader*>( rasterShader->rasterShaderFunction() );
792  if ( colorRampShader )
793  {
794  if ( colorRampShader->sourceColorRamp() )
795  {
796  btnColorRamp->setColorRamp( colorRampShader->sourceColorRamp() );
797  }
798  else
799  {
800  QSettings settings;
801  QString defaultPalette = settings.value( "/Raster/defaultPalette", "Spectral" ).toString();
802  btnColorRamp->setColorRampFromName( defaultPalette );
803  }
804 
805  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findData( colorRampShader->colorRampType() ) );
806 
807  const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
808  QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
809  for ( ; it != colorRampItemList.end(); ++it )
810  {
811  QgsTreeWidgetItemObject* newItem = new QgsTreeWidgetItemObject( mColormapTreeWidget );
812  newItem->setText( ValueColumn, QString::number( it->value, 'g', 15 ) );
813  newItem->setBackground( ColorColumn, QBrush( it->color ) );
814  newItem->setText( LabelColumn, it->label );
815  newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
816  connect( newItem, SIGNAL( itemEdited( QTreeWidgetItem*, int ) ),
817  this, SLOT( mColormapTreeWidget_itemEdited( QTreeWidgetItem*, int ) ) );
818  }
819  setUnitFromLabels();
820  mClipCheckBox->setChecked( colorRampShader->clip() );
821  }
822  }
823  setLineEditValue( mMinLineEdit, pr->classificationMin() );
824  setLineEditValue( mMaxLineEdit, pr->classificationMax() );
825  mMinMaxOrigin = pr->classificationMinMaxOrigin();
826  showMinMaxOrigin();
827  }
828 }
829 
830 void QgsSingleBandPseudoColorRendererWidget::on_mBandComboBox_currentIndexChanged( int index )
831 {
832  QList<int> bands;
833  bands.append( mBandComboBox->itemData( index ).toInt() );
834  mMinMaxWidget->setBands( bands );
835 }
836 
837 void QgsSingleBandPseudoColorRendererWidget::on_mColorInterpolationComboBox_currentIndexChanged( int index )
838 {
839  QgsColorRampShader::ColorRamp_TYPE interpolation = static_cast< QgsColorRampShader::ColorRamp_TYPE >( mColorInterpolationComboBox->itemData( index ).toInt() );
840 
841  mClipCheckBox->setEnabled( interpolation == QgsColorRampShader::INTERPOLATED );
842 
843  QString valueLabel;
844  QString valueToolTip;
845  switch ( interpolation )
846  {
848  valueLabel = tr( "Value" );
849  valueToolTip = tr( "Value for color stop" );
850  break;
852  valueLabel = tr( "Value <=" );
853  valueToolTip = tr( "Maximum value for class" );
854  break;
856  valueLabel = tr( "Value =" );
857  valueToolTip = tr( "Value for color" );
858  break;
859  }
860 
861  QTreeWidgetItem* header = mColormapTreeWidget->headerItem();
862  header->setText( ValueColumn, valueLabel );
863  header->setToolTip( ValueColumn, valueToolTip );
864 
865  autoLabel();
866  emit widgetChanged();
867 }
868 
869 void QgsSingleBandPseudoColorRendererWidget::loadMinMax( int theBandNo, double theMin, double theMax, int theOrigin )
870 {
871  Q_UNUSED( theBandNo );
872  QgsDebugMsg( QString( "theBandNo = %1 theMin = %2 theMax = %3" ).arg( theBandNo ).arg( theMin ).arg( theMax ) );
873 
874  if ( qIsNaN( theMin ) )
875  {
876  mMinLineEdit->clear();
877  }
878  else
879  {
880  mMinLineEdit->setText( QString::number( theMin ) );
881  }
882 
883  if ( qIsNaN( theMax ) )
884  {
885  mMaxLineEdit->clear();
886  }
887  else
888  {
889  mMaxLineEdit->setText( QString::number( theMax ) );
890  }
891 
892  mMinMaxOrigin = theOrigin;
893  showMinMaxOrigin();
894 }
895 
896 void QgsSingleBandPseudoColorRendererWidget::showMinMaxOrigin()
897 {
898  mMinMaxOriginLabel->setText( QgsRasterRenderer::minMaxOriginLabel( mMinMaxOrigin ) );
899 }
900 
901 void QgsSingleBandPseudoColorRendererWidget::setLineEditValue( QLineEdit * theLineEdit, double theValue )
902 {
903  QString s;
904  if ( !qIsNaN( theValue ) )
905  {
906  s = QString::number( theValue );
907  }
908  theLineEdit->setText( s );
909 }
910 
911 double QgsSingleBandPseudoColorRendererWidget::lineEditValue( const QLineEdit * theLineEdit ) const
912 {
913  if ( theLineEdit->text().isEmpty() )
914  {
915  return std::numeric_limits<double>::quiet_NaN();
916  }
917 
918  return theLineEdit->text().toDouble();
919 }
920 
921 void QgsSingleBandPseudoColorRendererWidget::resetClassifyButton()
922 {
923  mClassifyButton->setEnabled( true );
924  double min = lineEditValue( mMinLineEdit );
925  double max = lineEditValue( mMaxLineEdit );
926  if ( qIsNaN( min ) || qIsNaN( max ) || min >= max )
927  {
928  mClassifyButton->setEnabled( false );
929  }
930 }
Assigns the color of the exact matching value in the color ramp item list.
virtual int bandCount() const =0
Get number of bands.
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Interface for all raster shaders.
QgsColorRampShader::ColorRamp_TYPE colorRampType() const
Get the color ramp type.
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
void colorRampChanged()
Emitted whenever a new color ramp is set for the button.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
void setBands(const QList< int > &theBands)
QList< QgsColorRampShader::ColorRampItem > colorRampItemList() const
Get the custom colormap.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &theList)
Set custom colormap.
QgsRasterRenderer * renderer() const
void setClip(bool clip)
Sets whether the shader should not render values out of range.
void loadMinMax(int theBandNo, double theMin, double theMax, int theOrigin)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:106
virtual void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the widget.
void setMapCanvas(QgsMapCanvas *canvas) override
Sets the map canvas associated with the widget.
virtual double value(int index) const override
Returns relative value between [0,1] of color at specified index.
virtual QString min(int index=0)
void setExtent(const QgsRectangle &theExtent)
Sets the extent to use for minimum and maximum value calculation.
QgsRasterShaderFunction * rasterShaderFunction()
static QString minMaxOriginLabel(int theOrigin)
QString displayBandName(int band) const
Returns a band name for display. First choice is color name, otherwise band number.
Raster renderer pipe for single band pseudocolor.
static QColor getColor(const QColor &initialColor, QWidget *parent, const QString &title=QString(), const bool allowAlpha=false)
Return a color selection from a color dialog.
bool isDiscrete() const
Returns true if the gradient is using discrete interpolation, rather than smoothly interpolating betw...
Definition: qgscolorramp.h:165
void setSourceColorRamp(QgsColorRamp *colorramp)
Set the source color ramp.
void setRasterShaderFunction(QgsRasterShaderFunction *)
A public method that allows the user to set their own shader function.
void setColorRampType(QgsColorRampShader::ColorRamp_TYPE theColorRampType)
Set the color ramp type.
Custom QgsTreeWidgetItem with extra signals when item is edited.
int sampleSize()
Return the selected sample size.
QgsRectangle extent()
Return the extent selected by the user.
void setMapCanvas(QgsMapCanvas *canvas)
Sets the map canvas associated with the widget.
Interpolates the color between two class breaks linearly.
void classify()
Executes the single band pseudo raster classficiation.
Assigns the color of the higher class for every pixel between two class breaks.
QgsSingleBandPseudoColorRendererWidget(QgsRasterLayer *layer, const QgsRectangle &extent=QgsRectangle())
bool clip() const
Returns whether the shader will clip values which are out of range.
virtual QString max(int index=0)
QgsRasterDataProvider * dataProvider()
Returns the data provider.
virtual void cumulativeCut(int theBandNo, double theLowerCount, double theUpperCount, double &theLowerValue, double &theUpperValue, const QgsRectangle &theExtent=QgsRectangle(), int theSampleSize=0)
Find values for cumulative pixel count cut.
QgsMapCanvas * mCanvas
Associated map canvas.
QgsColorRamp * sourceColorRamp() const
Get the source color ramp.
Gradient color ramp, which smoothly interpolates between two colors and also supports optional extra ...
Definition: qgscolorramp.h:108
void widgetChanged()
Emitted when something on the widget has changed.
ColorRamp_TYPE
Supported methods for color interpolation.
Raster renderer pipe that applies colors to a raster.
int band() const
Returns the band used by the renderer.
Base class for raster data providers.