16 #include <QIntValidator>
18 #include <QTextStream>
19 #include <QMessageBox>
20 #include <QFileDialog>
21 #include <QRegularExpression>
42 , TRSTRING_NOT_SET( tr(
"Not Set" ) )
43 , mRasterLayer( layer )
44 , mMapCanvas( canvas )
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 );
54 mNodataColorButton->setShowNoColor(
true );
55 mNodataColorButton->setColorDialogTitle( tr(
"Select No Data Color" ) );
62 leNoDataValue->setValidator(
new QgsDoubleValidator( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(),
this ) );
65 mPixelSelectorTool =
nullptr;
73 pbnAddValuesFromDisplay->setEnabled(
false );
97 expContext << generator->createExpressionContextScope();
109 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
127 gboxNoDataValue->setEnabled(
false );
128 gboxCustomTransparency->setEnabled(
false );
131 cboxTransparencyBand->setShowNotSetOption(
true, tr(
"None" ) );
132 cboxTransparencyBand->setLayer( mRasterLayer );
135 if ( mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) )
141 lblSrcNoDataValue->setText( tr(
"not defined" ) );
144 mSrcNoDataValueCheckBox->setChecked( mRasterLayer->dataProvider()->useSourceNoDataValue( 1 ) );
146 const bool enableSrcNoData = mRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) && !std::isnan( mRasterLayer->dataProvider()->sourceNoDataValue( 1 ) );
148 mSrcNoDataValueCheckBox->setEnabled( enableSrcNoData );
149 lblSrcNoDataValue->setEnabled( enableSrcNoData );
154 mNodataColorButton->setColor( renderer->
nodataColor() );
156 mNodataColorButton->setToNull();
158 mOpacityWidget->setOpacity( renderer->
opacity() );
160 cboxTransparencyBand->setBand( renderer->
alphaBand() );
163 const QgsRasterRangeList noDataRangeList = mRasterLayer->dataProvider()->userNoDataValues( 1 );
164 QgsDebugMsg( QStringLiteral(
"noDataRangeList.size = %1" ).arg( noDataRangeList.size() ) );
165 if ( !noDataRangeList.isEmpty() )
168 leNoDataValue->setText( QLocale().toString( v ) );
172 leNoDataValue->setText( QString() );
175 mPropertyCollection = mRasterLayer->pipe()->dataDefinedProperties();
176 updateDataDefinedButtons();
178 populateTransparencyTable( mRasterLayer->renderer() );
181 void QgsRasterTransparencyWidget::transparencyCellTextEdited(
const QString &text )
184 QgsDebugMsg( QStringLiteral(
"text = %1" ).arg( text ) );
190 const int nBands = renderer->
usesBands().size();
193 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender() );
194 if ( !lineEdit )
return;
197 for (
int r = 0; r < tableTransparency->rowCount(); r++ )
199 for (
int c = 0;
c < tableTransparency->columnCount();
c++ )
201 if ( tableTransparency->cellWidget( r,
c ) == sender() )
208 if ( row != -1 )
break;
210 QgsDebugMsg( QStringLiteral(
"row = %1 column =%2" ).arg( row ).arg( column ) );
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 ) );
220 toLineEdit->setText( lineEdit->text() );
223 else if ( column == 1 )
225 setTransparencyToEdited( row );
228 emit widgetChanged();
231 void QgsRasterTransparencyWidget::pbnAddValuesFromDisplay_clicked()
233 if ( mMapCanvas && mPixelSelectorTool )
235 mMapCanvas->setMapTool( mPixelSelectorTool );
239 void QgsRasterTransparencyWidget::pbnAddValuesManually_clicked()
247 tableTransparency->insertRow( tableTransparency->rowCount() );
252 for (
int i = 0; i < n; i++ )
254 setTransparencyCell( tableTransparency->rowCount() - 1, i, std::numeric_limits<double>::quiet_NaN() );
257 setTransparencyCell( tableTransparency->rowCount() - 1, n, 100 );
263 void QgsRasterTransparencyWidget::pbnDefaultValues_clicked()
271 const int nBands = r->
usesBands().size();
273 setupTransparencyTable( nBands );
280 void QgsRasterTransparencyWidget::pbnExportTransparentPixelValues_clicked()
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() )
287 if ( !myFileName.endsWith( QLatin1String(
".txt" ), Qt::CaseInsensitive ) )
289 myFileName = myFileName +
".txt";
292 QFile myOutputFile( myFileName );
293 if ( myOutputFile.open( QFile::WriteOnly | QIODevice::Truncate ) )
295 QTextStream myOutputStream( &myOutputFile );
296 myOutputStream <<
"# " << tr(
"QGIS Generated Transparent Pixel Value Export File" ) <<
'\n';
297 if ( rasterIsMultiBandColor() )
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++ )
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 ) );
310 myOutputStream <<
"#\n#\n# " << tr(
"Value" ) <<
"\t" << tr(
"Percent Transparent" );
312 for (
int myTableRunner = 0; myTableRunner < tableTransparency->rowCount(); myTableRunner++ )
314 myOutputStream <<
'\n' << QString::number( transparencyCellValue( myTableRunner, 0 ) ) <<
"\t"
315 << QString::number( transparencyCellValue( myTableRunner, 1 ) ) <<
"\t"
316 << QString::number( transparencyCellValue( myTableRunner, 2 ) );
322 QMessageBox::warning(
this, tr(
"Save Pixel Values as File" ), tr(
"Write access denied. Adjust the file permissions and try again.\n\n" ) );
327 void QgsRasterTransparencyWidget::pbnImportTransparentPixelValues_clicked()
329 int myLineCounter = 0;
330 bool myImportError =
false;
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 ) )
338 QTextStream myInputStream( &myInputFile );
340 if ( rasterIsMultiBandColor() )
342 for (
int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
344 tableTransparency->removeRow( myTableRunner );
347 while ( !myInputStream.atEnd() )
350 myInputLine = myInputStream.readLine();
351 if ( !myInputLine.isEmpty() )
353 if ( !myInputLine.simplified().startsWith(
'#' ) )
355 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
356 QStringList myTokens = myInputLine.split( QRegExp(
"\\s+" ), QString::SkipEmptyParts );
358 QStringList myTokens = myInputLine.split( QRegularExpression(
"\\s+" ), Qt::SkipEmptyParts );
360 if ( myTokens.count() != 4 )
362 myImportError =
true;
363 myBadLines = myBadLines + QString::number( myLineCounter ) +
":\t[" + myInputLine +
"]\n";
367 tableTransparency->insertRow( tableTransparency->rowCount() );
368 for (
int col = 0; col < 4; col++ )
370 setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
379 for (
int myTableRunner = tableTransparency->rowCount() - 1; myTableRunner >= 0; myTableRunner-- )
381 tableTransparency->removeRow( myTableRunner );
384 while ( !myInputStream.atEnd() )
387 myInputLine = myInputStream.readLine();
388 if ( !myInputLine.isEmpty() )
390 if ( !myInputLine.simplified().startsWith(
'#' ) )
392 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
393 QStringList myTokens = myInputLine.split( QRegExp(
"\\s+" ), QString::SkipEmptyParts );
395 QStringList myTokens = myInputLine.split( QRegularExpression(
"\\s+" ), Qt::SkipEmptyParts );
397 if ( myTokens.count() != 3 && myTokens.count() != 2 )
399 myImportError =
true;
400 myBadLines = myBadLines + QString::number( myLineCounter ) +
":\t[" + myInputLine +
"]\n";
404 if ( myTokens.count() == 2 )
406 myTokens.insert( 1, myTokens[0] );
408 tableTransparency->insertRow( tableTransparency->rowCount() );
409 for (
int col = 0; col < 3; col++ )
411 setTransparencyCell( tableTransparency->rowCount() - 1, col, myTokens[col].toDouble() );
421 QMessageBox::warning(
this, tr(
"Load Pixel Values from File" ), tr(
"The following lines contained errors\n\n%1" ).arg( myBadLines ) );
424 else if ( !myFileName.isEmpty() )
426 QMessageBox::warning(
this, tr(
"Load Pixel Values from File" ), tr(
"Read access denied. Adjust the file permissions and try again.\n\n" ) );
430 emit widgetChanged();
433 void QgsRasterTransparencyWidget::pbnRemoveSelectedRow_clicked()
435 if ( 0 < tableTransparency->rowCount() )
437 tableTransparency->removeRow( tableTransparency->currentRow() );
439 emit widgetChanged();
442 bool QgsRasterTransparencyWidget::rasterIsMultiBandColor()
451 if (
"" != leNoDataValue->text() )
453 bool myDoubleOk =
false;
457 const QgsRasterRange myNoDataRange( myNoDataValue, myNoDataValue );
458 myNoDataRangeList << myNoDataRange;
461 for (
int bandNo = 1; bandNo <= mRasterLayer->dataProvider()->bandCount(); bandNo++ )
463 mRasterLayer->dataProvider()->setUserNoDataValue( bandNo, myNoDataRangeList );
464 mRasterLayer->dataProvider()->setUseSourceNoDataValue( bandNo, mSrcNoDataValueCheckBox->isChecked() );
469 if ( rasterRenderer )
471 rasterRenderer->
setAlphaBand( cboxTransparencyBand->currentBand() );
476 if ( tableTransparency->columnCount() == 4 )
479 QList<QgsRasterTransparency::TransparentThreeValuePixel> myTransparentThreeValuePixelList;
480 myTransparentThreeValuePixelList.reserve( tableTransparency->rowCount() );
481 for (
int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
483 myTransparentPixel.
red = transparencyCellValue( myListRunner, 0 );
484 myTransparentPixel.
green = transparencyCellValue( myListRunner, 1 );
485 myTransparentPixel.
blue = transparencyCellValue( myListRunner, 2 );
487 myTransparentThreeValuePixelList.append( myTransparentPixel );
491 else if ( tableTransparency->columnCount() == 3 )
494 QList<QgsRasterTransparency::TransparentSingleValuePixel> myTransparentSingleValuePixelList;
495 myTransparentSingleValuePixelList.reserve( tableTransparency->rowCount() );
496 for (
int myListRunner = 0; myListRunner < tableTransparency->rowCount(); myListRunner++ )
498 myTransparentPixel.
min = transparencyCellValue( myListRunner, 0 );
499 myTransparentPixel.
max = transparencyCellValue( myListRunner, 1 );
502 myTransparentSingleValuePixelList.append( myTransparentPixel );
510 rasterRenderer->
setOpacity( mOpacityWidget->opacity() );
513 mRasterLayer->pipe()->setDataDefinedProperties( mPropertyCollection );
518 button->blockSignals(
true );
522 button->blockSignals(
false );
527 const auto propertyOverrideButtons { findChildren< QgsPropertyOverrideButton * >() };
530 updateDataDefinedButton( button );
543 whileBlocking( button )->setToProperty( mPropertyCollection.property( key ) );
546 void QgsRasterTransparencyWidget::updateProperty()
550 mPropertyCollection.setProperty( key, button->
toProperty() );
551 emit widgetChanged();
554 void QgsRasterTransparencyWidget::pixelSelected(
const QgsPointXY &canvasPoint )
563 if ( mMapCanvas && mPixelSelectorTool )
565 mMapCanvas->unsetMapTool( mPixelSelectorTool );
571 const double mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
572 const int myWidth = mMapCanvas->extent().width() / mapUnitsPerPixel;
573 const int myHeight = mMapCanvas->extent().height() / mapUnitsPerPixel;
575 const QMap<int, QVariant> myPixelMap = mRasterLayer->dataProvider()->identify( myPoint,
QgsRaster::IdentifyFormatValue, myExtent, myWidth, myHeight ).results();
577 const QList<int> bands = renderer->
usesBands();
579 QList<double> values;
580 for (
int i = 0; i < bands.size(); ++i )
582 const int bandNo = bands.value( i );
583 if ( myPixelMap.count( bandNo ) == 1 )
585 if ( myPixelMap.value( bandNo ).isNull() )
589 const double value = myPixelMap.value( bandNo ).toDouble();
590 QgsDebugMsg( QStringLiteral(
"value = %1" ).arg( value, 0,
'g', 17 ) );
591 values.append( value );
594 if ( bands.size() == 1 )
597 values.insert( 1, values.value( 0 ) );
599 tableTransparency->insertRow( tableTransparency->rowCount() );
600 for (
int i = 0; i < values.size(); i++ )
602 setTransparencyCell( tableTransparency->rowCount() - 1, i, values.value( i ) );
604 setTransparencyCell( tableTransparency->rowCount() - 1, tableTransparency->columnCount() - 1, 100 );
611 void QgsRasterTransparencyWidget::populateTransparencyTable(
QgsRasterRenderer *renderer )
623 const int nBands = renderer->
usesBands().size();
624 setupTransparencyTable( nBands );
627 if ( !rasterTransparency )
635 for (
int i = 0; i < pixelList.size(); ++i )
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 );
642 if ( pixelList[i].min != pixelList[i].max )
644 setTransparencyToEdited( i );
648 else if ( nBands == 3 )
651 for (
int i = 0; i < pixelList.size(); ++i )
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 );
661 tableTransparency->resizeColumnsToContents();
662 tableTransparency->resizeRowsToContents();
666 void QgsRasterTransparencyWidget::setupTransparencyTable(
int nBands )
668 tableTransparency->clear();
669 tableTransparency->setColumnCount( 0 );
670 tableTransparency->setRowCount( 0 );
671 mTransparencyToEdited.clear();
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" ) ) );
683 tableTransparency->setColumnCount( 3 );
686 if ( QgsRasterLayer::PalettedColor != mRasterLayer->drawingStyle() &&
687 QgsRasterLayer::PalettedSingleBandGray != mRasterLayer->drawingStyle() &&
688 QgsRasterLayer::PalettedSingleBandPseudoColor != mRasterLayer->drawingStyle() &&
689 QgsRasterLayer::PalettedMultiBandColor != mRasterLayer->drawingStyle() )
691 tableTransparency->setHorizontalHeaderItem( 0,
new QTableWidgetItem( tr(
"Gray" ) ) );
695 tableTransparency->setHorizontalHeaderItem( 0,
new QTableWidgetItem( tr(
"Indexed Value" ) ) );
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" ) ) );
704 void QgsRasterTransparencyWidget::setTransparencyCell(
int row,
int column,
double value )
706 QgsDebugMsg( QStringLiteral(
"value = %1" ).arg( value, 0,
'g', 17 ) );
708 if ( !provider )
return;
711 if ( !renderer )
return;
712 const int nBands = renderer->
usesBands().size();
714 QLineEdit *lineEdit =
new QLineEdit();
715 lineEdit->setFrame(
false );
717 lineEdit->setContentsMargins( 1, 1, 1, 1 );
719 if ( column == tableTransparency->columnCount() - 1 )
723 lineEdit->setValidator(
new QIntValidator(
nullptr ) );
724 lineEdit->setText( QString::number(
static_cast<int>( value ) ) );
735 if ( !std::isnan( value ) )
738 valueString = QLocale().toString( v );
742 lineEdit->setValidator(
new QIntValidator(
nullptr ) );
743 if ( !std::isnan( value ) )
745 valueString = QString::number(
static_cast<int>( value ) );
749 lineEdit->setText( valueString );
752 tableTransparency->setCellWidget( row, column, lineEdit );
753 adjustTransparencyCellWidth( row, column );
755 if ( nBands == 1 && ( column == 0 || column == 1 ) )
757 connect( lineEdit, &QLineEdit::textEdited,
this, &QgsRasterTransparencyWidget::transparencyCellTextEdited );
760 emit widgetChanged();
763 void QgsRasterTransparencyWidget::adjustTransparencyCellWidth(
int row,
int column )
765 QLineEdit *lineEdit =
dynamic_cast<QLineEdit *
>( tableTransparency->cellWidget( row, column ) );
766 if ( !lineEdit )
return;
768 int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
769 width = std::max( width, tableTransparency->columnWidth( column ) );
771 lineEdit->setFixedWidth( width );
774 void QgsRasterTransparencyWidget::setTransparencyToEdited(
int row )
776 if ( row >= mTransparencyToEdited.size() )
778 mTransparencyToEdited.resize( row + 1 );
780 mTransparencyToEdited[row] =
true;
783 double QgsRasterTransparencyWidget::transparencyCellValue(
int row,
int column )
785 QLineEdit *lineEdit =
dynamic_cast<QLineEdit *
>( tableTransparency->cellWidget( row, column ) );
786 if ( !lineEdit || lineEdit->text().isEmpty() )
788 return std::numeric_limits<double>::quiet_NaN();
796 return mPixelSelectorTool;