35 #include <QPushButton> 37 #include <QMessageBox> 38 #include <QFileDialog> 42 #include <ogr_srs_api.h> 43 #include <gdal_version.h> 44 #include <cpl_error.h> 45 #include <cpl_string.h> 47 #define DEFAULT_OGR_FID_COLUMN_TITLE "fid" // default value from OGR 50 : QDialog( parent, fl )
55 connect( mAddAttributeButton, &QToolButton::clicked,
this, &QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked );
56 connect( mRemoveAttributeButton, &QToolButton::clicked,
this, &QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked );
57 connect( mFieldTypeBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged );
58 connect( mGeometryTypeBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged );
59 connect( mTableNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged );
60 connect( mTableNameEdit, &QLineEdit::textEdited,
this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited );
61 connect( mLayerIdentifierEdit, &QLineEdit::textEdited,
this, &QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited );
62 connect( buttonBox, &QDialogButtonBox::accepted,
this, &QgsNewGeoPackageLayerDialog::buttonBox_accepted );
63 connect( buttonBox, &QDialogButtonBox::rejected,
this, &QgsNewGeoPackageLayerDialog::buttonBox_rejected );
64 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsNewGeoPackageLayerDialog::showHelp );
73 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPointLayer.svg" ) ), tr(
"MultiPoint" ), wkbMultiPoint );
74 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconLineLayer.svg" ) ), tr(
"MultiLine" ), wkbMultiLineString );
75 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPolygonLayer.svg" ) ), tr(
"MultiPolygon" ), wkbMultiPolygon );
79 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconLineLayer.svg" ) ), tr(
"CircularString" ), wkbCircularString );
81 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconLineLayer.svg" ) ), tr(
"CompoundCurve" ), wkbCompoundCurve );
82 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPolygonLayer.svg" ) ), tr(
"CurvePolygon" ), wkbCurvePolygon );
84 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPolygonLayer.svg" ) ), tr(
"MultiSurface" ), wkbMultiSurface );
86 mGeometryWithZCheckBox->setEnabled(
false );
87 mGeometryWithMCheckBox->setEnabled(
false );
88 mGeometryColumnEdit->setEnabled(
false );
89 mGeometryColumnEdit->setText(
"geometry" );
91 mCheckBoxCreateSpatialIndex->setEnabled(
false );
92 mCrsSelector->setEnabled(
false );
95 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldInteger.svg" ) ), tr(
"Whole number (integer)" ),
"integer" );
96 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldInteger.svg" ) ), tr(
"Whole number (integer 64 bit)" ),
"integer64" );
97 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldFloat.svg" ) ), tr(
"Decimal number (real)" ),
"real" );
101 mOkButton = buttonBox->button( QDialogButtonBox::Ok );
102 mOkButton->setEnabled(
false );
104 connect( mFieldNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::fieldNameChanged );
105 connect( mAttributeView, &QTreeWidget::itemSelectionChanged,
this, &QgsNewGeoPackageLayerDialog::selectionChanged );
106 connect( mTableNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::checkOk );
108 mAddAttributeButton->setEnabled(
false );
109 mRemoveAttributeButton->setEnabled(
false );
111 mCheckBoxCreateSpatialIndex->setChecked(
true );
115 mDatabase->setFilter( tr(
"GeoPackage" ) +
" (*.gpkg)" );
116 mDatabase->setDialogTitle( tr(
"Select Existing or Create a New GeoPackage Database Fileā¦" ) );
117 mDatabase->setDefaultRoot( settings.
value( QStringLiteral(
"UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
118 mDatabase->setConfirmOverwrite(
false );
122 QFileInfo tmplFileInfo( filePath );
123 settings.
setValue( QStringLiteral(
"UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
124 if ( !filePath.isEmpty() && !mTableNameEdited )
126 QFileInfo fileInfo( filePath );
127 mTableNameEdit->setText( fileInfo.baseName() );
135 mCrsSelector->setCrs( crs );
140 mDatabase->setReadOnly(
true );
143 void QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged(
int )
145 QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
146 mFieldLengthEdit->setEnabled( myType == QLatin1String(
"text" ) );
147 if ( myType != QLatin1String(
"text" ) )
148 mFieldLengthEdit->clear();
152 void QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged(
int )
154 OGRwkbGeometryType geomType =
static_cast<OGRwkbGeometryType
> 155 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
156 bool isSpatial = geomType != wkbNone;
157 mGeometryWithZCheckBox->setEnabled( isSpatial );
158 mGeometryWithMCheckBox->setEnabled( isSpatial );
159 mGeometryColumnEdit->setEnabled( isSpatial );
160 mCheckBoxCreateSpatialIndex->setEnabled( isSpatial );
161 mCrsSelector->setEnabled( isSpatial );
164 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged(
const QString &text )
166 mTableNameEdited = !text.isEmpty();
167 if ( !text.isEmpty() && !mLayerIdentifierEdited )
169 mLayerIdentifierEdit->setText( text );
173 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited(
const QString &text )
176 mTableNameEdited = !text.isEmpty();
179 void QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited(
const QString &text )
182 mLayerIdentifierEdited = !text.isEmpty();
185 void QgsNewGeoPackageLayerDialog::checkOk()
187 bool ok = !mDatabase->filePath().isEmpty() &&
188 !mTableNameEdit->text().isEmpty();
189 mOkButton->setEnabled( ok );
192 void QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked()
194 if ( !mFieldNameEdit->text().isEmpty() )
196 QString myName = mFieldNameEdit->text();
198 if ( myName.compare( featureId, Qt::CaseInsensitive ) == 0 )
200 QMessageBox::critical(
this, tr(
"Add Field" ), tr(
"The field cannot have the same name as the feature identifier." ) );
205 QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
206 QString length = mFieldLengthEdit->text();
207 mAttributeView->addTopLevelItem(
new QTreeWidgetItem( QStringList() << myName << myType << length ) );
211 mFieldNameEdit->clear();
215 void QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked()
217 delete mAttributeView->currentItem();
222 void QgsNewGeoPackageLayerDialog::fieldNameChanged(
const QString &name )
224 mAddAttributeButton->setDisabled( name.isEmpty() || ! mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
227 void QgsNewGeoPackageLayerDialog::selectionChanged()
229 mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
232 void QgsNewGeoPackageLayerDialog::buttonBox_accepted()
238 void QgsNewGeoPackageLayerDialog::buttonBox_rejected()
243 bool QgsNewGeoPackageLayerDialog::apply()
245 QString fileName( mDatabase->filePath() );
246 if ( !fileName.endsWith( QLatin1String(
".gpkg" ), Qt::CaseInsensitive ) )
247 fileName += QLatin1String(
".gpkg" );
249 bool createNewDb =
false;
251 if ( QFile( fileName ).exists( fileName ) )
253 bool overwrite =
false;
260 msgBox.setIcon( QMessageBox::Question );
261 msgBox.setWindowTitle( tr(
"New GeoPackage Layer" ) );
262 msgBox.setText( tr(
"The File already exists. Do you want to overwrite the existing file with a new database or add a new layer to it?" ) );
263 QPushButton *overwriteButton = msgBox.addButton( tr(
"Overwrite" ), QMessageBox::ActionRole );
264 QPushButton *addNewLayerButton = msgBox.addButton( tr(
"Add New Layer" ), QMessageBox::ActionRole );
265 msgBox.setStandardButtons( QMessageBox::Cancel );
266 msgBox.setDefaultButton( addNewLayerButton );
268 if ( property(
"hideDialogs" ).toBool() )
270 overwrite = property(
"question_existing_db_answer_overwrite" ).toBool();
272 cancel = !property(
"question_existing_db_answer_add_new_layer" ).toBool();
276 int ret = msgBox.exec();
277 if ( ret == QMessageBox::Cancel )
279 if ( msgBox.clickedButton() == overwriteButton )
300 QFile( fileName ).remove();
309 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
312 if ( !property(
"hideDialogs" ).toBool() )
313 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ),
314 tr(
"Layer creation failed. GeoPackage driver not found." ) );
321 hDS.reset( OGR_Dr_CreateDataSource( hGpkgDriver, fileName.toUtf8().constData(), nullptr ) );
324 QString msg( tr(
"Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
325 if ( !property(
"hideDialogs" ).toBool() )
326 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
332 OGRSFDriverH hDriver =
nullptr;
333 hDS.reset( OGROpen( fileName.toUtf8().constData(),
true, &hDriver ) );
336 QString msg( tr(
"Opening of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
337 if ( !property(
"hideDialogs" ).toBool() )
338 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
341 if ( hDriver != hGpkgDriver )
343 QString msg( tr(
"Opening of file succeeded, but this is not a GeoPackage database." ) );
344 if ( !property(
"hideDialogs" ).toBool() )
345 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
350 QString tableName( mTableNameEdit->text() );
352 bool overwriteTable =
false;
353 if ( OGR_DS_GetLayerByName( hDS.get(), tableName.toUtf8().constData() ) )
355 if ( property(
"hideDialogs" ).toBool() )
357 overwriteTable = property(
"question_existing_layer_answer_overwrite" ).toBool();
359 else if ( QMessageBox::question(
this, tr(
"New GeoPackage Layer" ),
360 tr(
"A table with the same name already exists. Do you want to overwrite it?" ),
361 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes )
363 overwriteTable =
true;
366 if ( !overwriteTable )
372 QString layerIdentifier( mLayerIdentifierEdit->text() );
373 QString layerDescription( mLayerDescriptionEdit->text() );
375 OGRwkbGeometryType wkbType =
static_cast<OGRwkbGeometryType
> 376 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
379 if ( mGeometryWithZCheckBox->isChecked() )
380 wkbType = OGR_GT_SetZ( wkbType );
382 if ( mGeometryWithMCheckBox->isChecked() )
383 wkbType = OGR_GT_SetM( wkbType );
388 if ( wkbType != wkbNone && srs.
isValid() )
390 QString srsWkt = srs.
toWkt();
391 hSRS = OSRNewSpatialReference( srsWkt.toLocal8Bit().data() );
395 char **options =
nullptr;
397 if ( overwriteTable )
398 options = CSLSetNameValue( options,
"OVERWRITE",
"YES" );
399 if ( !layerIdentifier.isEmpty() )
400 options = CSLSetNameValue( options,
"IDENTIFIER", layerIdentifier.toUtf8().constData() );
401 if ( !layerDescription.isEmpty() )
402 options = CSLSetNameValue( options,
"DESCRIPTION", layerDescription.toUtf8().constData() );
404 QString featureId( mFeatureIdColumnEdit->text() );
405 if ( !featureId.isEmpty() )
406 options = CSLSetNameValue( options,
"FID", featureId.toUtf8().constData() );
408 QString geometryColumn( mGeometryColumnEdit->text() );
409 if ( wkbType != wkbNone && !geometryColumn.isEmpty() )
410 options = CSLSetNameValue( options,
"GEOMETRY_COLUMN", geometryColumn.toUtf8().constData() );
412 if ( wkbType != wkbNone )
413 options = CSLSetNameValue( options,
"SPATIAL_INDEX", mCheckBoxCreateSpatialIndex->isChecked() ?
"YES" :
"NO" );
415 OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS, wkbType, options );
416 CSLDestroy( options );
421 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
422 if ( !property(
"hideDialogs" ).toBool() )
423 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
427 QTreeWidgetItemIterator it( mAttributeView );
430 QString fieldName( ( *it )->text( 0 ) );
431 QString fieldType( ( *it )->text( 1 ) );
432 QString fieldWidth( ( *it )->text( 2 ) );
434 OGRFieldType ogrType( OFTString );
435 if ( fieldType == QLatin1String(
"text" ) )
437 else if ( fieldType == QLatin1String(
"integer" ) )
438 ogrType = OFTInteger;
439 else if ( fieldType == QLatin1String(
"integer64" ) )
440 ogrType = OFTInteger64;
441 else if ( fieldType == QLatin1String(
"real" ) )
443 else if ( fieldType == QLatin1String(
"date" ) )
445 else if ( fieldType == QLatin1String(
"datetime" ) )
446 ogrType = OFTDateTime;
448 int ogrWidth = fieldWidth.toInt();
451 OGR_Fld_SetWidth( fld.get(), ogrWidth );
453 if ( OGR_L_CreateField( hLayer, fld.get(), true ) != OGRERR_NONE )
455 if ( !property(
"hideDialogs" ).toBool() )
457 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ),
458 tr(
"Creation of field %1 failed (OGR error: %2)" )
459 .arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
470 OGR_L_ResetReading( hLayer );
471 if ( CPLGetLastErrorType() != CE_None )
473 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
474 if ( !property(
"hideDialogs" ).toBool() )
475 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
480 QString uri( QStringLiteral(
"%1|layername=%2" ).arg( fileName, tableName ) );
481 QString userVisiblelayerName( layerIdentifier.isEmpty() ? tableName : layerIdentifier );
483 if ( layer->isValid() )
486 QList<QgsMapLayer *> myList;
495 if ( !property(
"hideDialogs" ).toBool() )
496 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), tr(
"%1 is an invalid layer and cannot be loaded." ).arg( tableName ) );
505 mBehavior = behavior;
508 void QgsNewGeoPackageLayerDialog::showHelp()
510 QgsHelp::openHelp( QStringLiteral(
"managing_data_source/create_layers.html#creating-a-new-geopackage-layer" ) );
void setOverwriteBehavior(OverwriteBehavior behavior)
Sets the behavior to use when a path to an existing geopackage file is used.
#define DEFAULT_OGR_FID_COLUMN_TITLE
This class is a composition of two QSettings instances:
QgsNewGeoPackageLayerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor.
QString toWkt() const
Returns a WKT representation of this CRS.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Keep existing contents and add new layer.
const QgsCoordinateReferenceSystem & crs
OverwriteBehavior
Behavior to use when an existing geopackage already exists.
Overwrite whole geopackage.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
void lockDatabasePath()
Sets the database path widgets to a locked and read-only mode.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static QgsProject * instance()
Returns the QgsProject singleton instance.
This class represents a coordinate reference system (CRS).
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs value for the new layer in the dialog.
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
Represents a vector layer which manages a vector based data sets.
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
void * OGRSpatialReferenceH