24 #include "qgssettings.h" 
   27 #include <QInputDialog> 
   28 #include <QMessageBox> 
   30 #include <QMouseEvent> 
   34 QMap< QString, QStringList > QgsRasterFormatSaveOptionsWidget::sBuiltinProfiles;
 
   36 static const QString PYRAMID_JPEG_YCBCR_COMPRESSION( QStringLiteral( 
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG PHOTOMETRIC_OVERVIEW=YCBCR INTERLEAVE_OVERVIEW=PIXEL" ) );
 
   37 static const QString PYRAMID_JPEG_COMPRESSION( QStringLiteral( 
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG INTERLEAVE_OVERVIEW=PIXEL" ) );
 
   43   , mProvider( provider )
 
   46   connect( mProfileNewButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked );
 
   47   connect( mProfileDeleteButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked );
 
   48   connect( mProfileResetButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked );
 
   49   connect( mOptionsAddButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked );
 
   50   connect( mOptionsDeleteButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked );
 
   51   connect( mOptionsLineEdit, &QLineEdit::editingFinished, 
this, &QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished );
 
   55   if ( sBuiltinProfiles.isEmpty() )
 
   58     sBuiltinProfiles[ QStringLiteral( 
"z_adefault" )] = ( QStringList() << QString() << tr( 
"Default" ) << QString() );
 
   63     sBuiltinProfiles[ QStringLiteral( 
"z_gtiff_1big" )] =
 
   64       ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"No Compression" )
 
   65         << QStringLiteral( 
"COMPRESS=NONE BIGTIFF=IF_NEEDED" ) );
 
   66     sBuiltinProfiles[ QStringLiteral( 
"z_gtiff_2medium" )] =
 
   67       ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"Low Compression" )
 
   68         << QStringLiteral( 
"COMPRESS=PACKBITS" ) );
 
   69     sBuiltinProfiles[ QStringLiteral( 
"z_gtiff_3small" )] =
 
   70       ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"High Compression" )
 
   71         << QStringLiteral( 
"COMPRESS=DEFLATE PREDICTOR=2 ZLEVEL=9" ) );
 
   72     sBuiltinProfiles[ QStringLiteral( 
"z_gtiff_4jpeg" )] =
 
   73       ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"JPEG Compression" )
 
   74         << QStringLiteral( 
"COMPRESS=JPEG JPEG_QUALITY=75" ) );
 
   79     sBuiltinProfiles[ QStringLiteral( 
"z__pyramids_gtiff_1big" )] =
 
   80       ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"No Compression" )
 
   81         << QStringLiteral( 
"COMPRESS_OVERVIEW=NONE BIGTIFF_OVERVIEW=IF_NEEDED" ) );
 
   82     sBuiltinProfiles[ QStringLiteral( 
"z__pyramids_gtiff_2medium" )] =
 
   83       ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"Low Compression" )
 
   84         << QStringLiteral( 
"COMPRESS_OVERVIEW=PACKBITS" ) );
 
   85     sBuiltinProfiles[ QStringLiteral( 
"z__pyramids_gtiff_3small" )] =
 
   86       ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"High Compression" )
 
   87         << QStringLiteral( 
"COMPRESS_OVERVIEW=DEFLATE PREDICTOR_OVERVIEW=2 ZLEVEL=9" ) ); 
 
   88     sBuiltinProfiles[ QStringLiteral( 
"z__pyramids_gtiff_4jpeg" )] =
 
   89       ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"JPEG Compression" )
 
   90         << PYRAMID_JPEG_YCBCR_COMPRESSION );
 
   93   connect( mProfileComboBox, &QComboBox::currentTextChanged,
 
   94            this, &QgsRasterFormatSaveOptionsWidget::updateOptions );
 
   95   connect( mOptionsTable, &QTableWidget::cellChanged, 
this, &QgsRasterFormatSaveOptionsWidget::optionsTableChanged );
 
   97   connect( mOptionsValidateButton, &QAbstractButton::clicked, 
this, [ = ] { 
validateOptions(); } );
 
  101   mOptionsLineEdit->installEventFilter( 
this );
 
  102   mOptionsStackedWidget->installEventFilter( 
this );
 
  119   mProvider = provider;
 
  126   QList< QWidget * > widgets = this->findChildren<QWidget *>();
 
  130     const auto constWidgets = widgets;
 
  131     for ( QWidget *widget : constWidgets )
 
  132       widget->setVisible( 
false );
 
  133     mOptionsStackedWidget->setVisible( 
true );
 
  134     const auto children { mOptionsStackedWidget->findChildren<QWidget *>() };
 
  135     for ( QWidget *widget : children )
 
  136       widget->setVisible( 
true );
 
  147     const auto constWidgets = widgets;
 
  148     for ( QWidget *widget : constWidgets )
 
  149       widget->setVisible( 
true );
 
  151       mProfileButtons->setVisible( 
false );
 
  159 QString QgsRasterFormatSaveOptionsWidget::pseudoFormat()
 const 
  161   return mPyramids ? QStringLiteral( 
"_pyramids" ) : mFormat;
 
  167   QString format = pseudoFormat();
 
  168   QStringList profileKeys = profiles();
 
  169   QMapIterator<QString, QStringList> it( sBuiltinProfiles );
 
  170   while ( it.hasNext() )
 
  173     QString profileKey = it.key();
 
  174     if ( ! profileKeys.contains( profileKey ) && !it.value().isEmpty() )
 
  177       if ( it.value()[0].isEmpty() ||  it.value()[0] == format )
 
  179         profileKeys.insert( 0, profileKey );
 
  183   std::sort( profileKeys.begin(), profileKeys.end() );
 
  187   mProfileComboBox->blockSignals( 
true );
 
  188   mProfileComboBox->clear();
 
  189   const auto constProfileKeys = profileKeys;
 
  190   for ( 
const QString &profileKey : constProfileKeys )
 
  192     QString profileName, profileOptions;
 
  193     profileOptions = createOptions( profileKey );
 
  194     if ( sBuiltinProfiles.contains( profileKey ) )
 
  196       profileName = sBuiltinProfiles[ profileKey ][ 1 ];
 
  197       if ( profileOptions.isEmpty() )
 
  198         profileOptions = sBuiltinProfiles[ profileKey ][ 2 ];
 
  202       profileName = profileKey;
 
  204     mOptionsMap[ profileKey ] = profileOptions;
 
  205     mProfileComboBox->addItem( profileName, profileKey );
 
  209   mProfileComboBox->blockSignals( 
false );
 
  211   QgsSettings mySettings;
 
  212   mProfileComboBox->setCurrentIndex( mProfileComboBox->findData( mySettings.value(
 
  213                                        mProvider + 
"/driverOptions/" + format.toLower() + 
"/defaultProfile",
 
  218 void QgsRasterFormatSaveOptionsWidget::updateOptions()
 
  220   mBlockOptionUpdates++;
 
  221   QString myOptions = mOptionsMap.value( currentProfileKey() );
 
  222 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) 
  223   QStringList myOptionsList = myOptions.trimmed().split( 
' ', QString::SkipEmptyParts );
 
  225   QStringList myOptionsList = myOptions.trimmed().split( 
' ', Qt::SkipEmptyParts );
 
  230   if ( mRasterLayer && mRasterLayer->
bandCount() != 3 &&
 
  231        myOptions == PYRAMID_JPEG_YCBCR_COMPRESSION )
 
  233     myOptions = PYRAMID_JPEG_COMPRESSION;
 
  236   if ( mOptionsStackedWidget->currentIndex() == 0 )
 
  238     mOptionsTable->setRowCount( 0 );
 
  239     for ( 
int i = 0; i < myOptionsList.count(); i++ )
 
  241       QStringList key_value = myOptionsList[i].split( 
'=' );
 
  242       if ( key_value.count() == 2 )
 
  244         mOptionsTable->insertRow( i );
 
  245         mOptionsTable->setItem( i, 0, 
new QTableWidgetItem( key_value[0] ) );
 
  246         mOptionsTable->setItem( i, 1, 
new QTableWidgetItem( key_value[1] ) );
 
  252     mOptionsLineEdit->setText( myOptions );
 
  253     mOptionsLineEdit->setCursorPosition( 0 );
 
  256   mBlockOptionUpdates--;
 
  269   if ( mProvider == QLatin1String( 
"gdal" ) && !mFormat.isEmpty() && ! mPyramids )
 
  272     if ( message.isEmpty() )
 
  273       message = tr( 
"Cannot get create options for driver %1" ).arg( mFormat );
 
  275   else if ( mProvider == QLatin1String( 
"gdal" ) && mPyramids )
 
  277     message = tr( 
"For details on pyramids options please see the following pages" );
 
  278     message += QLatin1String( 
"\n\nhttps://gdal.org/programs/gdaladdo.html\n\nhttps://gdal.org/drivers/raster/gtiff.html" );
 
  281     message = tr( 
"No help available" );
 
  285   dlg->setWindowTitle( tr( 
"Create Options for %1" ).arg( mFormat ) );
 
  286   QTextEdit *textEdit = 
new QTextEdit( dlg );
 
  287   textEdit->setReadOnly( 
true );
 
  289   textEdit->setText( message );
 
  290   dlg->
layout()->addWidget( textEdit );
 
  291   dlg->resize( 600, 400 );
 
  301   QStringList createOptions = 
options();
 
  304   QgsDebugMsg( QStringLiteral( 
"layer: [%1] file: [%2] format: [%3]" ).arg( mRasterLayer ? mRasterLayer->
id() : 
"none", mRasterFileName, mFormat ) );
 
  308   bool tmpLayer = 
false;
 
  309   if ( !( mRasterLayer && rasterLayer->
dataProvider() ) && ! mRasterFileName.isNull() )
 
  313     options.skipCrsValidation = 
true;
 
  314     rasterLayer = 
new QgsRasterLayer( mRasterFileName, QFileInfo( mRasterFileName ).baseName(), QStringLiteral( 
"gdal" ), 
options );
 
  317   if ( mProvider == QLatin1String( 
"gdal" ) && mPyramids )
 
  321       QgsDebugMsg( QStringLiteral( 
"calling validate pyramids on layer's data provider" ) );
 
  326       message = tr( 
"cannot validate pyramid options" );
 
  329   else if ( !createOptions.isEmpty() && mProvider == QLatin1String( 
"gdal" ) && !mFormat.isEmpty() )
 
  333       QgsDebugMsg( QStringLiteral( 
"calling validate on layer's data provider" ) );
 
  343   else if ( ! createOptions.isEmpty() )
 
  345     QMessageBox::information( 
this, QString(), tr( 
"Cannot validate creation options." ), QMessageBox::Close );
 
  353     if ( message.isNull() )
 
  356         QMessageBox::information( 
this, QString(), tr( 
"Valid" ), QMessageBox::Close );
 
  360       QMessageBox::warning( 
this, QString(), tr( 
"Invalid %1:\n\n%2\n\nClick on help button to get valid creation options for this format." ).arg( mPyramids ? tr( 
"pyramid creation option" ) : tr( 
"creation option" ), message ), QMessageBox::Close );
 
  370 void QgsRasterFormatSaveOptionsWidget::optionsTableChanged()
 
  372   if ( mBlockOptionUpdates )
 
  375   QTableWidgetItem *key, *value;
 
  377   for ( 
int i = 0; i < mOptionsTable->rowCount(); i++ )
 
  379     key = mOptionsTable->item( i, 0 );
 
  380     if ( ! key  || key->text().isEmpty() )
 
  382     value = mOptionsTable->item( i, 1 );
 
  383     if ( ! value  || value->text().isEmpty() )
 
  385     options += key->text() + 
'=' + value->text() + 
' ';
 
  388   mOptionsMap[ currentProfileKey()] = 
options;
 
  389   mOptionsLineEdit->setText( 
options );
 
  390   mOptionsLineEdit->setCursorPosition( 0 );
 
  393 void QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished()
 
  395   mOptionsMap[ currentProfileKey()] = mOptionsLineEdit->text().trimmed();
 
  398 void QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked()
 
  400   QString profileName = QInputDialog::getText( 
this, QString(), tr( 
"Profile name:" ) );
 
  401   if ( ! profileName.isEmpty() )
 
  403     profileName = profileName.trimmed();
 
  404     mOptionsMap[ profileName ] = QString();
 
  405     mProfileComboBox->addItem( profileName, profileName );
 
  406     mProfileComboBox->setCurrentIndex( mProfileComboBox->count() - 1 );
 
  410 void QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked()
 
  412   int index = mProfileComboBox->currentIndex();
 
  413   QString profileKey = currentProfileKey();
 
  414   if ( index != -1 && ! sBuiltinProfiles.contains( profileKey ) )
 
  416     mOptionsMap.remove( profileKey );
 
  417     mProfileComboBox->removeItem( index );
 
  421 void QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked()
 
  423   QString profileKey = currentProfileKey();
 
  424   if ( sBuiltinProfiles.contains( profileKey ) )
 
  426     mOptionsMap[ profileKey ] = sBuiltinProfiles[ profileKey ][ 2 ];
 
  430     mOptionsMap[ profileKey ] = QString();
 
  432   mOptionsLineEdit->setText( mOptionsMap.value( currentProfileKey() ) );
 
  433   mOptionsLineEdit->setCursorPosition( 0 );
 
  437 void QgsRasterFormatSaveOptionsWidget::optionsTableEnableDeleteButton()
 
  439   mOptionsDeleteButton->setEnabled( mOptionsTable->currentRow() >= 0 );
 
  442 void QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked()
 
  444   mOptionsTable->insertRow( mOptionsTable->rowCount() );
 
  446   int newRow = mOptionsTable->rowCount() - 1;
 
  447   QTableWidgetItem *item = 
new QTableWidgetItem();
 
  448   mOptionsTable->setItem( newRow, 0, item );
 
  449   mOptionsTable->setCurrentItem( item );
 
  452 void QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked()
 
  454   if ( mOptionsTable->currentRow() >= 0 )
 
  456     mOptionsTable->removeRow( mOptionsTable->currentRow() );
 
  458     QTableWidgetItem *item = mOptionsTable->item( mOptionsTable->currentRow(), 0 );
 
  459     mOptionsTable->setCurrentItem( item );
 
  460     optionsTableChanged();
 
  464 QString QgsRasterFormatSaveOptionsWidget::settingsKey( QString profileName )
 const 
  466   if ( !profileName.isEmpty() )
 
  467     profileName = 
"/profile_" + profileName;
 
  469     profileName = 
"/profile_default" + profileName;
 
  470   return mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + profileName + 
"/create";
 
  473 QString QgsRasterFormatSaveOptionsWidget::currentProfileKey()
 const 
  475   return mProfileComboBox->currentData().toString();
 
  480 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) 
  481   return mOptionsMap.value( currentProfileKey() ).trimmed().split( 
' ', QString::SkipEmptyParts );
 
  483   return mOptionsMap.value( currentProfileKey() ).trimmed().split( 
' ', Qt::SkipEmptyParts );
 
  487 QString QgsRasterFormatSaveOptionsWidget::createOptions( 
const QString &profileName )
 const 
  489   QgsSettings mySettings;
 
  490   return mySettings.value( settingsKey( profileName ), 
"" ).toString();
 
  493 void QgsRasterFormatSaveOptionsWidget::deleteCreateOptions( 
const QString &profileName )
 
  495   QgsSettings mySettings;
 
  496   mySettings.remove( settingsKey( profileName ) );
 
  499 void QgsRasterFormatSaveOptionsWidget::setCreateOptions()
 
  501   QgsSettings mySettings;
 
  502   QStringList myProfiles;
 
  503   QMap< QString, QString >::const_iterator i = mOptionsMap.constBegin();
 
  504   while ( i != mOptionsMap.constEnd() )
 
  506     setCreateOptions( i.key(), i.value() );
 
  507     myProfiles << i.key();
 
  510   mySettings.setValue( mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + 
"/profiles",
 
  512   mySettings.setValue( mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + 
"/defaultProfile",
 
  513                        currentProfileKey().trimmed() );
 
  516 void QgsRasterFormatSaveOptionsWidget::setCreateOptions( 
const QString &profileName, 
const QString &options )
 
  518   QgsSettings mySettings;
 
  519   mySettings.setValue( settingsKey( profileName ), 
options.trimmed() );
 
  522 void QgsRasterFormatSaveOptionsWidget::setCreateOptions( 
const QString &profileName, 
const QStringList &list )
 
  524   setCreateOptions( profileName, list.join( QLatin1Char( 
' ' ) ) );
 
  527 QStringList QgsRasterFormatSaveOptionsWidget::profiles()
 const 
  529   QgsSettings mySettings;
 
  530   return mySettings.value( mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + 
"/profiles", 
"" ).toStringList();
 
  533 void QgsRasterFormatSaveOptionsWidget::swapOptionsUI( 
int newIndex )
 
  537   if ( newIndex == -1 )
 
  539     oldIndex = mOptionsStackedWidget->currentIndex();
 
  540     newIndex = ( oldIndex + 1 ) % 2;
 
  544     oldIndex = ( newIndex + 1 ) % 2;
 
  548   mOptionsStackedWidget->setCurrentIndex( newIndex );
 
  549   mOptionsStackedWidget->widget( newIndex )->setSizePolicy(
 
  550     QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) );
 
  551   mOptionsStackedWidget->widget( oldIndex )->setSizePolicy(
 
  552     QSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored ) );
 
  553   layout()->activate();
 
  558 void QgsRasterFormatSaveOptionsWidget::updateControls()
 
  560   bool valid = mProvider == QLatin1String( 
"gdal" ) && !mFormat.isEmpty();
 
  561   mOptionsValidateButton->setEnabled( valid );
 
  562   mOptionsHelpButton->setEnabled( valid );
 
  566 bool QgsRasterFormatSaveOptionsWidget::eventFilter( QObject *obj, QEvent *event )
 
  568   if ( event->type() == QEvent::MouseButtonPress )
 
  570     QMouseEvent *mouseEvent = 
static_cast<QMouseEvent *
>( event );
 
  571     if ( mouseEvent && ( mouseEvent->button() == Qt::RightButton ) )
 
  573       QMenu *menu = 
nullptr;
 
  575       if ( mOptionsStackedWidget->currentIndex() == 0 )
 
  576         text = tr( 
"Use simple interface" );
 
  578         text = tr( 
"Use table interface" );
 
  579       if ( obj->objectName() == QLatin1String( 
"mOptionsLineEdit" ) )
 
  581         menu = mOptionsLineEdit->createStandardContextMenu();
 
  582         menu->addSeparator();
 
  585         menu = 
new QMenu( 
this );
 
  586       QAction *action = 
new QAction( text, menu );
 
  587       menu->addAction( action );
 
  588       connect( action, &QAction::triggered, 
this, &QgsRasterFormatSaveOptionsWidget::swapOptionsUI );
 
  589       menu->exec( mouseEvent->globalPos() );
 
  595   return QObject::eventFilter( obj, event );
 
  601   mOptionsTable->horizontalHeader()->resizeSection( 0, mOptionsTable->width() - 115 );
 
  607   mBlockOptionUpdates++;
 
  608   mOptionsTable->clearContents();
 
  610 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) 
  611   const QStringList optionsList = 
options.trimmed().split( 
' ', QString::SkipEmptyParts );
 
  613   const QStringList optionsList = 
options.trimmed().split( 
' ', Qt::SkipEmptyParts );
 
  615   for ( 
const QString &opt : optionsList )
 
  617     int rowCount = mOptionsTable->rowCount();
 
  618     mOptionsTable->insertRow( rowCount );
 
  620     const QStringList values = opt.split( 
'=' );
 
  621     if ( values.count() == 2 )
 
  623       QTableWidgetItem *nameItem = 
new QTableWidgetItem( values.at( 0 ) );
 
  624       mOptionsTable->setItem( rowCount, 0, nameItem );
 
  625       QTableWidgetItem *valueItem = 
new QTableWidgetItem( values.at( 1 ) );
 
  626       mOptionsTable->setItem( rowCount, 1, valueItem );
 
  632   mProfileComboBox->setCurrentIndex( 0 );
 
  634   mOptionsMap[ currentProfileKey()] = 
options.trimmed();
 
  635   mOptionsLineEdit->setText( 
options.trimmed() );
 
  636   mOptionsLineEdit->setCursorPosition( 0 );
 
  638   mBlockOptionUpdates--;
 
A generic dialog with layout and button box.
QVBoxLayout * layout()
Returns the central layout. Widgets added to it must have this dialog as parent.
static QString helpCreationOptionsFormat(const QString &format)
Gets creation options metadata for a given format.
static QString validateCreationOptionsFormat(const QStringList &createOptions, const QString &format)
Validates creation options for a given format, regardless of layer.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
virtual QString validateCreationOptions(const QStringList &createOptions, const QString &format)
Validates creation options for a specific dataset and destination format.
virtual QString validatePyramidsConfigOptions(QgsRaster::RasterPyramidsFormat pyramidsFormat, const QStringList &configOptions, const QString &fileFormat)
Validates pyramid creation options for a specific dataset and destination format.
Represents a raster layer.
int bandCount() const
Returns the number of bands in this layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Setting options for loading raster layers.