QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
qgsrastertransparencywidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrastertransparencywidget.cpp
3 ---------------------
4 begin : May 2016
5 copyright : (C) 2016 by Nathan Woodrow
6 email : woodrow dot nathan at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15#include <QWidget>
16#include <QIntValidator>
17#include <QFile>
18#include <QTextStream>
19#include <QMessageBox>
20#include <QFileDialog>
21#include <QRegularExpression>
22
23#include "qgssettings.h"
25#include "moc_qgsrastertransparencywidget.cpp"
26#include "qgsrasterlayer.h"
29#include "qgsmaptoolemitpoint.h"
30#include "qgsmapsettings.h"
31#include "qgsrectangle.h"
32#include "qgsmapcanvas.h"
34#include "qgsdoublevalidator.h"
36#include "qgsrasterrenderer.h"
38
40 : QgsMapLayerConfigWidget( layer, canvas, parent )
41 , TRSTRING_NOT_SET( tr( "Not Set" ) )
42 , mRasterLayer( layer )
43 , mMapCanvas( canvas )
44{
45 setupUi( this );
46 connect( pbnAddValuesFromDisplay, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked );
47 connect( pbnAddValuesManually, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnAddValuesManually_clicked );
48 connect( pbnDefaultValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnDefaultValues_clicked );
49 connect( pbnExportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked );
50 connect( pbnImportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked );
51 connect( pbnRemoveSelectedRow, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked );
52
53 mNodataColorButton->setShowNoColor( true );
54 mNodataColorButton->setColorDialogTitle( tr( "Select NoData Color" ) );
56
57 connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsPanelWidget::widgetChanged );
58 connect( cboxTransparencyBand, &QgsRasterBandComboBox::bandChanged, this, &QgsPanelWidget::widgetChanged );
59 connect( mSrcNoDataValueCheckBox, &QCheckBox::stateChanged, this, &QgsPanelWidget::widgetChanged );
60 connect( leNoDataValue, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
61 leNoDataValue->setValidator( new QgsDoubleValidator( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), this ) );
62 connect( mNodataColorButton, &QgsColorButton::colorChanged, this, &QgsPanelWidget::widgetChanged );
63
64 mPixelSelectorTool = nullptr;
65 if ( mMapCanvas )
66 {
67 mPixelSelectorTool = new QgsMapToolEmitPoint( mMapCanvas );
68 connect( mPixelSelectorTool, &QgsMapToolEmitPoint::canvasClicked, this, &QgsRasterTransparencyWidget::pixelSelected );
69 }
70 else
71 {
72 pbnAddValuesFromDisplay->setEnabled( false );
73 }
74
76}
77
79{
80 mContext = context;
81}
82
84{
85 QgsExpressionContext expContext;
86
87 if ( QgsMapCanvas *canvas = mContext.mapCanvas() )
88 {
89 expContext = canvas->createExpressionContext();
90 }
91 else
92 {
97 }
98
99 if ( mRasterLayer )
100 expContext << QgsExpressionContextUtils::layerScope( mRasterLayer );
101
102 // additional scopes
103 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
104 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
105 {
106 expContext.appendScope( new QgsExpressionContextScope( scope ) );
107 }
108
109 return expContext;
110}
111
113{
114 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
115 QgsRasterRenderer *renderer = mRasterLayer->renderer();
116 if ( provider )
117 {
118 if ( provider->dataType( 1 ) == Qgis::DataType::ARGB32
120 {
121 gboxNoDataValue->setEnabled( false );
122 gboxCustomTransparency->setEnabled( false );
123 }
124
125 cboxTransparencyBand->setShowNotSetOption( true, tr( "None" ) );
126 cboxTransparencyBand->setLayer( mRasterLayer );
127 if ( provider->sourceHasNoDataValue( 1 ) )
128 {
129 lblSrcNoDataValue->setText( QgsRasterBlock::printValue( provider->sourceNoDataValue( 1 ) ) );
130 }
131 else
132 {
133 lblSrcNoDataValue->setText( tr( "not defined" ) );
134 }
135
136 mSrcNoDataValueCheckBox->setChecked( provider->useSourceNoDataValue( 1 ) );
137
138 const bool enableSrcNoData = provider->sourceHasNoDataValue( 1 ) && !std::isnan( provider->sourceNoDataValue( 1 ) );
139
140 mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData );
141 lblSrcNoDataValue->setEnabled( enableSrcNoData );
142 }
143
144 if ( renderer )
145 {
146 if ( renderer->nodataColor().isValid() )
147 mNodataColorButton->setColor( renderer->nodataColor() );
148 else
149 mNodataColorButton->setToNull();
150
151 mOpacityWidget->setOpacity( renderer->opacity() );
152
153 cboxTransparencyBand->setBand( renderer->alphaBand() );
154 }
155
156 if ( provider )
157 {
158 const QgsRasterRangeList noDataRangeList = provider->userNoDataValues( 1 );
159 QgsDebugMsgLevel( QStringLiteral( "noDataRangeList.size = %1" ).arg( noDataRangeList.size() ), 2 );
160 if ( !noDataRangeList.isEmpty() )
161 {
162 const double v = QgsRasterBlock::printValue( noDataRangeList.value( 0 ).min() ).toDouble();
163 leNoDataValue->setText( QLocale().toString( v, 'g', 20 ) );
164 }
165 else
166 {
167 leNoDataValue->setText( QString() );
168 }
169 }
170 else
171 {
172 leNoDataValue->setText( QString() );
173 }
174
177
178 populateTransparencyTable( mRasterLayer->renderer() );
179}
180
181void QgsRasterTransparencyWidget::transparencyCellTextEdited( const QString &text )
182{
183 Q_UNUSED( text )
184 QgsDebugMsgLevel( QStringLiteral( "text = %1" ).arg( text ), 2 );
185
186 switch ( mCurrentMode )
187 {
188 case Mode::SingleBand:
189 {
190 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender() );
191 if ( !lineEdit )
192 return;
193 int row = -1;
194 int column = -1;
195 for ( int r = 0; r < tableTransparency->rowCount(); r++ )
196 {
197 for ( int c = 0; c < tableTransparency->columnCount(); c++ )
198 {
199 if ( tableTransparency->cellWidget( r, c ) == sender() )
200 {
201 row = r;
202 column = c;
203 break;
204 }
205 }
206 if ( row != -1 )
207 break;
208 }
209 QgsDebugMsgLevel( QStringLiteral( "row = %1 column =%2" ).arg( row ).arg( column ), 2 );
210
211 if ( column == static_cast< int >( SingleBandTableColumns::From ) )
212 {
213 QLineEdit *toLineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, static_cast< int >( SingleBandTableColumns::To ) ) );
214 if ( !toLineEdit )
215 return;
216
217 const bool toChanged = mTransparencyToEdited.value( row );
218 QgsDebugMsgLevel( QStringLiteral( "toChanged = %1" ).arg( toChanged ), 2 );
219 if ( !toChanged )
220 {
221 toLineEdit->setText( lineEdit->text() );
222 }
223 }
224 else if ( column == static_cast< int >( SingleBandTableColumns::To ) )
225 {
226 setTransparencyToEdited( row );
227 }
228 break;
229 }
230
231 case Mode::RgbBands:
232 break;
233 }
234
235 emit widgetChanged();
236}
237
238void QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked()
239{
240 if ( mMapCanvas && mPixelSelectorTool )
241 {
242 mMapCanvas->setMapTool( mPixelSelectorTool );
243 }
244}
245
246void QgsRasterTransparencyWidget::pbnAddValuesManually_clicked()
247{
248 QgsRasterRenderer *renderer = mRasterLayer->renderer();
249 if ( !renderer )
250 {
251 return;
252 }
253
254 tableTransparency->insertRow( tableTransparency->rowCount() );
255
256 int n = 0;
257 switch ( mCurrentMode )
258 {
259 case Mode::SingleBand:
260 n = 2; // set both From and To columns
261 break;
262
263 case Mode::RgbBands:
264 n = 3;
265 break;
266 }
267
268 for ( int i = 0; i < n; i++ )
269 {
270 setTransparencyCell( tableTransparency->rowCount() - 1, i, std::numeric_limits<double>::quiet_NaN() );
271 }
272
273 switch ( mCurrentMode )
274 {
275 case Mode::SingleBand:
276 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::Opacity ), 100 );
277 break;
278
279 case Mode::RgbBands:
280 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Opacity ), 100 );
281 break;
282 }
283
284 //tableTransparency->resizeColumnsToContents();
285 //tableTransparency->resizeRowsToContents();
286}
287
288void QgsRasterTransparencyWidget::pbnDefaultValues_clicked()
289{
290 QgsRasterRenderer *r = mRasterLayer->renderer();
291 if ( !r )
292 {
293 return;
294 }
295
296 const int nBands = r->usesBands().size();
297
298 setupTransparencyTable( nBands );
299
300 //tableTransparency->resizeColumnsToContents(); // works only with values
301 //tableTransparency->resizeRowsToContents();
302
303}
304
305void QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked()
306{
307 const QgsSettings myQSettings;
308 const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
309 QString myFileName = QFileDialog::getSaveFileName( this, tr( "Save Pixel Values as File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
310 if ( !myFileName.isEmpty() )
311 {
312 if ( !myFileName.endsWith( QLatin1String( ".txt" ), Qt::CaseInsensitive ) )
313 {
314 myFileName = myFileName + ".txt";
315 }
316
317 QFile myOutputFile( myFileName );
318 if ( myOutputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
319 {
320 QTextStream myOutputStream( &myOutputFile );
321 myOutputStream << "# " << tr( "QGIS Generated Transparent Pixel Value Export File" ) << '\n';
322 switch ( mCurrentMode )
323 {
324 case Mode::RgbBands:
325 {
326 myOutputStream << "#\n#\n# " << tr( "Red" ) << "\t" << tr( "Green" ) << "\t" << tr( "Blue" ) << "\t" << tr( "Percent Transparent" );
327 for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
328 {
329 myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( RgbBandTableColumns::Red ) ) ) << "\t"
330 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( RgbBandTableColumns::Green ) ) ) << "\t"
331 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( RgbBandTableColumns::Blue ) ) ) << "\t"
332 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( RgbBandTableColumns::Opacity ) ) );
333 }
334 break;
335 }
336 case Mode::SingleBand:
337 {
338 myOutputStream << "#\n#\n# " << tr( "Value" ) << "\t" << tr( "Percent Transparent" );
339
340 for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
341 {
342 myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( SingleBandTableColumns::From ) ) ) << "\t"
343 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( SingleBandTableColumns::To ) ) ) << "\t"
344 << QString::number( transparencyCellValue( myTableRunner, static_cast< int >( SingleBandTableColumns::Opacity ) ) );
345 }
346 break;
347 }
348 }
349 }
350 else
351 {
352 QMessageBox::warning( this, tr( "Save Pixel Values as File" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
353 }
354 }
355}
356
357void QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked()
358{
359 int myLineCounter = 0;
360 bool myImportError = false;
361 QString myBadLines;
362 const QgsSettings myQSettings;
363 const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
364 const QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load Pixel Values from File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
365 QFile myInputFile( myFileName );
366
367 const thread_local QRegularExpression sRxWhitespace( "\\s+" );
368
369 if ( myInputFile.open( QFile::ReadOnly ) )
370 {
371 QTextStream myInputStream( &myInputFile );
372 QString myInputLine;
373 switch ( mCurrentMode )
374 {
375 case Mode::RgbBands:
376 {
377 for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
378 {
379 tableTransparency->removeRow( myTableRunner );
380 }
381
382 while ( !myInputStream.atEnd() )
383 {
384 myLineCounter++;
385 myInputLine = myInputStream.readLine();
386 if ( !myInputLine.isEmpty() )
387 {
388 if ( !myInputLine.simplified().startsWith( '#' ) )
389 {
390 QStringList myTokens = myInputLine.split( sRxWhitespace, Qt::SkipEmptyParts );
391 if ( myTokens.count() != 4 )
392 {
393 myImportError = true;
394 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
395 }
396 else
397 {
398 tableTransparency->insertRow( tableTransparency->rowCount() );
399 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Red ), myTokens[0].toDouble() );
400 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Green ), myTokens[1].toDouble() );
401 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Blue ), myTokens[2].toDouble() );
402 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Tolerance ), 0 );
403 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( RgbBandTableColumns::Opacity ), myTokens[3].toDouble() );
404 }
405 }
406 }
407 }
408 break;
409 }
410 case Mode::SingleBand:
411 {
412 for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
413 {
414 tableTransparency->removeRow( myTableRunner );
415 }
416
417 while ( !myInputStream.atEnd() )
418 {
419 myLineCounter++;
420 myInputLine = myInputStream.readLine();
421 if ( !myInputLine.isEmpty() )
422 {
423 if ( !myInputLine.simplified().startsWith( '#' ) )
424 {
425 QStringList myTokens = myInputLine.split( sRxWhitespace, Qt::SkipEmptyParts );
426 if ( myTokens.count() != 3 && myTokens.count() != 2 ) // 2 for QGIS < 1.9 compatibility
427 {
428 myImportError = true;
429 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
430 }
431 else
432 {
433 if ( myTokens.count() == 2 )
434 {
435 myTokens.insert( 1, myTokens[0] ); // add 'to' value, QGIS < 1.9 compatibility
436 }
437 tableTransparency->insertRow( tableTransparency->rowCount() );
438
439 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::From ), myTokens[0].toDouble() );
440 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::To ), myTokens[1].toDouble() );
441 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast< int >( SingleBandTableColumns::Opacity ), myTokens[2].toDouble() );
442 }
443 }
444 }
445 }
446 break;
447 }
448 }
449
450 if ( myImportError )
451 {
452 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "The following lines contained errors\n\n%1" ).arg( myBadLines ) );
453 }
454 }
455 else if ( !myFileName.isEmpty() )
456 {
457 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "Read access denied. Adjust the file permissions and try again.\n\n" ) );
458 }
459 //tableTransparency->resizeColumnsToContents();
460 //tableTransparency->resizeRowsToContents();
461 emit widgetChanged();
462}
463
464void QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked()
465{
466 if ( 0 < tableTransparency->rowCount() )
467 {
468 tableTransparency->removeRow( tableTransparency->currentRow() );
469 }
470 emit widgetChanged();
471}
472
474{
475 applyToRasterProvider( mRasterLayer->dataProvider() );
476 applyToRasterRenderer( mRasterLayer->renderer() );
478}
479
481{
482 //set NoDataValue
483 QgsRasterRangeList myNoDataRangeList;
484 if ( !leNoDataValue->text().isEmpty() )
485 {
486 bool myDoubleOk = false;
487 const double myNoDataValue = QgsDoubleValidator::toDouble( leNoDataValue->text(), &myDoubleOk );
488 if ( myDoubleOk )
489 {
490 const QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
491 myNoDataRangeList << myNoDataRange;
492 }
493 }
494 if ( provider )
495 {
496 for ( int bandNo = 1; bandNo <= provider->bandCount(); bandNo++ )
497 {
498 provider->setUserNoDataValue( bandNo, myNoDataRangeList );
499 provider->setUseSourceNoDataValue( bandNo, mSrcNoDataValueCheckBox->isChecked() );
500 }
501 }
502}
503
505{
506 if ( rasterRenderer )
507 {
508 rasterRenderer->setAlphaBand( cboxTransparencyBand->currentBand() );
509 rasterRenderer->setNodataColor( mNodataColorButton->color() );
510
511 //Walk through each row in table and test value. If not valid set to 0.0 and continue building transparency list
512 QgsRasterTransparency *rasterTransparency = new QgsRasterTransparency();
513 switch ( mCurrentMode )
514 {
515 case Mode::RgbBands:
516 {
517 QVector<QgsRasterTransparency::TransparentThreeValuePixel> myTransparentThreeValuePixelList;
518 myTransparentThreeValuePixelList.reserve( tableTransparency->rowCount() );
519 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
520 {
521 const double red = transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Red ) );
522 const double green = transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Green ) );
523 const double blue = transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Blue ) );
524 const double opacity = 1.0 - transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Opacity ) ) / 100.0;
525 const double tolerance = transparencyCellValue( myListRunner, static_cast< int >( RgbBandTableColumns::Tolerance ) );
526 myTransparentThreeValuePixelList.append(
527 QgsRasterTransparency::TransparentThreeValuePixel( red, green, blue, opacity,
528 !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon(),
529 !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon(),
530 !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 auto 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}
726
727void QgsRasterTransparencyWidget::setupTransparencyTable( int nBands )
728{
729 tableTransparency->clear();
730 tableTransparency->setColumnCount( 0 );
731 tableTransparency->setRowCount( 0 );
732 mTransparencyToEdited.clear();
733
734 if ( nBands == 3 )
735 {
736 mCurrentMode = Mode::RgbBands;
737 tableTransparency->setColumnCount( static_cast< int >( RgbBandTableColumns::ColumnCount ) );
738 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Red ), new QTableWidgetItem( tr( "Red" ) ) );
739 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Green ), new QTableWidgetItem( tr( "Green" ) ) );
740 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Blue ), new QTableWidgetItem( tr( "Blue" ) ) );
741 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Tolerance ), new QTableWidgetItem( tr( "Tolerance" ) ) );
742 tableTransparency->setHorizontalHeaderItem( static_cast< int >( RgbBandTableColumns::Opacity ), new QTableWidgetItem( tr( "Percent Transparent" ) ) );
743 }
744 else //1 band
745 {
746 mCurrentMode = Mode::SingleBand;
747 tableTransparency->setColumnCount( static_cast< int >( SingleBandTableColumns::ColumnCount ) );
748 tableTransparency->setHorizontalHeaderItem( static_cast< int >( SingleBandTableColumns::From ), new QTableWidgetItem( tr( "From" ) ) );
749 tableTransparency->setHorizontalHeaderItem( static_cast< int >( SingleBandTableColumns::To ), new QTableWidgetItem( tr( "To" ) ) );
750 tableTransparency->setHorizontalHeaderItem( static_cast< int >( SingleBandTableColumns::Opacity ), new QTableWidgetItem( tr( "Percent Transparent" ) ) );
751 }
752}
753
754void QgsRasterTransparencyWidget::setTransparencyCell( int row, int column, double value )
755{
756 QgsDebugMsgLevel( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ), 2 );
757 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
758 if ( !provider )
759 return;
760
761 QLineEdit *lineEdit = new QLineEdit();
762 lineEdit->setFrame( false ); // frame looks bad in table
763 // Without margins row selection is not displayed (important for delete row)
764 lineEdit->setContentsMargins( 1, 1, 1, 1 );
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 adjustTransparencyCellWidth( row, column );
802
803 if ( mCurrentMode == Mode::SingleBand && ( column == static_cast< int >( SingleBandTableColumns::From ) || column == static_cast< int >( SingleBandTableColumns::To ) ) )
804 {
805 connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterTransparencyWidget::transparencyCellTextEdited );
806 }
807 //tableTransparency->resizeColumnsToContents();
808 emit widgetChanged();
809}
810
811void QgsRasterTransparencyWidget::adjustTransparencyCellWidth( int row, int column )
812{
813 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
814 if ( !lineEdit )
815 return;
816
817 int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
818 width = std::max( width, tableTransparency->columnWidth( column ) );
819
820 lineEdit->setFixedWidth( width );
821}
822
823void QgsRasterTransparencyWidget::setTransparencyToEdited( int row )
824{
825 if ( row >= mTransparencyToEdited.size() )
826 {
827 mTransparencyToEdited.resize( row + 1 );
828 }
829 mTransparencyToEdited[row] = true;
830}
831
832double QgsRasterTransparencyWidget::transparencyCellValue( int row, int column )
833{
834 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
835 if ( !lineEdit || lineEdit->text().isEmpty() )
836 {
837 return std::numeric_limits<double>::quiet_NaN();
838 }
839 return QgsDoubleValidator::toDouble( lineEdit->text() );
840}
841
843{
844 return mPixelSelectorTool;
845}
@ Float32
Thirty two bit floating point (float)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Float64
Sixty four bit floating point (double)
@ Value
Numerical pixel value.
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
QgsDoubleValidator is a QLineEdit Validator that combines QDoubleValidator and QRegularExpressionVali...
static double toDouble(const QString &input, bool *ok)
Converts input string to double value.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Map canvas is a class for displaying all GIS data types on a canvas.
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
A panel widget that can be shown in the map style dock.
The QgsMapSettings class contains configuration for rendering of the map.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
A map tool that simply emits a point when clicking on the map.
void canvasClicked(const QgsPointXY &point, Qt::MouseButton button)
signal emitted on canvas click
void opacityChanged(double opacity)
Emitted when the opacity is changed in the widget, where opacity ranges from 0.0 (transparent) to 1....
void widgetChanged()
Emitted when the widget state changes.
A class to represent a 2D point.
Definition qgspointxy.h:60
static QgsProject * instance()
Returns the QgsProject singleton instance.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
A button for controlling property overrides which may apply to a widget.
QgsProperty toProperty() const
Returns a QgsProperty object encapsulating the current state of the widget.
void changed()
Emitted when property definition changes.
void init(int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer=nullptr, bool auxiliaryStorageEnabled=false)
Initialize a newly constructed property button (useful if button was included in a UI layout).
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
int propertyKey() const
Returns the property key linked to the button.
void bandChanged(int band)
Emitted when the currently selected band changes.
static QString printValue(double value, bool localized=false)
Print double value with all necessary significant digits.
Base class for raster data providers.
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
virtual bool useSourceNoDataValue(int bandNo) const
Returns the source nodata value usage.
Qgis::DataType sourceDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual void setUseSourceNoDataValue(int bandNo, bool use)
Sets the source nodata value usage.
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
virtual QgsRasterIdentifyResult identify(const QgsPointXY &point, Qgis::RasterIdentifyFormat format, const QgsRectangle &boundingBox=QgsRectangle(), int width=0, int height=0, int dpi=96)
Identify raster value(s) found on the point position.
virtual QgsRasterRangeList userNoDataValues(int bandNo) const
Returns a list of user no data value ranges.
virtual void setUserNoDataValue(int bandNo, const QgsRasterRangeList &noData)
QMap< int, QVariant > results() const
Returns the identify results.
virtual int bandCount() const =0
Gets number of bands.
Represents a raster layer.
QgsRasterPipe * pipe()
Returns the raster pipe.
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the pipe's property collection, used for data defined overrides.
Property
Data definable properties.
@ RendererOpacity
Raster renderer global opacity.
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in raster pipes.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the pipe's property collection, used for data defined overrides.
Raster values range container.
Raster renderer pipe that applies colors to a raster.
QColor nodataColor() const
Returns the color to use for shading nodata pixels.
const QgsRasterTransparency * rasterTransparency() const
double opacity() const
Returns the opacity for the renderer, where opacity is a value between 0 (totally transparent) and 1....
virtual QList< int > usesBands() const
Returns a list of band numbers used by the renderer.
void setAlphaBand(int band)
void setOpacity(double opacity)
Sets the opacity for the renderer, where opacity is a value between 0 (totally transparent) and 1....
void setRasterTransparency(QgsRasterTransparency *t)
void setNodataColor(const QColor &color)
Sets the color to use for shading nodata pixels.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void updateDataDefinedButtons()
Updates all property override buttons to reflect the widgets's current properties.
QgsPropertyCollection mPropertyCollection
Temporary property collection.
void applyToRasterRenderer(QgsRasterRenderer *renderer) SIP_SKIP
Applies widget settings to a raster renderer.
void syncToLayer()
Sync the widget state to the layer set for the widget.
QgsMapToolEmitPoint * pixelSelectorTool() const
Returns the (possibly nullptr) map pixel selector tool.
void initializeDataDefinedButton(QgsPropertyOverrideButton *button, QgsRasterPipe::Property key)
Registers a property override button, setting up its initial value, connections and description.
QgsRasterTransparencyWidget(QgsRasterLayer *layer, QgsMapCanvas *canvas, QWidget *parent=nullptr)
Widget to control a layers transparency and related options.
void updateDataDefinedButton(QgsPropertyOverrideButton *button)
Updates a specific property override button to reflect the widgets's current properties.
void apply() override
Apply any changes on the widget to the set layer.
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the dialog is shown, e.g., the associated map canvas and expression context...
void applyToRasterProvider(QgsRasterDataProvider *provider) SIP_SKIP
Applies widget settings to a raster provider.
Defines the list of pixel values to be considered as transparent or semi transparent when rendering r...
void setTransparentSingleValuePixelList(const QVector< QgsRasterTransparency::TransparentSingleValuePixel > &newList)
Sets the transparent single value pixel list, replacing the whole existing list.
QVector< QgsRasterTransparency::TransparentSingleValuePixel > transparentSingleValuePixelList() const
Returns the transparent single value pixel list.
void setTransparentThreeValuePixelList(const QVector< QgsRasterTransparency::TransparentThreeValuePixel > &newList)
Sets the transparent three value pixel list, replacing the whole existing list.
QVector< QgsRasterTransparency::TransparentThreeValuePixel > transparentThreeValuePixelList() const
Returns the transparent three value pixel list.
A rectangle specified with double values.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5958
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5862
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
QList< QgsRasterRange > QgsRasterRangeList
Defines the transparency for a range of single-band pixel values.
Defines the transparency for a RGB pixel value.