19#include "moc_qgsrasterformatsaveoptionswidget.cpp" 
   27#include <QInputDialog> 
   30#include <QContextMenuEvent> 
   35QMap<QString, QStringList> QgsRasterFormatSaveOptionsWidget::sBuiltinProfiles;
 
   37static const QString PYRAMID_JPEG_YCBCR_COMPRESSION( QStringLiteral( 
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG PHOTOMETRIC_OVERVIEW=YCBCR INTERLEAVE_OVERVIEW=PIXEL" ) );
 
   38static const QString PYRAMID_JPEG_COMPRESSION( QStringLiteral( 
"JPEG_QUALITY_OVERVIEW=75 COMPRESS_OVERVIEW=JPEG INTERLEAVE_OVERVIEW=PIXEL" ) );
 
   43  , mProvider( provider )
 
   48  mOptionsTable->setMinimumSize( 200, mOptionsTable->verticalHeader()->defaultSectionSize() * 4 + mOptionsTable->horizontalHeader()->height() + 2 );
 
   50  connect( mProfileNewButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked );
 
   51  connect( mProfileDeleteButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked );
 
   52  connect( mProfileResetButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked );
 
   53  connect( mOptionsAddButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked );
 
   54  connect( mOptionsDeleteButton, &QPushButton::clicked, 
this, &QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked );
 
   55  connect( mOptionsLineEdit, &QLineEdit::editingFinished, 
this, &QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished );
 
   59  if ( sBuiltinProfiles.isEmpty() )
 
   62    sBuiltinProfiles[QStringLiteral( 
"z_adefault" )] = ( QStringList() << QString() << tr( 
"Default" ) << QString() );
 
   67    sBuiltinProfiles[QStringLiteral( 
"z_gtiff_1big" )] = ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"No Compression" ) << QStringLiteral( 
"COMPRESS=NONE BIGTIFF=IF_NEEDED" ) );
 
   68    sBuiltinProfiles[QStringLiteral( 
"z_gtiff_2medium" )] = ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"Low Compression" ) << QStringLiteral( 
"COMPRESS=PACKBITS" ) );
 
   69    sBuiltinProfiles[QStringLiteral( 
"z_gtiff_3small" )] = ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"High Compression" ) << QStringLiteral( 
"COMPRESS=DEFLATE PREDICTOR=2 ZLEVEL=9" ) );
 
   70    sBuiltinProfiles[QStringLiteral( 
"z_gtiff_4jpeg" )] = ( QStringList() << QStringLiteral( 
"GTiff" ) << tr( 
"JPEG Compression" ) << QStringLiteral( 
"COMPRESS=JPEG JPEG_QUALITY=75" ) );
 
   75    sBuiltinProfiles[QStringLiteral( 
"z__pyramids_gtiff_1big" )] = ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"No Compression" ) << QStringLiteral( 
"COMPRESS_OVERVIEW=NONE BIGTIFF_OVERVIEW=IF_NEEDED" ) );
 
   76    sBuiltinProfiles[QStringLiteral( 
"z__pyramids_gtiff_2medium" )] = ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"Low Compression" ) << QStringLiteral( 
"COMPRESS_OVERVIEW=PACKBITS" ) );
 
   77    sBuiltinProfiles[QStringLiteral( 
"z__pyramids_gtiff_3small" )] = ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"High Compression" ) << QStringLiteral( 
"COMPRESS_OVERVIEW=DEFLATE PREDICTOR_OVERVIEW=2 ZLEVEL=9" ) ); 
 
   78    sBuiltinProfiles[QStringLiteral( 
"z__pyramids_gtiff_4jpeg" )] = ( QStringList() << QStringLiteral( 
"_pyramids" ) << tr( 
"JPEG Compression" ) << PYRAMID_JPEG_YCBCR_COMPRESSION );
 
   81  connect( mProfileComboBox, &QComboBox::currentTextChanged, 
this, &QgsRasterFormatSaveOptionsWidget::updateOptions );
 
   82  connect( mOptionsTable, &QTableWidget::cellChanged, 
this, &QgsRasterFormatSaveOptionsWidget::optionsTableChanged );
 
   84  connect( mOptionsValidateButton, &QAbstractButton::clicked, 
this, [
this] { 
validateOptions(); } );
 
   87  mOptionsLineEdit->installEventFilter( 
this );
 
   90  setContextMenuPolicy( Qt::CustomContextMenu );
 
   91  connect( 
this, &QWidget::customContextMenuRequested, 
this, [
this]( QPoint pos ) {
 
   94    if ( mTableWidget->isVisible() )
 
   95      text = tr( 
"Use Simple Interface" );
 
   97      text = tr( 
"Use Table Interface" );
 
   98    QAction *swapAction = menu.addAction( text );
 
   99    connect( swapAction, &QAction::triggered, 
this, [
this]() { swapOptionsUI( -1 ); } );
 
  100    menu.exec( this->mapToGlobal( pos ) );
 
 
  119  mProvider = provider;
 
 
  126  const QList<QWidget *> widgets = this->findChildren<QWidget *>();
 
  130    const auto constWidgets = widgets;
 
  131    for ( QWidget *widget : constWidgets )
 
  132      widget->setVisible( 
false );
 
  133    mOptionsWidget->setVisible( 
true );
 
  144    const auto constWidgets = widgets;
 
  145    for ( QWidget *widget : constWidgets )
 
  146      widget->setVisible( 
true );
 
  148      mProfileButtons->setVisible( 
false );
 
 
  158QString QgsRasterFormatSaveOptionsWidget::pseudoFormat()
 const 
  160  return mPyramids ? QStringLiteral( 
"_pyramids" ) : mFormat;
 
  166  const QString format = pseudoFormat();
 
  167  QStringList profileKeys = profiles();
 
  168  QMapIterator<QString, QStringList> it( sBuiltinProfiles );
 
  169  while ( it.hasNext() )
 
  172    const QString profileKey = it.key();
 
  173    if ( !profileKeys.contains( profileKey ) && !it.value().isEmpty() )
 
  176      if ( it.value()[0].isEmpty() || it.value()[0] == format )
 
  178        profileKeys.insert( 0, profileKey );
 
  182  std::sort( profileKeys.begin(), profileKeys.end() );
 
  186  mProfileComboBox->blockSignals( 
true );
 
  187  mProfileComboBox->clear();
 
  188  const auto constProfileKeys = profileKeys;
 
  189  for ( 
const QString &profileKey : constProfileKeys )
 
  191    QString profileName, profileOptions;
 
  192    profileOptions = creationOptions( profileKey );
 
  193    if ( sBuiltinProfiles.contains( profileKey ) )
 
  195      profileName = sBuiltinProfiles[profileKey][1];
 
  196      if ( profileOptions.isEmpty() )
 
  197        profileOptions = sBuiltinProfiles[profileKey][2];
 
  201      profileName = profileKey;
 
  203    mOptionsMap[profileKey] = profileOptions;
 
  204    mProfileComboBox->addItem( profileName, profileKey );
 
  208  mProfileComboBox->blockSignals( 
false );
 
  211  mProfileComboBox->setCurrentIndex( mProfileComboBox->findData( mySettings.
value(
 
  212    mProvider + 
"/driverOptions/" + format.toLower() + 
"/defaultProfile",
 
 
  218void QgsRasterFormatSaveOptionsWidget::updateOptions()
 
  220  mBlockOptionUpdates++;
 
  221  QString myOptions = mOptionsMap.value( currentProfileKey() );
 
  222  QStringList myOptionsList = myOptions.trimmed().split( 
' ', Qt::SkipEmptyParts );
 
  226  if ( mRasterLayer && mRasterLayer->
bandCount() != 3 && myOptions == PYRAMID_JPEG_YCBCR_COMPRESSION )
 
  228    myOptions = PYRAMID_JPEG_COMPRESSION;
 
  231  if ( mTableWidget->isVisible() )
 
  233    mOptionsTable->setRowCount( 0 );
 
  234    for ( 
int i = 0; i < myOptionsList.count(); i++ )
 
  236      QStringList key_value = myOptionsList[i].split( 
'=' );
 
  237      if ( key_value.count() == 2 )
 
  239        mOptionsTable->insertRow( i );
 
  240        mOptionsTable->setItem( i, 0, 
new QTableWidgetItem( key_value[0] ) );
 
  241        mOptionsTable->setItem( i, 1, 
new QTableWidgetItem( key_value[1] ) );
 
  247    mOptionsLineEdit->setText( myOptions );
 
  248    mOptionsLineEdit->setCursorPosition( 0 );
 
  251  mBlockOptionUpdates--;
 
  257  setCreationOptions();
 
 
  264  if ( mProvider == QLatin1String( 
"gdal" ) && !mFormat.isEmpty() && !mPyramids )
 
  267    if ( message.isEmpty() )
 
  268      message = tr( 
"Cannot get create options for driver %1" ).arg( mFormat );
 
  270  else if ( mProvider == QLatin1String( 
"gdal" ) && mPyramids )
 
  272    message = tr( 
"For details on pyramids options please see the following pages" );
 
  273    message += QLatin1String( 
"\n\nhttps://gdal.org/programs/gdaladdo.html\n\nhttps://gdal.org/drivers/raster/gtiff.html" );
 
  276    message = tr( 
"No help available" );
 
  280  dlg->setWindowTitle( tr( 
"Create Options for %1" ).arg( mFormat ) );
 
  281  QTextEdit *textEdit = 
new QTextEdit( dlg );
 
  282  textEdit->setReadOnly( 
true );
 
  284  textEdit->setText( message );
 
  285  dlg->
layout()->addWidget( textEdit );
 
  286  dlg->resize( 600, 400 );
 
 
  296  const QStringList creationOptions = 
options();
 
  299  QgsDebugMsgLevel( QStringLiteral( 
"layer: [%1] file: [%2] format: [%3]" ).arg( mRasterLayer ? mRasterLayer->
id() : 
"none", mRasterFileName, mFormat ), 2 );
 
  303  bool tmpLayer = 
false;
 
  304  if ( !( mRasterLayer && rasterLayer->
dataProvider() ) && !mRasterFileName.isNull() )
 
  309    rasterLayer = 
new QgsRasterLayer( mRasterFileName, QFileInfo( mRasterFileName ).baseName(), QStringLiteral( 
"gdal" ), 
options );
 
  312  if ( mProvider == QLatin1String( 
"gdal" ) && mPyramids )
 
  316      QgsDebugMsgLevel( QStringLiteral( 
"calling validate pyramids on layer's data provider" ), 2 );
 
  321      message = tr( 
"cannot validate pyramid options" );
 
  324  else if ( !creationOptions.isEmpty() && mProvider == QLatin1String( 
"gdal" ) && !mFormat.isEmpty() )
 
  328      QgsDebugMsgLevel( QStringLiteral( 
"calling validate on layer's data provider" ), 2 );
 
  337  else if ( !creationOptions.isEmpty() )
 
  339    QMessageBox::information( 
this, QString(), tr( 
"Cannot validate creation options." ), QMessageBox::Close );
 
  347    if ( message.isNull() )
 
  350        QMessageBox::information( 
this, QString(), tr( 
"Valid" ), QMessageBox::Close );
 
  354      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 );
 
 
  364void QgsRasterFormatSaveOptionsWidget::optionsTableChanged()
 
  366  if ( mBlockOptionUpdates )
 
  369  QTableWidgetItem *key, *value;
 
  371  for ( 
int i = 0; i < mOptionsTable->rowCount(); i++ )
 
  373    key = mOptionsTable->item( i, 0 );
 
  374    if ( !key || key->text().isEmpty() )
 
  376    value = mOptionsTable->item( i, 1 );
 
  377    if ( !value || value->text().isEmpty() )
 
  379    options += key->text() + 
'=' + value->text() + 
' ';
 
  382  mOptionsMap[currentProfileKey()] = 
options;
 
  383  mOptionsLineEdit->setText( 
options );
 
  384  mOptionsLineEdit->setCursorPosition( 0 );
 
  387void QgsRasterFormatSaveOptionsWidget::mOptionsLineEdit_editingFinished()
 
  389  mOptionsMap[currentProfileKey()] = mOptionsLineEdit->text().trimmed();
 
  392void QgsRasterFormatSaveOptionsWidget::mProfileNewButton_clicked()
 
  394  QString profileName = QInputDialog::getText( 
this, QString(), tr( 
"Profile name:" ) );
 
  395  if ( !profileName.isEmpty() )
 
  397    profileName = profileName.trimmed();
 
  398    mOptionsMap[profileName] = QString();
 
  399    mProfileComboBox->addItem( profileName, profileName );
 
  400    mProfileComboBox->setCurrentIndex( mProfileComboBox->count() - 1 );
 
  404void QgsRasterFormatSaveOptionsWidget::mProfileDeleteButton_clicked()
 
  406  const int index = mProfileComboBox->currentIndex();
 
  407  const QString profileKey = currentProfileKey();
 
  408  if ( index != -1 && !sBuiltinProfiles.contains( profileKey ) )
 
  410    mOptionsMap.remove( profileKey );
 
  411    mProfileComboBox->removeItem( index );
 
  415void QgsRasterFormatSaveOptionsWidget::mProfileResetButton_clicked()
 
  417  const QString profileKey = currentProfileKey();
 
  418  if ( sBuiltinProfiles.contains( profileKey ) )
 
  420    mOptionsMap[profileKey] = sBuiltinProfiles[profileKey][2];
 
  424    mOptionsMap[profileKey] = QString();
 
  426  mOptionsLineEdit->setText( mOptionsMap.value( currentProfileKey() ) );
 
  427  mOptionsLineEdit->setCursorPosition( 0 );
 
  431void QgsRasterFormatSaveOptionsWidget::optionsTableEnableDeleteButton()
 
  433  mOptionsDeleteButton->setEnabled( mOptionsTable->currentRow() >= 0 );
 
  436void QgsRasterFormatSaveOptionsWidget::mOptionsAddButton_clicked()
 
  438  mOptionsTable->insertRow( mOptionsTable->rowCount() );
 
  440  const int newRow = mOptionsTable->rowCount() - 1;
 
  441  QTableWidgetItem *item = 
new QTableWidgetItem();
 
  442  mOptionsTable->setItem( newRow, 0, item );
 
  443  mOptionsTable->setCurrentItem( item );
 
  446void QgsRasterFormatSaveOptionsWidget::mOptionsDeleteButton_clicked()
 
  448  if ( mOptionsTable->currentRow() >= 0 )
 
  450    mOptionsTable->removeRow( mOptionsTable->currentRow() );
 
  452    QTableWidgetItem *item = mOptionsTable->item( mOptionsTable->currentRow(), 0 );
 
  453    mOptionsTable->setCurrentItem( item );
 
  454    optionsTableChanged();
 
  458QString QgsRasterFormatSaveOptionsWidget::settingsKey( QString profileName )
 const 
  460  if ( !profileName.isEmpty() )
 
  461    profileName = 
"/profile_" + profileName;
 
  463    profileName = 
"/profile_default" + profileName;
 
  464  return mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + profileName + 
"/create";
 
  467QString QgsRasterFormatSaveOptionsWidget::currentProfileKey()
 const 
  469  return mProfileComboBox->currentData().toString();
 
  474  return mOptionsMap.value( currentProfileKey() ).trimmed().split( 
' ', Qt::SkipEmptyParts );
 
 
  477QString QgsRasterFormatSaveOptionsWidget::creationOptions( 
const QString &profileName )
 const 
  480  return mySettings.
value( settingsKey( profileName ), 
"" ).toString();
 
  483void QgsRasterFormatSaveOptionsWidget::deleteCreationOptions( 
const QString &profileName )
 
  486  mySettings.
remove( settingsKey( profileName ) );
 
  489void QgsRasterFormatSaveOptionsWidget::setCreationOptions()
 
  492  QStringList myProfiles;
 
  493  QMap<QString, QString>::const_iterator i = mOptionsMap.constBegin();
 
  494  while ( i != mOptionsMap.constEnd() )
 
  496    setCreationOptions( i.key(), i.value() );
 
  497    myProfiles << i.key();
 
  500  mySettings.
setValue( mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + 
"/profiles", myProfiles );
 
  501  mySettings.
setValue( mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + 
"/defaultProfile", currentProfileKey().trimmed() );
 
  504void QgsRasterFormatSaveOptionsWidget::setCreationOptions( 
const QString &profileName, 
const QString &options )
 
  510void QgsRasterFormatSaveOptionsWidget::setCreationOptions( 
const QString &profileName, 
const QStringList &options )
 
  512  setCreationOptions( profileName, 
options.join( QLatin1Char( 
' ' ) ) );
 
  515QStringList QgsRasterFormatSaveOptionsWidget::profiles()
 const 
  518  return mySettings.
value( mProvider + 
"/driverOptions/" + pseudoFormat().toLower() + 
"/profiles", 
"" ).toStringList();
 
  521void QgsRasterFormatSaveOptionsWidget::swapOptionsUI( 
int newIndex )
 
  526  bool lineEditMode = mOptionsLineEdit->isVisible();
 
  527  mOptionsLineEdit->setVisible( ( newIndex == -1 && !lineEditMode ) || newIndex == 1 );
 
  528  mTableWidget->setVisible( ( newIndex == -1 && lineEditMode ) || newIndex == 0 );
 
  532void QgsRasterFormatSaveOptionsWidget::updateControls()
 
  534  const bool valid = mProvider == QLatin1String( 
"gdal" ) && !mFormat.isEmpty();
 
  535  mOptionsValidateButton->setEnabled( valid );
 
  536  mOptionsHelpButton->setEnabled( valid );
 
  540bool QgsRasterFormatSaveOptionsWidget::eventFilter( QObject *obj, QEvent *event )
 
  542  if ( event->type() == QEvent::ContextMenu )
 
  544    QContextMenuEvent *contextEvent = 
static_cast<QContextMenuEvent *
>( event );
 
  545    QMenu *menu = 
nullptr;
 
  546    menu = mOptionsLineEdit->createStandardContextMenu();
 
  547    menu->addSeparator();
 
  548    QAction *action = 
new QAction( tr( 
"Use Table Interface" ), menu );
 
  549    menu->addAction( action );
 
  550    connect( action, &QAction::triggered, 
this, [
this] { swapOptionsUI( 0 ); } );
 
  551    menu->exec( contextEvent->globalPos() );
 
  556  return QObject::eventFilter( obj, event );
 
  563  mOptionsTable->horizontalHeader()->resizeSection( 0, mOptionsTable->width() - 115 );
 
 
  569  mBlockOptionUpdates++;
 
  570  mOptionsTable->clearContents();
 
  572  const QStringList optionsList = 
options.trimmed().split( 
' ', Qt::SkipEmptyParts );
 
  573  for ( 
const QString &opt : optionsList )
 
  575    const int rowCount = mOptionsTable->rowCount();
 
  576    mOptionsTable->insertRow( rowCount );
 
  578    const QStringList values = opt.split( 
'=' );
 
  579    if ( values.count() == 2 )
 
  581      QTableWidgetItem *nameItem = 
new QTableWidgetItem( values.at( 0 ) );
 
  582      mOptionsTable->setItem( rowCount, 0, nameItem );
 
  583      QTableWidgetItem *valueItem = 
new QTableWidgetItem( values.at( 1 ) );
 
  584      mOptionsTable->setItem( rowCount, 1, valueItem );
 
  590  mProfileComboBox->setCurrentIndex( 0 );
 
  592  mOptionsMap[currentProfileKey()] = 
options.trimmed();
 
  593  mOptionsLineEdit->setText( 
options.trimmed() );
 
  594  mOptionsLineEdit->setCursorPosition( 0 );
 
  596  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 &creationOptions, const QString &format)
Validates creation options for a given format, regardless of layer.
 
virtual QString validateCreationOptions(const QStringList &createOptions, const QString &format)
Validates creation options for a specific dataset and destination format.
 
virtual QString validatePyramidsConfigOptions(Qgis::RasterPyramidFormat 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.
 
Stores settings for use within QGIS.
 
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
 
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
 
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
 
#define QgsDebugMsgLevel(str, level)
 
Setting options for loading raster layers.
 
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.