36 #include <QPushButton>
38 #include <QMessageBox>
39 #include <QFileDialog>
43 #include <ogr_srs_api.h>
44 #include <gdal_version.h>
45 #include <cpl_error.h>
46 #include <cpl_string.h>
48 #define DEFAULT_OGR_FID_COLUMN_TITLE "fid" // default value from OGR
51 : QDialog( parent, fl )
54 setObjectName( QStringLiteral(
"QgsNewGeoPackageLayerDialog" ) );
57 connect( mAddAttributeButton, &QToolButton::clicked,
this, &QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked );
58 connect( mRemoveAttributeButton, &QToolButton::clicked,
this, &QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked );
59 connect( mFieldTypeBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged );
60 connect( mGeometryTypeBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged );
61 connect( mTableNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged );
62 connect( mTableNameEdit, &QLineEdit::textEdited,
this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited );
63 connect( mLayerIdentifierEdit, &QLineEdit::textEdited,
this, &QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited );
64 connect( buttonBox, &QDialogButtonBox::accepted,
this, &QgsNewGeoPackageLayerDialog::buttonBox_accepted );
65 connect( buttonBox, &QDialogButtonBox::rejected,
this, &QgsNewGeoPackageLayerDialog::buttonBox_rejected );
66 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsNewGeoPackageLayerDialog::showHelp );
75 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPointLayer.svg" ) ), tr(
"MultiPoint" ), wkbMultiPoint );
76 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconLineLayer.svg" ) ), tr(
"MultiLine" ), wkbMultiLineString );
77 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPolygonLayer.svg" ) ), tr(
"MultiPolygon" ), wkbMultiPolygon );
81 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconLineLayer.svg" ) ), tr(
"CircularString" ), wkbCircularString );
83 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconLineLayer.svg" ) ), tr(
"CompoundCurve" ), wkbCompoundCurve );
84 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPolygonLayer.svg" ) ), tr(
"CurvePolygon" ), wkbCurvePolygon );
86 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPolygonLayer.svg" ) ), tr(
"MultiSurface" ), wkbMultiSurface );
87 mGeometryTypeBox->setCurrentIndex( -1 );
89 mGeometryWithZCheckBox->setEnabled(
false );
90 mGeometryWithMCheckBox->setEnabled(
false );
91 mGeometryColumnEdit->setEnabled(
false );
92 mGeometryColumnEdit->setText( QStringLiteral(
"geometry" ) );
94 mCheckBoxCreateSpatialIndex->setEnabled(
false );
95 mCrsSelector->setEnabled(
false );
98 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldInteger.svg" ) ), tr(
"Whole Number (integer)" ),
"integer" );
99 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldInteger.svg" ) ), tr(
"Whole Number (integer 64 bit)" ),
"integer64" );
100 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldFloat.svg" ) ), tr(
"Decimal Number (real)" ),
"real" );
102 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldDateTime.svg" ) ), tr(
"Date and Time" ),
"datetime" );
106 mOkButton = buttonBox->button( QDialogButtonBox::Ok );
107 mOkButton->setEnabled(
false );
109 connect( mFieldNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::fieldNameChanged );
110 connect( mAttributeView, &QTreeWidget::itemSelectionChanged,
this, &QgsNewGeoPackageLayerDialog::selectionChanged );
111 connect( mTableNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::checkOk );
112 connect( mGeometryTypeBox,
static_cast<void( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::checkOk );
114 mAddAttributeButton->setEnabled(
false );
115 mRemoveAttributeButton->setEnabled(
false );
117 mCheckBoxCreateSpatialIndex->setChecked(
true );
121 mDatabase->setFilter( tr(
"GeoPackage" ) +
" (*.gpkg)" );
122 mDatabase->setDialogTitle( tr(
"Select Existing or Create a New GeoPackage Database Fileā¦" ) );
123 mDatabase->setDefaultRoot( settings.
value( QStringLiteral(
"UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
124 mDatabase->setConfirmOverwrite(
false );
128 QFileInfo tmplFileInfo( filePath );
129 settings.
setValue( QStringLiteral(
"UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
130 if ( !filePath.isEmpty() && !mTableNameEdited )
132 QFileInfo fileInfo( filePath );
133 mTableNameEdit->setText( fileInfo.baseName() );
140 QCompleter *completer =
new QCompleter(
this );
141 completer->setModel( ogrProviderModel );
143 completer->setCompletionMode( QCompleter::PopupCompletion );
144 completer->setFilterMode( Qt::MatchContains );
145 mDatabase->lineEdit()->setCompleter( completer );
150 mCrsSelector->setCrs(
crs );
155 mDatabase->setReadOnly(
true );
158 void QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged(
int )
160 QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
161 mFieldLengthEdit->setEnabled( myType == QLatin1String(
"text" ) );
162 if ( myType != QLatin1String(
"text" ) )
163 mFieldLengthEdit->clear();
167 void QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged(
int )
169 OGRwkbGeometryType geomType =
static_cast<OGRwkbGeometryType
>
170 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
171 bool isSpatial = geomType != wkbNone;
172 mGeometryWithZCheckBox->setEnabled( isSpatial );
173 mGeometryWithMCheckBox->setEnabled( isSpatial );
174 mGeometryColumnEdit->setEnabled( isSpatial );
175 mCheckBoxCreateSpatialIndex->setEnabled( isSpatial );
176 mCrsSelector->setEnabled( isSpatial );
179 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged(
const QString &text )
181 mTableNameEdited = !text.isEmpty();
182 if ( !text.isEmpty() && !mLayerIdentifierEdited )
184 mLayerIdentifierEdit->setText( text );
188 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited(
const QString &text )
191 mTableNameEdited = !text.isEmpty();
194 void QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited(
const QString &text )
197 mLayerIdentifierEdited = !text.isEmpty();
200 void QgsNewGeoPackageLayerDialog::checkOk()
202 bool ok = !mDatabase->filePath().isEmpty() &&
203 !mTableNameEdit->text().isEmpty() &&
204 mGeometryTypeBox->currentIndex() != -1;
206 mOkButton->setEnabled( ok );
209 void QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked()
211 if ( !mFieldNameEdit->text().isEmpty() )
213 QString myName = mFieldNameEdit->text();
214 const QString featureId = mFeatureIdColumnEdit->text().isEmpty() ? QStringLiteral(
DEFAULT_OGR_FID_COLUMN_TITLE ) : mFeatureIdColumnEdit->text();
215 if ( myName.compare( featureId, Qt::CaseInsensitive ) == 0 )
217 QMessageBox::critical(
this, tr(
"Add Field" ), tr(
"The field cannot have the same name as the feature identifier." ) );
222 QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
223 QString length = mFieldLengthEdit->text();
224 mAttributeView->addTopLevelItem(
new QTreeWidgetItem( QStringList() << myName << myType << length ) );
228 mFieldNameEdit->clear();
232 void QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked()
234 delete mAttributeView->currentItem();
239 void QgsNewGeoPackageLayerDialog::fieldNameChanged(
const QString &name )
241 mAddAttributeButton->setDisabled( name.isEmpty() || ! mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
244 void QgsNewGeoPackageLayerDialog::selectionChanged()
246 mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
249 void QgsNewGeoPackageLayerDialog::buttonBox_accepted()
255 void QgsNewGeoPackageLayerDialog::buttonBox_rejected()
260 bool QgsNewGeoPackageLayerDialog::apply()
262 QString fileName( mDatabase->filePath() );
263 if ( !fileName.endsWith( QLatin1String(
".gpkg" ), Qt::CaseInsensitive ) )
264 fileName += QLatin1String(
".gpkg" );
266 bool createNewDb =
false;
268 if ( QFile( fileName ).exists( fileName ) )
270 bool overwrite =
false;
277 msgBox.setIcon( QMessageBox::Question );
278 msgBox.setWindowTitle( tr(
"New GeoPackage Layer" ) );
279 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?" ) );
280 QPushButton *overwriteButton = msgBox.addButton( tr(
"Overwrite" ), QMessageBox::ActionRole );
281 QPushButton *addNewLayerButton = msgBox.addButton( tr(
"Add New Layer" ), QMessageBox::ActionRole );
282 msgBox.setStandardButtons( QMessageBox::Cancel );
283 msgBox.setDefaultButton( addNewLayerButton );
285 if ( property(
"hideDialogs" ).toBool() )
287 overwrite = property(
"question_existing_db_answer_overwrite" ).toBool();
289 cancel = !property(
"question_existing_db_answer_add_new_layer" ).toBool();
293 int ret = msgBox.exec();
294 if ( ret == QMessageBox::Cancel )
296 if ( msgBox.clickedButton() == overwriteButton )
317 QFile( fileName ).remove();
326 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
329 if ( !property(
"hideDialogs" ).toBool() )
330 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ),
331 tr(
"Layer creation failed. GeoPackage driver not found." ) );
338 hDS.reset( OGR_Dr_CreateDataSource( hGpkgDriver, fileName.toUtf8().constData(),
nullptr ) );
341 QString msg( tr(
"Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
342 if ( !property(
"hideDialogs" ).toBool() )
343 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
349 OGRSFDriverH hDriver =
nullptr;
350 hDS.reset( OGROpen( fileName.toUtf8().constData(),
true, &hDriver ) );
353 QString msg( tr(
"Opening of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
354 if ( !property(
"hideDialogs" ).toBool() )
355 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
358 if ( hDriver != hGpkgDriver )
360 QString msg( tr(
"Opening of file succeeded, but this is not a GeoPackage database." ) );
361 if ( !property(
"hideDialogs" ).toBool() )
362 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
367 QString tableName( mTableNameEdit->text() );
369 bool overwriteTable =
false;
370 if ( OGR_DS_GetLayerByName( hDS.get(), tableName.toUtf8().constData() ) )
372 if ( property(
"hideDialogs" ).toBool() )
374 overwriteTable = property(
"question_existing_layer_answer_overwrite" ).toBool();
376 else if ( QMessageBox::question(
this, tr(
"New GeoPackage Layer" ),
377 tr(
"A table with the same name already exists. Do you want to overwrite it?" ),
378 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes )
380 overwriteTable =
true;
383 if ( !overwriteTable )
389 QString layerIdentifier( mLayerIdentifierEdit->text() );
390 QString layerDescription( mLayerDescriptionEdit->text() );
392 OGRwkbGeometryType wkbType =
static_cast<OGRwkbGeometryType
>
393 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
396 if ( mGeometryWithZCheckBox->isChecked() )
397 wkbType = OGR_GT_SetZ( wkbType );
399 if ( mGeometryWithMCheckBox->isChecked() )
400 wkbType = OGR_GT_SetM( wkbType );
405 if ( wkbType != wkbNone && srs.
isValid() )
408 hSRS = OSRNewSpatialReference( srsWkt.toLocal8Bit().data() );
412 char **options =
nullptr;
414 if ( overwriteTable )
415 options = CSLSetNameValue( options,
"OVERWRITE",
"YES" );
416 if ( !layerIdentifier.isEmpty() )
417 options = CSLSetNameValue( options,
"IDENTIFIER", layerIdentifier.toUtf8().constData() );
418 if ( !layerDescription.isEmpty() )
419 options = CSLSetNameValue( options,
"DESCRIPTION", layerDescription.toUtf8().constData() );
421 QString featureId( mFeatureIdColumnEdit->text() );
422 if ( !featureId.isEmpty() )
423 options = CSLSetNameValue( options,
"FID", featureId.toUtf8().constData() );
425 QString geometryColumn( mGeometryColumnEdit->text() );
426 if ( wkbType != wkbNone && !geometryColumn.isEmpty() )
427 options = CSLSetNameValue( options,
"GEOMETRY_COLUMN", geometryColumn.toUtf8().constData() );
429 if ( wkbType != wkbNone )
430 options = CSLSetNameValue( options,
"SPATIAL_INDEX", mCheckBoxCreateSpatialIndex->isChecked() ?
"YES" :
"NO" );
432 OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS, wkbType, options );
433 CSLDestroy( options );
438 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
439 if ( !property(
"hideDialogs" ).toBool() )
440 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
444 QTreeWidgetItemIterator it( mAttributeView );
447 QString fieldName( ( *it )->text( 0 ) );
448 QString fieldType( ( *it )->text( 1 ) );
449 QString fieldWidth( ( *it )->text( 2 ) );
452 OGRFieldType ogrType( OFTString );
453 if ( fieldType == QLatin1String(
"text" ) )
455 else if ( fieldType == QLatin1String(
"integer" ) )
456 ogrType = OFTInteger;
457 else if ( fieldType == QLatin1String(
"integer64" ) )
458 ogrType = OFTInteger64;
459 else if ( fieldType == QLatin1String(
"real" ) )
461 else if ( fieldType == QLatin1String(
"date" ) )
463 else if ( fieldType == QLatin1String(
"datetime" ) )
464 ogrType = OFTDateTime;
465 else if ( fieldType == QLatin1String(
"bool" ) )
467 ogrType = OFTInteger;
470 else if ( fieldType == QLatin1String(
"binary" ) )
473 int ogrWidth = fieldWidth.toInt();
476 if ( ogrType != OFTBinary )
477 OGR_Fld_SetWidth( fld.get(), ogrWidth );
479 OGR_Fld_SetSubType( fld.get(), OFSTBoolean );
481 if ( OGR_L_CreateField( hLayer, fld.get(),
true ) != OGRERR_NONE )
483 if ( !property(
"hideDialogs" ).toBool() )
485 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ),
486 tr(
"Creation of field %1 failed (OGR error: %2)" )
487 .arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
498 OGR_L_ResetReading( hLayer );
499 if ( CPLGetLastErrorType() != CE_None )
501 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
502 if ( !property(
"hideDialogs" ).toBool() )
503 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
508 QString uri( QStringLiteral(
"%1|layername=%2" ).arg( fileName, tableName ) );
509 QString userVisiblelayerName( layerIdentifier.isEmpty() ? tableName : layerIdentifier );
511 std::unique_ptr< QgsVectorLayer > layer = qgis::make_unique< QgsVectorLayer >( uri, userVisiblelayerName, QStringLiteral(
"ogr" ), layerOptions );
512 if ( layer->isValid() )
517 QList<QgsMapLayer *> myList;
518 myList << layer.release();
531 if ( !property(
"hideDialogs" ).toBool() )
532 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), tr(
"%1 is an invalid layer and cannot be loaded." ).arg( tableName ) );
540 mBehavior = behavior;
545 mAddToProject = addToProject;
548 void QgsNewGeoPackageLayerDialog::showHelp()
550 QgsHelp::openHelp( QStringLiteral(
"managing_data_source/create_layers.html#creating-a-new-geopackage-layer" ) );