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.