QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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 {
98 expContext
103 }
104
105 if ( mRasterLayer )
106 expContext << QgsExpressionContextUtils::layerScope( mRasterLayer );
107
108 // additional scopes
109 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
110 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
111 {
112 expContext.appendScope( new QgsExpressionContextScope( scope ) );
113 }
114
115 return expContext;
116}
117
119{
120 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
121 QgsRasterRenderer *renderer = mRasterLayer->renderer();
122 if ( provider )
123 {
124 if ( provider->dataType( 1 ) == Qgis::DataType::ARGB32 || provider->dataType( 1 ) == Qgis::DataType::ARGB32_Premultiplied )
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
322 << '\n'
323 << QString::number( transparencyCellValue( myTableRunner, static_cast<int>( RgbBandTableColumns::Red ) ) )
324 << "\t"
325 << QString::number( transparencyCellValue( myTableRunner, static_cast<int>( RgbBandTableColumns::Green ) ) )
326 << "\t"
327 << QString::number( transparencyCellValue( myTableRunner, static_cast<int>( RgbBandTableColumns::Blue ) ) )
328 << "\t"
329 << QString::number( transparencyCellValue( myTableRunner, static_cast<int>( RgbBandTableColumns::Opacity ) ) );
330 }
331 break;
332 }
333 case Mode::SingleBand:
334 {
335 myOutputStream << "#\n#\n# " << tr( "Value" ) << "\t" << tr( "Percent Transparent" );
336
337 for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
338 {
339 myOutputStream
340 << '\n'
341 << QString::number( transparencyCellValue( myTableRunner, static_cast<int>( SingleBandTableColumns::From ) ) )
342 << "\t"
343 << QString::number( transparencyCellValue( myTableRunner, static_cast<int>( SingleBandTableColumns::To ) ) )
344 << "\t"
345 << QString::number( transparencyCellValue( myTableRunner, static_cast<int>( SingleBandTableColumns::Opacity ) ) );
346 }
347 break;
348 }
349 }
350 }
351 else
352 {
353 QMessageBox::warning( this, tr( "Save Pixel Values as File" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
354 }
355 }
356}
357
358void QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked()
359{
360 int myLineCounter = 0;
361 bool myImportError = false;
362 QString myBadLines;
363 const QgsSettings myQSettings;
364 const QString myLastDir = myQSettings.value( u"lastRasterFileFilterDir"_s, QDir::homePath() ).toString();
365 const QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load Pixel Values from File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
366 QFile myInputFile( myFileName );
367
368 const thread_local QRegularExpression sRxWhitespace( "\\s+" );
369
370 if ( myInputFile.open( QFile::ReadOnly ) )
371 {
372 QTextStream myInputStream( &myInputFile );
373 QString myInputLine;
374 switch ( mCurrentMode )
375 {
376 case Mode::RgbBands:
377 {
378 for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
379 {
380 tableTransparency->removeRow( myTableRunner );
381 }
382
383 while ( !myInputStream.atEnd() )
384 {
385 myLineCounter++;
386 myInputLine = myInputStream.readLine();
387 if ( !myInputLine.isEmpty() )
388 {
389 if ( !myInputLine.simplified().startsWith( '#' ) )
390 {
391 QStringList myTokens = myInputLine.split( sRxWhitespace, Qt::SkipEmptyParts );
392 if ( myTokens.count() != 4 )
393 {
394 myImportError = true;
395 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
396 }
397 else
398 {
399 tableTransparency->insertRow( tableTransparency->rowCount() );
400 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Red ), myTokens[0].toDouble() );
401 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Green ), myTokens[1].toDouble() );
402 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Blue ), myTokens[2].toDouble() );
403 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Tolerance ), 0 );
404 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Opacity ), myTokens[3].toDouble() );
405 }
406 }
407 }
408 }
409 break;
410 }
411 case Mode::SingleBand:
412 {
413 for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
414 {
415 tableTransparency->removeRow( myTableRunner );
416 }
417
418 while ( !myInputStream.atEnd() )
419 {
420 myLineCounter++;
421 myInputLine = myInputStream.readLine();
422 if ( !myInputLine.isEmpty() )
423 {
424 if ( !myInputLine.simplified().startsWith( '#' ) )
425 {
426 QStringList myTokens = myInputLine.split( sRxWhitespace, Qt::SkipEmptyParts );
427 if ( myTokens.count() != 3 && myTokens.count() != 2 ) // 2 for QGIS < 1.9 compatibility
428 {
429 myImportError = true;
430 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
431 }
432 else
433 {
434 if ( myTokens.count() == 2 )
435 {
436 myTokens.insert( 1, myTokens[0] ); // add 'to' value, QGIS < 1.9 compatibility
437 }
438 tableTransparency->insertRow( tableTransparency->rowCount() );
439
440 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( SingleBandTableColumns::From ), myTokens[0].toDouble() );
441 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( SingleBandTableColumns::To ), myTokens[1].toDouble() );
442 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( SingleBandTableColumns::Opacity ), myTokens[2].toDouble() );
443 }
444 }
445 }
446 }
447 break;
448 }
449 }
450
451 if ( myImportError )
452 {
453 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "The following lines contained errors\n\n%1" ).arg( myBadLines ) );
454 }
455 }
456 else if ( !myFileName.isEmpty() )
457 {
458 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "Read access denied. Adjust the file permissions and try again.\n\n" ) );
459 }
460 //tableTransparency->resizeColumnsToContents();
461 //tableTransparency->resizeRowsToContents();
462 emit widgetChanged();
463}
464
465void QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked()
466{
467 if ( 0 < tableTransparency->rowCount() )
468 {
469 tableTransparency->removeRow( tableTransparency->currentRow() );
470 }
471 emit widgetChanged();
472}
473
475{
476 applyToRasterProvider( mRasterLayer->dataProvider() );
477 applyToRasterRenderer( mRasterLayer->renderer() );
478 mRasterLayer->pipe()->setDataDefinedProperties( mPropertyCollection );
479}
480
482{
483 //set NoDataValue
484 QgsRasterRangeList myNoDataRangeList;
485 if ( !leNoDataValue->text().isEmpty() )
486 {
487 bool myDoubleOk = false;
488 const double myNoDataValue = QgsDoubleValidator::toDouble( leNoDataValue->text(), &myDoubleOk );
489 if ( myDoubleOk )
490 {
491 const QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
492 myNoDataRangeList << myNoDataRange;
493 }
494 }
495 if ( provider )
496 {
497 for ( int bandNo = 1; bandNo <= provider->bandCount(); bandNo++ )
498 {
499 provider->setUserNoDataValue( bandNo, myNoDataRangeList );
500 provider->setUseSourceNoDataValue( bandNo, mSrcNoDataValueCheckBox->isChecked() );
501 }
502 }
503}
504
506{
507 if ( rasterRenderer )
508 {
509 rasterRenderer->setAlphaBand( cboxTransparencyBand->currentBand() );
510 rasterRenderer->setNodataColor( mNodataColorButton->color() );
511
512 //Walk through each row in table and test value. If not valid set to 0.0 and continue building transparency list
513 QgsRasterTransparency *rasterTransparency = new QgsRasterTransparency();
514 switch ( mCurrentMode )
515 {
516 case Mode::RgbBands:
517 {
518 QVector<QgsRasterTransparency::TransparentThreeValuePixel> myTransparentThreeValuePixelList;
519 myTransparentThreeValuePixelList.reserve( tableTransparency->rowCount() );
520 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
521 {
522 const double red = transparencyCellValue( myListRunner, static_cast<int>( RgbBandTableColumns::Red ) );
523 const double green = transparencyCellValue( myListRunner, static_cast<int>( RgbBandTableColumns::Green ) );
524 const double blue = transparencyCellValue( myListRunner, static_cast<int>( RgbBandTableColumns::Blue ) );
525 const double opacity = 1.0 - transparencyCellValue( myListRunner, static_cast<int>( RgbBandTableColumns::Opacity ) ) / 100.0;
526 const double tolerance = transparencyCellValue( myListRunner, static_cast<int>( RgbBandTableColumns::Tolerance ) );
527 myTransparentThreeValuePixelList.append(
529 red,
530 green,
531 blue,
532 opacity,
533 !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon(),
534 !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon(),
535 !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon()
536 )
537 );
538 }
539 rasterTransparency->setTransparentThreeValuePixelList( myTransparentThreeValuePixelList );
540 break;
541 }
542 case Mode::SingleBand:
543 {
544 QVector<QgsRasterTransparency::TransparentSingleValuePixel> myTransparentSingleValuePixelList;
545 myTransparentSingleValuePixelList.reserve( tableTransparency->rowCount() );
546 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
547 {
548 const double min = transparencyCellValue( myListRunner, static_cast<int>( SingleBandTableColumns::From ) );
549 const double max = transparencyCellValue( myListRunner, static_cast<int>( SingleBandTableColumns::To ) );
550 const double opacity = 1.0 - transparencyCellValue( myListRunner, static_cast<int>( SingleBandTableColumns::Opacity ) ) / 100.0;
551
552 myTransparentSingleValuePixelList.append( QgsRasterTransparency::TransparentSingleValuePixel( min, max, opacity ) );
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 QList<QgsPropertyOverrideButton *> 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( u"value = %1"_s.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
730void QgsRasterTransparencyWidget::setupTransparencyTable( int nBands )
731{
732 tableTransparency->clear();
733 tableTransparency->setColumnCount( 0 );
734 tableTransparency->setRowCount( 0 );
735 mTransparencyToEdited.clear();
736
737 if ( nBands == 3 )
738 {
739 mCurrentMode = Mode::RgbBands;
740 tableTransparency->setColumnCount( static_cast<int>( RgbBandTableColumns::ColumnCount ) );
741 tableTransparency->horizontalHeader()->setSectionResizeMode( static_cast<int>( RgbBandTableColumns::Red ), QHeaderView::Stretch );
742 tableTransparency->horizontalHeader()->setSectionResizeMode( static_cast<int>( RgbBandTableColumns::Green ), QHeaderView::Stretch );
743 tableTransparency->horizontalHeader()->setSectionResizeMode( static_cast<int>( RgbBandTableColumns::Blue ), QHeaderView::Stretch );
744 tableTransparency->horizontalHeader()->setSectionResizeMode( static_cast<int>( RgbBandTableColumns::Tolerance ), QHeaderView::Stretch );
745 tableTransparency->horizontalHeader()->setSectionResizeMode( static_cast<int>( RgbBandTableColumns::Opacity ), QHeaderView::Stretch );
746 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Red ), new QTableWidgetItem( tr( "Red" ) ) );
747 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Green ), new QTableWidgetItem( tr( "Green" ) ) );
748 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Blue ), new QTableWidgetItem( tr( "Blue" ) ) );
749 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Tolerance ), new QTableWidgetItem( tr( "Tolerance" ) ) );
750 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Opacity ), new QTableWidgetItem( tr( "Opacity [%]" ) ) );
751 }
752 else //1 band
753 {
754 mCurrentMode = Mode::SingleBand;
755 tableTransparency->setColumnCount( static_cast<int>( SingleBandTableColumns::ColumnCount ) );
756 tableTransparency->horizontalHeader()->setSectionResizeMode( static_cast<int>( SingleBandTableColumns::From ), QHeaderView::Stretch );
757 tableTransparency->horizontalHeader()->setSectionResizeMode( static_cast<int>( SingleBandTableColumns::To ), QHeaderView::Stretch );
758 tableTransparency->horizontalHeader()->setSectionResizeMode( static_cast<int>( SingleBandTableColumns::Opacity ), QHeaderView::Stretch );
759 tableTransparency->setHorizontalHeaderItem( static_cast<int>( SingleBandTableColumns::From ), new QTableWidgetItem( tr( "From" ) ) );
760 tableTransparency->setHorizontalHeaderItem( static_cast<int>( SingleBandTableColumns::To ), new QTableWidgetItem( tr( "To" ) ) );
761 tableTransparency->setHorizontalHeaderItem( static_cast<int>( SingleBandTableColumns::Opacity ), new QTableWidgetItem( tr( "Opacity [%]" ) ) );
762 }
763}
764
765void QgsRasterTransparencyWidget::setTransparencyCell( int row, int column, double value )
766{
767 QgsDebugMsgLevel( u"value = %1"_s.arg( value, 0, 'g', 17 ), 2 );
768 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
769 if ( !provider )
770 return;
771
772 QLineEdit *lineEdit = new QLineEdit();
773 // frame looks bad in table
774 lineEdit->setFrame( false );
775 // Without margins row selection is not displayed (important for delete row)
776 lineEdit->setContentsMargins( 1, 1, 1, 1 );
777 lineEdit->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
778
779 if ( column == tableTransparency->columnCount() - 1 )
780 {
781 // transparency
782 // Who needs transparency as floating point?
783 lineEdit->setValidator( new QIntValidator( nullptr ) );
784 lineEdit->setText( QString::number( static_cast<int>( value ) ) );
785 connect( lineEdit, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
786 }
787 else
788 {
789 // value
790 QString valueString;
791 switch ( provider->sourceDataType( 1 ) )
792 {
795 lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
796 if ( !std::isnan( value ) )
797 {
798 const double v = QgsRasterBlock::printValue( value ).toDouble();
799 valueString = QLocale().toString( v );
800 }
801 break;
802 default:
803 lineEdit->setValidator( new QIntValidator( nullptr ) );
804 if ( !std::isnan( value ) )
805 {
806 valueString = QString::number( static_cast<int>( value ) );
807 }
808 break;
809 }
810 lineEdit->setText( valueString );
811 connect( lineEdit, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
812 }
813 tableTransparency->setCellWidget( row, column, lineEdit );
814
815 if ( mCurrentMode == Mode::SingleBand && ( column == static_cast<int>( SingleBandTableColumns::From ) || column == static_cast<int>( SingleBandTableColumns::To ) ) )
816 {
817 connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterTransparencyWidget::transparencyCellTextEdited );
818 }
819 //tableTransparency->resizeColumnsToContents();
820 emit widgetChanged();
821}
822
823void QgsRasterTransparencyWidget::adjustTransparencyCellWidth( int row, int column )
824{
825 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
826 if ( !lineEdit )
827 return;
828
829 int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
830 width = std::max( width, tableTransparency->columnWidth( column ) );
831
832 lineEdit->setFixedWidth( width );
833}
834
835void QgsRasterTransparencyWidget::setTransparencyToEdited( int row )
836{
837 if ( row >= mTransparencyToEdited.size() )
838 {
839 mTransparencyToEdited.resize( row + 1 );
840 }
841 mTransparencyToEdited[row] = true;
842}
843
844double QgsRasterTransparencyWidget::transparencyCellValue( int row, int column )
845{
846 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
847 if ( !lineEdit || lineEdit->text().isEmpty() )
848 {
849 return std::numeric_limits<double>::quiet_NaN();
850 }
851 return QgsDoubleValidator::toDouble( lineEdit->text() );
852}
853
855{
856 return mPixelSelectorTool;
857}
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:401
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:408
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:407
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:402
@ Value
Numerical pixel value.
Definition qgis.h:4991
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:6975
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:6880
#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.