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