QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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"
34#include "qgsdoublevalidator.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 No Data 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;
88
89 if ( QgsMapCanvas *canvas = mContext.mapCanvas() )
90 {
91 expContext << QgsExpressionContextUtils::mapSettingsScope( canvas->mapSettings() )
92 << new QgsExpressionContextScope( canvas->expressionContextScope() );
93 if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( canvas->temporalController() ) )
94 {
95 expContext << generator->createExpressionContextScope();
96 }
97 }
98 else
99 {
101 }
102
103 if ( mRasterLayer )
104 expContext << QgsExpressionContextUtils::layerScope( mRasterLayer );
105
106 // additional scopes
107 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
108 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
109 {
110 expContext.appendScope( new QgsExpressionContextScope( scope ) );
111 }
112
113 return expContext;
114}
115
117{
118 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
119 QgsRasterRenderer *renderer = mRasterLayer->renderer();
120 if ( provider )
121 {
122 if ( provider->dataType( 1 ) == Qgis::DataType::ARGB32
124 {
125 gboxNoDataValue->setEnabled( false );
126 gboxCustomTransparency->setEnabled( false );
127 }
128
129 cboxTransparencyBand->setShowNotSetOption( true, tr( "None" ) );
130 cboxTransparencyBand->setLayer( mRasterLayer );
131 }
132
133 if ( mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) )
134 {
135 lblSrcNoDataValue->setText( QgsRasterBlock::printValue( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) ) );
136 }
137 else
138 {
139 lblSrcNoDataValue->setText( tr( "not defined" ) );
140 }
141
142 mSrcNoDataValueCheckBox->setChecked( mRasterLayer->dataProvider()->useSourceNoDataValue( 1 ) );
143
144 const bool enableSrcNoData = mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) && !std::isnan( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) );
145
146 mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData );
147 lblSrcNoDataValue->setEnabled( enableSrcNoData );
148
149 if ( renderer )
150 {
151 if ( renderer->nodataColor().isValid() )
152 mNodataColorButton->setColor( renderer->nodataColor() );
153 else
154 mNodataColorButton->setToNull();
155
156 mOpacityWidget->setOpacity( renderer->opacity() );
157
158 cboxTransparencyBand->setBand( renderer->alphaBand() );
159 }
160
161 const QgsRasterRangeList noDataRangeList = mRasterLayer->dataProvider()->userNoDataValues( 1 );
162 QgsDebugMsgLevel( QStringLiteral( "noDataRangeList.size = %1" ).arg( noDataRangeList.size() ), 2 );
163 if ( !noDataRangeList.isEmpty() )
164 {
165 const double v = QgsRasterBlock::printValue( noDataRangeList.value( 0 ).min() ).toDouble();
166 leNoDataValue->setText( QLocale().toString( v ) );
167 }
168 else
169 {
170 leNoDataValue->setText( QString() );
171 }
172
175
176 populateTransparencyTable( mRasterLayer->renderer() );
177}
178
179void QgsRasterTransparencyWidget::transparencyCellTextEdited( const QString &text )
180{
181 Q_UNUSED( text )
182 QgsDebugMsgLevel( QStringLiteral( "text = %1" ).arg( text ), 2 );
183 QgsRasterRenderer *renderer = mRasterLayer->renderer();
184 if ( !renderer )
185 {
186 return;
187 }
188 const int nBands = renderer->usesBands().size();
189 if ( nBands == 1 )
190 {
191 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender() );
192 if ( !lineEdit ) 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 ) break;
207 }
208 QgsDebugMsgLevel( QStringLiteral( "row = %1 column =%2" ).arg( row ).arg( column ), 2 );
209
210 if ( column == 0 )
211 {
212 QLineEdit *toLineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, 1 ) );
213 if ( !toLineEdit ) return;
214 const bool toChanged = mTransparencyToEdited.value( row );
215 QgsDebugMsgLevel( QStringLiteral( "toChanged = %1" ).arg( toChanged ), 2 );
216 if ( !toChanged )
217 {
218 toLineEdit->setText( lineEdit->text() );
219 }
220 }
221 else if ( column == 1 )
222 {
223 setTransparencyToEdited( row );
224 }
225 }
226 emit widgetChanged();
227}
228
229void QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked()
230{
231 if ( mMapCanvas && mPixelSelectorTool )
232 {
233 mMapCanvas->setMapTool( mPixelSelectorTool );
234 }
235}
236
237void QgsRasterTransparencyWidget::pbnAddValuesManually_clicked()
238{
239 QgsRasterRenderer *renderer = mRasterLayer->renderer();
240 if ( !renderer )
241 {
242 return;
243 }
244
245 tableTransparency->insertRow( tableTransparency->rowCount() );
246
247 int n = renderer->usesBands().size();
248 if ( n == 1 ) n++;
249
250 for ( int i = 0; i < n; i++ )
251 {
252 setTransparencyCell( tableTransparency->rowCount() - 1, i, std::numeric_limits<double>::quiet_NaN() );
253 }
254
255 setTransparencyCell( tableTransparency->rowCount() - 1, n, 100 );
256
257 //tableTransparency->resizeColumnsToContents();
258 //tableTransparency->resizeRowsToContents();
259}
260
261void QgsRasterTransparencyWidget::pbnDefaultValues_clicked()
262{
263 QgsRasterRenderer *r = mRasterLayer->renderer();
264 if ( !r )
265 {
266 return;
267 }
268
269 const int nBands = r->usesBands().size();
270
271 setupTransparencyTable( nBands );
272
273 //tableTransparency->resizeColumnsToContents(); // works only with values
274 //tableTransparency->resizeRowsToContents();
275
276}
277
278void QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked()
279{
280 const QgsSettings myQSettings;
281 const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
282 QString myFileName = QFileDialog::getSaveFileName( this, tr( "Save Pixel Values as File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
283 if ( !myFileName.isEmpty() )
284 {
285 if ( !myFileName.endsWith( QLatin1String( ".txt" ), Qt::CaseInsensitive ) )
286 {
287 myFileName = myFileName + ".txt";
288 }
289
290 QFile myOutputFile( myFileName );
291 if ( myOutputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
292 {
293 QTextStream myOutputStream( &myOutputFile );
294 myOutputStream << "# " << tr( "QGIS Generated Transparent Pixel Value Export File" ) << '\n';
295 if ( rasterIsMultiBandColor() )
296 {
297 myOutputStream << "#\n#\n# " << tr( "Red" ) << "\t" << tr( "Green" ) << "\t" << tr( "Blue" ) << "\t" << tr( "Percent Transparent" );
298 for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
299 {
300 myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, 0 ) ) << "\t"
301 << QString::number( transparencyCellValue( myTableRunner, 1 ) ) << "\t"
302 << QString::number( transparencyCellValue( myTableRunner, 2 ) ) << "\t"
303 << QString::number( transparencyCellValue( myTableRunner, 3 ) );
304 }
305 }
306 else
307 {
308 myOutputStream << "#\n#\n# " << tr( "Value" ) << "\t" << tr( "Percent Transparent" );
309
310 for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
311 {
312 myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, 0 ) ) << "\t"
313 << QString::number( transparencyCellValue( myTableRunner, 1 ) ) << "\t"
314 << QString::number( transparencyCellValue( myTableRunner, 2 ) );
315 }
316 }
317 }
318 else
319 {
320 QMessageBox::warning( this, tr( "Save Pixel Values as File" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
321 }
322 }
323}
324
325void QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked()
326{
327 int myLineCounter = 0;
328 bool myImportError = false;
329 QString myBadLines;
330 const QgsSettings myQSettings;
331 const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
332 const QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load Pixel Values from File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
333 QFile myInputFile( myFileName );
334 if ( myInputFile.open( QFile::ReadOnly ) )
335 {
336 QTextStream myInputStream( &myInputFile );
337 QString myInputLine;
338 if ( rasterIsMultiBandColor() )
339 {
340 for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
341 {
342 tableTransparency->removeRow( myTableRunner );
343 }
344
345 while ( !myInputStream.atEnd() )
346 {
347 myLineCounter++;
348 myInputLine = myInputStream.readLine();
349 if ( !myInputLine.isEmpty() )
350 {
351 if ( !myInputLine.simplified().startsWith( '#' ) )
352 {
353#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
354 QStringList myTokens = myInputLine.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
355#else
356 QStringList myTokens = myInputLine.split( QRegularExpression( "\\s+" ), Qt::SkipEmptyParts );
357#endif
358 if ( myTokens.count() != 4 )
359 {
360 myImportError = true;
361 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
362 }
363 else
364 {
365 tableTransparency->insertRow( tableTransparency->rowCount() );
366 for ( int col = 0; col < 4; col++ )
367 {
368 setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
369 }
370 }
371 }
372 }
373 }
374 }
375 else
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#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
391 QStringList myTokens = myInputLine.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
392#else
393 QStringList myTokens = myInputLine.split( QRegularExpression( "\\s+" ), Qt::SkipEmptyParts );
394#endif
395 if ( myTokens.count() != 3 && myTokens.count() != 2 ) // 2 for QGIS < 1.9 compatibility
396 {
397 myImportError = true;
398 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
399 }
400 else
401 {
402 if ( myTokens.count() == 2 )
403 {
404 myTokens.insert( 1, myTokens[0] ); // add 'to' value, QGIS < 1.9 compatibility
405 }
406 tableTransparency->insertRow( tableTransparency->rowCount() );
407 for ( int col = 0; col < 3; col++ )
408 {
409 setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
410 }
411 }
412 }
413 }
414 }
415 }
416
417 if ( myImportError )
418 {
419 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "The following lines contained errors\n\n%1" ).arg( myBadLines ) );
420 }
421 }
422 else if ( !myFileName.isEmpty() )
423 {
424 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "Read access denied. Adjust the file permissions and try again.\n\n" ) );
425 }
426 //tableTransparency->resizeColumnsToContents();
427 //tableTransparency->resizeRowsToContents();
428 emit widgetChanged();
429}
430
431void QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked()
432{
433 if ( 0 < tableTransparency->rowCount() )
434 {
435 tableTransparency->removeRow( tableTransparency->currentRow() );
436 }
437 emit widgetChanged();
438}
439
440bool QgsRasterTransparencyWidget::rasterIsMultiBandColor()
441{
442 return mRasterLayer && nullptr != dynamic_cast<QgsMultiBandColorRenderer *>( mRasterLayer->renderer() );
443}
444
446{
447 //set NoDataValue
448 QgsRasterRangeList myNoDataRangeList;
449 if ( "" != leNoDataValue->text() )
450 {
451 bool myDoubleOk = false;
452 const double myNoDataValue = QgsDoubleValidator::toDouble( leNoDataValue->text(), &myDoubleOk );
453 if ( myDoubleOk )
454 {
455 const QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
456 myNoDataRangeList << myNoDataRange;
457 }
458 }
459 for ( int bandNo = 1; bandNo <= mRasterLayer->dataProvider()->bandCount(); bandNo++ )
460 {
461 mRasterLayer->dataProvider()->setUserNoDataValue( bandNo, myNoDataRangeList );
462 mRasterLayer->dataProvider()->setUseSourceNoDataValue( bandNo, mSrcNoDataValueCheckBox->isChecked() );
463 }
464
465 //transparency settings
466 QgsRasterRenderer *rasterRenderer = mRasterLayer->renderer();
467 if ( rasterRenderer )
468 {
469 rasterRenderer->setAlphaBand( cboxTransparencyBand->currentBand() );
470 rasterRenderer->setNodataColor( mNodataColorButton->color() );
471
472 //Walk through each row in table and test value. If not valid set to 0.0 and continue building transparency list
473 QgsRasterTransparency *rasterTransparency = new QgsRasterTransparency();
474 if ( tableTransparency->columnCount() == 4 )
475 {
477 QList<QgsRasterTransparency::TransparentThreeValuePixel> myTransparentThreeValuePixelList;
478 myTransparentThreeValuePixelList.reserve( tableTransparency->rowCount() );
479 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
480 {
481 myTransparentPixel.red = transparencyCellValue( myListRunner, 0 );
482 myTransparentPixel.green = transparencyCellValue( myListRunner, 1 );
483 myTransparentPixel.blue = transparencyCellValue( myListRunner, 2 );
484 myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 3 );
485 myTransparentThreeValuePixelList.append( myTransparentPixel );
486 }
487 rasterTransparency->setTransparentThreeValuePixelList( myTransparentThreeValuePixelList );
488 }
489 else if ( tableTransparency->columnCount() == 3 )
490 {
492 QList<QgsRasterTransparency::TransparentSingleValuePixel> myTransparentSingleValuePixelList;
493 myTransparentSingleValuePixelList.reserve( tableTransparency->rowCount() );
494 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
495 {
496 myTransparentPixel.min = transparencyCellValue( myListRunner, 0 );
497 myTransparentPixel.max = transparencyCellValue( myListRunner, 1 );
498 myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 2 );
499
500 myTransparentSingleValuePixelList.append( myTransparentPixel );
501 }
502 rasterTransparency->setTransparentSingleValuePixelList( myTransparentSingleValuePixelList );
503 }
504
505 rasterRenderer->setRasterTransparency( rasterTransparency );
506
507 //set global transparency
508 rasterRenderer->setOpacity( mOpacityWidget->opacity() );
509 }
510
512}
513
515{
516 button->blockSignals( true );
518 connect( button, &QgsPropertyOverrideButton::changed, this, &QgsRasterTransparencyWidget::updateProperty );
520 button->blockSignals( false );
521}
522
524{
525 const auto propertyOverrideButtons { findChildren< QgsPropertyOverrideButton * >() };
526 for ( QgsPropertyOverrideButton *button : propertyOverrideButtons )
527 {
528 updateDataDefinedButton( button );
529 }
530}
531
533{
534 if ( !button )
535 return;
536
537 if ( button->propertyKey() < 0 )
538 return;
539
540 const QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
541 whileBlocking( button )->setToProperty( mPropertyCollection.property( key ) );
542}
543
544void QgsRasterTransparencyWidget::updateProperty()
545{
546 QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
547 const QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
549 emit widgetChanged();
550}
551
552void QgsRasterTransparencyWidget::pixelSelected( const QgsPointXY &canvasPoint )
553{
554 QgsRasterRenderer *renderer = mRasterLayer->renderer();
555 if ( !renderer )
556 {
557 return;
558 }
559
560 //Get the pixel values and add a new entry to the transparency table
561 if ( mMapCanvas && mPixelSelectorTool )
562 {
563 mMapCanvas->unsetMapTool( mPixelSelectorTool );
564
565 const QgsMapSettings &ms = mMapCanvas->mapSettings();
566 const QgsPointXY myPoint = ms.mapToLayerCoordinates( mRasterLayer, canvasPoint );
567
568 const QgsRectangle myExtent = ms.mapToLayerCoordinates( mRasterLayer, mMapCanvas->extent() );
569 const double mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
570 const int myWidth = mMapCanvas->extent().width() / mapUnitsPerPixel;
571 const int myHeight = mMapCanvas->extent().height() / mapUnitsPerPixel;
572
573 const QMap<int, QVariant> myPixelMap = mRasterLayer->dataProvider()->identify( myPoint, Qgis::RasterIdentifyFormat::Value, myExtent, myWidth, myHeight ).results();
574
575 const QList<int> bands = renderer->usesBands();
576
577 QList<double> values;
578 for ( int i = 0; i < bands.size(); ++i )
579 {
580 const int bandNo = bands.value( i );
581 if ( myPixelMap.count( bandNo ) == 1 )
582 {
583 if ( QgsVariantUtils::isNull( myPixelMap.value( bandNo ) ) )
584 {
585 return; // Don't add nodata, transparent anyway
586 }
587 const double value = myPixelMap.value( bandNo ).toDouble();
588 QgsDebugMsgLevel( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ), 2 );
589 values.append( value );
590 }
591 }
592 if ( bands.size() == 1 )
593 {
594 // Set 'to'
595 values.insert( 1, values.value( 0 ) );
596 }
597 tableTransparency->insertRow( tableTransparency->rowCount() );
598 for ( int i = 0; i < values.size(); i++ )
599 {
600 setTransparencyCell( tableTransparency->rowCount() - 1, i, values.value( i ) );
601 }
602 setTransparencyCell( tableTransparency->rowCount() - 1, tableTransparency->columnCount() - 1, 100 );
603 }
604
605 //tableTransparency->resizeColumnsToContents();
606 //tableTransparency->resizeRowsToContents();
607}
608
609void QgsRasterTransparencyWidget::populateTransparencyTable( QgsRasterRenderer *renderer )
610{
611 if ( !mRasterLayer )
612 {
613 return;
614 }
615
616 if ( !renderer )
617 {
618 return;
619 }
620
621 const int nBands = renderer->usesBands().size();
622 setupTransparencyTable( nBands );
623
624 const QgsRasterTransparency *rasterTransparency = renderer->rasterTransparency();
625 if ( !rasterTransparency )
626 {
627 return;
628 }
629
630 if ( nBands == 1 )
631 {
632 QList<QgsRasterTransparency::TransparentSingleValuePixel> pixelList = rasterTransparency->transparentSingleValuePixelList();
633 for ( int i = 0; i < pixelList.size(); ++i )
634 {
635 tableTransparency->insertRow( i );
636 setTransparencyCell( i, 0, pixelList[i].min );
637 setTransparencyCell( i, 1, pixelList[i].max );
638 setTransparencyCell( i, 2, pixelList[i].percentTransparent );
639 // break synchronization only if values differ
640 if ( pixelList[i].min != pixelList[i].max )
641 {
642 setTransparencyToEdited( i );
643 }
644 }
645 }
646 else if ( nBands == 3 )
647 {
648 QList<QgsRasterTransparency::TransparentThreeValuePixel> pixelList = rasterTransparency->transparentThreeValuePixelList();
649 for ( int i = 0; i < pixelList.size(); ++i )
650 {
651 tableTransparency->insertRow( i );
652 setTransparencyCell( i, 0, pixelList[i].red );
653 setTransparencyCell( i, 1, pixelList[i].green );
654 setTransparencyCell( i, 2, pixelList[i].blue );
655 setTransparencyCell( i, 3, pixelList[i].percentTransparent );
656 }
657 }
658
659 tableTransparency->resizeColumnsToContents();
660 tableTransparency->resizeRowsToContents();
661
662}
663
664void QgsRasterTransparencyWidget::setupTransparencyTable( int nBands )
665{
666 tableTransparency->clear();
667 tableTransparency->setColumnCount( 0 );
668 tableTransparency->setRowCount( 0 );
669 mTransparencyToEdited.clear();
670
671 if ( nBands == 3 )
672 {
673 tableTransparency->setColumnCount( 4 );
674 tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Red" ) ) );
675 tableTransparency->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "Green" ) ) );
676 tableTransparency->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Blue" ) ) );
677 tableTransparency->setHorizontalHeaderItem( 3, new QTableWidgetItem( tr( "Percent Transparent" ) ) );
678 }
679 else //1 band
680 {
681 tableTransparency->setColumnCount( 3 );
682// Is it important to distinguish the header? It becomes difficult with range.
683#if 0
684 if ( QgsRasterLayer::PalettedColor != mRasterLayer->drawingStyle() &&
685 QgsRasterLayer::PalettedSingleBandGray != mRasterLayer->drawingStyle() &&
686 QgsRasterLayer::PalettedSingleBandPseudoColor != mRasterLayer->drawingStyle() &&
687 QgsRasterLayer::PalettedMultiBandColor != mRasterLayer->drawingStyle() )
688 {
689 tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Gray" ) ) );
690 }
691 else
692 {
693 tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Indexed Value" ) ) );
694 }
695#endif
696 tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "From" ) ) );
697 tableTransparency->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "To" ) ) );
698 tableTransparency->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Percent Transparent" ) ) );
699 }
700}
701
702void QgsRasterTransparencyWidget::setTransparencyCell( int row, int column, double value )
703{
704 QgsDebugMsgLevel( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ), 2 );
705 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
706 if ( !provider ) return;
707
708 QgsRasterRenderer *renderer = mRasterLayer->renderer();
709 if ( !renderer ) return;
710 const int nBands = renderer->usesBands().size();
711
712 QLineEdit *lineEdit = new QLineEdit();
713 lineEdit->setFrame( false ); // frame looks bad in table
714 // Without margins row selection is not displayed (important for delete row)
715 lineEdit->setContentsMargins( 1, 1, 1, 1 );
716
717 if ( column == tableTransparency->columnCount() - 1 )
718 {
719 // transparency
720 // Who needs transparency as floating point?
721 lineEdit->setValidator( new QIntValidator( nullptr ) );
722 lineEdit->setText( QString::number( static_cast<int>( value ) ) );
723 }
724 else
725 {
726 // value
727 QString valueString;
728 switch ( provider->sourceDataType( 1 ) )
729 {
732 lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
733 if ( !std::isnan( value ) )
734 {
735 const double v = QgsRasterBlock::printValue( value ).toDouble();
736 valueString = QLocale().toString( v );
737 }
738 break;
739 default:
740 lineEdit->setValidator( new QIntValidator( nullptr ) );
741 if ( !std::isnan( value ) )
742 {
743 valueString = QString::number( static_cast<int>( value ) );
744 }
745 break;
746 }
747 lineEdit->setText( valueString );
748 connect( lineEdit, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
749 }
750 tableTransparency->setCellWidget( row, column, lineEdit );
751 adjustTransparencyCellWidth( row, column );
752
753 if ( nBands == 1 && ( column == 0 || column == 1 ) )
754 {
755 connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterTransparencyWidget::transparencyCellTextEdited );
756 }
757 //tableTransparency->resizeColumnsToContents();
758 emit widgetChanged();
759}
760
761void QgsRasterTransparencyWidget::adjustTransparencyCellWidth( int row, int column )
762{
763 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
764 if ( !lineEdit ) return;
765
766 int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
767 width = std::max( width, tableTransparency->columnWidth( column ) );
768
769 lineEdit->setFixedWidth( width );
770}
771
772void QgsRasterTransparencyWidget::setTransparencyToEdited( int row )
773{
774 if ( row >= mTransparencyToEdited.size() )
775 {
776 mTransparencyToEdited.resize( row + 1 );
777 }
778 mTransparencyToEdited[row] = true;
779}
780
781double QgsRasterTransparencyWidget::transparencyCellValue( int row, int column )
782{
783 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
784 if ( !lineEdit || lineEdit->text().isEmpty() )
785 {
786 return std::numeric_limits<double>::quiet_NaN();
787 }
788 return QgsDoubleValidator::toDouble( lineEdit->text() );
789
790}
791
793{
794 return mPixelSelectorTool;
795}
@ 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)
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.
Abstract interface for generating an expression context scope.
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.
Definition: qgsmapcanvas.h:90
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
Renderer for multiband images with the color components.
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:59
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
A button for controlling property overrides which may apply to a widget.
QgsProperty toProperty() const
Returns a QgsProperty object encapsulating the current state of the widget.
void changed()
Emitted when property definition changes.
void init(int propertyKey, const QgsProperty &property, const QgsPropertiesDefinition &definitions, const QgsVectorLayer *layer=nullptr, bool auxiliaryStorageEnabled=false)
Initialize a newly constructed property button (useful if button was included in a UI layout).
void registerExpressionContextGenerator(QgsExpressionContextGenerator *generator)
Register an expression context generator class that will be used to retrieve an expression context fo...
int propertyKey() const
Returns the property key linked to the button.
void bandChanged(int band)
Emitted when the currently selected band changes.
static QString printValue(double value)
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.
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.
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in raster pipes.
Property
Data definable properties.
Definition: qgsrasterpipe.h:58
@ RendererOpacity
Raster renderer global opacity.
Definition: qgsrasterpipe.h:59
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 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...
Defines the list of pixel values to be considered as transparent or semi transparent when rendering r...
void setTransparentSingleValuePixelList(const QList< QgsRasterTransparency::TransparentSingleValuePixel > &newList)
Sets the transparent single value pixel list, replacing the whole existing list.
QList< QgsRasterTransparency::TransparentThreeValuePixel > transparentThreeValuePixelList() const
Returns the transparent three value pixel list.
void setTransparentThreeValuePixelList(const QList< QgsRasterTransparency::TransparentThreeValuePixel > &newList)
Sets the transparent three value pixel list, replacing the whole existing list.
QList< QgsRasterTransparency::TransparentSingleValuePixel > transparentSingleValuePixelList() const
Returns the transparent single value pixel list.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
This class is a composition of two QSettings instances:
Definition: qgssettings.h:63
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)
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
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:3435
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QList< QgsRasterRange > QgsRasterRangeList