QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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"
30#include "qgsmaptoolemitpoint.h"
31#include "qgsmapsettings.h"
32#include "qgsrectangle.h"
33#include "qgsmapcanvas.h"
36#include "qgsdoublevalidator.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" ) );
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
126 {
127 gboxNoDataValue->setEnabled( false );
128 gboxCustomTransparency->setEnabled( false );
129 }
130
131 cboxTransparencyBand->setShowNotSetOption( true, tr( "None" ) );
132 cboxTransparencyBand->setLayer( mRasterLayer );
133 }
134
135 if ( mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) )
136 {
137 lblSrcNoDataValue->setText( QgsRasterBlock::printValue( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) ) );
138 }
139 else
140 {
141 lblSrcNoDataValue->setText( tr( "not defined" ) );
142 }
143
144 mSrcNoDataValueCheckBox->setChecked( mRasterLayer->dataProvider()->useSourceNoDataValue( 1 ) );
145
146 const bool enableSrcNoData = mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) && !std::isnan( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) );
147
148 mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData );
149 lblSrcNoDataValue->setEnabled( enableSrcNoData );
150
151 if ( renderer )
152 {
153 if ( renderer->nodataColor().isValid() )
154 mNodataColorButton->setColor( renderer->nodataColor() );
155 else
156 mNodataColorButton->setToNull();
157
158 mOpacityWidget->setOpacity( renderer->opacity() );
159
160 cboxTransparencyBand->setBand( renderer->alphaBand() );
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
177
178 populateTransparencyTable( mRasterLayer->renderer() );
179}
180
181void 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
231void QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked()
232{
233 if ( mMapCanvas && mPixelSelectorTool )
234 {
235 mMapCanvas->setMapTool( mPixelSelectorTool );
236 }
237}
238
239void 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
263void 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
280void 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
327void 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
433void QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked()
434{
435 if ( 0 < tableTransparency->rowCount() )
436 {
437 tableTransparency->removeRow( tableTransparency->currentRow() );
438 }
439 emit widgetChanged();
440}
441
442bool 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 );
520 connect( button, &QgsPropertyOverrideButton::changed, this, &QgsRasterTransparencyWidget::updateProperty );
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
546void QgsRasterTransparencyWidget::updateProperty()
547{
548 QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
549 const QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
551 emit widgetChanged();
552}
553
554void 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 ( QgsVariantUtils::isNull( myPixelMap.value( bandNo ) ) )
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
611void 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
666void 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
704void 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
763void 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
774void QgsRasterTransparencyWidget::setTransparencyToEdited( int row )
775{
776 if ( row >= mTransparencyToEdited.size() )
777 {
778 mTransparencyToEdited.resize( row + 1 );
779 }
780 mTransparencyToEdited[row] = true;
781}
782
783double 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:90
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:477
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....
virtual QList< int > usesBands() const
Returns a list of band numbers used by the renderer.
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)
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
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
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:2453
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsRasterRange > QgsRasterRangeList