QGIS API Documentation 3.41.0-Master (cea29feecf2)
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
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'
329 << 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'
343 << QString::number( transparencyCellValue( myTableRunner, static_cast<int>( SingleBandTableColumns::From ) ) ) << "\t"
344 << QString::number( transparencyCellValue( myTableRunner, static_cast<int>( SingleBandTableColumns::To ) ) ) << "\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( QStringLiteral( "lastRasterFileFilterDir" ), 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() );
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(
528 QgsRasterTransparency::TransparentThreeValuePixel( red, green, blue, opacity, !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon(), !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon(), !qgsDoubleNear( tolerance, 0 ) ? tolerance : 4 * std::numeric_limits<double>::epsilon() )
529 );
530 }
531 rasterTransparency->setTransparentThreeValuePixelList( myTransparentThreeValuePixelList );
532 break;
533 }
534 case Mode::SingleBand:
535 {
536 QVector<QgsRasterTransparency::TransparentSingleValuePixel> myTransparentSingleValuePixelList;
537 myTransparentSingleValuePixelList.reserve( tableTransparency->rowCount() );
538 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
539 {
540 const double min = transparencyCellValue( myListRunner, static_cast<int>( SingleBandTableColumns::From ) );
541 const double max = transparencyCellValue( myListRunner, static_cast<int>( SingleBandTableColumns::To ) );
542 const double opacity = 1.0 - transparencyCellValue( myListRunner, static_cast<int>( SingleBandTableColumns::Opacity ) ) / 100.0;
543
544 myTransparentSingleValuePixelList.append(
546 );
547 }
548 rasterTransparency->setTransparentSingleValuePixelList( myTransparentSingleValuePixelList );
549 break;
550 }
551 }
552
553 rasterRenderer->setRasterTransparency( rasterTransparency );
554
555 //set global transparency
556 rasterRenderer->setOpacity( mOpacityWidget->opacity() );
557 }
558}
559
561{
562 button->blockSignals( true );
563 button->init( static_cast<int>( key ), mPropertyCollection, QgsRasterPipe::propertyDefinitions(), nullptr );
564 connect( button, &QgsPropertyOverrideButton::changed, this, &QgsRasterTransparencyWidget::updateProperty );
566 button->blockSignals( false );
567}
568
570{
571 const auto propertyOverrideButtons { findChildren<QgsPropertyOverrideButton *>() };
572 for ( QgsPropertyOverrideButton *button : propertyOverrideButtons )
573 {
574 updateDataDefinedButton( button );
575 }
576}
577
579{
580 if ( !button )
581 return;
582
583 if ( button->propertyKey() < 0 )
584 return;
585
586 const QgsRasterPipe::Property key = static_cast<QgsRasterPipe::Property>( button->propertyKey() );
587 whileBlocking( button )->setToProperty( mPropertyCollection.property( key ) );
588}
589
590void QgsRasterTransparencyWidget::updateProperty()
591{
592 QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
593 const QgsRasterPipe::Property key = static_cast<QgsRasterPipe::Property>( button->propertyKey() );
595 emit widgetChanged();
596}
597
598void QgsRasterTransparencyWidget::pixelSelected( const QgsPointXY &canvasPoint )
599{
600 QgsRasterRenderer *renderer = mRasterLayer->renderer();
601 if ( !renderer )
602 {
603 return;
604 }
605
606 //Get the pixel values and add a new entry to the transparency table
607 if ( mMapCanvas && mPixelSelectorTool && mRasterLayer->dataProvider() )
608 {
609 mMapCanvas->unsetMapTool( mPixelSelectorTool );
610
611 const QgsMapSettings &ms = mMapCanvas->mapSettings();
612 const QgsPointXY myPoint = ms.mapToLayerCoordinates( mRasterLayer, canvasPoint );
613
614 const QgsRectangle myExtent = ms.mapToLayerCoordinates( mRasterLayer, mMapCanvas->extent() );
615 const double mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
616 const int myWidth = static_cast<int>( mMapCanvas->extent().width() / mapUnitsPerPixel );
617 const int myHeight = static_cast<int>( mMapCanvas->extent().height() / mapUnitsPerPixel );
618
619 const QMap<int, QVariant> myPixelMap = mRasterLayer->dataProvider()->identify( myPoint, Qgis::RasterIdentifyFormat::Value, myExtent, myWidth, myHeight ).results();
620
621 const QList<int> bands = renderer->usesBands();
622
623 QList<double> values;
624 for ( int i = 0; i < bands.size(); ++i )
625 {
626 const int bandNo = bands.value( i );
627 if ( myPixelMap.count( bandNo ) == 1 )
628 {
629 if ( QgsVariantUtils::isNull( myPixelMap.value( bandNo ) ) )
630 {
631 return; // Don't add nodata, transparent anyway
632 }
633 const double value = myPixelMap.value( bandNo ).toDouble();
634 QgsDebugMsgLevel( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ), 2 );
635 values.append( value );
636 }
637 }
638
639 tableTransparency->insertRow( tableTransparency->rowCount() );
640
641 switch ( mCurrentMode )
642 {
643 case Mode::SingleBand:
644 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( SingleBandTableColumns::From ), values.value( 0 ) );
645 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( SingleBandTableColumns::To ), values.value( 0 ) );
646 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( SingleBandTableColumns::Opacity ), 100 );
647 break;
648 case Mode::RgbBands:
649 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Red ), values.value( 0 ) );
650 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Green ), values.value( 1 ) );
651 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Blue ), values.value( 2 ) );
652 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Tolerance ), 0 );
653 setTransparencyCell( tableTransparency->rowCount() - 1, static_cast<int>( RgbBandTableColumns::Opacity ), 100 );
654 break;
655 }
656 }
657
658 //tableTransparency->resizeColumnsToContents();
659 //tableTransparency->resizeRowsToContents();
660}
661
662void QgsRasterTransparencyWidget::populateTransparencyTable( QgsRasterRenderer *renderer )
663{
664 if ( !mRasterLayer )
665 {
666 return;
667 }
668
669 if ( !renderer )
670 {
671 return;
672 }
673
674 const int nBands = renderer->usesBands().size();
675 setupTransparencyTable( nBands );
676
677 const QgsRasterTransparency *rasterTransparency = renderer->rasterTransparency();
678 if ( !rasterTransparency )
679 {
680 return;
681 }
682
683 switch ( mCurrentMode )
684 {
685 case Mode::SingleBand:
686 {
687 QVector<QgsRasterTransparency::TransparentSingleValuePixel> pixelList = rasterTransparency->transparentSingleValuePixelList();
688 for ( int i = 0; i < pixelList.size(); ++i )
689 {
690 tableTransparency->insertRow( i );
691 setTransparencyCell( i, static_cast<int>( SingleBandTableColumns::From ), pixelList[i].min );
692 setTransparencyCell( i, static_cast<int>( SingleBandTableColumns::To ), pixelList[i].max );
693 setTransparencyCell( i, static_cast<int>( SingleBandTableColumns::Opacity ), 100 * ( 1 - pixelList[i].opacity ) );
694 // break synchronization only if values differ
695 if ( pixelList[i].min != pixelList[i].max )
696 {
697 setTransparencyToEdited( i );
698 }
699 }
700 break;
701 }
702 case Mode::RgbBands:
703 {
704 QVector<QgsRasterTransparency::TransparentThreeValuePixel> pixelList = rasterTransparency->transparentThreeValuePixelList();
705 for ( int i = 0; i < pixelList.size(); ++i )
706 {
707 tableTransparency->insertRow( i );
708 setTransparencyCell( i, static_cast<int>( RgbBandTableColumns::Red ), pixelList[i].red );
709 setTransparencyCell( i, static_cast<int>( RgbBandTableColumns::Green ), pixelList[i].green );
710 setTransparencyCell( i, static_cast<int>( RgbBandTableColumns::Blue ), pixelList[i].blue );
711 setTransparencyCell( i, static_cast<int>( RgbBandTableColumns::Opacity ), 100 * ( 1 - pixelList[i].opacity ) );
712 // while the API supports different tolerances for red/green/blue channels, we only expose a single value here
713 // If needed, we could expose the three separate tolerances in future... but be wary of UI bloat!
714 setTransparencyCell( i, static_cast<int>( RgbBandTableColumns::Tolerance ), !qgsDoubleNear( pixelList[i].fuzzyToleranceRed, 0 ) ? pixelList[i].fuzzyToleranceRed : 0 );
715 }
716 break;
717 }
718 }
719
720 tableTransparency->resizeColumnsToContents();
721 tableTransparency->resizeRowsToContents();
722}
723
724void QgsRasterTransparencyWidget::setupTransparencyTable( int nBands )
725{
726 tableTransparency->clear();
727 tableTransparency->setColumnCount( 0 );
728 tableTransparency->setRowCount( 0 );
729 mTransparencyToEdited.clear();
730
731 if ( nBands == 3 )
732 {
733 mCurrentMode = Mode::RgbBands;
734 tableTransparency->setColumnCount( static_cast<int>( RgbBandTableColumns::ColumnCount ) );
735 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Red ), new QTableWidgetItem( tr( "Red" ) ) );
736 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Green ), new QTableWidgetItem( tr( "Green" ) ) );
737 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Blue ), new QTableWidgetItem( tr( "Blue" ) ) );
738 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Tolerance ), new QTableWidgetItem( tr( "Tolerance" ) ) );
739 tableTransparency->setHorizontalHeaderItem( static_cast<int>( RgbBandTableColumns::Opacity ), new QTableWidgetItem( tr( "Percent Transparent" ) ) );
740 }
741 else //1 band
742 {
743 mCurrentMode = Mode::SingleBand;
744 tableTransparency->setColumnCount( static_cast<int>( SingleBandTableColumns::ColumnCount ) );
745 tableTransparency->setHorizontalHeaderItem( static_cast<int>( SingleBandTableColumns::From ), new QTableWidgetItem( tr( "From" ) ) );
746 tableTransparency->setHorizontalHeaderItem( static_cast<int>( SingleBandTableColumns::To ), new QTableWidgetItem( tr( "To" ) ) );
747 tableTransparency->setHorizontalHeaderItem( static_cast<int>( SingleBandTableColumns::Opacity ), new QTableWidgetItem( tr( "Percent Transparent" ) ) );
748 }
749}
750
751void QgsRasterTransparencyWidget::setTransparencyCell( int row, int column, double value )
752{
753 QgsDebugMsgLevel( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ), 2 );
754 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
755 if ( !provider )
756 return;
757
758 QLineEdit *lineEdit = new QLineEdit();
759 lineEdit->setFrame( false ); // frame looks bad in table
760 // Without margins row selection is not displayed (important for delete row)
761 lineEdit->setContentsMargins( 1, 1, 1, 1 );
762
763 if ( column == tableTransparency->columnCount() - 1 )
764 {
765 // transparency
766 // Who needs transparency as floating point?
767 lineEdit->setValidator( new QIntValidator( nullptr ) );
768 lineEdit->setText( QString::number( static_cast<int>( value ) ) );
769 connect( lineEdit, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
770 }
771 else
772 {
773 // value
774 QString valueString;
775 switch ( provider->sourceDataType( 1 ) )
776 {
779 lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
780 if ( !std::isnan( value ) )
781 {
782 const double v = QgsRasterBlock::printValue( value ).toDouble();
783 valueString = QLocale().toString( v );
784 }
785 break;
786 default:
787 lineEdit->setValidator( new QIntValidator( nullptr ) );
788 if ( !std::isnan( value ) )
789 {
790 valueString = QString::number( static_cast<int>( value ) );
791 }
792 break;
793 }
794 lineEdit->setText( valueString );
795 connect( lineEdit, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
796 }
797 tableTransparency->setCellWidget( row, column, lineEdit );
798 adjustTransparencyCellWidth( row, column );
799
800 if ( mCurrentMode == Mode::SingleBand && ( column == static_cast<int>( SingleBandTableColumns::From ) || column == static_cast<int>( SingleBandTableColumns::To ) ) )
801 {
802 connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterTransparencyWidget::transparencyCellTextEdited );
803 }
804 //tableTransparency->resizeColumnsToContents();
805 emit widgetChanged();
806}
807
808void QgsRasterTransparencyWidget::adjustTransparencyCellWidth( int row, int column )
809{
810 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
811 if ( !lineEdit )
812 return;
813
814 int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
815 width = std::max( width, tableTransparency->columnWidth( column ) );
816
817 lineEdit->setFixedWidth( width );
818}
819
820void QgsRasterTransparencyWidget::setTransparencyToEdited( int row )
821{
822 if ( row >= mTransparencyToEdited.size() )
823 {
824 mTransparencyToEdited.resize( row + 1 );
825 }
826 mTransparencyToEdited[row] = true;
827}
828
829double QgsRasterTransparencyWidget::transparencyCellValue( int row, int column )
830{
831 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
832 if ( !lineEdit || lineEdit->text().isEmpty() )
833 {
834 return std::numeric_limits<double>::quiet_NaN();
835 }
836 return QgsDoubleValidator::toDouble( lineEdit->text() );
837}
838
840{
841 return mPixelSelectorTool;
842}
@ 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:6024
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5928
#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.