QGIS API Documentation 3.39.0-Master (d85f3c2a281)
Loading...
Searching...
No Matches
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"
28#include "qgsmaptoolemitpoint.h"
29#include "qgsmapsettings.h"
30#include "qgsrectangle.h"
31#include "qgsmapcanvas.h"
33#include "qgsdoublevalidator.h"
35#include "qgsrasterrenderer.h"
37
39 : QgsMapLayerConfigWidget( layer, canvas, parent )
40 , TRSTRING_NOT_SET( tr( "Not Set" ) )
41 , mRasterLayer( layer )
42 , mMapCanvas( canvas )
43{
44 setupUi( this );
45 connect( pbnAddValuesFromDisplay, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked );
46 connect( pbnAddValuesManually, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnAddValuesManually_clicked );
47 connect( pbnDefaultValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnDefaultValues_clicked );
48 connect( pbnExportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked );
49 connect( pbnImportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked );
50 connect( pbnRemoveSelectedRow, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked );
51
52 mNodataColorButton->setShowNoColor( true );
53 mNodataColorButton->setColorDialogTitle( tr( "Select NoData Color" ) );
55
56 connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsPanelWidget::widgetChanged );
57 connect( cboxTransparencyBand, &QgsRasterBandComboBox::bandChanged, this, &QgsPanelWidget::widgetChanged );
58 connect( mSrcNoDataValueCheckBox, &QCheckBox::stateChanged, this, &QgsPanelWidget::widgetChanged );
59 connect( leNoDataValue, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
60 leNoDataValue->setValidator( new QgsDoubleValidator( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), this ) );
61 connect( mNodataColorButton, &QgsColorButton::colorChanged, this, &QgsPanelWidget::widgetChanged );
62
63 mPixelSelectorTool = nullptr;
64 if ( mMapCanvas )
65 {
66 mPixelSelectorTool = new QgsMapToolEmitPoint( mMapCanvas );
67 connect( mPixelSelectorTool, &QgsMapToolEmitPoint::canvasClicked, this, &QgsRasterTransparencyWidget::pixelSelected );
68 }
69 else
70 {
71 pbnAddValuesFromDisplay->setEnabled( false );
72 }
73
75}
76
78{
79 mContext = context;
80}
81
83{
84 QgsExpressionContext expContext;
88
89 if ( QgsMapCanvas *canvas = mContext.mapCanvas() )
90 {
91 expContext << QgsExpressionContextUtils::mapSettingsScope( canvas->mapSettings() )
92 << new QgsExpressionContextScope( canvas->expressionContextScope() );
93 if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( canvas->temporalController() ) )
94 {
95 expContext << generator->createExpressionContextScope();
96 }
97 }
98 else
99 {
101 }
102
103 if ( mRasterLayer )
104 expContext << QgsExpressionContextUtils::layerScope( mRasterLayer );
105
106 // additional scopes
107 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
108 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
109 {
110 expContext.appendScope( new QgsExpressionContextScope( scope ) );
111 }
112
113 return expContext;
114}
115
117{
118 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
119 QgsRasterRenderer *renderer = mRasterLayer->renderer();
120 if ( provider )
121 {
122 if ( provider->dataType( 1 ) == Qgis::DataType::ARGB32
124 {
125 gboxNoDataValue->setEnabled( false );
126 gboxCustomTransparency->setEnabled( false );
127 }
128
129 cboxTransparencyBand->setShowNotSetOption( true, tr( "None" ) );
130 cboxTransparencyBand->setLayer( mRasterLayer );
131 if ( provider->sourceHasNoDataValue( 1 ) )
132 {
133 lblSrcNoDataValue->setText( QgsRasterBlock::printValue( provider->sourceNoDataValue( 1 ) ) );
134 }
135 else
136 {
137 lblSrcNoDataValue->setText( tr( "not defined" ) );
138 }
139
140 mSrcNoDataValueCheckBox->setChecked( provider->useSourceNoDataValue( 1 ) );
141
142 const bool enableSrcNoData = provider->sourceHasNoDataValue( 1 ) && !std::isnan( provider->sourceNoDataValue( 1 ) );
143
144 mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData );
145 lblSrcNoDataValue->setEnabled( enableSrcNoData );
146 }
147
148 if ( renderer )
149 {
150 if ( renderer->nodataColor().isValid() )
151 mNodataColorButton->setColor( renderer->nodataColor() );
152 else
153 mNodataColorButton->setToNull();
154
155 mOpacityWidget->setOpacity( renderer->opacity() );
156
157 cboxTransparencyBand->setBand( renderer->alphaBand() );
158 }
159
160 if ( provider )
161 {
162 const QgsRasterRangeList noDataRangeList = provider->userNoDataValues( 1 );
163 QgsDebugMsgLevel( QStringLiteral( "noDataRangeList.size = %1" ).arg( noDataRangeList.size() ), 2 );
164 if ( !noDataRangeList.isEmpty() )
165 {
166 const double v = QgsRasterBlock::printValue( noDataRangeList.value( 0 ).min() ).toDouble();
167 leNoDataValue->setText( QLocale().toString( v, 'g', 20 ) );
168 }
169 else
170 {
171 leNoDataValue->setText( QString() );
172 }
173 }
174 else
175 {
176 leNoDataValue->setText( QString() );
177 }
178
181
182 populateTransparencyTable( mRasterLayer->renderer() );
183}
184
185void QgsRasterTransparencyWidget::transparencyCellTextEdited( const QString &text )
186{
187 Q_UNUSED( text )
188 QgsDebugMsgLevel( QStringLiteral( "text = %1" ).arg( text ), 2 );
189
190 switch ( mCurrentMode )
191 {
192 case Mode::SingleBand:
193 {
194 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender() );
195 if ( !lineEdit )
196 return;
197 int row = -1;
198 int column = -1;
199 for ( int r = 0; r < tableTransparency->rowCount(); r++ )
200 {
201 for ( int c = 0; c < tableTransparency->columnCount(); c++ )
202 {
203 if ( tableTransparency->cellWidget( r, c ) == sender() )
204 {
205 row = r;
206 column = c;
207 break;
208 }
209 }
210 if ( row != -1 )
211 break;
212 }
213 QgsDebugMsgLevel( QStringLiteral( "row = %1 column =%2" ).arg( row ).arg( column ), 2 );
214
215 if ( column == static_cast< int >( SingleBandTableColumns::From ) )
216 {
217 QLineEdit *toLineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, static_cast< int >( SingleBandTableColumns::To ) ) );
218 if ( !toLineEdit )
219 return;
220
221 const bool toChanged = mTransparencyToEdited.value( row );
222 QgsDebugMsgLevel( QStringLiteral( "toChanged = %1" ).arg( toChanged ), 2 );
223 if ( !toChanged )
224 {
225 toLineEdit->setText( lineEdit->text() );
226 }
227 }
228 else if ( column == static_cast< int >( SingleBandTableColumns::To ) )
229 {
230 setTransparencyToEdited( row );
231 }
232 break;
233 }
234
235 case Mode::RgbBands:
236 break;
237 }
238
239 emit widgetChanged();
240}
241
242void QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked()
243{
244 if ( mMapCanvas && mPixelSelectorTool )
245 {
246 mMapCanvas->setMapTool( mPixelSelectorTool );
247 }
248}
249
250void QgsRasterTransparencyWidget::pbnAddValuesManually_clicked()
251{
252 QgsRasterRenderer *renderer = mRasterLayer->renderer();
253 if ( !renderer )
254 {
255 return;
256 }
257
258 tableTransparency->insertRow( tableTransparency->rowCount() );
259
260 int n = 0;
261 switch ( mCurrentMode )
262 {
263 case Mode::SingleBand:
264 n = 2; // set both From and To columns
265 break;
266
267 case Mode::RgbBands:
268 n = 3;
269 break;
270 }
271
272 for ( int i = 0; i < n; i++ )
273 {
274 setTransparencyCell( tableTransparency->rowCount() - 1, i, std::numeric_limits<double>::quiet_NaN() );
275 }
276
277 switch ( mCurrentMode )
278 {
279 case Mode::SingleBand:
280 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::Opacity ), 100 );
281 break;
282
283 case Mode::RgbBands:
284 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Opacity ), 100 );
285 break;
286 }
287
288 //tableTransparency->resizeColumnsToContents();
289 //tableTransparency->resizeRowsToContents();
290}
291
292void QgsRasterTransparencyWidget::pbnDefaultValues_clicked()
293{
294 QgsRasterRenderer *r = mRasterLayer->renderer();
295 if ( !r )
296 {
297 return;
298 }
299
300 const int nBands = r->usesBands().size();
301
302 setupTransparencyTable( nBands );
303
304 //tableTransparency->resizeColumnsToContents(); // works only with values
305 //tableTransparency->resizeRowsToContents();
306
307}
308
309void QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked()
310{
311 const QgsSettings myQSettings;
312 const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
313 QString myFileName = QFileDialog::getSaveFileName( this, tr( "Save Pixel Values as File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
314 if ( !myFileName.isEmpty() )
315 {
316 if ( !myFileName.endsWith( QLatin1String( ".txt" ), Qt::CaseInsensitive ) )
317 {
318 myFileName = myFileName + ".txt";
319 }
320
321 QFile myOutputFile( myFileName );
322 if ( myOutputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
323 {
324 QTextStream myOutputStream( &myOutputFile );
325 myOutputStream << "# " << tr( "QGIS Generated Transparent Pixel Value Export File" ) << '\n';
326 switch ( mCurrentMode )
327 {
328 case Mode::RgbBands:
329 {
330 myOutputStream << "#\n#\n# " << tr( "Red" ) << "\t" << tr( "Green" ) << "\t" << tr( "Blue" ) << "\t" << tr( "Percent Transparent" );
331 for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
332 {
333 myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( RgbBandTableColumns::Red ) ) ) << "\t"
334 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( RgbBandTableColumns::Green ) ) ) << "\t"
335 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( RgbBandTableColumns::Blue ) ) ) << "\t"
336 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( RgbBandTableColumns::Opacity ) ) );
337 }
338 break;
339 }
340 case Mode::SingleBand:
341 {
342 myOutputStream << "#\n#\n# " << tr( "Value" ) << "\t" << tr( "Percent Transparent" );
343
344 for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
345 {
346 myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( SingleBandTableColumns::From ) ) ) << "\t"
347 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( SingleBandTableColumns::To ) ) ) << "\t"
348 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( SingleBandTableColumns::Opacity ) ) );
349 }
350 break;
351 }
352 }
353 }
354 else
355 {
356 QMessageBox::warning( this, tr( "Save Pixel Values as File" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
357 }
358 }
359}
360
361void QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked()
362{
363 int myLineCounter = 0;
364 bool myImportError = false;
365 QString myBadLines;
366 const QgsSettings myQSettings;
367 const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
368 const QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load Pixel Values from File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
369 QFile myInputFile( myFileName );
370
371 const thread_local QRegularExpression sRxWhitespace( "\\s+" );
372
373 if ( myInputFile.open( QFile::ReadOnly ) )
374 {
375 QTextStream myInputStream( &myInputFile );
376 QString myInputLine;
377 switch ( mCurrentMode )
378 {
379 case Mode::RgbBands:
380 {
381 for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
382 {
383 tableTransparency->removeRow( myTableRunner );
384 }
385
386 while ( !myInputStream.atEnd() )
387 {
388 myLineCounter++;
389 myInputLine = myInputStream.readLine();
390 if ( !myInputLine.isEmpty() )
391 {
392 if ( !myInputLine.simplified().startsWith( '#' ) )
393 {
394 QStringList myTokens = myInputLine.split( sRxWhitespace, Qt::SkipEmptyParts );
395 if ( myTokens.count() != 4 )
396 {
397 myImportError = true;
398 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
399 }
400 else
401 {
402 tableTransparency->insertRow( tableTransparency->rowCount() );
403 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Red ), myTokens[0].toDouble() );
404 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Green ), myTokens[1].toDouble() );
405 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Blue ), myTokens[2].toDouble() );
406 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Tolerance ), 0 );
407 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Opacity ), myTokens[3].toDouble() );
408 }
409 }
410 }
411 }
412 break;
413 }
414 case Mode::SingleBand:
415 {
416 for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
417 {
418 tableTransparency->removeRow( myTableRunner );
419 }
420
421 while ( !myInputStream.atEnd() )
422 {
423 myLineCounter++;
424 myInputLine = myInputStream.readLine();
425 if ( !myInputLine.isEmpty() )
426 {
427 if ( !myInputLine.simplified().startsWith( '#' ) )
428 {
429 QStringList myTokens = myInputLine.split( sRxWhitespace, Qt::SkipEmptyParts );
430 if ( myTokens.count() != 3 && myTokens.count() != 2 ) // 2 for QGIS < 1.9 compatibility
431 {
432 myImportError = true;
433 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
434 }
435 else
436 {
437 if ( myTokens.count() == 2 )
438 {
439 myTokens.insert( 1, myTokens[0] ); // add 'to' value, QGIS < 1.9 compatibility
440 }
441 tableTransparency->insertRow( tableTransparency->rowCount() );
442
443 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::From ), myTokens[0].toDouble() );
444 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::To ), myTokens[1].toDouble() );
445 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::Opacity ), myTokens[2].toDouble() );
446 }
447 }
448 }
449 }
450 break;
451 }
452 }
453
454 if ( myImportError )
455 {
456 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "The following lines contained errors\n\n%1" ).arg( myBadLines ) );
457 }
458 }
459 else if ( !myFileName.isEmpty() )
460 {
461 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "Read access denied. Adjust the file permissions and try again.\n\n" ) );
462 }
463 //tableTransparency->resizeColumnsToContents();
464 //tableTransparency->resizeRowsToContents();
465 emit widgetChanged();
466}
467
468void QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked()
469{
470 if ( 0 < tableTransparency->rowCount() )
471 {
472 tableTransparency->removeRow( tableTransparency->currentRow() );
473 }
474 emit widgetChanged();
475}
476
478{
479 applyToRasterProvider( mRasterLayer->dataProvider() );
480 applyToRasterRenderer( mRasterLayer->renderer() );
482}
483
485{
486 //set NoDataValue
487 QgsRasterRangeList myNoDataRangeList;
488 if ( !leNoDataValue->text().isEmpty() )
489 {
490 bool myDoubleOk = false;
491 const double myNoDataValue = QgsDoubleValidator::toDouble( leNoDataValue->text(), &myDoubleOk );
492 if ( myDoubleOk )
493 {
494 const QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
495 myNoDataRangeList << myNoDataRange;
496 }
497 }
498 if ( provider )
499 {
500 for ( int bandNo = 1; bandNo <= provider->bandCount(); bandNo++ )
501 {
502 provider->setUserNoDataValue( bandNo, myNoDataRangeList );
503 provider->setUseSourceNoDataValue( bandNo, mSrcNoDataValueCheckBox->isChecked() );
504 }
505 }
506}
507
509{
510 if ( rasterRenderer )
511 {
512 rasterRenderer->setAlphaBand( cboxTransparencyBand->currentBand() );
513 rasterRenderer->setNodataColor( mNodataColorButton->color() );
514
515 //Walk through each row in table and test value. If not valid set to 0.0 and continue building transparency list
516 QgsRasterTransparency *rasterTransparency = new QgsRasterTransparency();
517 switch ( mCurrentMode )
518 {
519 case Mode::RgbBands:
520 {
521 QVector<QgsRasterTransparency::TransparentThreeValuePixel> myTransparentThreeValuePixelList;
522 myTransparentThreeValuePixelList.reserve( tableTransparency->rowCount() );
523 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
524 {
525 const double red = transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Red ) );
526 const double green = transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Green ) );
527 const double blue = transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Blue ) );
528 const double opacity = 1.0 - transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Opacity ) ) / 100.0;
529 const double tolerance = transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Tolerance ) );
530 myTransparentThreeValuePixelList.append(
531 QgsRasterTransparency::TransparentThreeValuePixel( red, green, blue, opacity,
532 !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon(),
533 !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon(),
534 !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon() )
535 );
536 }
537 rasterTransparency->setTransparentThreeValuePixelList( myTransparentThreeValuePixelList );
538 break;
539 }
540 case Mode::SingleBand:
541 {
542 QVector<QgsRasterTransparency::TransparentSingleValuePixel> myTransparentSingleValuePixelList;
543 myTransparentSingleValuePixelList.reserve( tableTransparency->rowCount() );
544 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
545 {
546 const double min = transparencyCellValue( myListRunner, static_cast< int >( SingleBandTableColumns::From ) );
547 const double max = transparencyCellValue( myListRunner, static_cast< int >( SingleBandTableColumns::To ) );
548 const double opacity = 1.0 - transparencyCellValue( myListRunner, static_cast< int >( SingleBandTableColumns::Opacity ) ) / 100.0;
549
550 myTransparentSingleValuePixelList.append(
552 );
553 }
554 rasterTransparency->setTransparentSingleValuePixelList( myTransparentSingleValuePixelList );
555 break;
556 }
557 }
558
559 rasterRenderer->setRasterTransparency( rasterTransparency );
560
561 //set global transparency
562 rasterRenderer->setOpacity( mOpacityWidget->opacity() );
563 }
564}
565
567{
568 button->blockSignals( true );
569 button->init( static_cast< int >( key ), mPropertyCollection, QgsRasterPipe::propertyDefinitions(), nullptr );
570 connect( button, &QgsPropertyOverrideButton::changed, this, &QgsRasterTransparencyWidget::updateProperty );
572 button->blockSignals( false );
573}
574
576{
577 const auto propertyOverrideButtons { findChildren< QgsPropertyOverrideButton * >() };
578 for ( QgsPropertyOverrideButton *button : propertyOverrideButtons )
579 {
580 updateDataDefinedButton( button );
581 }
582}
583
585{
586 if ( !button )
587 return;
588
589 if ( button->propertyKey() < 0 )
590 return;
591
592 const QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
593 whileBlocking( button )->setToProperty( mPropertyCollection.property( key ) );
594}
595
596void QgsRasterTransparencyWidget::updateProperty()
597{
598 QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
599 const QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
601 emit widgetChanged();
602}
603
604void QgsRasterTransparencyWidget::pixelSelected( const QgsPointXY &canvasPoint )
605{
606 QgsRasterRenderer *renderer = mRasterLayer->renderer();
607 if ( !renderer )
608 {
609 return;
610 }
611
612 //Get the pixel values and add a new entry to the transparency table
613 if ( mMapCanvas && mPixelSelectorTool && mRasterLayer->dataProvider() )
614 {
615 mMapCanvas->unsetMapTool( mPixelSelectorTool );
616
617 const QgsMapSettings &ms = mMapCanvas->mapSettings();
618 const QgsPointXY myPoint = ms.mapToLayerCoordinates( mRasterLayer, canvasPoint );
619
620 const QgsRectangle myExtent = ms.mapToLayerCoordinates( mRasterLayer, mMapCanvas->extent() );
621 const double mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
622 const int myWidth = static_cast< int >( mMapCanvas->extent().width() / mapUnitsPerPixel );
623 const int myHeight = static_cast< int >( mMapCanvas->extent().height() / mapUnitsPerPixel );
624
625 const QMap<int, QVariant> myPixelMap = mRasterLayer->dataProvider()->identify( myPoint, Qgis::RasterIdentifyFormat::Value, myExtent, myWidth, myHeight ).results();
626
627 const QList<int> bands = renderer->usesBands();
628
629 QList<double> values;
630 for ( int i = 0; i < bands.size(); ++i )
631 {
632 const int bandNo = bands.value( i );
633 if ( myPixelMap.count( bandNo ) == 1 )
634 {
635 if ( QgsVariantUtils::isNull( myPixelMap.value( bandNo ) ) )
636 {
637 return; // Don't add nodata, transparent anyway
638 }
639 const double value = myPixelMap.value( bandNo ).toDouble();
640 QgsDebugMsgLevel( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ), 2 );
641 values.append( value );
642 }
643 }
644
645 tableTransparency->insertRow( tableTransparency->rowCount() );
646
647 switch ( mCurrentMode )
648 {
649 case Mode::SingleBand:
650 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::From ), values.value( 0 ) );
651 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::To ), values.value( 0 ) );
652 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::Opacity ), 100 );
653 break;
654 case Mode::RgbBands:
655 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Red ), values.value( 0 ) );
656 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Green ), values.value( 1 ) );
657 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Blue ), values.value( 2 ) );
658 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Tolerance ), 0 );
659 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Opacity ), 100 );
660 break;
661 }
662 }
663
664 //tableTransparency->resizeColumnsToContents();
665 //tableTransparency->resizeRowsToContents();
666}
667
668void QgsRasterTransparencyWidget::populateTransparencyTable( QgsRasterRenderer *renderer )
669{
670 if ( !mRasterLayer )
671 {
672 return;
673 }
674
675 if ( !renderer )
676 {
677 return;
678 }
679
680 const int nBands = renderer->usesBands().size();
681 setupTransparencyTable( nBands );
682
683 const QgsRasterTransparency *rasterTransparency = renderer->rasterTransparency();
684 if ( !rasterTransparency )
685 {
686 return;
687 }
688
689 switch ( mCurrentMode )
690 {
691 case Mode::SingleBand:
692 {
693 QVector<QgsRasterTransparency::TransparentSingleValuePixel> pixelList = rasterTransparency->transparentSingleValuePixelList();
694 for ( int i = 0; i < pixelList.size(); ++i )
695 {
696 tableTransparency->insertRow( i );
697 setTransparencyCell( i, static_cast< int >( SingleBandTableColumns::From ), pixelList[i].min );
698 setTransparencyCell( i, static_cast< int >( SingleBandTableColumns::To ), pixelList[i].max );
699 setTransparencyCell( i, static_cast< int >( SingleBandTableColumns::Opacity ), 100 * ( 1 - pixelList[i].opacity ) );
700 // break synchronization only if values differ
701 if ( pixelList[i].min != pixelList[i].max )
702 {
703 setTransparencyToEdited( i );
704 }
705 }
706 break;
707 }
708 case Mode::RgbBands:
709 {
710 QVector<QgsRasterTransparency::TransparentThreeValuePixel> pixelList = rasterTransparency->transparentThreeValuePixelList();
711 for ( int i = 0; i < pixelList.size(); ++i )
712 {
713 tableTransparency->insertRow( i );
714 setTransparencyCell( i, static_cast< int >( RgbBandTableColumns::Red ), pixelList[i].red );
715 setTransparencyCell( i, static_cast< int >( RgbBandTableColumns::Green ), pixelList[i].green );
716 setTransparencyCell( i, static_cast< int >( RgbBandTableColumns::Blue ), pixelList[i].blue );
717 setTransparencyCell( i, static_cast< int >( RgbBandTableColumns::Opacity ), 100 * ( 1 - pixelList[i].opacity ) );
718 // while the API supports different tolerances for red/green/blue channels, we only expose a single value here
719 // If needed, we could expose the three separate tolerances in future... but be wary of UI bloat!
720 setTransparencyCell( i, static_cast< int >( RgbBandTableColumns::Tolerance ), !qgsDoubleNear( pixelList[i].fuzzyToleranceRed, 0 ) ? pixelList[i].fuzzyToleranceRed : 0 );
721 }
722 break;
723 }
724 }
725
726 tableTransparency->resizeColumnsToContents();
727 tableTransparency->resizeRowsToContents();
728
729}
730
731void QgsRasterTransparencyWidget::setupTransparencyTable( int nBands )
732{
733 tableTransparency->clear();
734 tableTransparency->setColumnCount( 0 );
735 tableTransparency->setRowCount( 0 );
736 mTransparencyToEdited.clear();
737
738 if ( nBands == 3 )
739 {
740 mCurrentMode = Mode::RgbBands;
741 tableTransparency->setColumnCount( static_cast< int >( RgbBandTableColumns::ColumnCount ) );
742 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Red ), new QTableWidgetItem( tr( "Red" ) ) );
743 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Green ), new QTableWidgetItem( tr( "Green" ) ) );
744 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Blue ), new QTableWidgetItem( tr( "Blue" ) ) );
745 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Tolerance ), new QTableWidgetItem( tr( "Tolerance" ) ) );
746 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Opacity ), new QTableWidgetItem( tr( "Percent Transparent" ) ) );
747 }
748 else //1 band
749 {
750 mCurrentMode = Mode::SingleBand;
751 tableTransparency->setColumnCount( static_cast< int >( SingleBandTableColumns::ColumnCount ) );
752 tableTransparency->setHorizontalHeaderItem( static_cast< int >( SingleBandTableColumns::From ), new QTableWidgetItem( tr( "From" ) ) );
753 tableTransparency->setHorizontalHeaderItem( static_cast< int >( SingleBandTableColumns::To ), new QTableWidgetItem( tr( "To" ) ) );
754 tableTransparency->setHorizontalHeaderItem( static_cast< int >( SingleBandTableColumns::Opacity ), new QTableWidgetItem( tr( "Percent Transparent" ) ) );
755 }
756}
757
758void QgsRasterTransparencyWidget::setTransparencyCell( int row, int column, double value )
759{
760 QgsDebugMsgLevel( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ), 2 );
761 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
762 if ( !provider )
763 return;
764
765 QLineEdit *lineEdit = new QLineEdit();
766 lineEdit->setFrame( false ); // frame looks bad in table
767 // Without margins row selection is not displayed (important for delete row)
768 lineEdit->setContentsMargins( 1, 1, 1, 1 );
769
770 if ( column == tableTransparency->columnCount() - 1 )
771 {
772 // transparency
773 // Who needs transparency as floating point?
774 lineEdit->setValidator( new QIntValidator( nullptr ) );
775 lineEdit->setText( QString::number( static_cast<int>( value ) ) );
776 connect( lineEdit, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
777 }
778 else
779 {
780 // value
781 QString valueString;
782 switch ( provider->sourceDataType( 1 ) )
783 {
786 lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
787 if ( !std::isnan( value ) )
788 {
789 const double v = QgsRasterBlock::printValue( value ).toDouble();
790 valueString = QLocale().toString( v );
791 }
792 break;
793 default:
794 lineEdit->setValidator( new QIntValidator( nullptr ) );
795 if ( !std::isnan( value ) )
796 {
797 valueString = QString::number( static_cast<int>( value ) );
798 }
799 break;
800 }
801 lineEdit->setText( valueString );
802 connect( lineEdit, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
803 }
804 tableTransparency->setCellWidget( row, column, lineEdit );
805 adjustTransparencyCellWidth( row, column );
806
807 if ( mCurrentMode == Mode::SingleBand && ( column == static_cast< int >( SingleBandTableColumns::From ) || column == static_cast< int >( SingleBandTableColumns::To ) ) )
808 {
809 connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterTransparencyWidget::transparencyCellTextEdited );
810 }
811 //tableTransparency->resizeColumnsToContents();
812 emit widgetChanged();
813}
814
815void QgsRasterTransparencyWidget::adjustTransparencyCellWidth( int row, int column )
816{
817 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
818 if ( !lineEdit )
819 return;
820
821 int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
822 width = std::max( width, tableTransparency->columnWidth( column ) );
823
824 lineEdit->setFixedWidth( width );
825}
826
827void QgsRasterTransparencyWidget::setTransparencyToEdited( int row )
828{
829 if ( row >= mTransparencyToEdited.size() )
830 {
831 mTransparencyToEdited.resize( row + 1 );
832 }
833 mTransparencyToEdited[row] = true;
834}
835
836double QgsRasterTransparencyWidget::transparencyCellValue( int row, int column )
837{
838 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
839 if ( !lineEdit || lineEdit->text().isEmpty() )
840 {
841 return std::numeric_limits<double>::quiet_NaN();
842 }
843 return QgsDoubleValidator::toDouble( lineEdit->text() );
844}
845
847{
848 return mPixelSelectorTool;
849}
@ 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)
@ Value
Numerical pixel value.
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.
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
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:60
static QgsProject * instance()
Returns the QgsProject singleton instance.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
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, bool localized=false)
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.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
virtual QgsRasterIdentifyResult identify(const QgsPointXY &point, Qgis::RasterIdentifyFormat format, const QgsRectangle &boundingBox=QgsRectangle(), int width=0, int height=0, int dpi=96)
Identify raster value(s) found on the point position.
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.
virtual int bandCount() const =0
Gets number of bands.
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.
Property
Data definable properties.
@ RendererOpacity
Raster renderer global opacity.
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in raster pipes.
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 applyToRasterRenderer(QgsRasterRenderer *renderer) SIP_SKIP
Applies widget settings to a raster renderer.
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...
void applyToRasterProvider(QgsRasterDataProvider *provider) SIP_SKIP
Applies widget settings to a raster provider.
Defines the list of pixel values to be considered as transparent or semi transparent when rendering r...
void setTransparentSingleValuePixelList(const QVector< QgsRasterTransparency::TransparentSingleValuePixel > &newList)
Sets the transparent single value pixel list, replacing the whole existing list.
QVector< QgsRasterTransparency::TransparentSingleValuePixel > transparentSingleValuePixelList() const
Returns the transparent single value pixel list.
void setTransparentThreeValuePixelList(const QVector< QgsRasterTransparency::TransparentThreeValuePixel > &newList)
Sets the transparent three value pixel list, replacing the whole existing list.
QVector< QgsRasterTransparency::TransparentThreeValuePixel > transparentThreeValuePixelList() const
Returns the transparent three value pixel list.
A rectangle specified with double values.
double width() const
Returns the width of the rectangle.
double height() const
Returns the height of the rectangle.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
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, bool silenceNullWarnings=false)
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
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5857
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5761
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
QList< QgsRasterRange > QgsRasterRangeList
Defines the transparency for a range of single-band pixel values.
Defines the transparency for a RGB pixel value.