35 #include <QPushButton> 37 #include <QMessageBox> 38 #include <QFileDialog> 41 #include <ogr_srs_api.h> 42 #include <gdal_version.h> 43 #include <cpl_error.h> 44 #include <cpl_string.h> 46 #define DEFAULT_OGR_FID_COLUMN_TITLE "fid" // default value from OGR 49 : QDialog( parent, fl )
54 connect( mAddAttributeButton, &QToolButton::clicked,
this, &QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked );
55 connect( mRemoveAttributeButton, &QToolButton::clicked,
this, &QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked );
56 connect( mFieldTypeBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged );
57 connect( mGeometryTypeBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged );
58 connect( mTableNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged );
59 connect( mTableNameEdit, &QLineEdit::textEdited,
this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited );
60 connect( mLayerIdentifierEdit, &QLineEdit::textEdited,
this, &QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited );
61 connect( buttonBox, &QDialogButtonBox::accepted,
this, &QgsNewGeoPackageLayerDialog::buttonBox_accepted );
62 connect( buttonBox, &QDialogButtonBox::rejected,
this, &QgsNewGeoPackageLayerDialog::buttonBox_rejected );
63 connect( buttonBox, &QDialogButtonBox::helpRequested,
this, &QgsNewGeoPackageLayerDialog::showHelp );
72 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPointLayer.svg" ) ), tr(
"MultiPoint" ), wkbMultiPoint );
73 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconLineLayer.svg" ) ), tr(
"MultiLine" ), wkbMultiLineString );
74 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPolygonLayer.svg" ) ), tr(
"MultiPolygon" ), wkbMultiPolygon );
78 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconLineLayer.svg" ) ), tr(
"CircularString" ), wkbCircularString );
80 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconLineLayer.svg" ) ), tr(
"CompoundCurve" ), wkbCompoundCurve );
81 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPolygonLayer.svg" ) ), tr(
"CurvePolygon" ), wkbCurvePolygon );
83 mGeometryTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconPolygonLayer.svg" ) ), tr(
"MultiSurface" ), wkbMultiSurface );
85 mGeometryWithZCheckBox->setEnabled(
false );
86 mGeometryWithMCheckBox->setEnabled(
false );
87 mGeometryColumnEdit->setEnabled(
false );
88 mGeometryColumnEdit->setText( QStringLiteral(
"geometry" ) );
90 mCheckBoxCreateSpatialIndex->setEnabled(
false );
91 mCrsSelector->setEnabled(
false );
94 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldInteger.svg" ) ), tr(
"Whole number (integer)" ),
"integer" );
95 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldInteger.svg" ) ), tr(
"Whole number (integer 64 bit)" ),
"integer64" );
96 mFieldTypeBox->addItem(
QgsApplication::getThemeIcon( QStringLiteral(
"/mIconFieldFloat.svg" ) ), tr(
"Decimal number (real)" ),
"real" );
102 mOkButton = buttonBox->button( QDialogButtonBox::Ok );
103 mOkButton->setEnabled(
false );
105 connect( mFieldNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::fieldNameChanged );
106 connect( mAttributeView, &QTreeWidget::itemSelectionChanged,
this, &QgsNewGeoPackageLayerDialog::selectionChanged );
107 connect( mTableNameEdit, &QLineEdit::textChanged,
this, &QgsNewGeoPackageLayerDialog::checkOk );
109 mAddAttributeButton->setEnabled(
false );
110 mRemoveAttributeButton->setEnabled(
false );
112 mCheckBoxCreateSpatialIndex->setChecked(
true );
116 mDatabase->setFilter( tr(
"GeoPackage" ) +
" (*.gpkg)" );
117 mDatabase->setDialogTitle( tr(
"Select Existing or Create a New GeoPackage Database File…" ) );
118 mDatabase->setDefaultRoot( settings.
value( QStringLiteral(
"UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
119 mDatabase->setConfirmOverwrite(
false );
123 QFileInfo tmplFileInfo( filePath );
124 settings.
setValue( QStringLiteral(
"UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
125 if ( !filePath.isEmpty() && !mTableNameEdited )
127 QFileInfo fileInfo( filePath );
128 mTableNameEdit->setText( fileInfo.baseName() );
136 mCrsSelector->setCrs( crs );
141 mDatabase->setReadOnly(
true );
144 void QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged(
int )
146 QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
147 mFieldLengthEdit->setEnabled( myType == QLatin1String(
"text" ) );
148 if ( myType != QLatin1String(
"text" ) )
149 mFieldLengthEdit->clear();
153 void QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged(
int )
155 OGRwkbGeometryType geomType =
static_cast<OGRwkbGeometryType
> 156 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
157 bool isSpatial = geomType != wkbNone;
158 mGeometryWithZCheckBox->setEnabled( isSpatial );
159 mGeometryWithMCheckBox->setEnabled( isSpatial );
160 mGeometryColumnEdit->setEnabled( isSpatial );
161 mCheckBoxCreateSpatialIndex->setEnabled( isSpatial );
162 mCrsSelector->setEnabled( isSpatial );
165 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged(
const QString &text )
167 mTableNameEdited = !text.isEmpty();
168 if ( !text.isEmpty() && !mLayerIdentifierEdited )
170 mLayerIdentifierEdit->setText( text );
174 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited(
const QString &text )
177 mTableNameEdited = !text.isEmpty();
180 void QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited(
const QString &text )
183 mLayerIdentifierEdited = !text.isEmpty();
186 void QgsNewGeoPackageLayerDialog::checkOk()
188 bool ok = !mDatabase->filePath().isEmpty() &&
189 !mTableNameEdit->text().isEmpty();
190 mOkButton->setEnabled( ok );
193 void QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked()
195 if ( !mFieldNameEdit->text().isEmpty() )
197 QString myName = mFieldNameEdit->text();
198 const QString featureId = mFeatureIdColumnEdit->text().isEmpty() ? QStringLiteral(
DEFAULT_OGR_FID_COLUMN_TITLE ) : mFeatureIdColumnEdit->text();
199 if ( myName.compare( featureId, Qt::CaseInsensitive ) == 0 )
201 QMessageBox::critical(
this, tr(
"Add Field" ), tr(
"The field cannot have the same name as the feature identifier." ) );
206 QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
207 QString length = mFieldLengthEdit->text();
208 mAttributeView->addTopLevelItem(
new QTreeWidgetItem( QStringList() << myName << myType << length ) );
212 mFieldNameEdit->clear();
216 void QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked()
218 delete mAttributeView->currentItem();
223 void QgsNewGeoPackageLayerDialog::fieldNameChanged(
const QString &name )
225 mAddAttributeButton->setDisabled( name.isEmpty() || ! mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
228 void QgsNewGeoPackageLayerDialog::selectionChanged()
230 mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
233 void QgsNewGeoPackageLayerDialog::buttonBox_accepted()
239 void QgsNewGeoPackageLayerDialog::buttonBox_rejected()
244 bool QgsNewGeoPackageLayerDialog::apply()
246 QString fileName( mDatabase->filePath() );
247 if ( !fileName.endsWith( QLatin1String(
".gpkg" ), Qt::CaseInsensitive ) )
248 fileName += QLatin1String(
".gpkg" );
250 bool createNewDb =
false;
252 if ( QFile( fileName ).exists( fileName ) )
254 bool overwrite =
false;
261 msgBox.setIcon( QMessageBox::Question );
262 msgBox.setWindowTitle( tr(
"New GeoPackage Layer" ) );
263 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?" ) );
264 QPushButton *overwriteButton = msgBox.addButton( tr(
"Overwrite" ), QMessageBox::ActionRole );
265 QPushButton *addNewLayerButton = msgBox.addButton( tr(
"Add New Layer" ), QMessageBox::ActionRole );
266 msgBox.setStandardButtons( QMessageBox::Cancel );
267 msgBox.setDefaultButton( addNewLayerButton );
269 if ( property(
"hideDialogs" ).toBool() )
271 overwrite = property(
"question_existing_db_answer_overwrite" ).toBool();
273 cancel = !property(
"question_existing_db_answer_add_new_layer" ).toBool();
277 int ret = msgBox.exec();
278 if ( ret == QMessageBox::Cancel )
280 if ( msgBox.clickedButton() == overwriteButton )
301 QFile( fileName ).remove();
310 OGRSFDriverH hGpkgDriver = OGRGetDriverByName(
"GPKG" );
313 if ( !property(
"hideDialogs" ).toBool() )
314 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ),
315 tr(
"Layer creation failed. GeoPackage driver not found." ) );
322 hDS.reset( OGR_Dr_CreateDataSource( hGpkgDriver, fileName.toUtf8().constData(), nullptr ) );
325 QString msg( tr(
"Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
326 if ( !property(
"hideDialogs" ).toBool() )
327 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
333 OGRSFDriverH hDriver =
nullptr;
334 hDS.reset( OGROpen( fileName.toUtf8().constData(),
true, &hDriver ) );
337 QString msg( tr(
"Opening of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
338 if ( !property(
"hideDialogs" ).toBool() )
339 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
342 if ( hDriver != hGpkgDriver )
344 QString msg( tr(
"Opening of file succeeded, but this is not a GeoPackage database." ) );
345 if ( !property(
"hideDialogs" ).toBool() )
346 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
351 QString tableName( mTableNameEdit->text() );
353 bool overwriteTable =
false;
354 if ( OGR_DS_GetLayerByName( hDS.get(), tableName.toUtf8().constData() ) )
356 if ( property(
"hideDialogs" ).toBool() )
358 overwriteTable = property(
"question_existing_layer_answer_overwrite" ).toBool();
360 else if ( QMessageBox::question(
this, tr(
"New GeoPackage Layer" ),
361 tr(
"A table with the same name already exists. Do you want to overwrite it?" ),
362 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes )
364 overwriteTable =
true;
367 if ( !overwriteTable )
373 QString layerIdentifier( mLayerIdentifierEdit->text() );
374 QString layerDescription( mLayerDescriptionEdit->text() );
376 OGRwkbGeometryType wkbType =
static_cast<OGRwkbGeometryType
> 377 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
380 if ( mGeometryWithZCheckBox->isChecked() )
381 wkbType = OGR_GT_SetZ( wkbType );
383 if ( mGeometryWithMCheckBox->isChecked() )
384 wkbType = OGR_GT_SetM( wkbType );
389 if ( wkbType != wkbNone && srs.
isValid() )
391 QString srsWkt = srs.
toWkt();
392 hSRS = OSRNewSpatialReference( srsWkt.toLocal8Bit().data() );
396 char **options =
nullptr;
398 if ( overwriteTable )
399 options = CSLSetNameValue( options,
"OVERWRITE",
"YES" );
400 if ( !layerIdentifier.isEmpty() )
401 options = CSLSetNameValue( options,
"IDENTIFIER", layerIdentifier.toUtf8().constData() );
402 if ( !layerDescription.isEmpty() )
403 options = CSLSetNameValue( options,
"DESCRIPTION", layerDescription.toUtf8().constData() );
405 QString featureId( mFeatureIdColumnEdit->text() );
406 if ( !featureId.isEmpty() )
407 options = CSLSetNameValue( options,
"FID", featureId.toUtf8().constData() );
409 QString geometryColumn( mGeometryColumnEdit->text() );
410 if ( wkbType != wkbNone && !geometryColumn.isEmpty() )
411 options = CSLSetNameValue( options,
"GEOMETRY_COLUMN", geometryColumn.toUtf8().constData() );
413 if ( wkbType != wkbNone )
414 options = CSLSetNameValue( options,
"SPATIAL_INDEX", mCheckBoxCreateSpatialIndex->isChecked() ?
"YES" :
"NO" );
416 OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS, wkbType, options );
417 CSLDestroy( options );
422 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
423 if ( !property(
"hideDialogs" ).toBool() )
424 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
428 QTreeWidgetItemIterator it( mAttributeView );
431 QString fieldName( ( *it )->text( 0 ) );
432 QString fieldType( ( *it )->text( 1 ) );
433 QString fieldWidth( ( *it )->text( 2 ) );
436 OGRFieldType ogrType( OFTString );
437 if ( fieldType == QLatin1String(
"text" ) )
439 else if ( fieldType == QLatin1String(
"integer" ) )
440 ogrType = OFTInteger;
441 else if ( fieldType == QLatin1String(
"integer64" ) )
442 ogrType = OFTInteger64;
443 else if ( fieldType == QLatin1String(
"real" ) )
445 else if ( fieldType == QLatin1String(
"date" ) )
447 else if ( fieldType == QLatin1String(
"datetime" ) )
448 ogrType = OFTDateTime;
449 else if ( fieldType == QLatin1String(
"bool" ) )
451 ogrType = OFTInteger;
454 else if ( fieldType == QLatin1String(
"binary" ) )
457 int ogrWidth = fieldWidth.toInt();
460 if ( ogrType != OFTBinary )
461 OGR_Fld_SetWidth( fld.get(), ogrWidth );
463 OGR_Fld_SetSubType( fld.get(), OFSTBoolean );
465 if ( OGR_L_CreateField( hLayer, fld.get(), true ) != OGRERR_NONE )
467 if ( !property(
"hideDialogs" ).toBool() )
469 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ),
470 tr(
"Creation of field %1 failed (OGR error: %2)" )
471 .arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
482 OGR_L_ResetReading( hLayer );
483 if ( CPLGetLastErrorType() != CE_None )
485 QString msg( tr(
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
486 if ( !property(
"hideDialogs" ).toBool() )
487 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), msg );
492 QString uri( QStringLiteral(
"%1|layername=%2" ).arg( fileName, tableName ) );
493 QString userVisiblelayerName( layerIdentifier.isEmpty() ? tableName : layerIdentifier );
495 std::unique_ptr< QgsVectorLayer > layer = qgis::make_unique< QgsVectorLayer >( uri, userVisiblelayerName, QStringLiteral(
"ogr" ), layerOptions );
496 if ( layer->isValid() )
501 QList<QgsMapLayer *> myList;
502 myList << layer.release();
515 if ( !property(
"hideDialogs" ).toBool() )
516 QMessageBox::critical(
this, tr(
"New GeoPackage Layer" ), tr(
"%1 is an invalid layer and cannot be loaded." ).arg( tableName ) );
524 mBehavior = behavior;
529 mAddToProject = addToProject;
532 void QgsNewGeoPackageLayerDialog::showHelp()
534 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:
Setting options for loading vector layers.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QgsNewGeoPackageLayerDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
Constructor.
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.
void setAddToProject(bool addToProject)
Sets whether a newly created layer should automatically be added to the current project.
QgsCoordinateTransformContext transformContext
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.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
void * OGRSpatialReferenceH
bool isValid() const
Returns whether this CRS is correctly initialized and usable.