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"
24 #include "qgsrastertransparencywidget.h"
25 #include "qgsrasterlayer.h"
26 #include "qgsraster.h"
27 #include "qgsrasterlayerrenderer.h"
28 #include "qgsrasterdataprovider.h"
29 #include "qgsrastertransparency.h"
30 #include "qgsmaptoolemitpoint.h"
31 #include "qgsmapsettings.h"
32 #include "qgsrectangle.h"
33 #include "qgsmapcanvas.h"
34 #include "qgsrasteridentifyresult.h"
35 #include "qgsmultibandcolorrenderer.h"
36 #include "qgsdoublevalidator.h"
37 #include "qgsexpressioncontextutils.h"
38 #include "qgstemporalcontroller.h"
39
QgsRasterTransparencyWidget(QgsRasterLayer * layer,QgsMapCanvas * canvas,QWidget * parent)40 QgsRasterTransparencyWidget::QgsRasterTransparencyWidget( QgsRasterLayer *layer, QgsMapCanvas *canvas, QWidget *parent )
41 : QgsMapLayerConfigWidget( layer, canvas, parent )
42 , TRSTRING_NOT_SET( tr( "Not Set" ) )
43 , mRasterLayer( layer )
44 , mMapCanvas( canvas )
45 {
46 setupUi( this );
47 connect( pbnAddValuesFromDisplay, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked );
48 connect( pbnAddValuesManually, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnAddValuesManually_clicked );
49 connect( pbnDefaultValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnDefaultValues_clicked );
50 connect( pbnExportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked );
51 connect( pbnImportTransparentPixelValues, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked );
52 connect( pbnRemoveSelectedRow, &QToolButton::clicked, this, &QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked );
53
54 mNodataColorButton->setShowNoColor( true );
55 mNodataColorButton->setColorDialogTitle( tr( "Select No Data Color" ) );
56 syncToLayer();
57
58 connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsPanelWidget::widgetChanged );
59 connect( cboxTransparencyBand, &QgsRasterBandComboBox::bandChanged, this, &QgsPanelWidget::widgetChanged );
60 connect( mSrcNoDataValueCheckBox, &QCheckBox::stateChanged, this, &QgsPanelWidget::widgetChanged );
61 connect( leNoDataValue, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
62 leNoDataValue->setValidator( new QgsDoubleValidator( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), this ) );
63 connect( mNodataColorButton, &QgsColorButton::colorChanged, this, &QgsPanelWidget::widgetChanged );
64
65 mPixelSelectorTool = nullptr;
66 if ( mMapCanvas )
67 {
68 mPixelSelectorTool = new QgsMapToolEmitPoint( mMapCanvas );
69 connect( mPixelSelectorTool, &QgsMapToolEmitPoint::canvasClicked, this, &QgsRasterTransparencyWidget::pixelSelected );
70 }
71 else
72 {
73 pbnAddValuesFromDisplay->setEnabled( false );
74 }
75
76 initializeDataDefinedButton( mOpacityDDBtn, QgsRasterPipe::RendererOpacity );
77 }
78
setContext(const QgsSymbolWidgetContext & context)79 void QgsRasterTransparencyWidget::setContext( const QgsSymbolWidgetContext &context )
80 {
81 mContext = context;
82 }
83
createExpressionContext() const84 QgsExpressionContext QgsRasterTransparencyWidget::createExpressionContext() const
85 {
86 QgsExpressionContext expContext;
87 expContext << QgsExpressionContextUtils::globalScope()
88 << QgsExpressionContextUtils::projectScope( QgsProject::instance() )
89 << QgsExpressionContextUtils::atlasScope( nullptr );
90
91 if ( QgsMapCanvas *canvas = mContext.mapCanvas() )
92 {
93 expContext << QgsExpressionContextUtils::mapSettingsScope( canvas->mapSettings() )
94 << new QgsExpressionContextScope( canvas->expressionContextScope() );
95 if ( const QgsExpressionContextScopeGenerator *generator = dynamic_cast< const QgsExpressionContextScopeGenerator * >( canvas->temporalController() ) )
96 {
97 expContext << generator->createExpressionContextScope();
98 }
99 }
100 else
101 {
102 expContext << QgsExpressionContextUtils::mapSettingsScope( QgsMapSettings() );
103 }
104
105 if ( mRasterLayer )
106 expContext << QgsExpressionContextUtils::layerScope( mRasterLayer );
107
108 // additional scopes
109 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
110 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
111 {
112 expContext.appendScope( new QgsExpressionContextScope( scope ) );
113 }
114
115 return expContext;
116 }
117
syncToLayer()118 void QgsRasterTransparencyWidget::syncToLayer()
119 {
120 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
121 QgsRasterRenderer *renderer = mRasterLayer->renderer();
122 if ( provider )
123 {
124 if ( provider->dataType( 1 ) == Qgis::DataType::ARGB32
125 || provider->dataType( 1 ) == Qgis::DataType::ARGB32_Premultiplied )
126 {
127 gboxNoDataValue->setEnabled( false );
128 gboxCustomTransparency->setEnabled( false );
129 }
130
131 cboxTransparencyBand->setShowNotSetOption( true, tr( "None" ) );
132 cboxTransparencyBand->setLayer( mRasterLayer );
133 }
134
135 if ( mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) )
136 {
137 lblSrcNoDataValue->setText( QgsRasterBlock::printValue( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) ) );
138 }
139 else
140 {
141 lblSrcNoDataValue->setText( tr( "not defined" ) );
142 }
143
144 mSrcNoDataValueCheckBox->setChecked( mRasterLayer->dataProvider()->useSourceNoDataValue( 1 ) );
145
146 const bool enableSrcNoData = mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) && !std::isnan( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) );
147
148 mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData );
149 lblSrcNoDataValue->setEnabled( enableSrcNoData );
150
151 if ( renderer )
152 {
153 if ( renderer->nodataColor().isValid() )
154 mNodataColorButton->setColor( renderer->nodataColor() );
155 else
156 mNodataColorButton->setToNull();
157
158 mOpacityWidget->setOpacity( renderer->opacity() );
159
160 cboxTransparencyBand->setBand( renderer->alphaBand() );
161 }
162
163 const QgsRasterRangeList noDataRangeList = mRasterLayer->dataProvider()->userNoDataValues( 1 );
164 QgsDebugMsg( QStringLiteral( "noDataRangeList.size = %1" ).arg( noDataRangeList.size() ) );
165 if ( !noDataRangeList.isEmpty() )
166 {
167 const double v = QgsRasterBlock::printValue( noDataRangeList.value( 0 ).min() ).toDouble();
168 leNoDataValue->setText( QLocale().toString( v ) );
169 }
170 else
171 {
172 leNoDataValue->setText( QString() );
173 }
174
175 mPropertyCollection = mRasterLayer->pipe()->dataDefinedProperties();
176 updateDataDefinedButtons();
177
178 populateTransparencyTable( mRasterLayer->renderer() );
179 }
180
transparencyCellTextEdited(const QString & text)181 void QgsRasterTransparencyWidget::transparencyCellTextEdited( const QString &text )
182 {
183 Q_UNUSED( text )
184 QgsDebugMsg( QStringLiteral( "text = %1" ).arg( text ) );
185 QgsRasterRenderer *renderer = mRasterLayer->renderer();
186 if ( !renderer )
187 {
188 return;
189 }
190 const int nBands = renderer->usesBands().size();
191 if ( nBands == 1 )
192 {
193 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender() );
194 if ( !lineEdit ) return;
195 int row = -1;
196 int column = -1;
197 for ( int r = 0; r < tableTransparency->rowCount(); r++ )
198 {
199 for ( int c = 0; c < tableTransparency->columnCount(); c++ )
200 {
201 if ( tableTransparency->cellWidget( r, c ) == sender() )
202 {
203 row = r;
204 column = c;
205 break;
206 }
207 }
208 if ( row != -1 ) break;
209 }
210 QgsDebugMsg( QStringLiteral( "row = %1 column =%2" ).arg( row ).arg( column ) );
211
212 if ( column == 0 )
213 {
214 QLineEdit *toLineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, 1 ) );
215 if ( !toLineEdit ) return;
216 const bool toChanged = mTransparencyToEdited.value( row );
217 QgsDebugMsg( QStringLiteral( "toChanged = %1" ).arg( toChanged ) );
218 if ( !toChanged )
219 {
220 toLineEdit->setText( lineEdit->text() );
221 }
222 }
223 else if ( column == 1 )
224 {
225 setTransparencyToEdited( row );
226 }
227 }
228 emit widgetChanged();
229 }
230
pbnAddValuesFromDisplay_clicked()231 void QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked()
232 {
233 if ( mMapCanvas && mPixelSelectorTool )
234 {
235 mMapCanvas->setMapTool( mPixelSelectorTool );
236 }
237 }
238
pbnAddValuesManually_clicked()239 void QgsRasterTransparencyWidget::pbnAddValuesManually_clicked()
240 {
241 QgsRasterRenderer *renderer = mRasterLayer->renderer();
242 if ( !renderer )
243 {
244 return;
245 }
246
247 tableTransparency->insertRow( tableTransparency->rowCount() );
248
249 int n = renderer->usesBands().size();
250 if ( n == 1 ) n++;
251
252 for ( int i = 0; i < n; i++ )
253 {
254 setTransparencyCell( tableTransparency->rowCount() - 1, i, std::numeric_limits<double>::quiet_NaN() );
255 }
256
257 setTransparencyCell( tableTransparency->rowCount() - 1, n, 100 );
258
259 //tableTransparency->resizeColumnsToContents();
260 //tableTransparency->resizeRowsToContents();
261 }
262
pbnDefaultValues_clicked()263 void QgsRasterTransparencyWidget::pbnDefaultValues_clicked()
264 {
265 QgsRasterRenderer *r = mRasterLayer->renderer();
266 if ( !r )
267 {
268 return;
269 }
270
271 const int nBands = r->usesBands().size();
272
273 setupTransparencyTable( nBands );
274
275 //tableTransparency->resizeColumnsToContents(); // works only with values
276 //tableTransparency->resizeRowsToContents();
277
278 }
279
pbnExportTransparentPixelValues_clicked()280 void QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked()
281 {
282 const QgsSettings myQSettings;
283 const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
284 QString myFileName = QFileDialog::getSaveFileName( this, tr( "Save Pixel Values as File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
285 if ( !myFileName.isEmpty() )
286 {
287 if ( !myFileName.endsWith( QLatin1String( ".txt" ), Qt::CaseInsensitive ) )
288 {
289 myFileName = myFileName + ".txt";
290 }
291
292 QFile myOutputFile( myFileName );
293 if ( myOutputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
294 {
295 QTextStream myOutputStream( &myOutputFile );
296 myOutputStream << "# " << tr( "QGIS Generated Transparent Pixel Value Export File" ) << '\n';
297 if ( rasterIsMultiBandColor() )
298 {
299 myOutputStream << "#\n#\n# " << tr( "Red" ) << "\t" << tr( "Green" ) << "\t" << tr( "Blue" ) << "\t" << tr( "Percent Transparent" );
300 for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
301 {
302 myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, 0 ) ) << "\t"
303 << QString::number( transparencyCellValue( myTableRunner, 1 ) ) << "\t"
304 << QString::number( transparencyCellValue( myTableRunner, 2 ) ) << "\t"
305 << QString::number( transparencyCellValue( myTableRunner, 3 ) );
306 }
307 }
308 else
309 {
310 myOutputStream << "#\n#\n# " << tr( "Value" ) << "\t" << tr( "Percent Transparent" );
311
312 for ( int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
313 {
314 myOutputStream << '\n' << QString::number( transparencyCellValue( myTableRunner, 0 ) ) << "\t"
315 << QString::number( transparencyCellValue( myTableRunner, 1 ) ) << "\t"
316 << QString::number( transparencyCellValue( myTableRunner, 2 ) );
317 }
318 }
319 }
320 else
321 {
322 QMessageBox::warning( this, tr( "Save Pixel Values as File" ), tr( "Write access denied. Adjust the file permissions and try again.\n\n" ) );
323 }
324 }
325 }
326
pbnImportTransparentPixelValues_clicked()327 void QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked()
328 {
329 int myLineCounter = 0;
330 bool myImportError = false;
331 QString myBadLines;
332 const QgsSettings myQSettings;
333 const QString myLastDir = myQSettings.value( QStringLiteral( "lastRasterFileFilterDir" ), QDir::homePath() ).toString();
334 const QString myFileName = QFileDialog::getOpenFileName( this, tr( "Load Pixel Values from File" ), myLastDir, tr( "Textfile" ) + " (*.txt)" );
335 QFile myInputFile( myFileName );
336 if ( myInputFile.open( QFile::ReadOnly ) )
337 {
338 QTextStream myInputStream( &myInputFile );
339 QString myInputLine;
340 if ( rasterIsMultiBandColor() )
341 {
342 for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
343 {
344 tableTransparency->removeRow( myTableRunner );
345 }
346
347 while ( !myInputStream.atEnd() )
348 {
349 myLineCounter++;
350 myInputLine = myInputStream.readLine();
351 if ( !myInputLine.isEmpty() )
352 {
353 if ( !myInputLine.simplified().startsWith( '#' ) )
354 {
355 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
356 QStringList myTokens = myInputLine.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
357 #else
358 QStringList myTokens = myInputLine.split( QRegularExpression( "\\s+" ), Qt::SkipEmptyParts );
359 #endif
360 if ( myTokens.count() != 4 )
361 {
362 myImportError = true;
363 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
364 }
365 else
366 {
367 tableTransparency->insertRow( tableTransparency->rowCount() );
368 for ( int col = 0; col < 4; col++ )
369 {
370 setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
371 }
372 }
373 }
374 }
375 }
376 }
377 else
378 {
379 for ( int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
380 {
381 tableTransparency->removeRow( myTableRunner );
382 }
383
384 while ( !myInputStream.atEnd() )
385 {
386 myLineCounter++;
387 myInputLine = myInputStream.readLine();
388 if ( !myInputLine.isEmpty() )
389 {
390 if ( !myInputLine.simplified().startsWith( '#' ) )
391 {
392 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
393 QStringList myTokens = myInputLine.split( QRegExp( "\\s+" ), QString::SkipEmptyParts );
394 #else
395 QStringList myTokens = myInputLine.split( QRegularExpression( "\\s+" ), Qt::SkipEmptyParts );
396 #endif
397 if ( myTokens.count() != 3 && myTokens.count() != 2 ) // 2 for QGIS < 1.9 compatibility
398 {
399 myImportError = true;
400 myBadLines = myBadLines + QString::number( myLineCounter ) + ":\t[" + myInputLine + "]\n";
401 }
402 else
403 {
404 if ( myTokens.count() == 2 )
405 {
406 myTokens.insert( 1, myTokens[0] ); // add 'to' value, QGIS < 1.9 compatibility
407 }
408 tableTransparency->insertRow( tableTransparency->rowCount() );
409 for ( int col = 0; col < 3; col++ )
410 {
411 setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
412 }
413 }
414 }
415 }
416 }
417 }
418
419 if ( myImportError )
420 {
421 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "The following lines contained errors\n\n%1" ).arg( myBadLines ) );
422 }
423 }
424 else if ( !myFileName.isEmpty() )
425 {
426 QMessageBox::warning( this, tr( "Load Pixel Values from File" ), tr( "Read access denied. Adjust the file permissions and try again.\n\n" ) );
427 }
428 //tableTransparency->resizeColumnsToContents();
429 //tableTransparency->resizeRowsToContents();
430 emit widgetChanged();
431 }
432
pbnRemoveSelectedRow_clicked()433 void QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked()
434 {
435 if ( 0 < tableTransparency->rowCount() )
436 {
437 tableTransparency->removeRow( tableTransparency->currentRow() );
438 }
439 emit widgetChanged();
440 }
441
rasterIsMultiBandColor()442 bool QgsRasterTransparencyWidget::rasterIsMultiBandColor()
443 {
444 return mRasterLayer && nullptr != dynamic_cast<QgsMultiBandColorRenderer *>( mRasterLayer->renderer() );
445 }
446
apply()447 void QgsRasterTransparencyWidget::apply()
448 {
449 //set NoDataValue
450 QgsRasterRangeList myNoDataRangeList;
451 if ( "" != leNoDataValue->text() )
452 {
453 bool myDoubleOk = false;
454 const double myNoDataValue = QgsDoubleValidator::toDouble( leNoDataValue->text(), &myDoubleOk );
455 if ( myDoubleOk )
456 {
457 const QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
458 myNoDataRangeList << myNoDataRange;
459 }
460 }
461 for ( int bandNo = 1; bandNo <= mRasterLayer->dataProvider()->bandCount(); bandNo++ )
462 {
463 mRasterLayer->dataProvider()->setUserNoDataValue( bandNo, myNoDataRangeList );
464 mRasterLayer->dataProvider()->setUseSourceNoDataValue( bandNo, mSrcNoDataValueCheckBox->isChecked() );
465 }
466
467 //transparency settings
468 QgsRasterRenderer *rasterRenderer = mRasterLayer->renderer();
469 if ( rasterRenderer )
470 {
471 rasterRenderer->setAlphaBand( cboxTransparencyBand->currentBand() );
472 rasterRenderer->setNodataColor( mNodataColorButton->color() );
473
474 //Walk through each row in table and test value. If not valid set to 0.0 and continue building transparency list
475 QgsRasterTransparency *rasterTransparency = new QgsRasterTransparency();
476 if ( tableTransparency->columnCount() == 4 )
477 {
478 QgsRasterTransparency::TransparentThreeValuePixel myTransparentPixel;
479 QList<QgsRasterTransparency::TransparentThreeValuePixel> myTransparentThreeValuePixelList;
480 myTransparentThreeValuePixelList.reserve( tableTransparency->rowCount() );
481 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
482 {
483 myTransparentPixel.red = transparencyCellValue( myListRunner, 0 );
484 myTransparentPixel.green = transparencyCellValue( myListRunner, 1 );
485 myTransparentPixel.blue = transparencyCellValue( myListRunner, 2 );
486 myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 3 );
487 myTransparentThreeValuePixelList.append( myTransparentPixel );
488 }
489 rasterTransparency->setTransparentThreeValuePixelList( myTransparentThreeValuePixelList );
490 }
491 else if ( tableTransparency->columnCount() == 3 )
492 {
493 QgsRasterTransparency::TransparentSingleValuePixel myTransparentPixel;
494 QList<QgsRasterTransparency::TransparentSingleValuePixel> myTransparentSingleValuePixelList;
495 myTransparentSingleValuePixelList.reserve( tableTransparency->rowCount() );
496 for ( int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
497 {
498 myTransparentPixel.min = transparencyCellValue( myListRunner, 0 );
499 myTransparentPixel.max = transparencyCellValue( myListRunner, 1 );
500 myTransparentPixel.percentTransparent = transparencyCellValue( myListRunner, 2 );
501
502 myTransparentSingleValuePixelList.append( myTransparentPixel );
503 }
504 rasterTransparency->setTransparentSingleValuePixelList( myTransparentSingleValuePixelList );
505 }
506
507 rasterRenderer->setRasterTransparency( rasterTransparency );
508
509 //set global transparency
510 rasterRenderer->setOpacity( mOpacityWidget->opacity() );
511 }
512
513 mRasterLayer->pipe()->setDataDefinedProperties( mPropertyCollection );
514 }
515
initializeDataDefinedButton(QgsPropertyOverrideButton * button,QgsRasterPipe::Property key)516 void QgsRasterTransparencyWidget::initializeDataDefinedButton( QgsPropertyOverrideButton *button, QgsRasterPipe::Property key )
517 {
518 button->blockSignals( true );
519 button->init( key, mPropertyCollection, QgsRasterPipe::propertyDefinitions(), nullptr );
520 connect( button, &QgsPropertyOverrideButton::changed, this, &QgsRasterTransparencyWidget::updateProperty );
521 button->registerExpressionContextGenerator( this );
522 button->blockSignals( false );
523 }
524
updateDataDefinedButtons()525 void QgsRasterTransparencyWidget::updateDataDefinedButtons()
526 {
527 const auto propertyOverrideButtons { findChildren< QgsPropertyOverrideButton * >() };
528 for ( QgsPropertyOverrideButton *button : propertyOverrideButtons )
529 {
530 updateDataDefinedButton( button );
531 }
532 }
533
updateDataDefinedButton(QgsPropertyOverrideButton * button)534 void QgsRasterTransparencyWidget::updateDataDefinedButton( QgsPropertyOverrideButton *button )
535 {
536 if ( !button )
537 return;
538
539 if ( button->propertyKey() < 0 )
540 return;
541
542 const QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
543 whileBlocking( button )->setToProperty( mPropertyCollection.property( key ) );
544 }
545
updateProperty()546 void QgsRasterTransparencyWidget::updateProperty()
547 {
548 QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
549 const QgsRasterPipe::Property key = static_cast< QgsRasterPipe::Property >( button->propertyKey() );
550 mPropertyCollection.setProperty( key, button->toProperty() );
551 emit widgetChanged();
552 }
553
pixelSelected(const QgsPointXY & canvasPoint)554 void QgsRasterTransparencyWidget::pixelSelected( const QgsPointXY &canvasPoint )
555 {
556 QgsRasterRenderer *renderer = mRasterLayer->renderer();
557 if ( !renderer )
558 {
559 return;
560 }
561
562 //Get the pixel values and add a new entry to the transparency table
563 if ( mMapCanvas && mPixelSelectorTool )
564 {
565 mMapCanvas->unsetMapTool( mPixelSelectorTool );
566
567 const QgsMapSettings &ms = mMapCanvas->mapSettings();
568 const QgsPointXY myPoint = ms.mapToLayerCoordinates( mRasterLayer, canvasPoint );
569
570 const QgsRectangle myExtent = ms.mapToLayerCoordinates( mRasterLayer, mMapCanvas->extent() );
571 const double mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
572 const int myWidth = mMapCanvas->extent().width() / mapUnitsPerPixel;
573 const int myHeight = mMapCanvas->extent().height() / mapUnitsPerPixel;
574
575 const QMap<int, QVariant> myPixelMap = mRasterLayer->dataProvider()->identify( myPoint, QgsRaster::IdentifyFormatValue, myExtent, myWidth, myHeight ).results();
576
577 const QList<int> bands = renderer->usesBands();
578
579 QList<double> values;
580 for ( int i = 0; i < bands.size(); ++i )
581 {
582 const int bandNo = bands.value( i );
583 if ( myPixelMap.count( bandNo ) == 1 )
584 {
585 if ( myPixelMap.value( bandNo ).isNull() )
586 {
587 return; // Don't add nodata, transparent anyway
588 }
589 const double value = myPixelMap.value( bandNo ).toDouble();
590 QgsDebugMsg( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ) );
591 values.append( value );
592 }
593 }
594 if ( bands.size() == 1 )
595 {
596 // Set 'to'
597 values.insert( 1, values.value( 0 ) );
598 }
599 tableTransparency->insertRow( tableTransparency->rowCount() );
600 for ( int i = 0; i < values.size(); i++ )
601 {
602 setTransparencyCell( tableTransparency->rowCount() - 1, i, values.value( i ) );
603 }
604 setTransparencyCell( tableTransparency->rowCount() - 1, tableTransparency->columnCount() - 1, 100 );
605 }
606
607 //tableTransparency->resizeColumnsToContents();
608 //tableTransparency->resizeRowsToContents();
609 }
610
populateTransparencyTable(QgsRasterRenderer * renderer)611 void QgsRasterTransparencyWidget::populateTransparencyTable( QgsRasterRenderer *renderer )
612 {
613 if ( !mRasterLayer )
614 {
615 return;
616 }
617
618 if ( !renderer )
619 {
620 return;
621 }
622
623 const int nBands = renderer->usesBands().size();
624 setupTransparencyTable( nBands );
625
626 const QgsRasterTransparency *rasterTransparency = renderer->rasterTransparency();
627 if ( !rasterTransparency )
628 {
629 return;
630 }
631
632 if ( nBands == 1 )
633 {
634 QList<QgsRasterTransparency::TransparentSingleValuePixel> pixelList = rasterTransparency->transparentSingleValuePixelList();
635 for ( int i = 0; i < pixelList.size(); ++i )
636 {
637 tableTransparency->insertRow( i );
638 setTransparencyCell( i, 0, pixelList[i].min );
639 setTransparencyCell( i, 1, pixelList[i].max );
640 setTransparencyCell( i, 2, pixelList[i].percentTransparent );
641 // break synchronization only if values differ
642 if ( pixelList[i].min != pixelList[i].max )
643 {
644 setTransparencyToEdited( i );
645 }
646 }
647 }
648 else if ( nBands == 3 )
649 {
650 QList<QgsRasterTransparency::TransparentThreeValuePixel> pixelList = rasterTransparency->transparentThreeValuePixelList();
651 for ( int i = 0; i < pixelList.size(); ++i )
652 {
653 tableTransparency->insertRow( i );
654 setTransparencyCell( i, 0, pixelList[i].red );
655 setTransparencyCell( i, 1, pixelList[i].green );
656 setTransparencyCell( i, 2, pixelList[i].blue );
657 setTransparencyCell( i, 3, pixelList[i].percentTransparent );
658 }
659 }
660
661 tableTransparency->resizeColumnsToContents();
662 tableTransparency->resizeRowsToContents();
663
664 }
665
setupTransparencyTable(int nBands)666 void QgsRasterTransparencyWidget::setupTransparencyTable( int nBands )
667 {
668 tableTransparency->clear();
669 tableTransparency->setColumnCount( 0 );
670 tableTransparency->setRowCount( 0 );
671 mTransparencyToEdited.clear();
672
673 if ( nBands == 3 )
674 {
675 tableTransparency->setColumnCount( 4 );
676 tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Red" ) ) );
677 tableTransparency->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "Green" ) ) );
678 tableTransparency->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Blue" ) ) );
679 tableTransparency->setHorizontalHeaderItem( 3, new QTableWidgetItem( tr( "Percent Transparent" ) ) );
680 }
681 else //1 band
682 {
683 tableTransparency->setColumnCount( 3 );
684 // Is it important to distinguish the header? It becomes difficult with range.
685 #if 0
686 if ( QgsRasterLayer::PalettedColor != mRasterLayer->drawingStyle() &&
687 QgsRasterLayer::PalettedSingleBandGray != mRasterLayer->drawingStyle() &&
688 QgsRasterLayer::PalettedSingleBandPseudoColor != mRasterLayer->drawingStyle() &&
689 QgsRasterLayer::PalettedMultiBandColor != mRasterLayer->drawingStyle() )
690 {
691 tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Gray" ) ) );
692 }
693 else
694 {
695 tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "Indexed Value" ) ) );
696 }
697 #endif
698 tableTransparency->setHorizontalHeaderItem( 0, new QTableWidgetItem( tr( "From" ) ) );
699 tableTransparency->setHorizontalHeaderItem( 1, new QTableWidgetItem( tr( "To" ) ) );
700 tableTransparency->setHorizontalHeaderItem( 2, new QTableWidgetItem( tr( "Percent Transparent" ) ) );
701 }
702 }
703
setTransparencyCell(int row,int column,double value)704 void QgsRasterTransparencyWidget::setTransparencyCell( int row, int column, double value )
705 {
706 QgsDebugMsg( QStringLiteral( "value = %1" ).arg( value, 0, 'g', 17 ) );
707 QgsRasterDataProvider *provider = mRasterLayer->dataProvider();
708 if ( !provider ) return;
709
710 QgsRasterRenderer *renderer = mRasterLayer->renderer();
711 if ( !renderer ) return;
712 const int nBands = renderer->usesBands().size();
713
714 QLineEdit *lineEdit = new QLineEdit();
715 lineEdit->setFrame( false ); // frame looks bad in table
716 // Without margins row selection is not displayed (important for delete row)
717 lineEdit->setContentsMargins( 1, 1, 1, 1 );
718
719 if ( column == tableTransparency->columnCount() - 1 )
720 {
721 // transparency
722 // Who needs transparency as floating point?
723 lineEdit->setValidator( new QIntValidator( nullptr ) );
724 lineEdit->setText( QString::number( static_cast<int>( value ) ) );
725 }
726 else
727 {
728 // value
729 QString valueString;
730 switch ( provider->sourceDataType( 1 ) )
731 {
732 case Qgis::DataType::Float32:
733 case Qgis::DataType::Float64:
734 lineEdit->setValidator( new QgsDoubleValidator( nullptr ) );
735 if ( !std::isnan( value ) )
736 {
737 const double v = QgsRasterBlock::printValue( value ).toDouble();
738 valueString = QLocale().toString( v );
739 }
740 break;
741 default:
742 lineEdit->setValidator( new QIntValidator( nullptr ) );
743 if ( !std::isnan( value ) )
744 {
745 valueString = QString::number( static_cast<int>( value ) );
746 }
747 break;
748 }
749 lineEdit->setText( valueString );
750 connect( lineEdit, &QLineEdit::textEdited, this, &QgsPanelWidget::widgetChanged );
751 }
752 tableTransparency->setCellWidget( row, column, lineEdit );
753 adjustTransparencyCellWidth( row, column );
754
755 if ( nBands == 1 && ( column == 0 || column == 1 ) )
756 {
757 connect( lineEdit, &QLineEdit::textEdited, this, &QgsRasterTransparencyWidget::transparencyCellTextEdited );
758 }
759 //tableTransparency->resizeColumnsToContents();
760 emit widgetChanged();
761 }
762
adjustTransparencyCellWidth(int row,int column)763 void QgsRasterTransparencyWidget::adjustTransparencyCellWidth( int row, int column )
764 {
765 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
766 if ( !lineEdit ) return;
767
768 int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
769 width = std::max( width, tableTransparency->columnWidth( column ) );
770
771 lineEdit->setFixedWidth( width );
772 }
773
setTransparencyToEdited(int row)774 void QgsRasterTransparencyWidget::setTransparencyToEdited( int row )
775 {
776 if ( row >= mTransparencyToEdited.size() )
777 {
778 mTransparencyToEdited.resize( row + 1 );
779 }
780 mTransparencyToEdited[row] = true;
781 }
782
transparencyCellValue(int row,int column)783 double QgsRasterTransparencyWidget::transparencyCellValue( int row, int column )
784 {
785 QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
786 if ( !lineEdit || lineEdit->text().isEmpty() )
787 {
788 return std::numeric_limits<double>::quiet_NaN();
789 }
790 return QgsDoubleValidator::toDouble( lineEdit->text() );
791
792 }
793
pixelSelectorTool() const794 QgsMapToolEmitPoint *QgsRasterTransparencyWidget::pixelSelectorTool() const
795 {
796 return mPixelSelectorTool;
797 }
798