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;