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