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 );
 
  105   mFieldTypeBox->addItem( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconFieldInteger.svg" ) ), tr( 
"Whole Number (integer)" ), 
"integer" );
 
  106   mFieldTypeBox->addItem( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconFieldInteger.svg" ) ), tr( 
"Whole Number (integer 64 bit)" ), 
"integer64" );
 
  107   mFieldTypeBox->addItem( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconFieldFloat.svg" ) ), tr( 
"Decimal Number (real)" ), 
"real" );
 
  109   mFieldTypeBox->addItem( 
QgsApplication::getThemeIcon( QStringLiteral( 
"/mIconFieldDateTime.svg" ) ), tr( 
"Date and Time" ), 
"datetime" );
 
  113   mOkButton = buttonBox->button( QDialogButtonBox::Ok );
 
  114   mOkButton->setEnabled( 
false );
 
  116   connect( mFieldNameEdit, &QLineEdit::textChanged, 
this, &QgsNewGeoPackageLayerDialog::fieldNameChanged );
 
  117   connect( mAttributeView, &QTreeWidget::itemSelectionChanged, 
this, &QgsNewGeoPackageLayerDialog::selectionChanged );
 
  118   connect( mTableNameEdit, &QLineEdit::textChanged, 
this, &QgsNewGeoPackageLayerDialog::checkOk );
 
  119   connect( mGeometryTypeBox, 
static_cast<void( QComboBox::* )( 
int )
>( &QComboBox::currentIndexChanged ), 
this,  &QgsNewGeoPackageLayerDialog::checkOk );
 
  121   mAddAttributeButton->setEnabled( 
false );
 
  122   mRemoveAttributeButton->setEnabled( 
false );
 
  124   mCheckBoxCreateSpatialIndex->setChecked( 
true );
 
  128   mDatabase->setFilter( tr( 
"GeoPackage" ) + 
" (*.gpkg)" );
 
  129   mDatabase->setDialogTitle( tr( 
"Select Existing or Create a New GeoPackage Database File…" ) );
 
  130   mDatabase->setDefaultRoot( settings.
value( QStringLiteral( 
"UI/lastVectorFileFilterDir" ), QDir::homePath() ).toString() );
 
  131   mDatabase->setConfirmOverwrite( 
false );
 
  135     QFileInfo tmplFileInfo( filePath );
 
  136     settings.
setValue( QStringLiteral( 
"UI/lastVectorFileFilterDir" ), tmplFileInfo.absolutePath() );
 
  137     if ( !filePath.isEmpty() && !mTableNameEdited )
 
  139       QFileInfo fileInfo( filePath );
 
  140       mTableNameEdit->setText( fileInfo.baseName() );
 
  147   QCompleter *completer = 
new QCompleter( 
this );
 
  148   completer->setModel( ogrProviderModel );
 
  150   completer->setCompletionMode( QCompleter::PopupCompletion );
 
  151   completer->setFilterMode( Qt::MatchContains );
 
  152   mDatabase->lineEdit()->setCompleter( completer );
 
  157   mCrsSelector->setCrs( 
crs );
 
  162   mDatabase->setReadOnly( 
true );
 
  165 void QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged( 
int )
 
  167   QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
 
  168   mFieldLengthEdit->setEnabled( myType == QLatin1String( 
"text" ) );
 
  169   if ( myType != QLatin1String( 
"text" ) )
 
  170     mFieldLengthEdit->clear();
 
  174 void QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged( 
int )
 
  176   OGRwkbGeometryType geomType = 
static_cast<OGRwkbGeometryType
> 
  177                                 ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
 
  178   bool isSpatial = geomType != wkbNone;
 
  179   mGeometryWithZCheckBox->setEnabled( isSpatial );
 
  180   mGeometryWithMCheckBox->setEnabled( isSpatial );
 
  181   mGeometryColumnEdit->setEnabled( isSpatial );
 
  182   mCheckBoxCreateSpatialIndex->setEnabled( isSpatial );
 
  183   mCrsSelector->setEnabled( isSpatial );
 
  186 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged( 
const QString &text )
 
  188   mTableNameEdited = !text.isEmpty();
 
  189   if ( !text.isEmpty() && !mLayerIdentifierEdited )
 
  191     mLayerIdentifierEdit->setText( text );
 
  195 void QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited( 
const QString &text )
 
  198   mTableNameEdited = !text.isEmpty();
 
  201 void QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited( 
const QString &text )
 
  204   mLayerIdentifierEdited = !text.isEmpty();
 
  207 void QgsNewGeoPackageLayerDialog::checkOk()
 
  209   bool ok = !mDatabase->filePath().isEmpty() &&
 
  210             !mTableNameEdit->text().isEmpty() &&
 
  211             mGeometryTypeBox->currentIndex() != -1;
 
  213   mOkButton->setEnabled( ok );
 
  216 void QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked()
 
  218   if ( !mFieldNameEdit->text().isEmpty() )
 
  220     QString myName = mFieldNameEdit->text();
 
  221     const QString featureId = mFeatureIdColumnEdit->text().isEmpty() ? QStringLiteral( 
DEFAULT_OGR_FID_COLUMN_TITLE ) : mFeatureIdColumnEdit->text();
 
  222     if ( myName.compare( featureId, Qt::CaseInsensitive ) == 0 )
 
  224       QMessageBox::critical( 
this, tr( 
"Add Field" ), tr( 
"The field cannot have the same name as the feature identifier." ) );
 
  229     QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
 
  230     QString length = mFieldLengthEdit->text();
 
  231     mAttributeView->addTopLevelItem( 
new QTreeWidgetItem( QStringList() << myName << myType << length ) );
 
  235     mFieldNameEdit->clear();
 
  239 void QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked()
 
  241   delete mAttributeView->currentItem();
 
  246 void QgsNewGeoPackageLayerDialog::fieldNameChanged( 
const QString &name )
 
  248   mAddAttributeButton->setDisabled( name.isEmpty() || ! mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
 
  251 void QgsNewGeoPackageLayerDialog::selectionChanged()
 
  253   mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
 
  256 void QgsNewGeoPackageLayerDialog::buttonBox_accepted()
 
  262 void QgsNewGeoPackageLayerDialog::buttonBox_rejected()
 
  267 bool QgsNewGeoPackageLayerDialog::apply()
 
  269   QString fileName( mDatabase->filePath() );
 
  270   if ( !fileName.endsWith( QLatin1String( 
".gpkg" ), Qt::CaseInsensitive ) )
 
  271     fileName += QLatin1String( 
".gpkg" );
 
  273   bool createNewDb = 
false;
 
  275   if ( QFile( fileName ).exists( fileName ) )
 
  277     bool overwrite = 
false;
 
  284         msgBox.setIcon( QMessageBox::Question );
 
  285         msgBox.setWindowTitle( tr( 
"New GeoPackage Layer" ) );
 
  286         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?" ) );
 
  287         QPushButton *overwriteButton = msgBox.addButton( tr( 
"Overwrite" ), QMessageBox::ActionRole );
 
  288         QPushButton *addNewLayerButton = msgBox.addButton( tr( 
"Add New Layer" ), QMessageBox::ActionRole );
 
  289         msgBox.setStandardButtons( QMessageBox::Cancel );
 
  290         msgBox.setDefaultButton( addNewLayerButton );
 
  292         if ( property( 
"hideDialogs" ).toBool() )
 
  294           overwrite = property( 
"question_existing_db_answer_overwrite" ).toBool();
 
  296             cancel = !property( 
"question_existing_db_answer_add_new_layer" ).toBool();
 
  300           int ret = msgBox.exec();
 
  301           if ( ret == QMessageBox::Cancel )
 
  303           if ( msgBox.clickedButton() == overwriteButton )
 
  324       QFile( fileName ).remove();
 
  333   OGRSFDriverH hGpkgDriver = OGRGetDriverByName( 
"GPKG" );
 
  336     if ( !property( 
"hideDialogs" ).toBool() )
 
  337       QMessageBox::critical( 
this, tr( 
"New GeoPackage Layer" ),
 
  338                              tr( 
"Layer creation failed. GeoPackage driver not found." ) );
 
  345     hDS.reset( OGR_Dr_CreateDataSource( hGpkgDriver, fileName.toUtf8().constData(), 
nullptr ) );
 
  348       QString msg( tr( 
"Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
 
  349       if ( !property( 
"hideDialogs" ).toBool() )
 
  350         QMessageBox::critical( 
this, tr( 
"New GeoPackage Layer" ), msg );
 
  356     OGRSFDriverH hDriver = 
nullptr;
 
  357     hDS.reset( OGROpen( fileName.toUtf8().constData(), 
true, &hDriver ) );
 
  360       QString msg( tr( 
"Opening of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
 
  361       if ( !property( 
"hideDialogs" ).toBool() )
 
  362         QMessageBox::critical( 
this, tr( 
"New GeoPackage Layer" ), msg );
 
  365     if ( hDriver != hGpkgDriver )
 
  367       QString msg( tr( 
"Opening of file succeeded, but this is not a GeoPackage database." ) );
 
  368       if ( !property( 
"hideDialogs" ).toBool() )
 
  369         QMessageBox::critical( 
this, tr( 
"New GeoPackage Layer" ), msg );
 
  374   QString tableName( mTableNameEdit->text() );
 
  376   bool overwriteTable = 
false;
 
  377   if ( OGR_DS_GetLayerByName( hDS.get(), tableName.toUtf8().constData() ) )
 
  379     if ( property( 
"hideDialogs" ).toBool() )
 
  381       overwriteTable = property( 
"question_existing_layer_answer_overwrite" ).toBool();
 
  383     else if ( QMessageBox::question( 
this, tr( 
"New GeoPackage Layer" ),
 
  384                                      tr( 
"A table with the same name already exists. Do you want to overwrite it?" ),
 
  385                                      QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes )
 
  387       overwriteTable = 
true;
 
  390     if ( !overwriteTable )
 
  396   QString layerIdentifier( mLayerIdentifierEdit->text() );
 
  397   QString layerDescription( mLayerDescriptionEdit->text() );
 
  399   OGRwkbGeometryType wkbType = 
static_cast<OGRwkbGeometryType
> 
  400                                ( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
 
  403   if ( mGeometryWithZCheckBox->isChecked() )
 
  404     wkbType = OGR_GT_SetZ( wkbType );
 
  406   if ( mGeometryWithMCheckBox->isChecked() )
 
  407     wkbType = OGR_GT_SetM( wkbType );
 
  412   if ( wkbType != wkbNone && srs.
isValid() )
 
  415     hSRS = OSRNewSpatialReference( srsWkt.toLocal8Bit().data() );
 
  419   char **options = 
nullptr;
 
  421   if ( overwriteTable )
 
  422     options = CSLSetNameValue( options, 
"OVERWRITE", 
"YES" );
 
  423   if ( !layerIdentifier.isEmpty() )
 
  424     options = CSLSetNameValue( options, 
"IDENTIFIER", layerIdentifier.toUtf8().constData() );
 
  425   if ( !layerDescription.isEmpty() )
 
  426     options = CSLSetNameValue( options, 
"DESCRIPTION", layerDescription.toUtf8().constData() );
 
  428   QString featureId( mFeatureIdColumnEdit->text() );
 
  429   if ( !featureId.isEmpty() )
 
  430     options = CSLSetNameValue( options, 
"FID", featureId.toUtf8().constData() );
 
  432   QString geometryColumn( mGeometryColumnEdit->text() );
 
  433   if ( wkbType != wkbNone && !geometryColumn.isEmpty() )
 
  434     options = CSLSetNameValue( options, 
"GEOMETRY_COLUMN", geometryColumn.toUtf8().constData() );
 
  436   if ( wkbType != wkbNone )
 
  437     options = CSLSetNameValue( options, 
"SPATIAL_INDEX", mCheckBoxCreateSpatialIndex->isChecked() ? 
"YES" : 
"NO" );
 
  439   OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS, wkbType, options );
 
  440   CSLDestroy( options );
 
  445     QString msg( tr( 
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
 
  446     if ( !property( 
"hideDialogs" ).toBool() )
 
  447       QMessageBox::critical( 
this, tr( 
"New GeoPackage Layer" ), msg );
 
  451   QTreeWidgetItemIterator it( mAttributeView );
 
  454     QString fieldName( ( *it )->text( 0 ) );
 
  455     QString fieldType( ( *it )->text( 1 ) );
 
  456     QString fieldWidth( ( *it )->text( 2 ) );
 
  459     OGRFieldType ogrType( OFTString );
 
  460     if ( fieldType == QLatin1String( 
"text" ) )
 
  462     else if ( fieldType == QLatin1String( 
"integer" ) )
 
  463       ogrType = OFTInteger;
 
  464     else if ( fieldType == QLatin1String( 
"integer64" ) )
 
  465       ogrType = OFTInteger64;
 
  466     else if ( fieldType == QLatin1String( 
"real" ) )
 
  468     else if ( fieldType == QLatin1String( 
"date" ) )
 
  470     else if ( fieldType == QLatin1String( 
"datetime" ) )
 
  471       ogrType = OFTDateTime;
 
  472     else if ( fieldType == QLatin1String( 
"bool" ) )
 
  474       ogrType = OFTInteger;
 
  477     else if ( fieldType == QLatin1String( 
"binary" ) )
 
  480     int ogrWidth = fieldWidth.toInt();
 
  483     if ( ogrType != OFTBinary )
 
  484       OGR_Fld_SetWidth( fld.get(), ogrWidth );
 
  486       OGR_Fld_SetSubType( fld.get(), OFSTBoolean );
 
  488     if ( OGR_L_CreateField( hLayer, fld.get(), 
true ) != OGRERR_NONE )
 
  490       if ( !property( 
"hideDialogs" ).toBool() )
 
  492         QMessageBox::critical( 
this, tr( 
"New GeoPackage Layer" ),
 
  493                                tr( 
"Creation of field %1 failed (OGR error: %2)" )
 
  494                                .arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
 
  505   OGR_L_ResetReading( hLayer );
 
  506   if ( CPLGetLastErrorType() != CE_None )
 
  508     QString msg( tr( 
"Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
 
  509     if ( !property( 
"hideDialogs" ).toBool() )
 
  510       QMessageBox::critical( 
this, tr( 
"New GeoPackage Layer" ), msg );
 
  515   QString uri( QStringLiteral( 
"%1|layername=%2" ).arg( fileName, tableName ) );
 
  516   QString userVisiblelayerName( layerIdentifier.isEmpty() ? tableName : layerIdentifier );
 
  518   std::unique_ptr< QgsVectorLayer > layer = qgis::make_unique< QgsVectorLayer >( uri, userVisiblelayerName, QStringLiteral( 
"ogr" ), layerOptions );
 
  519   if ( layer->isValid() )
 
  524       QList<QgsMapLayer *> myList;
 
  525       myList << layer.release();
 
  538     if ( !property( 
"hideDialogs" ).toBool() )
 
  539       QMessageBox::critical( 
this, tr( 
"New GeoPackage Layer" ), tr( 
"%1 is an invalid layer and cannot be loaded." ).arg( tableName ) );
 
  547   mBehavior = behavior;
 
  552   mAddToProject = addToProject;
 
  555 void QgsNewGeoPackageLayerDialog::showHelp()
 
  557   QgsHelp::openHelp( QStringLiteral( 
"managing_data_source/create_layers.html#creating-a-new-geopackage-layer" ) );
 
static QIcon getThemeIcon(const QString &name)
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.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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.