30 #include "qgssettings.h"
37 #include <QPushButton>
39 #include <QMessageBox>
40 #include <QFileDialog>
44 #include <ogr_srs_api.h>
45 #include <gdal_version.h>
46 #include <cpl_error.h>
47 #include <cpl_string.h>
49 #define DEFAULT_OGR_FID_COLUMN_TITLE "fid"
52 : QDialog( parent, fl )
55 setObjectName( QStringLiteral(
"QgsNewGeoPackageLayerDialog" ) );
58 connect( mAddAttributeButton, &QToolButton::clicked,
this, &QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked );
59 connect( mRemoveAttributeButton, &QToolButton::clicked,
this, &QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked );
60 connect( mFieldTypeBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged );
61 connect( mGeometryTypeBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged );
62 connect( mTableNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged );
63 connect( mTableNameEdit, &QLineEdit::textEdited,
this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited );
64 connect( mLayerIdentifierEdit, &QLineEdit::textEdited,
this, &QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited );
65 connect( buttonBox, &QDialogButtonBox::accepted,
this, &QgsNewGeoPackageLayerDialog::buttonBox_accepted );
66 connect( buttonBox, &QDialogButtonBox::rejected,
this, &QgsNewGeoPackageLayerDialog::buttonBox_rejected );
67 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsNewGeoPackageLayerDialog::showHelp );
72 const auto addGeomItem = [
this]( OGRwkbGeometryType ogrGeomType )
78 addGeomItem( wkbNone );
79 addGeomItem( wkbPoint );
80 addGeomItem( wkbLineString );
81 addGeomItem( wkbPolygon );
82 addGeomItem( wkbMultiPoint );
83 addGeomItem( wkbMultiLineString );
84 addGeomItem( wkbMultiPolygon );
88 addGeomItem( wkbCircularString );
90 addGeomItem( wkbCompoundCurve );
91 addGeomItem( wkbCurvePolygon );
92 addGeomItem( wkbMultiCurve );
93 addGeomItem( wkbMultiSurface );
94 mGeometryTypeBox->setCurrentIndex( -1 );
96 mGeometryWithZCheckBox->setEnabled(
false );
97 mGeometryWithMCheckBox->setEnabled(
false );
98 mGeometryColumnEdit->setEnabled(
false );
99 mGeometryColumnEdit->setText( QStringLiteral(
"geometry" ) );
101 mCheckBoxCreateSpatialIndex->setEnabled(
false );
102 mCrsSelector->setEnabled(
false );
103 mCrsSelector->setShowAccuracyWarnings(
true );
106 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldInteger.svg" ) ), tr(
"Whole Number (integer)" ),
"integer" );
107 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldInteger.svg" ) ), tr(
"Whole Number (integer 64 bit)" ),
"integer64" );
108 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldFloat.svg" ) ), tr(
"Decimal Number (real)" ),
"real" );
110 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldDateTime.svg" ) ), tr(
"Date and Time" ),
"datetime" );
114 mOkButton = buttonBox->button( QDialogButtonBox::Ok );
115 mOkButton->setEnabled(
false );
117 connect( mFieldNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::fieldNameChanged );
118 connect( mAttributeView, &QTreeWidget::itemSelectionChanged,
this, &QgsNewGeoPackageLayerDialog::selectionChanged );
119 connect( mTableNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::checkOk );
120 connect( mGeometryTypeBox,
static_cast<void( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::checkOk );
122 mAddAttributeButton->setEnabled(
false );
123 mRemoveAttributeButton->setEnabled(
false );
125 mCheckBoxCreateSpatialIndex->setChecked(
true );
127 QgsSettings settings;
129 mDatabase->setFilter( tr(
"GeoPackage" ) +
" (*.gpkg)" );
130 mDatabase->setDialogTitle( tr(
"Select Existing or Create a New GeoPackage Database Fileā¦" ) );
131 mDatabase->setDefaultRoot( settings.value( QStringLiteral(
"UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
132 mDatabase->setConfirmOverwrite(
false );
135 QgsSettings settings;
136 QFileInfo tmplFileInfo( filePath );
137 settings.setValue( QStringLiteral(
"UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
138 if ( !filePath.isEmpty() && !mTableNameEdited )
140 QFileInfo fileInfo( filePath );
141 mTableNameEdit->setText( fileInfo.baseName() );
148 QCompleter *completer =
new QCompleter(
this );
149 completer->setModel( ogrProviderModel );
151 completer->setCompletionMode( QCompleter::PopupCompletion );
152 completer->setFilterMode( Qt::MatchContains );
153 mDatabase->lineEdit()->setCompleter( completer );
158 mCrsSelector->setCrs(
crs );
163 mDatabase->setReadOnly(
true );
166 void QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged(
int )
168 QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
169 mFieldLengthEdit->setEnabled( myType == QLatin1String(
"text" ) );
170 if ( myType != QLatin1String(
"text" ) )
171 mFieldLengthEdit->clear();
175 void QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged(
int )
177 OGRwkbGeometryType geomType =
static_cast<OGRwkbGeometryType
>
178 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
179 bool isSpatial = geomType != wkbNone;
180 mGeometryWithZCheckBox->setEnabled( isSpatial );
181 mGeometryWithMCheckBox->setEnabled( isSpatial );
182 mGeometryColumnEdit->setEnabled( isSpatial );
183 mCheckBoxCreateSpatialIndex->setEnabled( isSpatial );
184 mCrsSelector->setEnabled( isSpatial );
187 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged(
const QString &text )
189 mTableNameEdited = !text.isEmpty();
190 if ( !text.isEmpty() && !mLayerIdentifierEdited )
192 mLayerIdentifierEdit->setText( text );
196 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited(
const QString &text )
199 mTableNameEdited = !text.isEmpty();
202 void QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited(
const QString &text )
205 mLayerIdentifierEdited = !text.isEmpty();
208 void QgsNewGeoPackageLayerDialog::checkOk()
210 bool ok = !mDatabase->filePath().isEmpty() &&
211 !mTableNameEdit->text().isEmpty() &&
212 mGeometryTypeBox->currentIndex() != -1;
214 mOkButton->setEnabled( ok );
217 void QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked()
219 if ( !mFieldNameEdit->text().isEmpty() )
221 QString myName = mFieldNameEdit->text();
222 const QString featureId = mFeatureIdColumnEdit->text().isEmpty() ? QStringLiteral(
DEFAULT_OGR_FID_COLUMN_TITLE ) : mFeatureIdColumnEdit->text();
223 if ( myName.compare( featureId, Qt::CaseInsensitive ) == 0 )
225 QMessageBox::critical(
this, tr(
"Add Field" ), tr(
"The field cannot have the same name as the feature identifier." ) );
230 QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
231 QString length = mFieldLengthEdit->text();
232 mAttributeView->addTopLevelItem(
new QTreeWidgetItem( QStringList() << myName << myType << length ) );
236 mFieldNameEdit->clear();
240 void QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked()
242 delete mAttributeView->currentItem();
247 void QgsNewGeoPackageLayerDialog::fieldNameChanged(
const QString &name )
249 mAddAttributeButton->setDisabled( name.isEmpty() || ! mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
252 void QgsNewGeoPackageLayerDialog::selectionChanged()
254 mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
257 void QgsNewGeoPackageLayerDialog::buttonBox_accepted()
263 void QgsNewGeoPackageLayerDialog::buttonBox_rejected()
268 bool QgsNewGeoPackageLayerDialog::apply()
270 QString fileName( mDatabase->filePath() );
271 if ( !fileName.endsWith( QLatin1String(
".gpkg" ), Qt::CaseInsensitive ) )
272 fileName += QLatin1String(
".gpkg" );
274 bool createNewDb =
false;
276 if ( QFile( fileName ).exists( fileName ) )
278 bool overwrite =
false;
285 msgBox.setIcon( QMessageBox::Question );
286 msgBox.setWindowTitle( tr(
"New GeoPackage Layer" ) );
287 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?" ) );
288 QPushButton *overwriteButton = msgBox.addButton( tr(
"Overwrite" ), QMessageBox::ActionRole );
289 QPushButton *addNewLayerButton = msgBox.addButton( tr(
"Add New Layer" ), QMessageBox::ActionRole );
290 msgBox.setStandardButtons( QMessageBox::Cancel );
291 msgBox.setDefaultButton( addNewLayerButton );
293 if ( property(
"hideDialogs" ).toBool() )
295 overwrite = property(
"question_existing_db_answer_overwrite" ).toBool();
297 cancel = !property(
"question_existing_db_answer_add_new_layer" ).toBool();
301 int ret = msgBox.exec();
302 if ( ret == QMessageBox::Cancel )
304 if ( msgBox.clickedButton() == overwriteButton )
325 QFile( fileName ).remove();
334 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
337 if ( !property(
"hideDialogs" ).toBool() )
338 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ),
339 tr(
"Layer creation failed. GeoPackage driver not found." ) );
346 hDS.reset( OGR_Dr_CreateDataSource( hGpkgDriver, fileName.toUtf8().constData(),
nullptr ) );
349 QString msg( tr(
"Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
350 if ( !property(
"hideDialogs" ).toBool() )
351 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
357 OGRSFDriverH hDriver =
nullptr;
358 hDS.reset( OGROpen( fileName.toUtf8().constData(),
true, &hDriver ) );
361 QString msg( tr(
"Opening of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
362 if ( !property(
"hideDialogs" ).toBool() )
363 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
366 if ( hDriver != hGpkgDriver )
368 QString msg( tr(
"Opening of file succeeded, but this is not a GeoPackage database." ) );
369 if ( !property(
"hideDialogs" ).toBool() )
370 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
375 QString tableName( mTableNameEdit->text() );
377 bool overwriteTable =
false;
378 if ( OGR_DS_GetLayerByName( hDS.get(), tableName.toUtf8().constData() ) )
380 if ( property(
"hideDialogs" ).toBool() )
382 overwriteTable = property(
"question_existing_layer_answer_overwrite" ).toBool();
384 else if ( QMessageBox::question(
this, tr(
"New GeoPackage Layer" ),
385 tr(
"A table with the same name already exists. Do you want to overwrite it?" ),
386 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes )
388 overwriteTable =
true;
391 if ( !overwriteTable )
397 QString layerIdentifier( mLayerIdentifierEdit->text() );
398 QString layerDescription( mLayerDescriptionEdit->text() );
400 OGRwkbGeometryType wkbType =
static_cast<OGRwkbGeometryType
>
401 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
404 if ( mGeometryWithZCheckBox->isChecked() )
405 wkbType = OGR_GT_SetZ( wkbType );
407 if ( mGeometryWithMCheckBox->isChecked() )
408 wkbType = OGR_GT_SetM( wkbType );
413 if ( wkbType != wkbNone && srs.
isValid() )
416 hSRS = OSRNewSpatialReference( srsWkt.toLocal8Bit().data() );
420 char **options =
nullptr;
422 if ( overwriteTable )
423 options = CSLSetNameValue( options,
"OVERWRITE",
"YES" );
424 if ( !layerIdentifier.isEmpty() )
425 options = CSLSetNameValue( options,
"IDENTIFIER", layerIdentifier.toUtf8().constData() );
426 if ( !layerDescription.isEmpty() )
427 options = CSLSetNameValue( options,
"DESCRIPTION", layerDescription.toUtf8().constData() );
429 QString featureId( mFeatureIdColumnEdit->text() );
430 if ( !featureId.isEmpty() )
431 options = CSLSetNameValue( options,
"FID", featureId.toUtf8().constData() );
433 QString geometryColumn( mGeometryColumnEdit->text() );
434 if ( wkbType != wkbNone && !geometryColumn.isEmpty() )
435 options = CSLSetNameValue( options,
"GEOMETRY_COLUMN", geometryColumn.toUtf8().constData() );
437 if ( wkbType != wkbNone )
438 options = CSLSetNameValue( options,
"SPATIAL_INDEX", mCheckBoxCreateSpatialIndex->isChecked() ?
"YES" :
"NO" );
440 OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS, wkbType, options );
441 CSLDestroy( options );
446 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
447 if ( !property(
"hideDialogs" ).toBool() )
448 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
452 QTreeWidgetItemIterator it( mAttributeView );
455 QString fieldName( ( *it )->text( 0 ) );
456 QString fieldType( ( *it )->text( 1 ) );
457 QString fieldWidth( ( *it )->text( 2 ) );
460 OGRFieldType ogrType( OFTString );
461 if ( fieldType == QLatin1String(
"text" ) )
463 else if ( fieldType == QLatin1String(
"integer" ) )
464 ogrType = OFTInteger;
465 else if ( fieldType == QLatin1String(
"integer64" ) )
466 ogrType = OFTInteger64;
467 else if ( fieldType == QLatin1String(
"real" ) )
469 else if ( fieldType == QLatin1String(
"date" ) )
471 else if ( fieldType == QLatin1String(
"datetime" ) )
472 ogrType = OFTDateTime;
473 else if ( fieldType == QLatin1String(
"bool" ) )
475 ogrType = OFTInteger;
478 else if ( fieldType == QLatin1String(
"binary" ) )
481 int ogrWidth = fieldWidth.toInt();
484 if ( ogrType != OFTBinary )
485 OGR_Fld_SetWidth( fld.get(), ogrWidth );
487 OGR_Fld_SetSubType( fld.get(), OFSTBoolean );
489 if ( OGR_L_CreateField( hLayer, fld.get(),
true ) != OGRERR_NONE )
491 if ( !property(
"hideDialogs" ).toBool() )
493 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ),
494 tr(
"Creation of field %1 failed (OGR error: %2)" )
495 .arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
506 OGR_L_ResetReading( hLayer );
507 if ( CPLGetLastErrorType() != CE_None )
509 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
510 if ( !property(
"hideDialogs" ).toBool() )
511 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
516 QString uri( QStringLiteral(
"%1|layername=%2" ).arg( fileName, tableName ) );
517 QString userVisiblelayerName( layerIdentifier.isEmpty() ? tableName : layerIdentifier );
519 std::unique_ptr< QgsVectorLayer > layer = std::make_unique< QgsVectorLayer >( uri, userVisiblelayerName, QStringLiteral(
"ogr" ), layerOptions );
520 if ( layer->isValid() )
525 QList<QgsMapLayer *> myList;
526 myList << layer.release();
539 if ( !property(
"hideDialogs" ).toBool() )
540 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), tr(
"%1 is an invalid layer and cannot be loaded." ).arg( tableName ) );
548 mBehavior = behavior;
553 mAddToProject = addToProject;
556 void QgsNewGeoPackageLayerDialog::showHelp()
558 QgsHelp::openHelp( QStringLiteral(
"managing_data_source/create_layers.html#creating-a-new-geopackage-layer" ) );
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
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.
static QIcon iconForWkbType(QgsWkbTypes::Type type)
Returns the icon for a vector layer whose geometry type is provided.
OverwriteBehavior
Behavior to use when an existing geopackage already exists.
@ AddNewLayer
Keep existing contents and add new layer.
@ Overwrite
Overwrite whole geopackage.
@ Prompt
Prompt user for action.
void setAddToProject(bool addToProject)
Sets whether a newly created layer should automatically be added to the current project.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the crs value for the new layer in the dialog.
void lockDatabasePath()
Sets the database path widgets to a locked and read-only mode.
QgsNewGeoPackageLayerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor.
void setOverwriteBehavior(OverwriteBehavior behavior)
Sets the behavior to use when a path to an existing geopackage file is used.
static QgsWkbTypes::Type ogrGeometryTypeToQgsWkbType(OGRwkbGeometryType ogrGeomType)
Converts a OGRwkbGeometryType to QgsWkbTypes::Type.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsCoordinateTransformContext transformContext
A model containing registered connection names for a specific data provider.
@ RoleUri
Connection URI string.
static QString translatedDisplayString(Type type) SIP_HOLDGIL
Returns a translated display string type for a WKB type, e.g., the geometry name used in WKT geometry...
Type
The WKB type describes the number of dimensions a geometry has.
std::unique_ptr< std::remove_pointer< OGRDataSourceH >::type, OGRDataSourceDeleter > ogr_datasource_unique_ptr
Scoped OGR data source.
std::unique_ptr< std::remove_pointer< OGRFieldDefnH >::type, OGRFldDeleter > ogr_field_def_unique_ptr
Scoped OGR field definition.
void * OGRSpatialReferenceH
#define DEFAULT_OGR_FID_COLUMN_TITLE
const QgsCoordinateReferenceSystem & crs
Setting options for loading vector layers.