Skip to content

Commit 4ac834d

Browse files
authored
Merge pull request #7879 from borysiasty/append_raster_to_geopackage
Append raster layer to an existing GeoPackage
2 parents ec2ddb4 + daf7f3e commit 4ac834d

File tree

5 files changed

+153
-9
lines changed

5 files changed

+153
-9
lines changed

python/gui/auto_generated/qgsrasterlayersaveasdialog.sip.in

+8
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ Constructor for QgsRasterLayerSaveAsDialog
5454
bool tileMode() const;
5555
bool addToCanvas() const;
5656
QString outputFileName() const;
57+
58+
QString outputLayerName() const;
59+
%Docstring
60+
Name of the output layer within GeoPackage file
61+
62+
.. versionadded:: 3.4
63+
%End
5764
QString outputFormat() const;
5865
QgsCoordinateReferenceSystem outputCrs();
5966
QStringList createOptions() const;
@@ -72,6 +79,7 @@ Constructor for QgsRasterLayerSaveAsDialog
7279
public slots:
7380
virtual void accept();
7481

82+
7583
};
7684

7785

src/app/qgisapp.cpp

+23-3
Original file line numberDiff line numberDiff line change
@@ -7156,13 +7156,16 @@ void QgisApp::saveAsRasterFile( QgsRasterLayer *rasterLayer )
71567156
bool tileMode = d.tileMode();
71577157
bool addToCanvas = d.addToCanvas();
71587158
QPointer< QgsRasterLayer > rlWeakPointer( rasterLayer );
7159+
QString outputLayerName = d.outputLayerName();
7160+
QString outputFormat = d.outputFormat();
71597161

71607162
QgsRasterFileWriterTask *writerTask = new QgsRasterFileWriterTask( fileWriter, pipe.release(), d.nColumns(), d.nRows(),
71617163
d.outputRectangle(), d.outputCrs() );
71627164

71637165
// when writer is successful:
71647166

7165-
connect( writerTask, &QgsRasterFileWriterTask::writeComplete, this, [this, tileMode, addToCanvas, rlWeakPointer ]( const QString & newFilename )
7167+
connect( writerTask, &QgsRasterFileWriterTask::writeComplete, this,
7168+
[this, tileMode, addToCanvas, rlWeakPointer, outputLayerName, outputFormat]( const QString & newFilename )
71667169
{
71677170
QString fileName = newFilename;
71687171
if ( tileMode )
@@ -7173,7 +7176,14 @@ void QgisApp::saveAsRasterFile( QgsRasterLayer *rasterLayer )
71737176

71747177
if ( addToCanvas )
71757178
{
7176-
addRasterLayers( QStringList( fileName ) );
7179+
if ( outputFormat == QLatin1String( "GPKG" ) && !outputLayerName.isEmpty() )
7180+
{
7181+
addRasterLayers( QStringList( QStringLiteral( "GPKG:%1:%2" ).arg( fileName, outputLayerName ) ) );
7182+
}
7183+
else
7184+
{
7185+
addRasterLayers( QStringList( fileName ) );
7186+
}
71777187
}
71787188
if ( rlWeakPointer )
71797189
emit layerSavedAs( rlWeakPointer, fileName );
@@ -12813,8 +12823,18 @@ bool QgisApp::addRasterLayers( QStringList const &fileNameQStringList, bool guiW
1281312823
{
1281412824
QFileInfo myFileInfo( *myIterator );
1281512825

12826+
// set the layer name to the file base name...
12827+
QString layerName = myFileInfo.completeBaseName();
12828+
12829+
// ...unless provided explicitly
12830+
const QVariantMap uriDetails = QgsProviderRegistry::instance()->decodeUri( QStringLiteral( "gdal" ), *myIterator );
12831+
if ( !uriDetails[ QStringLiteral( "layerName" ) ].toString().isEmpty() )
12832+
{
12833+
layerName = uriDetails[ QStringLiteral( "layerName" ) ].toString();
12834+
}
12835+
1281612836
// try to create the layer
12817-
QgsRasterLayer *layer = addRasterLayerPrivate( *myIterator, myFileInfo.completeBaseName(),
12837+
QgsRasterLayer *layer = addRasterLayerPrivate( *myIterator, layerName,
1281812838
QString(), guiWarning, true );
1281912839
if ( layer && layer->isValid() )
1282012840
{

src/gui/qgsrasterlayersaveasdialog.cpp

+98-1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterLayer *rasterLa
179179
QFileInfo tmplFileInfo( filePath );
180180
settings.setValue( QStringLiteral( "UI/lastRasterFileDir" ), tmplFileInfo.absolutePath() );
181181

182+
if ( !filePath.isEmpty() && mLayerName->isEnabled() )
183+
{
184+
QFileInfo fileInfo( filePath );
185+
mLayerName->setText( fileInfo.baseName() );
186+
}
187+
182188
if ( mTileModeCheckBox->isChecked() )
183189
{
184190
QString fileName = filePath;
@@ -314,6 +320,21 @@ void QgsRasterLayerSaveAsDialog::mFormatComboBox_currentIndexChanged( const QStr
314320
tr( "All files (*.*)" ) );
315321
}
316322
mFilename->setFilter( filter );
323+
324+
// Disable mTileModeCheckBox for GeoPackages
325+
mTileModeCheckBox->setEnabled( outputFormat() != QStringLiteral( "GPKG" ) );
326+
mFilename->setConfirmOverwrite( outputFormat() != QStringLiteral( "GPKG" ) );
327+
mLayerName->setEnabled( outputFormat() == QStringLiteral( "GPKG" ) );
328+
if ( mLayerName->isEnabled() )
329+
{
330+
QString layerName = QFileInfo( mFilename->filePath() ).baseName();
331+
mLayerName->setText( layerName );
332+
mTileModeCheckBox->setChecked( false );
333+
}
334+
else
335+
{
336+
mLayerName->setText( QString() );
337+
}
317338
}
318339

319340
int QgsRasterLayerSaveAsDialog::nColumns() const
@@ -380,14 +401,55 @@ QString QgsRasterLayerSaveAsDialog::outputFileName() const
380401
return fileName;
381402
}
382403

404+
QString QgsRasterLayerSaveAsDialog::outputLayerName() const
405+
{
406+
if ( mLayerName->text().isEmpty() && outputFormat() == QStringLiteral( "GPKG" ) && !mTileModeCheckBox->isChecked() )
407+
{
408+
// Always return layer name for GeoPackages
409+
return QFileInfo( mFilename->filePath() ).baseName();
410+
}
411+
else
412+
{
413+
return mLayerName->text();
414+
}
415+
}
416+
383417
QString QgsRasterLayerSaveAsDialog::outputFormat() const
384418
{
385419
return mFormatComboBox->currentData().toString();
386420
}
387421

388422
QStringList QgsRasterLayerSaveAsDialog::createOptions() const
389423
{
390-
return mCreateOptionsGroupBox->isChecked() ? mCreateOptionsWidget->options() : QStringList();
424+
QStringList options = mCreateOptionsGroupBox->isChecked() ? mCreateOptionsWidget->options() : QStringList();
425+
if ( outputFormat() == QStringLiteral( "GPKG" ) )
426+
{
427+
// Overwrite the GPKG table options
428+
int indx = options.indexOf( QRegularExpression( "^RASTER_TABLE=.*", QRegularExpression::CaseInsensitiveOption | QRegularExpression::MultilineOption ) );
429+
if ( indx > -1 )
430+
{
431+
options.replace( indx, QStringLiteral( "RASTER_TABLE=%1" ).arg( outputLayerName() ) );
432+
}
433+
else
434+
{
435+
options.append( QStringLiteral( "RASTER_TABLE=%1" ).arg( outputLayerName() ) );
436+
}
437+
438+
// Only enable the append mode if the layer doesn't exist yet. For existing layers a 'confirm overwrite' dialog will be shown.
439+
if ( !outputLayerExists() )
440+
{
441+
indx = options.indexOf( QRegularExpression( "^APPEND_SUBDATASET=.*", QRegularExpression::CaseInsensitiveOption | QRegularExpression::MultilineOption ) );
442+
if ( indx > -1 )
443+
{
444+
options.replace( indx, QStringLiteral( "APPEND_SUBDATASET=YES" ) );
445+
}
446+
else
447+
{
448+
options.append( QStringLiteral( "APPEND_SUBDATASET=YES" ) );
449+
}
450+
}
451+
}
452+
return options;
391453
}
392454

393455
QgsRectangle QgsRasterLayerSaveAsDialog::outputRectangle() const
@@ -845,6 +907,41 @@ bool QgsRasterLayerSaveAsDialog::validate() const
845907
return true;
846908
}
847909

910+
bool QgsRasterLayerSaveAsDialog::outputLayerExists() const
911+
{
912+
QString uri;
913+
if ( outputFormat() == QStringLiteral( "GPKG" ) )
914+
{
915+
uri = QStringLiteral( "GPKG:%1:%2" ).arg( outputFileName(), outputLayerName() );
916+
}
917+
else
918+
{
919+
uri = outputFileName();
920+
}
921+
922+
std::unique_ptr< QgsRasterLayer > layer( new QgsRasterLayer( uri, "", QStringLiteral( "gdal" ) ) );
923+
return layer->isValid();
924+
}
925+
926+
void QgsRasterLayerSaveAsDialog::accept()
927+
{
928+
if ( !validate() )
929+
{
930+
return;
931+
}
932+
933+
if ( outputFormat() == QStringLiteral( "GPKG" ) && outputLayerExists() &&
934+
QMessageBox::warning( this, tr( "Save Raster Layer" ),
935+
tr( "The layer %1 already exists in the target file, and overwriting layers in GeoPackage is not supported. "
936+
"Do you want to overwrite the whole file?" ).arg( outputLayerName() ),
937+
QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No )
938+
{
939+
return;
940+
}
941+
942+
QDialog::accept();
943+
}
944+
848945
void QgsRasterLayerSaveAsDialog::showHelp()
849946
{
850947
QgsHelp::openHelp( QStringLiteral( "managing_data_source/create_layers.html#save-layer-from-an-existing-file" ) );

src/gui/qgsrasterlayersaveasdialog.h

+9-1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
7171
bool tileMode() const;
7272
bool addToCanvas() const;
7373
QString outputFileName() const;
74+
75+
/**
76+
* Name of the output layer within GeoPackage file
77+
* \since QGIS 3.4
78+
*/
79+
QString outputLayerName() const;
7480
QString outputFormat() const;
7581
QgsCoordinateReferenceSystem outputCrs();
7682
QStringList createOptions() const;
@@ -87,7 +93,7 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
8793
void hideOutput();
8894

8995
public slots:
90-
void accept() override { if ( validate() ) QDialog::accept(); }
96+
void accept() override;
9197

9298
private slots:
9399
void mRawModeRadioButton_toggled( bool );
@@ -138,6 +144,8 @@ class GUI_EXPORT QgsRasterLayerSaveAsDialog: public QDialog, private Ui::QgsRast
138144
double noDataCellValue( int row, int column ) const;
139145
void adjustNoDataCellWidth( int row, int column );
140146
bool validate() const;
147+
// Returns true if the output layer already exists.
148+
bool outputLayerExists() const;
141149

142150
void insertAvailableOutputFormats();
143151
};

src/ui/qgsrasterlayersaveasdialogbase.ui

+15-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<x>0</x>
88
<y>0</y>
99
<width>575</width>
10-
<height>580</height>
10+
<height>610</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
@@ -86,7 +86,7 @@
8686
</property>
8787
</widget>
8888
</item>
89-
<item row="3" column="1">
89+
<item row="4" column="1">
9090
<widget class="QgsProjectionSelectionWidget" name="mCrsSelector" native="true">
9191
<property name="focusPolicy">
9292
<enum>Qt::StrongFocus</enum>
@@ -118,7 +118,7 @@ datasets with maximum width and height specified below.</string>
118118
</item>
119119
</layout>
120120
</item>
121-
<item row="3" column="0">
121+
<item row="4" column="0">
122122
<widget class="QLabel" name="label_2">
123123
<property name="sizePolicy">
124124
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -147,7 +147,7 @@ datasets with maximum width and height specified below.</string>
147147
</property>
148148
</widget>
149149
</item>
150-
<item row="4" column="0" colspan="2">
150+
<item row="5" column="0" colspan="2">
151151
<widget class="QCheckBox" name="mAddToCanvas">
152152
<property name="text">
153153
<string>Add saved file to map</string>
@@ -157,6 +157,16 @@ datasets with maximum width and height specified below.</string>
157157
</property>
158158
</widget>
159159
</item>
160+
<item row="3" column="0">
161+
<widget class="QLabel" name="label_3">
162+
<property name="text">
163+
<string>Layer name</string>
164+
</property>
165+
</widget>
166+
</item>
167+
<item row="3" column="1">
168+
<widget class="QLineEdit" name="mLayerName"/>
169+
</item>
160170
</layout>
161171
</item>
162172
<item>
@@ -716,6 +726,7 @@ datasets with maximum width and height specified below.</string>
716726
<tabstop>mFormatComboBox</tabstop>
717727
<tabstop>mTileModeCheckBox</tabstop>
718728
<tabstop>mFilename</tabstop>
729+
<tabstop>mLayerName</tabstop>
719730
<tabstop>mCrsSelector</tabstop>
720731
<tabstop>mAddToCanvas</tabstop>
721732
<tabstop>mScrollArea</tabstop>

0 commit comments

Comments
 (0)