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