QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 
26 #include <QColorDialog>
27 #include <QFileDialog>
28 #include <QMessageBox>
29 #include <QSettings>
30 #include <QTextStream>
31 
33  QgsRasterRendererWidget( layer, extent )
34 {
35  QSettings settings;
36 
37  setupUi( this );
38 
39  mColormapTreeWidget->setColumnWidth( 1, 50 );
40 
41  QString defaultPalette = settings.value( "/Raster/defaultPalette", "Spectral" ).toString();
42 
43  mColorRampComboBox->populate( QgsStyleV2::defaultStyle() );
44 
45  QgsDebugMsg( "defaultPalette = " + defaultPalette );
46  mColorRampComboBox->setCurrentIndex( mColorRampComboBox->findText( defaultPalette ) );
47 
48  if ( !mRasterLayer )
49  {
50  return;
51  }
52 
54  if ( !provider )
55  {
56  return;
57  }
58 
59  // Must be before adding items to mBandComboBox (signal)
60  mMinLineEdit->setValidator( new QDoubleValidator( mMinLineEdit ) );
61  mMaxLineEdit->setValidator( new QDoubleValidator( mMaxLineEdit ) );
62 
63  mMinMaxWidget = new QgsRasterMinMaxWidget( layer, this );
64  mMinMaxWidget->setExtent( extent );
65  QHBoxLayout *layout = new QHBoxLayout();
66  layout->setContentsMargins( 0, 0, 0, 0 );
67  mMinMaxContainerWidget->setLayout( layout );
68  layout->addWidget( mMinMaxWidget );
69  connect( mMinMaxWidget, SIGNAL( load( int, double, double, int ) ),
70  this, SLOT( loadMinMax( int, double, double, int ) ) );
71 
72 
73  //fill available bands into combo box
74  int nBands = provider->bandCount();
75  for ( int i = 1; i <= nBands; ++i ) //band numbering seem to start at 1
76  {
77  mBandComboBox->addItem( displayBandName( i ), i );
78  }
79 
80  mColorInterpolationComboBox->addItem( tr( "Discrete" ), 0 );
81  mColorInterpolationComboBox->addItem( tr( "Linear" ), 1 );
82  mColorInterpolationComboBox->addItem( tr( "Exact" ), 2 );
83  mColorInterpolationComboBox->setCurrentIndex( 1 );
84  mClassificationModeComboBox->addItem( tr( "Continuous" ), Continuous );
85  mClassificationModeComboBox->addItem( tr( "Equal interval" ), EqualInterval );
86  //quantile would be nice as well
87 
88  mNumberOfEntriesSpinBox->setValue( 5 ); // some default
89 
90  setFromRenderer( layer->renderer() );
91 
92  // If there is currently no min/max, load default with user current default options
93  if ( mMinLineEdit->text().isEmpty() || mMaxLineEdit->text().isEmpty() )
94  {
96  }
97 
99 
101 }
102 
104 {
105 }
106 
108 {
109  QgsRasterShader* rasterShader = new QgsRasterShader();
110  QgsColorRampShader* colorRampShader = new QgsColorRampShader();
111  colorRampShader->setClip( mClipCheckBox->isChecked() );
112 
113  //iterate through mColormapTreeWidget and set colormap info of layer
114  QList<QgsColorRampShader::ColorRampItem> colorRampItems;
115  int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
116  QTreeWidgetItem* currentItem;
117  for ( int i = 0; i < topLevelItemCount; ++i )
118  {
119  currentItem = mColormapTreeWidget->topLevelItem( i );
120  if ( !currentItem )
121  {
122  continue;
123  }
124  QgsColorRampShader::ColorRampItem newColorRampItem;
125  newColorRampItem.value = currentItem->text( 0 ).toDouble();
126  newColorRampItem.color = currentItem->background( 1 ).color();
127  newColorRampItem.label = currentItem->text( 2 );
128  colorRampItems.append( newColorRampItem );
129  }
130  // sort the shader items
131  qSort( colorRampItems );
132  colorRampShader->setColorRampItemList( colorRampItems );
133 
134  if ( mColorInterpolationComboBox->currentText() == tr( "Linear" ) )
135  {
137  }
138  else if ( mColorInterpolationComboBox->currentText() == tr( "Discrete" ) )
139  {
141  }
142  else
143  {
144  colorRampShader->setColorRampType( QgsColorRampShader::EXACT );
145  }
146  rasterShader->setRasterShaderFunction( colorRampShader );
147 
148  int bandNumber = mBandComboBox->itemData( mBandComboBox->currentIndex() ).toInt();
150 
151  renderer->setClassificationMin( lineEditValue( mMinLineEdit ) );
152  renderer->setClassificationMax( lineEditValue( mMaxLineEdit ) );
154  return renderer;
155 }
156 
158 {
159  QTreeWidgetItem* newItem = new QTreeWidgetItem( mColormapTreeWidget );
160  newItem->setText( 0, "0.0" );
161  newItem->setBackground( 1, QBrush( QColor( Qt::magenta ) ) );
162  newItem->setText( 2, tr( "Custom color map entry" ) );
163 }
164 
166 {
167  QTreeWidgetItem* currentItem = mColormapTreeWidget->currentItem();
168  if ( currentItem )
169  {
170  delete currentItem;
171  }
172 }
173 
175 {
176  bool inserted = false;
177  int myCurrentIndex = 0;
178  int myTopLevelItemCount = mColormapTreeWidget->topLevelItemCount();
179  QTreeWidgetItem* myCurrentItem;
180  QList<QgsColorRampShader::ColorRampItem> myColorRampItems;
181  for ( int i = 0; i < myTopLevelItemCount; ++i )
182  {
183  myCurrentItem = mColormapTreeWidget->topLevelItem( i );
184  //If the item is null or does not have a pixel values set, skip
185  if ( !myCurrentItem || myCurrentItem->text( 0 ) == "" )
186  {
187  continue;
188  }
189 
190  //Create a copy of the new Color ramp Item
191  QgsColorRampShader::ColorRampItem myNewColorRampItem;
192  myNewColorRampItem.value = myCurrentItem->text( 0 ).toDouble();
193  myNewColorRampItem.color = myCurrentItem->background( 1 ).color();
194  myNewColorRampItem.label = myCurrentItem->text( 2 );
195 
196  //Simple insertion sort - speed is not a huge factor here
197  inserted = false;
198  myCurrentIndex = 0;
199  while ( !inserted )
200  {
201  if ( 0 == myColorRampItems.size() || myCurrentIndex == myColorRampItems.size() )
202  {
203  myColorRampItems.push_back( myNewColorRampItem );
204  inserted = true;
205  }
206  else if ( myColorRampItems[myCurrentIndex].value > myNewColorRampItem.value )
207  {
208  myColorRampItems.insert( myCurrentIndex, myNewColorRampItem );
209  inserted = true;
210  }
211  else if ( myColorRampItems[myCurrentIndex].value <= myNewColorRampItem.value && myCurrentIndex == myColorRampItems.size() - 1 )
212  {
213  myColorRampItems.push_back( myNewColorRampItem );
214  inserted = true;
215  }
216  else if ( myColorRampItems[myCurrentIndex].value <= myNewColorRampItem.value && myColorRampItems[myCurrentIndex+1].value > myNewColorRampItem.value )
217  {
218  myColorRampItems.insert( myCurrentIndex + 1, myNewColorRampItem );
219  inserted = true;
220  }
221  myCurrentIndex++;
222  }
223  }
224  populateColormapTreeWidget( myColorRampItems );
225 }
226 
228 {
229  int bandComboIndex = mBandComboBox->currentIndex();
230  if ( bandComboIndex == -1 || !mRasterLayer )
231  {
232  return;
233  }
234 
235  //int bandNr = mBandComboBox->itemData( bandComboIndex ).toInt();
236  //QgsRasterBandStats myRasterBandStats = mRasterLayer->dataProvider()->bandStatistics( bandNr );
237  int numberOfEntries = 0;
238 
239  QList<double> entryValues;
240  QList<QColor> entryColors;
241 
242  double min = lineEditValue( mMinLineEdit );
243  double max = lineEditValue( mMaxLineEdit );
244 
245  QgsVectorColorRampV2* colorRamp = mColorRampComboBox->currentColorRamp();
246 
247  if ( mClassificationModeComboBox->itemData( mClassificationModeComboBox->currentIndex() ).toInt() == Continuous )
248  {
249  if ( colorRamp )
250  {
251  numberOfEntries = colorRamp->count();
252  for ( int i = 0; i < colorRamp->count(); ++i )
253  {
254  double value = colorRamp->value( i );
255  entryValues.push_back( min + value * ( max - min ) );
256  }
257  }
258  }
259  else // EqualInterval
260  {
261  numberOfEntries = mNumberOfEntriesSpinBox->value();
262  //double currentValue = myRasterBandStats.minimumValue;
263  double currentValue = min;
264  double intervalDiff;
265  if ( numberOfEntries > 1 )
266  {
267  //because the highest value is also an entry, there are (numberOfEntries - 1)
268  //intervals
269  //intervalDiff = ( myRasterBandStats.maximumValue - myRasterBandStats.minimumValue ) /
270  intervalDiff = ( max - min ) / ( numberOfEntries - 1 );
271  }
272  else
273  {
274  //intervalDiff = myRasterBandStats.maximumValue - myRasterBandStats.minimumValue;
275  intervalDiff = max - min;
276  }
277 
278  for ( int i = 0; i < numberOfEntries; ++i )
279  {
280  entryValues.push_back( currentValue );
281  currentValue += intervalDiff;
282  }
283  }
284 
285 #if 0
286  //hard code color range from blue -> red for now. Allow choice of ramps in future
287  int colorDiff = 0;
288  if ( numberOfEntries != 0 )
289  {
290  colorDiff = ( int )( 255 / numberOfEntries );
291  }
292  for ( int i = 0; i < numberOfEntries; ++i )
293  {
294  QColor currentColor;
295  currentColor.setRgb( colorDiff*i, 0, 255 - colorDiff * i );
296  entryColors.push_back( currentColor );
297  }
298 #endif
299 
300  if ( ! colorRamp )
301  {
302  //hard code color range from blue -> red (previous default)
303  int colorDiff = 0;
304  if ( numberOfEntries != 0 )
305  {
306  colorDiff = ( int )( 255 / numberOfEntries );
307  }
308 
309  for ( int i = 0; i < numberOfEntries; ++i )
310  {
311  QColor currentColor;
312  int idx = mInvertCheckBox->isChecked() ? numberOfEntries - i - 1 : i;
313  currentColor.setRgb( colorDiff*idx, 0, 255 - colorDiff * idx );
314  entryColors.push_back( currentColor );
315  }
316  }
317  else
318  {
319  for ( int i = 0; i < numberOfEntries; ++i )
320  {
321  int idx = mInvertCheckBox->isChecked() ? numberOfEntries - i - 1 : i;
322  entryColors.push_back( colorRamp->color((( double ) idx ) / ( numberOfEntries - 1 ) ) );
323  }
324  }
325 
326  mColormapTreeWidget->clear();
327 
328  QList<double>::const_iterator value_it = entryValues.begin();
329  QList<QColor>::const_iterator color_it = entryColors.begin();
330 
331  for ( ; value_it != entryValues.end(); ++value_it, ++color_it )
332  {
333  QTreeWidgetItem* newItem = new QTreeWidgetItem( mColormapTreeWidget );
334  newItem->setText( 0, QString::number( *value_it, 'f' ) );
335  newItem->setBackground( 1, QBrush( *color_it ) );
336  newItem->setText( 2, QString::number( *value_it, 'f' ) );
337  newItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
338  }
339 }
340 
342 {
343  mNumberOfEntriesSpinBox->setEnabled( mClassificationModeComboBox->itemData( index ).toInt() == EqualInterval );
344 }
345 
347 {
348  Q_UNUSED( index );
349  QSettings settings;
350  settings.setValue( "/Raster/defaultPalette", mColorRampComboBox->currentText() );
351 }
352 
353 void QgsSingleBandPseudoColorRendererWidget::populateColormapTreeWidget( const QList<QgsColorRampShader::ColorRampItem>& colorRampItems )
354 {
355  mColormapTreeWidget->clear();
356  QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItems.constBegin();
357  for ( ; it != colorRampItems.constEnd(); ++it )
358  {
359  QTreeWidgetItem* newItem = new QTreeWidgetItem( mColormapTreeWidget );
360  newItem->setText( 0, QString::number( it->value, 'f' ) );
361  newItem->setBackground( 1, QBrush( it->color ) );
362  newItem->setText( 2, it->label );
363  }
364 }
365 
367 {
368  if ( !mRasterLayer || !mRasterLayer->dataProvider() )
369  {
370  return;
371  }
372 
373  int bandIndex = mBandComboBox->itemData( mBandComboBox->currentIndex() ).toInt();
374 
375 
376  QList<QgsColorRampShader::ColorRampItem> colorRampList = mRasterLayer->dataProvider()->colorTable( bandIndex );
377  if ( colorRampList.size() > 0 )
378  {
379  populateColormapTreeWidget( colorRampList );
380  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findText( tr( "Linear" ) ) );
381  }
382  else
383  {
384  QMessageBox::warning( this, tr( "Load Color Map" ), tr( "The color map for band %1 has no entries" ).arg( bandIndex ) );
385  }
386 }
387 
389 {
390  int lineCounter = 0;
391  bool importError = false;
392  QString badLines;
393  QSettings settings;
394  QString lastDir = settings.value( "lastRasterFileFilterDir", "" ).toString();
395  QString fileName = QFileDialog::getOpenFileName( this, tr( "Open file" ), lastDir, tr( "Textfile (*.txt)" ) );
396  QFile inputFile( fileName );
397  if ( inputFile.open( QFile::ReadOnly ) )
398  {
399  //clear the current tree
400  mColormapTreeWidget->clear();
401 
402  QTextStream inputStream( &inputFile );
403  QString inputLine;
404  QStringList inputStringComponents;
405  QList<QgsColorRampShader::ColorRampItem> colorRampItems;
406 
407  //read through the input looking for valid data
408  while ( !inputStream.atEnd() )
409  {
410  lineCounter++;
411  inputLine = inputStream.readLine();
412  if ( !inputLine.isEmpty() )
413  {
414  if ( !inputLine.simplified().startsWith( "#" ) )
415  {
416  if ( inputLine.contains( "INTERPOLATION", Qt::CaseInsensitive ) )
417  {
418  inputStringComponents = inputLine.split( ":" );
419  if ( inputStringComponents.size() == 2 )
420  {
421  if ( inputStringComponents[1].trimmed().toUpper().compare( "INTERPOLATED", Qt::CaseInsensitive ) == 0 )
422  {
423  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findText( tr( "Linear" ) ) );
424  }
425  else if ( inputStringComponents[1].trimmed().toUpper().compare( "DISCRETE", Qt::CaseInsensitive ) == 0 )
426  {
427  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findText( tr( "Discrete" ) ) );
428  }
429  else
430  {
431  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findText( tr( "Exact" ) ) );
432  }
433  }
434  else
435  {
436  importError = true;
437  badLines = badLines + QString::number( lineCounter ) + ":\t[" + inputLine + "]\n";
438  }
439  }
440  else
441  {
442  inputStringComponents = inputLine.split( "," );
443  if ( inputStringComponents.size() == 6 )
444  {
445  QgsColorRampShader::ColorRampItem currentItem( inputStringComponents[0].toDouble(),
446  QColor::fromRgb( inputStringComponents[1].toInt(), inputStringComponents[2].toInt(),
447  inputStringComponents[3].toInt(), inputStringComponents[4].toInt() ),
448  inputStringComponents[5] );
449  colorRampItems.push_back( currentItem );
450  }
451  else
452  {
453  importError = true;
454  badLines = badLines + QString::number( lineCounter ) + ":\t[" + inputLine + "]\n";
455  }
456  }
457  }
458  }
459  lineCounter++;
460  }
461  populateColormapTreeWidget( colorRampItems );
462 
463  if ( importError )
464  {
465  QMessageBox::warning( this, tr( "Import Error" ), tr( "The following lines contained errors\n\n" ) + badLines );
466  }
467  }
468  else if ( !fileName.isEmpty() )
469  {
470  QMessageBox::warning( this, tr( "Read access denied" ), tr( "Read access denied. Adjust the file permissions and try again.\n\n" ) );
471  }
472 }
473 
475 {
476  QSettings settings;
477  QString lastDir = settings.value( "lastRasterFileFilterDir", "" ).toString();
478  QString fileName = QFileDialog::getSaveFileName( this, tr( "Save file" ), lastDir, tr( "Textfile (*.txt)" ) );
479  if ( !fileName.isEmpty() )
480  {
481  if ( !fileName.endsWith( ".txt", Qt::CaseInsensitive ) )
482  {
483  fileName = fileName + ".txt";
484  }
485 
486  QFile outputFile( fileName );
487  if ( outputFile.open( QFile::WriteOnly ) )
488  {
489  QTextStream outputStream( &outputFile );
490  outputStream << "# " << tr( "QGIS Generated Color Map Export File" ) << "\n";
491  outputStream << "INTERPOLATION:";
492  if ( mColorInterpolationComboBox->currentText() == tr( "Linear" ) )
493  {
494  outputStream << "INTERPOLATED\n";
495  }
496  else if ( mColorInterpolationComboBox->currentText() == tr( "Discrete" ) )
497  {
498  outputStream << "DISCRETE\n";
499  }
500  else
501  {
502  outputStream << "EXACT\n";
503  }
504 
505  int topLevelItemCount = mColormapTreeWidget->topLevelItemCount();
506  QTreeWidgetItem* currentItem;
507  QColor color;
508  for ( int i = 0; i < topLevelItemCount; ++i )
509  {
510  currentItem = mColormapTreeWidget->topLevelItem( i );
511  if ( !currentItem )
512  {
513  continue;
514  }
515  color = currentItem->background( 1 ).color();
516  outputStream << currentItem->text( 0 ).toDouble() << ",";
517  outputStream << color.red() << "," << color.green() << "," << color.blue() << "," << color.alpha() << ",";
518  if ( currentItem->text( 2 ) == "" )
519  {
520  outputStream << "Color entry " << i + 1 << "\n";
521  }
522  else
523  {
524  outputStream << currentItem->text( 2 ) << "\n";
525  }
526  }
527  outputStream.flush();
528  outputFile.close();
529  }
530  else
531  {
532  QMessageBox::warning( this, tr( "Write access denied" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
533  }
534  }
535 }
536 
538 {
539  if ( !item )
540  {
541  return;
542  }
543 
544  if ( column == 1 ) //change item color
545  {
546  item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
547  QColor newColor = QColorDialog::getColor( item->background( column ).color(), this, "Change color", QColorDialog::ShowAlphaChannel );
548  if ( newColor.isValid() )
549  {
550  item->setBackground( 1, QBrush( newColor ) );
551  }
552  }
553  else
554  {
555  item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable );
556  }
557 }
558 
560 {
561  const QgsSingleBandPseudoColorRenderer* pr = dynamic_cast<const QgsSingleBandPseudoColorRenderer*>( r );
562  if ( pr )
563  {
564  const QgsRasterShader* rasterShader = pr->shader();
565  if ( rasterShader )
566  {
567  const QgsColorRampShader* colorRampShader = dynamic_cast<const QgsColorRampShader*>( rasterShader->rasterShaderFunction() );
568  if ( colorRampShader )
569  {
570  if ( colorRampShader->colorRampType() == QgsColorRampShader::INTERPOLATED )
571  {
572  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findText( tr( "Linear" ) ) );
573  }
574  else if ( colorRampShader->colorRampType() == QgsColorRampShader::DISCRETE )
575  {
576  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findText( tr( "Discrete" ) ) );
577  }
578  else
579  {
580  mColorInterpolationComboBox->setCurrentIndex( mColorInterpolationComboBox->findText( tr( "Exact" ) ) );
581  }
582 
583  const QList<QgsColorRampShader::ColorRampItem> colorRampItemList = colorRampShader->colorRampItemList();
584  QList<QgsColorRampShader::ColorRampItem>::const_iterator it = colorRampItemList.constBegin();
585  for ( ; it != colorRampItemList.end(); ++it )
586  {
587  QTreeWidgetItem* newItem = new QTreeWidgetItem( mColormapTreeWidget );
588  newItem->setText( 0, QString::number( it->value, 'f' ) );
589  newItem->setBackground( 1, QBrush( it->color ) );
590  newItem->setText( 2, it->label );
591  }
592  mClipCheckBox->setChecked( colorRampShader->clip() );
593  }
594  }
595  setLineEditValue( mMinLineEdit, pr->classificationMin() );
596  setLineEditValue( mMaxLineEdit, pr->classificationMax() );
599  }
600 }
601 
603 {
604  QList<int> myBands;
605  myBands.append( mBandComboBox->itemData( index ).toInt() );
606  mMinMaxWidget->setBands( myBands );
607 }
608 
609 void QgsSingleBandPseudoColorRendererWidget::loadMinMax( int theBandNo, double theMin, double theMax, int theOrigin )
610 {
611  Q_UNUSED( theBandNo );
612  QgsDebugMsg( QString( "theBandNo = %1 theMin = %2 theMax = %3" ).arg( theBandNo ).arg( theMin ).arg( theMax ) );
613 
614  if ( qIsNaN( theMin ) )
615  {
616  mMinLineEdit->clear();
617  }
618  else
619  {
620  mMinLineEdit->setText( QString::number( theMin ) );
621  }
622 
623  if ( qIsNaN( theMax ) )
624  {
625  mMaxLineEdit->clear();
626  }
627  else
628  {
629  mMaxLineEdit->setText( QString::number( theMax ) );
630  }
631 
632  mMinMaxOrigin = theOrigin;
634 }
635 
637 {
638  mMinMaxOriginLabel->setText( QgsRasterRenderer::minMaxOriginLabel( mMinMaxOrigin ) );
639 }
640 
641 void QgsSingleBandPseudoColorRendererWidget::setLineEditValue( QLineEdit * theLineEdit, double theValue )
642 {
643  QString s;
644  if ( !qIsNaN( theValue ) )
645  {
646  s = QString::number( theValue );
647  }
648  theLineEdit->setText( s );
649 }
650 
651 double QgsSingleBandPseudoColorRendererWidget::lineEditValue( const QLineEdit * theLineEdit ) const
652 {
653  if ( theLineEdit->text().isEmpty() )
654  {
655  return std::numeric_limits<double>::quiet_NaN();
656  }
657 
658  return theLineEdit->text().toDouble();
659 }
660 
662 {
663  mClassifyButton->setEnabled( true );
664  double min = lineEditValue( mMinLineEdit );
665  double max = lineEditValue( mMaxLineEdit );
666  if ( qIsNaN( min ) || qIsNaN( max ) || min >= max )
667  {
668  mClassifyButton->setEnabled( false );
669  }
670 }