29 #include "cpl_string.h"
35 #include <QFileDialog>
36 #include <QMessageBox>
41 QWidget *parent, Qt::WindowFlags f )
42 : QDialog( parent, f )
43 , mRasterLayer( rasterLayer )
44 , mDataProvider( sourceProvider )
45 , mCurrentExtent( currentExtent )
46 , mLayerCrs( layerCrs )
47 , mCurrentCrs( currentCrs )
48 , mResolutionState( OriginalResolution )
52 connect( mRawModeRadioButton, &QRadioButton::toggled,
this, &QgsRasterLayerSaveAsDialog::mRawModeRadioButton_toggled );
53 connect( mFormatComboBox, &QComboBox::currentTextChanged,
this, &QgsRasterLayerSaveAsDialog::mFormatComboBox_currentIndexChanged );
54 connect( mResolutionRadioButton, &QRadioButton::toggled,
this, &QgsRasterLayerSaveAsDialog::mResolutionRadioButton_toggled );
55 connect( mOriginalResolutionPushButton, &QPushButton::clicked,
this, &QgsRasterLayerSaveAsDialog::mOriginalResolutionPushButton_clicked );
56 connect( mXResolutionLineEdit, &QLineEdit::textEdited,
this, &QgsRasterLayerSaveAsDialog::mXResolutionLineEdit_textEdited );
57 connect( mYResolutionLineEdit, &QLineEdit::textEdited,
this, &QgsRasterLayerSaveAsDialog::mYResolutionLineEdit_textEdited );
58 connect( mOriginalSizePushButton, &QPushButton::clicked,
this, &QgsRasterLayerSaveAsDialog::mOriginalSizePushButton_clicked );
59 connect( mColumnsLineEdit, &QLineEdit::textEdited,
this, &QgsRasterLayerSaveAsDialog::mColumnsLineEdit_textEdited );
60 connect( mRowsLineEdit, &QLineEdit::textEdited,
this, &QgsRasterLayerSaveAsDialog::mRowsLineEdit_textEdited );
61 connect( mAddNoDataManuallyToolButton, &QPushButton::clicked,
this, &QgsRasterLayerSaveAsDialog::mAddNoDataManuallyToolButton_clicked );
62 connect( mLoadTransparentNoDataToolButton, &QPushButton::clicked,
this, &QgsRasterLayerSaveAsDialog::mLoadTransparentNoDataToolButton_clicked );
63 connect( mRemoveSelectedNoDataToolButton, &QPushButton::clicked,
this, &QgsRasterLayerSaveAsDialog::mRemoveSelectedNoDataToolButton_clicked );
64 connect( mRemoveAllNoDataToolButton, &QPushButton::clicked,
this, &QgsRasterLayerSaveAsDialog::mRemoveAllNoDataToolButton_clicked );
65 connect( mTileModeCheckBox, &QCheckBox::toggled,
this, &QgsRasterLayerSaveAsDialog::mTileModeCheckBox_toggled );
66 connect( mPyramidsGroupBox, &QgsCollapsibleGroupBox::toggled,
this, &QgsRasterLayerSaveAsDialog::mPyramidsGroupBox_toggled );
72 mNoDataTableWidget->setColumnCount( 2 );
73 mNoDataTableWidget->setHorizontalHeaderItem( 0,
new QTableWidgetItem( tr(
"From" ) ) );
74 mNoDataTableWidget->setHorizontalHeaderItem( 1,
new QTableWidgetItem( tr(
"To" ) ) );
76 mRawModeRadioButton_toggled(
true );
80 toggleResolutionSize();
82 insertAvailableOutputFormats();
89 setOriginalResolution();
90 int xSize = mDataProvider->
xSize();
91 int ySize = mDataProvider->
ySize();
92 mMaximumSizeXLineEdit->setText( QString::number( xSize ) );
93 mMaximumSizeYLineEdit->setText( QString::number( ySize ) );
97 mTileModeCheckBox->setChecked(
true );
98 mMaximumSizeXLineEdit->setText( QString::number( 2000 ) );
99 mMaximumSizeYLineEdit->setText( QString::number( 2000 ) );
103 mCreateOptionsWidget->setProvider( mDataProvider->
name() );
104 if ( mDataProvider->
name() == QLatin1String(
"gdal" ) )
106 mCreateOptionsWidget->setFormat( mFormatComboBox->currentData().toString() );
108 mCreateOptionsWidget->setRasterLayer( mRasterLayer );
109 mCreateOptionsWidget->update();
117 mPyramidsOptionsWidget->createOptionsWidget()->setRasterLayer( mRasterLayer );
122 mPyramidsUseExistingCheckBox->setEnabled(
false );
123 mPyramidsUseExistingCheckBox->setVisible(
false );
125 populatePyramidsLevels();
127 this, &QgsRasterLayerSaveAsDialog::populatePyramidsLevels );
131 mPyramidsGroupBox->setEnabled(
false );
136 mCreateOptionsGroupBox->setSaveCheckedState(
true );
141 mCrsSelector->setLayerCrs( mLayerCrs );
143 mCrsSelector->setCrs( mLayerCrs );
146 this, &QgsRasterLayerSaveAsDialog::crsChanged );
148 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
151 okButton->setEnabled(
false );
155 mHelpButtonBox->setVisible(
false );
156 mButtonBox->addButton( QDialogButtonBox::Help );
157 connect( mButtonBox, &QDialogButtonBox::helpRequested,
this, &QgsRasterLayerSaveAsDialog::showHelp );
159 connect( mHelpButtonBox, &QDialogButtonBox::helpRequested,
this, &QgsRasterLayerSaveAsDialog::showHelp );
162 connect( mButtonBox, &QDialogButtonBox::rejected,
this, &QgsRasterLayerSaveAsDialog::reject );
164 mExtentGroupBox->setOutputCrs(
outputCrs() );
165 mExtentGroupBox->setOriginalExtent( mDataProvider->
extent(), mLayerCrs );
166 mExtentGroupBox->setCurrentExtent( mCurrentExtent, mCurrentCrs );
167 mExtentGroupBox->setOutputExtentFromOriginal();
170 recalcResolutionSize();
174 if ( mTileModeCheckBox->isChecked() )
176 mTilesGroupBox->show();
178 mFilename->setDialogTitle( tr(
"Select Output Directory" ) );
182 mTilesGroupBox->hide();
184 mFilename->setDialogTitle( tr(
"Save Layer As" ) );
187 mFilename->setDefaultRoot( settings.
value( QStringLiteral(
"UI/lastRasterFileDir" ), QDir::homePath() ).toString() );
191 QFileInfo tmplFileInfo( filePath );
192 settings.
setValue( QStringLiteral(
"UI/lastRasterFileDir" ), tmplFileInfo.absolutePath() );
194 if ( !filePath.isEmpty() && mLayerName->isEnabled() )
196 QFileInfo fileInfo( filePath );
197 mLayerName->setText( fileInfo.baseName() );
200 if ( mTileModeCheckBox->isChecked() )
202 QString fileName = filePath;
207 if ( fileName.isEmpty() )
211 QDir dir( fileName );
212 QString baseName = QFileInfo( fileName ).baseName();
214 filters << QStringLiteral(
"%1.*" ).arg( baseName );
215 QStringList files = dir.entryList( filters );
216 if ( files.isEmpty() )
219 if ( QMessageBox::warning(
this, tr(
"Save Raster Layer" ),
220 tr(
"The directory %1 contains files which will be overwritten: %2" ).arg( dir.absolutePath(), files.join( QStringLiteral(
", " ) ) ),
221 QMessageBox::Ok | QMessageBox::Cancel ) == QMessageBox::Ok )
224 fileName = QFileDialog::getExistingDirectory(
this, tr(
"Select output directory" ), tmplFileInfo.absolutePath() );
228 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
233 okButton->setEnabled( tmplFileInfo.absoluteDir().exists() );
237 void QgsRasterLayerSaveAsDialog::insertAvailableOutputFormats()
241 int nDrivers = GDALGetDriverCount();
242 QMap< int, QPair< QString, QString > > topPriorityDrivers;
243 QMap< QString, QString > lowPriorityDrivers;
245 for (
int i = 0; i < nDrivers; ++i )
247 GDALDriverH driver = GDALGetDriver( i );
252 QString driverShortName = GDALGetDriverShortName( driver );
253 QString driverLongName = GDALGetDriverLongName( driver );
254 if ( driverShortName == QLatin1String(
"MEM" ) )
261 else if ( driverShortName == QLatin1String(
"VRT" ) )
266 else if ( driverShortName == QStringLiteral(
"GTiff" ) )
269 topPriorityDrivers.insert( 1, qMakePair( driverLongName, driverShortName ) );
271 else if ( driverShortName == QStringLiteral(
"GPKG" ) )
274 topPriorityDrivers.insert( 2, qMakePair( driverLongName, driverShortName ) );
278 lowPriorityDrivers.insert( driverLongName, driverShortName );
285 for (
auto priorityDriversIt = topPriorityDrivers.constBegin(); priorityDriversIt != topPriorityDrivers.constEnd(); ++priorityDriversIt )
287 mFormatComboBox->addItem( priorityDriversIt.value().first, priorityDriversIt.value().second );
290 for (
auto lowPriorityDriversIt = lowPriorityDrivers.constBegin(); lowPriorityDriversIt != lowPriorityDrivers.constEnd(); ++lowPriorityDriversIt )
292 mFormatComboBox->addItem( lowPriorityDriversIt.key(), lowPriorityDriversIt.value() );
297 void QgsRasterLayerSaveAsDialog::setValidators()
299 mXResolutionLineEdit->setValidator(
new QDoubleValidator(
this ) );
300 mYResolutionLineEdit->setValidator(
new QDoubleValidator(
this ) );
301 mColumnsLineEdit->setValidator(
new QIntValidator(
this ) );
302 mRowsLineEdit->setValidator(
new QIntValidator(
this ) );
303 mMaximumSizeXLineEdit->setValidator(
new QIntValidator(
this ) );
304 mMaximumSizeYLineEdit->setValidator(
new QIntValidator(
this ) );
307 void QgsRasterLayerSaveAsDialog::mFormatComboBox_currentIndexChanged(
const QString & )
310 if ( mDataProvider && mDataProvider->
name() == QLatin1String(
"gdal" ) )
313 mCreateOptionsWidget->update();
318 if ( extensions.empty() )
319 filter = tr(
"All files (*.*)" );
322 filter = QStringLiteral(
"%1 (*.%2);;%3" ).arg( mFormatComboBox->currentText(),
323 extensions.join( QStringLiteral(
" *." ) ),
324 tr(
"All files (*.*)" ) );
326 mFilename->setFilter( filter );
329 mTileModeCheckBox->setEnabled(
outputFormat() != QStringLiteral(
"GPKG" ) );
330 mFilename->setConfirmOverwrite(
outputFormat() != QStringLiteral(
"GPKG" ) );
331 mLayerName->setEnabled(
outputFormat() == QStringLiteral(
"GPKG" ) );
332 if ( mLayerName->isEnabled() )
334 QString layerName = QFileInfo( mFilename->filePath() ).baseName();
335 mLayerName->setText( layerName );
336 mTileModeCheckBox->setChecked(
false );
340 mLayerName->setText( QString() );
346 return mColumnsLineEdit->text().toInt();
351 return mRowsLineEdit->text().toInt();
356 return mXResolutionLineEdit->text().toDouble();
361 return mYResolutionLineEdit->text().toDouble();
366 return mMaximumSizeXLineEdit->text().toInt();
371 return mMaximumSizeYLineEdit->text().toInt();
376 return mTileModeCheckBox->isChecked();
381 return mAddToCanvas->isChecked();
386 mAddToCanvas->setChecked( checked );
391 QString fileName = mFilename->filePath();
397 if ( !extensions.empty() )
399 defaultExt = extensions.at( 0 );
403 QFileInfo fi( fileName );
404 if ( !fileName.isEmpty() && fi.suffix().isEmpty() )
406 fileName +=
'.' + defaultExt;
415 if ( mLayerName->text().isEmpty() &&
outputFormat() == QStringLiteral(
"GPKG" ) && !mTileModeCheckBox->isChecked() )
418 return QFileInfo( mFilename->filePath() ).baseName();
422 return mLayerName->text();
428 return mFormatComboBox->currentData().toString();
433 QStringList options = mCreateOptionsGroupBox->isChecked() ? mCreateOptionsWidget->options() : QStringList();
437 int indx = options.indexOf( QRegularExpression(
"^RASTER_TABLE=.*", QRegularExpression::CaseInsensitiveOption | QRegularExpression::MultilineOption ) );
440 options.replace( indx, QStringLiteral(
"RASTER_TABLE=%1" ).arg(
outputLayerName() ) );
444 options.append( QStringLiteral(
"RASTER_TABLE=%1" ).arg(
outputLayerName() ) );
448 if ( !outputLayerExists() )
450 indx = options.indexOf( QRegularExpression(
"^APPEND_SUBDATASET=.*", QRegularExpression::CaseInsensitiveOption | QRegularExpression::MultilineOption ) );
453 options.replace( indx, QStringLiteral(
"APPEND_SUBDATASET=YES" ) );
457 options.append( QStringLiteral(
"APPEND_SUBDATASET=YES" ) );
466 return mExtentGroupBox->outputExtent();
471 mFormatLabel->hide();
472 mFormatComboBox->hide();
477 mSaveAsLabel->hide();
479 QPushButton *okButton = mButtonBox->button( QDialogButtonBox::Ok );
482 okButton->setEnabled(
true );
486 void QgsRasterLayerSaveAsDialog::toggleResolutionSize()
490 bool on = mResolutionRadioButton->isChecked();
491 mXResolutionLineEdit->setEnabled( on );
492 mYResolutionLineEdit->setEnabled( on );
493 mOriginalResolutionPushButton->setEnabled( on && hasResolution );
494 mColumnsLineEdit->setEnabled( !on );
495 mRowsLineEdit->setEnabled( !on );
496 mOriginalSizePushButton->setEnabled( !on && hasResolution );
499 void QgsRasterLayerSaveAsDialog::setOriginalResolution()
511 xRes = yRes = mDataProvider->
extent().
width() / 100;
513 setResolution( xRes, yRes, mLayerCrs );
531 QgsRectangle srcExtent( srsCenter.
x() - xRes / 2, srsCenter.
y() - yRes / 2, srsCenter.
x() + xRes / 2, srsCenter.
y() + yRes / 2 );
534 xRes = extent.
width();
537 mXResolutionLineEdit->setText( QString::number( xRes ) );
538 mYResolutionLineEdit->setText( QString::number( yRes ) );
541 void QgsRasterLayerSaveAsDialog::recalcSize()
546 mColumnsLineEdit->setText( QString::number( xSize ) );
547 mRowsLineEdit->setText( QString::number( ySize ) );
548 updateResolutionStateMsg();
551 void QgsRasterLayerSaveAsDialog::setOriginalSize()
553 mColumnsLineEdit->setText( QString::number( mDataProvider->
xSize() ) );
554 mRowsLineEdit->setText( QString::number( mDataProvider->
ySize() ) );
558 void QgsRasterLayerSaveAsDialog::recalcResolution()
563 mXResolutionLineEdit->setText( QString::number( xRes ) );
564 mYResolutionLineEdit->setText( QString::number( yRes ) );
565 updateResolutionStateMsg();
568 void QgsRasterLayerSaveAsDialog::recalcResolutionSize()
570 if ( mResolutionRadioButton->isChecked() )
581 void QgsRasterLayerSaveAsDialog::updateResolutionStateMsg()
584 switch ( mResolutionState )
590 msg = tr(
"user defined" );
595 msg = tr(
"Resolution (current: %1)" ).arg( msg );
596 mResolutionGroupBox->setTitle( msg );
599 void QgsRasterLayerSaveAsDialog::extentChanged()
602 if ( mSizeRadioButton->isChecked() )
606 recalcResolutionSize();
609 void QgsRasterLayerSaveAsDialog::crsChanged()
613 mExtentGroupBox->setOutputCrs(
outputCrs() );
616 if ( mResolutionRadioButton->isChecked() )
620 setOriginalResolution();
639 return mCrsSelector->crs();
648 void QgsRasterLayerSaveAsDialog::mRawModeRadioButton_toggled(
bool checked )
650 mNoDataGroupBox->setEnabled( checked && mDataProvider->
bandCount() == 1 );
653 void QgsRasterLayerSaveAsDialog::mAddNoDataManuallyToolButton_clicked()
655 addNoDataRow( std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN() );
658 void QgsRasterLayerSaveAsDialog::mLoadTransparentNoDataToolButton_clicked()
660 if ( !mRasterLayer->
renderer() )
return;
662 if ( !rasterTransparency )
return;
667 if ( transparencyPixel.percentTransparent == 100 )
669 addNoDataRow( transparencyPixel.min, transparencyPixel.max );
670 if ( transparencyPixel.min != transparencyPixel.max )
672 setNoDataToEdited( mNoDataTableWidget->rowCount() - 1 );
678 void QgsRasterLayerSaveAsDialog::mRemoveSelectedNoDataToolButton_clicked()
680 mNoDataTableWidget->removeRow( mNoDataTableWidget->currentRow() );
683 void QgsRasterLayerSaveAsDialog::mRemoveAllNoDataToolButton_clicked()
685 while ( mNoDataTableWidget->rowCount() > 0 )
687 mNoDataTableWidget->removeRow( 0 );
691 void QgsRasterLayerSaveAsDialog::addNoDataRow(
double min,
double max )
693 mNoDataTableWidget->insertRow( mNoDataTableWidget->rowCount() );
694 for (
int i = 0; i < 2; i++ )
696 double value = i == 0 ? min : max;
697 QLineEdit *lineEdit =
new QLineEdit();
698 lineEdit->setFrame(
false );
699 lineEdit->setContentsMargins( 1, 1, 1, 1 );
705 lineEdit->setValidator(
new QDoubleValidator(
nullptr ) );
706 if ( !std::isnan( value ) )
712 lineEdit->setValidator(
new QIntValidator(
nullptr ) );
713 if ( !std::isnan( value ) )
715 valueString = QString::number(
static_cast<int>( value ) );
719 lineEdit->setText( valueString );
720 mNoDataTableWidget->setCellWidget( mNoDataTableWidget->rowCount() - 1, i, lineEdit );
722 adjustNoDataCellWidth( mNoDataTableWidget->rowCount() - 1, i );
724 connect( lineEdit, &QLineEdit::textEdited,
this, &QgsRasterLayerSaveAsDialog::noDataCellTextEdited );
726 mNoDataTableWidget->resizeColumnsToContents();
727 mNoDataTableWidget->resizeRowsToContents();
730 void QgsRasterLayerSaveAsDialog::noDataCellTextEdited(
const QString &text )
734 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender() );
735 if ( !lineEdit )
return;
738 for (
int r = 0; r < mNoDataTableWidget->rowCount(); r++ )
740 for (
int c = 0;
c < mNoDataTableWidget->columnCount();
c++ )
742 if ( mNoDataTableWidget->cellWidget( r,
c ) == sender() )
749 if ( row != -1 )
break;
751 QgsDebugMsg( QStringLiteral(
"row = %1 column =%2" ).arg( row ).arg( column ) );
755 QLineEdit *toLineEdit =
dynamic_cast<QLineEdit *
>( mNoDataTableWidget->cellWidget( row, 1 ) );
756 if ( !toLineEdit )
return;
757 bool toChanged = mNoDataToEdited.value( row );
758 QgsDebugMsg( QStringLiteral(
"toChanged = %1" ).arg( toChanged ) );
761 toLineEdit->setText( lineEdit->text() );
764 else if ( column == 1 )
766 setNoDataToEdited( row );
770 void QgsRasterLayerSaveAsDialog::mTileModeCheckBox_toggled(
bool toggled )
789 mTilesGroupBox->show();
791 mFilename->setDialogTitle( tr(
"Select Output Directory" ) );
795 mTilesGroupBox->hide();
797 mFilename->setDialogTitle( tr(
"Save Layer As" ) );
801 void QgsRasterLayerSaveAsDialog::mPyramidsGroupBox_toggled(
bool toggled )
804 populatePyramidsLevels();
807 void QgsRasterLayerSaveAsDialog::populatePyramidsLevels()
811 if ( mPyramidsGroupBox->isChecked() )
813 QList<QgsRasterPyramid> myPyramidList;
816 if ( mPyramidsUseExistingCheckBox->isChecked() )
822 if ( ! mPyramidsOptionsWidget->overviewList().isEmpty() )
823 myPyramidList = mDataProvider->
buildPyramidList( mPyramidsOptionsWidget->overviewList() );
825 QList<QgsRasterPyramid>::iterator myRasterPyramidIterator;
826 for ( myRasterPyramidIterator = myPyramidList.begin();
827 myRasterPyramidIterator != myPyramidList.end();
828 ++myRasterPyramidIterator )
830 if ( ! mPyramidsUseExistingCheckBox->isChecked() || myRasterPyramidIterator->exists )
832 text += QString::number( myRasterPyramidIterator->xDim ) + QStringLiteral(
"x" ) +
833 QString::number( myRasterPyramidIterator->yDim ) +
' ';
838 mPyramidResolutionsLineEdit->setText( text.trimmed() );
841 void QgsRasterLayerSaveAsDialog::setNoDataToEdited(
int row )
843 if ( row >= mNoDataToEdited.size() )
845 mNoDataToEdited.resize( row + 1 );
847 mNoDataToEdited[row] =
true;
850 double QgsRasterLayerSaveAsDialog::noDataCellValue(
int row,
int column )
const
852 QLineEdit *lineEdit =
dynamic_cast<QLineEdit *
>( mNoDataTableWidget->cellWidget( row, column ) );
853 if ( !lineEdit || lineEdit->text().isEmpty() )
855 return std::numeric_limits<double>::quiet_NaN();
857 return lineEdit->text().toDouble();
860 void QgsRasterLayerSaveAsDialog::adjustNoDataCellWidth(
int row,
int column )
862 QLineEdit *lineEdit =
dynamic_cast<QLineEdit *
>( mNoDataTableWidget->cellWidget( row, column ) );
863 if ( !lineEdit )
return;
865 int width = std::max( lineEdit->fontMetrics().boundingRect( lineEdit->text() ).width() + 10, 100 );
866 width = std::max( width, mNoDataTableWidget->columnWidth( column ) );
868 lineEdit->setFixedWidth( width );
874 if ( ! mNoDataGroupBox->isChecked() )
877 int rows = mNoDataTableWidget->rowCount();
878 noDataList.reserve( rows );
879 for (
int r = 0; r < rows; r++ )
882 noDataList.append(
noData );
890 return mPyramidsGroupBox->isChecked() ? mPyramidsOptionsWidget->overviewList() : QList<int>();
895 if ( ! mPyramidsGroupBox->isChecked() )
897 else if ( mPyramidsUseExistingCheckBox->isChecked() )
903 bool QgsRasterLayerSaveAsDialog::validate()
const
905 if ( mCreateOptionsGroupBox->isChecked() )
907 QString message = mCreateOptionsWidget->validateOptions(
true,
false );
908 if ( !message.isNull() )
911 if ( mPyramidsGroupBox->isChecked() )
913 QString message = mPyramidsOptionsWidget->createOptionsWidget()->validateOptions(
true,
false );
914 if ( !message.isNull() )
920 bool QgsRasterLayerSaveAsDialog::outputLayerExists()
const
934 QgsRasterLayer rasterLayer( rasterUri, QString( ), QStringLiteral(
"gdal" ) );
935 if ( !vectorUri.isEmpty() )
937 QgsVectorLayer vectorLayer( vectorUri, QString( ), QStringLiteral(
"ogr" ) );
938 return rasterLayer.isValid() || vectorLayer.isValid();
942 return rasterLayer.isValid();
953 if (
outputFormat() == QStringLiteral(
"GPKG" ) && outputLayerExists() &&
954 QMessageBox::warning(
this, tr(
"Save Raster Layer" ),
955 tr(
"The layer %1 already exists in the target file, and overwriting layers in GeoPackage is not supported. "
957 QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
965 void QgsRasterLayerSaveAsDialog::showHelp()
967 QgsHelp::openHelp( QStringLiteral(
"managing_data_source/create_layers.html#save-layer-from-an-existing-file" ) );