QGIS API Documentation  3.23.0-Master (eb871beae0)
qgsrastertransparencywidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrastertransparencywidget.cpp
3  ---------------------
4  begin : May 2016
5  copyright : (C) 2016 by Nathan Woodrow
6  email : woodrow dot nathan at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include <QWidget>
16 #include <QIntValidator>
17 #include <QFile>
18 #include <QTextStream>
19 #include <QMessageBox>
20 #include <QFileDialog>
21 #include <QRegularExpression>
22 
23 #include "qgssettings.h"
25 #include "qgsrasterlayer.h"
26 #include "qgsraster.h"
27 #include "qgsrasterlayerrenderer.h"
28 #include "qgsrasterdataprovider.h"
29 #include "qgsrastertransparency.h"
30 #include "qgsmaptoolemitpoint.h"
31 #include "qgsmapsettings.h"
32 #include "qgsrectangle.h"
33 #include "qgsmapcanvas.h"
36 #include "qgsdoublevalidator.h"
38 #include "qgstemporalcontroller.h"
39 
41  : QgsMapLayerConfigWidget( layer, canvas, parent )
42  , TRSTRING_NOT_SET( tr( "Not Set" ) )
43  , mRasterLayer( layer )
44  , mMapCanvas( canvas )
45 {
46  setupUi( this );
47  connect( pbnAddValuesFromDisplay, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked );
48  connect( pbnAddValuesManually, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnAddValuesManually_clicked );
49  connect( pbnDefaultValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnDefaultValues_clicked );
50  connect( pbnExportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked );
51  connect( pbnImportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked );
52  connect( pbnRemoveSelectedRow, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked );
53 
54  mNodataColorButton->setShowNoColor( true );
55  mNodataColorButton->setColorDialogTitle( tr( "Select No Data Color" ) );
56  syncToLayer();
57 
58  connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsPanelWidget::widgetChanged );
59  connect( cboxTransparencyBand, &QgsRasterBandComboBox::bandChanged, this, &QgsPanelWidget::widgetChanged );
60  connect( mSrcNoDataValueCheckBox, &QCheckBox::stateChanged, this, &QgsPanelWidget::widgetChanged );
61  connect( leNoDataValue, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
62  leNoDataValue->setValidator( new QgsDoubleValidator( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), this ) );
63  connect( mNodataColorButton, &QgsColorButton::colorChanged, this, &QgsPanelWidget::widgetChanged );
64 
65  mPixelSelectorTool = nullptr;
66  if ( mMapCanvas )
67  {
68  mPixelSelectorTool = new QgsMapToolEmitPoint( mMapCanvas );
69  connect( mPixelSelectorTool, &QgsMapToolEmitPoint::canvasClicked, this, &QgsRasterTransparencyWidget::pixelSelected );
70  }
71  else
72  {
73  pbnAddValuesFromDisplay->setEnabled( false );
74  }
75 
77 }
78 
80 {
81  mContext = context;
82 }
83 
85 {
86  QgsExpressionContext expContext;
90 
91  if ( QgsMapCanvas *canvas = mContext.mapCanvas() )
92  {
93  expContext << QgsExpressionContextUtils::mapSettingsScope( canvas->mapSettings() )
94  << new QgsExpressionContextScope( canvas->expressionContextScope() );
95  if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( canvas->temporalController() ) )
96  {
97  expContext << generator->createExpressionContextScope();
98  }
99  }
100  else
101  {
103  }
104 
105  if ( mRasterLayer )
106  expContext << QgsExpressionContextUtils::layerScope( mRasterLayer );
107 
108  // additional scopes
109  const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
110  for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
111  {
112  expContext.appendScope( new QgsExpressionContextScope( scope ) );
113  }
114 
115  return expContext;
116 }
117 
119 {
120  QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
121  QgsRasterRenderer *renderer = mRasterLayer->renderer();
122  if ( provider )
123  {
124  if ( provider->dataType( 1 ) == Qgis::DataType::ARGB32
125  || provider->dataType( 1 ) == Qgis::DataType::ARGB32_Premultiplied )
126  {
127  gboxNoDataValue->setEnabled( false );
128  gboxCustomTransparency->setEnabled( false );
129  }
130 
131  cboxTransparencyBand->setShowNotSetOption( true, tr( "None" ) );
132  cboxTransparencyBand->setLayer( mRasterLayer );
133 
134  mOpacityWidget->setOpacity( renderer->opacity() );
135 
136  cboxTransparencyBand->setBand( renderer->alphaBand() );
137  }
138 
139  if ( mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) )
140  {
141  lblSrcNoDataValue->setText( QgsRasterBlock::printValue( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) ) );
142  }
143  else
144  {
145  lblSrcNoDataValue->setText( tr( "not defined" ) );
146  }
147 
148  mSrcNoDataValueCheckBox->setChecked( mRasterLayer->dataProvider()->useSourceNoDataValue( 1 ) );
149 
150  const bool enableSrcNoData = mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) && !std::isnan( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) );
151 
152  mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData );
153  lblSrcNoDataValue->setEnabled( enableSrcNoData );
154 
155  if ( renderer )
156  {
157  if ( renderer->nodataColor().isValid() )
158  mNodataColorButton->setColor( renderer->nodataColor() );
159  else
160  mNodataColorButton->setToNull();
161  }
162 
163  const QgsRasterRangeList noDataRangeList = mRasterLayer->dataProvider()->userNoDataValues( 1 );
164  QgsDebugMsg( QStringLiteral( "noDataRangeList.size = %1" ).arg( noDataRangeList.size() ) );
165  if ( !noDataRangeList.isEmpty() )
166  {
167  const double v = QgsRasterBlock::printValue( noDataRangeList.value( 0 ).min() ).toDouble();
168  leNoDataValue->setText( QLocale().toString( v ) );
169  }
170  else
171  {
172  leNoDataValue->setText( QString() );
173  }
174 
175  mPropertyCollection = mRasterLayer->pipe()->dataDefinedProperties();
177 
178  populateTransparencyTable( mRasterLayer->renderer() );
179 }
180 
181 void QgsRasterTransparencyWidget::transparencyCellTextEdited( const QString &text )
182 {
183  Q_UNUSED( text )
184  QgsDebugMsg( QStringLiteral( "text = %1" ).arg( text ) );
185  QgsRasterRenderer *renderer = mRasterLayer->renderer();
186  if ( !renderer )
187  {
188  return;
189  }
190  const int nBands = renderer->usesBands().size();
191  if ( nBands == 1 )
192  {
193  QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender() );
194  if ( !lineEdit ) return;
195  int row = -1;
196  int column = -1;
197  for ( int r = 0; r < tableTransparency->rowCount(); r++ )
198  {
199  for ( int c = 0; c < tableTransparency->columnCount(); c++ )
200  {
201  if ( tableTransparency->cellWidget( r, c ) == sender() )
202  {
203  row = r;
204  column = c;
205  break;
206  }
207  }
208  if ( row != -1 ) break;
209  }
210  QgsDebugMsg( QStringLiteral( "row = %1 column =%2" ).arg( row ).arg( column ) );
211 
212  if ( column == 0 )
213  {
214  QLineEdit *toLineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, 1 ) );
215  if ( !toLineEdit ) return;
216  const bool toChanged = mTransparencyToEdited.value( row );
217  QgsDebugMsg( QStringLiteral( "toChanged = %1" ).arg( toChanged ) );
218  if ( !toChanged )
219  {
220  toLineEdit->setText( lineEdit->text() );
221  }
222  }
223  else if ( column == 1 )
224  {
225  setTransparencyToEdited( row );
226  }
227  }
228  emit widgetChanged();
229 }
230 
231 void QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked()
232 {
233  if ( mMapCanvas && mPixelSelectorTool )
234  {
235  mMapCanvas->setMapTool( mPixelSelectorTool );
236  }
237 }
238 
239 void QgsRasterTransparencyWidget::pbnAddValuesManually_clicked()
240 {
241  QgsRasterRenderer *renderer = mRasterLayer->renderer();
242  if ( !renderer )
243  {
244  return;
245  }
246 
247  tableTransparency->insertRow( tableTransparency->rowCount() );
248 
249  int n = renderer->usesBands().size();
250  if ( n == 1 ) n++;
251 
252  for ( int i = 0; i < n; i++ )
253  {
254  setTransparencyCell( tableTransparency->rowCount() - 1, i, std::numeric_limits<double>::quiet_NaN() );
255  }
256 
257  setTransparencyCell( tableTransparency->rowCount() - 1, n, 100 );
258 
259  //tableTransparency->resizeColumnsToContents();
260  //tableTransparency->resizeRowsToContents();
261 }
262 
263 void QgsRasterTransparencyWidget::pbnDefaultValues_clicked()
264 {
265  QgsRasterRenderer *r = mRasterLayer->renderer();
266  if ( !r )
267  {
268  return;
269  }
270 
271  const int nBands = r->usesBands().size();
272 
273  setupTransparencyTable( nBands );
274 
275  //tableTransparency->resizeColumnsToContents(); // works only with values
276  //tableTransparency->resizeRowsToContents();
277 
278 }
279 
280 void QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked()
281 {
282  const QgsSettings myQSettings;
283  const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
284  QString myFileName = QFileDialog::getSaveFileName( this, tr( "Save Pixel Values as File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
285  if ( !myFileName.isEmpty() )
286  {
287  if ( !myFileName.endsWith( QLatin1String( ".txt" ), Qt::CaseInsensitive ) )
288  {
289  myFileName = myFileName + ".txt";
290  }
291 
292  QFile myOutputFile( myFileName );
293  if ( myOutputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
294  {
295  QTextStream myOutputStream( &myOutputFile );
296  myOutputStream << "# " << tr( "QGIS Generated Transparent Pixel Value Export File" ) << '\n';
297  if ( rasterIsMultiBandColor() )
298  {
299  myOutputStream << "#\n#\n# " << tr( "Red" ) << "\t" << tr( "Green" ) << "\t" << tr( "Blue" ) << "\t" << tr( "Percent Transparent" );
300  for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
301  {
302  myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, 0 ) ) << "\t"
303  << QString::number( transparencyCellValue( myTableRunner, 1 ) ) << "\t"
304  << QString::number( transparencyCellValue( myTableRunner, 2 ) ) << "\t"
305  << QString::number( transparencyCellValue( myTableRunner, 3 ) );
306  }
307  }
308  else
309  {
310  myOutputStream << "#\n#\n# " << tr( "Value" ) << "\t" << tr( "Percent Transparent" );
311 
312  for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
313  {
314  myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, 0 ) ) << "\t"
315  << QString::number( transparencyCellValue( myTableRunner, 1 ) ) << "\t"
316  << QString::number( transparencyCellValue( myTableRunner, 2 ) );
317  }
318  }
319  }
320  else
321  {
322  QMessageBox::warning( this, tr( "Save Pixel Values as File" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
323  }
324  }
325 }
326 
327 void QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked()
328 {
329  int myLineCounter = 0;
330  bool myImportError = false;
331  QString myBadLines;
332  const QgsSettings myQSettings;
333  const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
334  const QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load Pixel Values from File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
335  QFile myInputFile( myFileName );
336  if ( myInputFile.open( QFile::ReadOnly ) )
337  {
338  QTextStream myInputStream( &myInputFile );
339  QString myInputLine;
340  if ( rasterIsMultiBandColor() )
341  {
342  for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
343  {
344  tableTransparency->removeRow( myTableRunner );
345  }
346 
347  while ( !myInputStream.atEnd() )
348  {
349  myLineCounter++;
350  myInputLine = myInputStream.readLine();
351  if ( !myInputLine.isEmpty() )
352  {
353  if ( !myInputLine.simplified().startsWith( '#' ) )
354  {
355 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
356  QStringList myTokens = myInputLine.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
357 #else
358  QStringList myTokens = myInputLine.split( QRegularExpression( "\\s+" ), Qt::SkipEmptyParts );
359 #endif
360  if ( myTokens.count() != 4 )
361  {
362  myImportError = true;
363  myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
364  }
365  else
366  {
367  tableTransparency->insertRow( tableTransparency->rowCount() );
368  for ( int col = 0; col < 4; col++ )
369  {
370  setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
371  }
372  }
373  }
374  }
375  }
376  }
377  else
378  {
379  for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
380  {
381  tableTransparency->removeRow( myTableRunner );
382  }
383 
384  while ( !myInputStream.atEnd() )
385  {
386  myLineCounter++;
387  myInputLine = myInputStream.readLine();
388  if ( !myInputLine.isEmpty() )
389  {
390  if ( !myInputLine.simplified().startsWith( '#' ) )
391  {
392 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
393  QStringList myTokens = myInputLine.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
394 #else
395  QStringList myTokens = myInputLine.split( QRegularExpression( "\\s+" ), Qt::SkipEmptyParts );
396 #endif
397  if ( myTokens.count() != 3 && myTokens.count() != 2 ) // 2 for QGIS < 1.9 compatibility
398  {
399  myImportError = true;
400  myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
401  }
402  else
403  {
404  if ( myTokens.count() == 2 )
405  {
406  myTokens.insert( 1, myTokens[0] ); // add 'to' value, QGIS < 1.9 compatibility
407  }
408  tableTransparency->insertRow( tableTransparency->rowCount() );
409  for ( int col = 0; col < 3; col++ )
410  {
411  setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
412  }
413  }
414  }
415  }
416  }
417  }
418 
419  if ( myImportError )
420  {
421  QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "The following lines contained errors\n\n%1" ).arg( myBadLines ) );
422  }
423  }
424  else if ( !myFileName.isEmpty() )
425  {
426  QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "Read access denied. Adjust the file permissions and try again.\n\n" ) );
427  }
428  //tableTransparency->resizeColumnsToContents();
429  //tableTransparency->resizeRowsToContents();
430  emit widgetChanged();
431 }
432 
433 void QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked()
434 {
435  if ( 0 < tableTransparency->rowCount() )
436  {
437  tableTransparency->removeRow( tableTransparency->currentRow() );
438  }
439  emit widgetChanged();
440 }
441 
442 bool QgsRasterTransparencyWidget::rasterIsMultiBandColor()
443 {
444  return mRasterLayer && nullptr != dynamic_cast<QgsMultiBandColorRenderer *>( mRasterLayer->renderer() );
445 }
446 
448 {
449  //set NoDataValue
450  QgsRasterRangeList myNoDataRangeList;
451  if ( "" != leNoDataValue->text() )
452  {
453  bool myDoubleOk = false;
454  const double myNoDataValue = QgsDoubleValidator::toDouble( leNoDataValue->text(), &myDoubleOk );
455  if ( myDoubleOk )
456  {
457  const QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
458  myNoDataRangeList << myNoDataRange;
459  }
460  }
461  for ( int bandNo = 1; bandNo <= mRasterLayer->dataProvider()->bandCount(); bandNo++ )
462  {
463  mRasterLayer->dataProvider()->setUserNoDataValue( bandNo, myNoDataRangeList );
464  mRasterLayer->dataProvider()->setUseSourceNoDataValue( bandNo, mSrcNoDataValueCheckBox->isChecked() );
465  }
466 
467  //transparency settings
468  QgsRasterRenderer *rasterRenderer = mRasterLayer->renderer();
469  if ( rasterRenderer )
470  {
471  rasterRenderer->setAlphaBand( cboxTransparencyBand->currentBand() );
472  rasterRenderer->setNodataColor( mNodataColorButton->color() );
473 
474  //Walk through each row in table and test value. If not valid set to 0.0 and continue building transparency list
475  QgsRasterTransparency *rasterTransparency = new QgsRasterTransparency();
476  if ( tableTransparency->columnCount() == 4 )
477  {
479  QList<QgsRasterTransparency::TransparentThreeValuePixel> myTransparentThreeValuePixelList;
480  myTransparentThreeValuePixelList.reserve( tableTransparency->rowCount() );
481  for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
482  {
483  myTransparentPixel.red = transparencyCellValue( myListRunner, 0 );
484  myTransparentPixel.green = transparencyCellValue( myListRunner, 1 );
485  myTransparentPixel.blue = transparencyCellValue( myListRunner, 2 );
486  myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 3 );
487  myTransparentThreeValuePixelList.append( myTransparentPixel );
488  }
489  rasterTransparency->setTransparentThreeValuePixelList( myTransparentThreeValuePixelList );
490  }
491  else if ( tableTransparency->columnCount() == 3 )
492  {
494  QList<QgsRasterTransparency::TransparentSingleValuePixel> myTransparentSingleValuePixelList;
495  myTransparentSingleValuePixelList.reserve( tableTransparency->rowCount() );
496  for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
497  {
498  myTransparentPixel.min = transparencyCellValue( myListRunner, 0 );
499  myTransparentPixel.max = transparencyCellValue( myListRunner, 1 );
500  myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 2 );
501 
502  myTransparentSingleValuePixelList.append( myTransparentPixel );
503  }
504  rasterTransparency->setTransparentSingleValuePixelList( myTransparentSingleValuePixelList );
505  }
506 
507  rasterRenderer->setRasterTransparency( rasterTransparency );
508 
509  //set global transparency
510  rasterRenderer->setOpacity( mOpacityWidget->opacity() );
511  }
512 
514 }
515 
517 {
518  button->blockSignals( true );
519  button->init( key, mPropertyCollection, QgsRasterPipe::propertyDefinitions(), nullptr );
520  connect( button, &QgsPropertyOverrideButton::changed, this, &QgsRasterTransparencyWidget::updateProperty );
521  button->registerExpressionContextGenerator( this );
522  button->blockSignals( false );
523 }
524 
526 {
527  const auto propertyOverrideButtons { findChildren< QgsPropertyOverrideButton * >() };
528  for ( QgsPropertyOverrideButton *button : propertyOverrideButtons )
529  {
530  updateDataDefinedButton( button );
531  }
532 }
533 
535 {
536  if ( !button )
537  return;
538 
539  if ( button->propertyKey() < 0 )
540  return;
541 
542  const QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
543  whileBlocking( button )->setToProperty( mPropertyCollection.property( key ) );
544 }
545 
546 void QgsRasterTransparencyWidget::updateProperty()
547 {
548  QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
549  const QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
550  mPropertyCollection.setProperty( key, button->toProperty() );
551  emit widgetChanged();
552 }
553 
554 void QgsRasterTransparencyWidget::pixelSelected( const QgsPointXY &canvasPoint )
555 {
556  QgsRasterRenderer *renderer = mRasterLayer->renderer();
557  if ( !renderer )
558  {
559  return;
560  }
561 
562  //Get the pixel values and add a new entry to the transparency table
563  if ( mMapCanvas && mPixelSelectorTool )
564  {
565  mMapCanvas->unsetMapTool( mPixelSelectorTool );
566 
567  const QgsMapSettings &ms = mMapCanvas->mapSettings();
568  const QgsPointXY myPoint = ms.mapToLayerCoordinates( mRasterLayer, canvasPoint );
569 
570  const QgsRectangle myExtent = ms.mapToLayerCoordinates( mRasterLayer, mMapCanvas->extent() );
571  const double mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
572  const int myWidth = mMapCanvas->extent().width() / mapUnitsPerPixel;
573  const int myHeight = mMapCanvas->extent().height() / mapUnitsPerPixel;
574 
575  const QMap<int, QVariant> myPixelMap = mRasterLayer->dataProvider()->identify( myPoint, QgsRaster::IdentifyFormatValue, myExtent, myWidth, myHeight ).results();
576 
577  const QList<int> bands = renderer->usesBands();
578 
579  QList<double> values;
580  for ( int i = 0; i < bands.size(); ++i )
581  {
582  const int bandNo = bands.value( i );
583  if ( myPixelMap.count( bandNo ) == 1 )
584  {
585  if ( myPixelMap.value( bandNo ).isNull() )
586  {
587  return; // Don't add nodata, transparent anyway
588  }
589  const double value = myPixelMap.value( bandNo ).toDouble();
590  QgsDebugMsg( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ) );
591  values.append( value );
592  }
593  }
594  if ( bands.size() == 1 )
595  {
596  // Set 'to'
597  values.insert( 1, values.value( 0 ) );
598  }
599  tableTransparency->insertRow( tableTransparency->rowCount() );
600  for ( int i = 0; i < values.size(); i++ )
601  {
602  setTransparencyCell( tableTransparency->rowCount() - 1, i, values.value( i ) );
603  }
604  setTransparencyCell( tableTransparency->rowCount() - 1, tableTransparency->columnCount() - 1, 100 );
605  }
606 
607  //tableTransparency->resizeColumnsToContents();
608  //tableTransparency->resizeRowsToContents();
609 }
610 
611 void QgsRasterTransparencyWidget::populateTransparencyTable( QgsRasterRenderer *renderer )
612 {
613  if ( !mRasterLayer )
614  {
615  return;
616  }
617 
618  if ( !renderer )
619  {
620  return;
621  }
622 
623  const int nBands = renderer->usesBands().size();
624  setupTransparencyTable( nBands );
625 
626  const QgsRasterTransparency *rasterTransparency = renderer->rasterTransparency();
627  if ( !rasterTransparency )
628  {
629  return;
630  }
631 
632  if ( nBands == 1 )
633  {
634  QList<QgsRasterTransparency::TransparentSingleValuePixel> pixelList = rasterTransparency->transparentSingleValuePixelList();
635  for ( int i = 0; i < pixelList.size(); ++i )
636  {
637  tableTransparency->insertRow( i );
638  setTransparencyCell( i, 0, pixelList[i].min );
639  setTransparencyCell( i, 1, pixelList[i].max );
640  setTransparencyCell( i, 2, pixelList[i].percentTransparent );
641  // break synchronization only if values differ
642  if ( pixelList[i].min != pixelList[i].max )
643  {
644  setTransparencyToEdited( i );
645  }
646  }
647  }
648  else if ( nBands == 3 )
649  {
650  QList<QgsRasterTransparency::TransparentThreeValuePixel> pixelList = rasterTransparency->transparentThreeValuePixelList();
651  for ( int i = 0; i < pixelList.size(); ++i )
652  {
653  tableTransparency->insertRow( i );
654  setTransparencyCell( i, 0, pixelList[i].red );
655  setTransparencyCell( i, 1, pixelList[i].green );
656  setTransparencyCell( i, 2, pixelList[i].blue );
657  setTransparencyCell( i, 3, pixelList[i].percentTransparent );
658  }
659  }
660 
661  tableTransparency->resizeColumnsToContents();
662  tableTransparency->resizeRowsToContents();
663 
664 }
665 
666 void QgsRasterTransparencyWidget::setupTransparencyTable( int nBands )
667 {
668  tableTransparency->clear();
669  tableTransparency->setColumnCount( 0 );
670  tableTransparency->setRowCount( 0 );
671  mTransparencyToEdited.clear();
672 
673  if ( nBands == 3 )
674  {
675  tableTransparency->setColumnCount( 4 );
676  tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Red" ) ) );
677  tableTransparency->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "Green" ) ) );
678  tableTransparency->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Blue" ) ) );
679  tableTransparency->setHorizontalHeaderItem( 3, new QTableWidgetItem( tr( "Percent Transparent" ) ) );
680  }
681  else //1 band
682  {
683  tableTransparency->setColumnCount( 3 );
684 // Is it important to distinguish the header? It becomes difficult with range.
685 #if 0
686  if ( QgsRasterLayer::PalettedColor != mRasterLayer->drawingStyle() &&
687  QgsRasterLayer::PalettedSingleBandGray != mRasterLayer->drawingStyle() &&
688  QgsRasterLayer::PalettedSingleBandPseudoColor != mRasterLayer->drawingStyle() &&
689  QgsRasterLayer::PalettedMultiBandColor != mRasterLayer->drawingStyle() )
690  {
691  tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Gray" ) ) );
692  }
693  else
694  {
695  tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Indexed Value" ) ) );
696  }
697 #endif
698  tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "From" ) ) );
699  tableTransparency->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "To" ) ) );
700  tableTransparency->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Percent Transparent" ) ) );
701  }
702 }
703 
704 void QgsRasterTransparencyWidget::setTransparencyCell( int row, int column, double value )
705 {
706  QgsDebugMsg( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ) );
707  QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
708  if ( !provider ) return;
709 
710  QgsRasterRenderer *renderer = mRasterLayer->renderer();
711  if ( !renderer ) return;
712  const int nBands = renderer->usesBands().size();
713 
714  QLineEdit *lineEdit = new QLineEdit();
715  lineEdit->setFrame( false ); // frame looks bad in table
716  // Without margins row selection is not displayed (important for delete row)
717  lineEdit->setContentsMargins( 1, 1, 1, 1 );
718 
719  if ( column == tableTransparency->columnCount() - 1 )
720  {
721  // transparency
722  // Who needs transparency as floating point?
723  lineEdit->setValidator( new QIntValidator( nullptr ) );
724  lineEdit->setText( QString::number( static_cast<int>( value ) ) );
725  }
726  else
727  {
728  // value
729  QString valueString;
730  switch ( provider->sourceDataType( 1 ) )
731  {
734  lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
735  if ( !std::isnan( value ) )
736  {
737  const double v = QgsRasterBlock::printValue( value ).toDouble();
738  valueString = QLocale().toString( v );
739  }
740  break;
741  default:
742  lineEdit->setValidator( new QIntValidator( nullptr ) );
743  if ( !std::isnan( value ) )
744  {
745  valueString = QString::number( static_cast<int>( value ) );
746  }
747  break;
748  }
749  lineEdit->setText( valueString );
750  connect( lineEdit, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
751  }
752  tableTransparency->setCellWidget( row, column, lineEdit );
753  adjustTransparencyCellWidth( row, column );
754 
755  if ( nBands == 1 && ( column == 0 || column == 1 ) )
756  {
757  connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterTransparencyWidget::transparencyCellTextEdited );
758  }
759  //tableTransparency->resizeColumnsToContents();
760  emit widgetChanged();
761 }
762 
763 void QgsRasterTransparencyWidget::adjustTransparencyCellWidth( int row, int column )
764 {
765  QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
766  if ( !lineEdit ) return;
767 
768  int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
769  width = std::max( width, tableTransparency->columnWidth( column ) );
770 
771  lineEdit->setFixedWidth( width );
772 }
773 
774 void QgsRasterTransparencyWidget::setTransparencyToEdited( int row )
775 {
776  if ( row >= mTransparencyToEdited.size() )
777  {
778  mTransparencyToEdited.resize( row + 1 );
779  }
780  mTransparencyToEdited[row] = true;
781 }
782 
783 double QgsRasterTransparencyWidget::transparencyCellValue( int row, int column )
784 {
785  QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
786  if ( !lineEdit || lineEdit->text().isEmpty() )
787  {
788  return std::numeric_limits<double>::quiet_NaN();
789  }
790  return QgsDoubleValidator::toDouble( lineEdit->text() );
791 
792 }
793 
795 {
796  return mPixelSelectorTool;
797 }
@ Float32
Thirty two bit floating point (float)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Float64
Sixty four bit floating point (double)
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
QgsDoubleValidator is a QLineEdit Validator that combines QDoubleValidator and QRegularExpressionVali...
static double toDouble(const QString &input, bool *ok)
Converts input string to double value.
Abstract interface for generating an expression context scope.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
A panel widget that can be shown in the map style dock.
The QgsMapSettings class contains configuration for rendering of the map.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
A map tool that simply emits a point when clicking on the map.
void canvasClicked(const QgsPointXY &point, Qt::MouseButton button)
signal emitted on canvas click
Renderer for multiband images with the color components.
void opacityChanged(double opacity)
Emitted when the opacity is changed in the widget, where opacity ranges from 0.0 (transparent) to 1....
void widgetChanged()
Emitted when the widget state changes.
A class to represent a 2D point.
Definition: qgspointxy.h:59
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
A button for controlling property overrides which may apply to a widget.
QgsProperty toProperty() const
Returns a QgsProperty object encapsulating the current state of the widget.
void changed()
Emitted when property definition changes.
void init(int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer=nullptr, bool auxiliaryStorageEnabled=false)
Initialize a newly constructed property button (useful if button was included in a UI layout).
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
int propertyKey() const
Returns the property key linked to the button.
void bandChanged(int band)
Emitted when the currently selected band changes.
static QString printValue(double value)
Print double value with all necessary significant digits.
Base class for raster data providers.
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
virtual bool useSourceNoDataValue(int bandNo) const
Returns the source nodata value usage.
Qgis::DataType sourceDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual void setUseSourceNoDataValue(int bandNo, bool use)
Sets the source nodata value usage.
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
virtual QgsRasterIdentifyResult identify(const QgsPointXY &point, QgsRaster::IdentifyFormat format, const QgsRectangle &boundingBox=QgsRectangle(), int width=0, int height=0, int dpi=96)
Identify raster value(s) found on the point position.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
virtual QgsRasterRangeList userNoDataValues(int bandNo) const
Returns a list of user no data value ranges.
virtual void setUserNoDataValue(int bandNo, const QgsRasterRangeList &noData)
QMap< int, QVariant > results() const
Returns the identify results.
Represents a raster layer.
QgsRasterPipe * pipe()
Returns the raster pipe.
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the pipe's property collection, used for data defined overrides.
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in raster pipes.
Property
Data definable properties.
Definition: qgsrasterpipe.h:58
@ RendererOpacity
Raster renderer global opacity.
Definition: qgsrasterpipe.h:59
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the pipe's property collection, used for data defined overrides.
Raster values range container.
Raster renderer pipe that applies colors to a raster.
QColor nodataColor() const
Returns the color to use for shading nodata pixels.
const QgsRasterTransparency * rasterTransparency() const
double opacity() const
Returns the opacity for the renderer, where opacity is a value between 0 (totally transparent) and 1....
void setAlphaBand(int band)
void setOpacity(double opacity)
Sets the opacity for the renderer, where opacity is a value between 0 (totally transparent) and 1....
void setRasterTransparency(QgsRasterTransparency *t)
virtual QList< int > usesBands() const
Returns a list of band numbers used by the renderer.
void setNodataColor(const QColor &color)
Sets the color to use for shading nodata pixels.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void updateDataDefinedButtons()
Updates all property override buttons to reflect the widgets's current properties.
QgsPropertyCollection mPropertyCollection
Temporary property collection.
void syncToLayer()
Sync the widget state to the layer set for the widget.
QgsMapToolEmitPoint * pixelSelectorTool() const
Returns the (possibly nullptr) map pixel selector tool.
void initializeDataDefinedButton(QgsPropertyOverrideButton *button, QgsRasterPipe::Property key)
Registers a property override button, setting up its initial value, connections and description.
QgsRasterTransparencyWidget(QgsRasterLayer *layer, QgsMapCanvas *canvas, QWidget *parent=nullptr)
Widget to control a layers transparency and related options.
void updateDataDefinedButton(QgsPropertyOverrideButton *button)
Updates a specific property override button to reflect the widgets's current properties.
void apply() override
Apply any changes on the widget to the set layer.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the dialog is shown, e.g., the associated map canvas and expression context...
Defines the list of pixel values to be considered as transparent or semi transparent when rendering r...
void setTransparentSingleValuePixelList(const QList< QgsRasterTransparency::TransparentSingleValuePixel > &newList)
Sets the transparent single value pixel list, replacing the whole existing list.
QList< QgsRasterTransparency::TransparentThreeValuePixel > transparentThreeValuePixelList() const
Returns the transparent three value pixel list.
void setTransparentThreeValuePixelList(const QList< QgsRasterTransparency::TransparentThreeValuePixel > &newList)
Sets the transparent three value pixel list, replacing the whole existing list.
QList< QgsRasterTransparency::TransparentSingleValuePixel > transparentSingleValuePixelList() const
Returns the transparent single value pixel list.
@ IdentifyFormatValue
Definition: qgsraster.h:60
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:1443
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsRasterRange > QgsRasterRangeList