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