QGIS API Documentation 3.99.0-Master (e9821da5c6b)
Loading...
Searching...
No Matches
qgsnewgeopackagelayerdialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsnewgeopackagelayerdialog.cpp
3
4 -------------------
5 begin : April 2016
6 copyright : (C) 2016 by Even Rouault
7 email : even.rouault at spatialys.com
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19
21
22#include <cpl_error.h>
23#include <cpl_string.h>
24#include <gdal_version.h>
25#include <ogr_api.h>
26#include <ogr_srs_api.h>
27
28#include "qgis.h"
29#include "qgsapplication.h"
31#include "qgsgui.h"
32#include "qgsguiutils.h"
33#include "qgshelp.h"
34#include "qgsiconutils.h"
35#include "qgsogrutils.h"
36#include "qgsproject.h"
38#include "qgssettings.h"
39#include "qgsvariantutils.h"
40#include "qgsvectorlayer.h"
41
42#include <QCompleter>
43#include <QFileDialog>
44#include <QLineEdit>
45#include <QMessageBox>
46#include <QPushButton>
47#include <QString>
48
49#include "moc_qgsnewgeopackagelayerdialog.cpp"
50
51using namespace Qt::StringLiterals;
52
53#define DEFAULT_OGR_FID_COLUMN_TITLE "fid" // default value from OGR
54
56 : QDialog( parent, fl )
57{
58 setupUi( this );
59 setObjectName( u"QgsNewGeoPackageLayerDialog"_s );
61
62 connect( mAddAttributeButton, &QToolButton::clicked, this, &QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked );
63 connect( mRemoveAttributeButton, &QToolButton::clicked, this, &QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked );
64 connect( mFieldTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged );
65 connect( mGeometryTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged );
66 connect( mTableNameEdit, &QLineEdit::textChanged, this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged );
67 connect( mTableNameEdit, &QLineEdit::textEdited, this, &QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited );
68 connect( mLayerIdentifierEdit, &QLineEdit::textEdited, this, &QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited );
69 connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsNewGeoPackageLayerDialog::buttonBox_accepted );
70 connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsNewGeoPackageLayerDialog::buttonBox_rejected );
71 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsNewGeoPackageLayerDialog::showHelp );
72 connect( mButtonUp, &QToolButton::clicked, this, &QgsNewGeoPackageLayerDialog::moveFieldsUp );
73 connect( mButtonDown, &QToolButton::clicked, this, &QgsNewGeoPackageLayerDialog::moveFieldsDown );
74
75 mAddAttributeButton->setIcon( QgsApplication::getThemeIcon( u"/mActionNewAttribute.svg"_s ) );
76 mRemoveAttributeButton->setIcon( QgsApplication::getThemeIcon( u"/mActionDeleteAttribute.svg"_s ) );
77
78 const auto addGeomItem = [this]( OGRwkbGeometryType ogrGeomType ) {
79 const Qgis::WkbType qgsType = QgsOgrUtils::ogrGeometryTypeToQgsWkbType( ogrGeomType );
80 mGeometryTypeBox->addItem( QgsIconUtils::iconForWkbType( qgsType ), QgsWkbTypes::translatedDisplayString( qgsType ), ogrGeomType );
81 };
82
83 addGeomItem( wkbNone );
84 addGeomItem( wkbPoint );
85 addGeomItem( wkbLineString );
86 addGeomItem( wkbPolygon );
87 addGeomItem( wkbMultiPoint );
88 addGeomItem( wkbMultiLineString );
89 addGeomItem( wkbMultiPolygon );
90
91#if 0
92 // QGIS always create CompoundCurve and there's no real interest of having just CircularString. CompoundCurve are more useful
93 addGeomItem( wkbCircularString );
94#endif
95 addGeomItem( wkbCompoundCurve );
96 addGeomItem( wkbCurvePolygon );
97 addGeomItem( wkbMultiCurve );
98 addGeomItem( wkbMultiSurface );
99 addGeomItem( wkbPolyhedralSurface );
100 addGeomItem( wkbTIN );
101 mGeometryTypeBox->setCurrentIndex( -1 );
102
103 mGeometryWithZCheckBox->setEnabled( false );
104 mGeometryWithMCheckBox->setEnabled( false );
105 mGeometryColumnEdit->setEnabled( false );
106 mGeometryColumnEdit->setText( u"geometry"_s );
107 mFeatureIdColumnEdit->setPlaceholderText( QStringLiteral( DEFAULT_OGR_FID_COLUMN_TITLE ) );
108 mCheckBoxCreateSpatialIndex->setEnabled( false );
109 mCrsSelector->setEnabled( false );
110 mCrsSelector->setShowAccuracyWarnings( true );
111
112 mFieldTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::QString ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::QString ), "text" );
113 mFieldTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::Int ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::Int ), "integer" );
114 mFieldTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::LongLong ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::LongLong ), "integer64" );
115 mFieldTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::Double ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::Double ), "real" );
116 mFieldTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::QDate ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDate ), "date" );
117 mFieldTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::QDateTime ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::QDateTime ), "datetime" );
118 mFieldTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::Bool ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::Bool ), "bool" );
119 mFieldTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::QByteArray ), QgsVariantUtils::typeToDisplayString( QMetaType::Type::QByteArray ), "binary" );
120 mFieldTypeBox->addItem( QgsFields::iconForFieldType( QMetaType::Type::QVariantMap ), tr( "JSON" ), "json" );
121
122 mOkButton = buttonBox->button( QDialogButtonBox::Ok );
123 mOkButton->setEnabled( false );
124
125 connect( mFieldNameEdit, &QLineEdit::textChanged, this, &QgsNewGeoPackageLayerDialog::fieldNameChanged );
126 connect( mAttributeView, &QTreeWidget::itemSelectionChanged, this, &QgsNewGeoPackageLayerDialog::selectionChanged );
127 connect( mTableNameEdit, &QLineEdit::textChanged, this, &QgsNewGeoPackageLayerDialog::checkOk );
128 connect( mGeometryTypeBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsNewGeoPackageLayerDialog::checkOk );
129
130 mAddAttributeButton->setEnabled( false );
131 mRemoveAttributeButton->setEnabled( false );
132 mButtonUp->setEnabled( false );
133 mButtonDown->setEnabled( false );
134
135 mCheckBoxCreateSpatialIndex->setChecked( true );
136
137 const QgsSettings settings;
138 mFileName->setStorageMode( QgsFileWidget::SaveFile );
139 mFileName->setFilter( tr( "GeoPackage" ) + " (*.gpkg)" );
140 mFileName->setDialogTitle( tr( "Select Existing or Create a New GeoPackage Database File…" ) );
141 mFileName->setDefaultRoot( settings.value( u"UI/lastVectorFileFilterDir"_s, QDir::homePath() ).toString() );
142 mFileName->setConfirmOverwrite( false );
143 connect( mFileName, &QgsFileWidget::fileChanged, this, [this]( const QString &filePath ) {
144 QgsSettings settings;
145 const QFileInfo tmplFileInfo( filePath );
146 settings.setValue( u"UI/lastVectorFileFilterDir"_s, tmplFileInfo.absolutePath() );
147 if ( !filePath.isEmpty() && !mTableNameEdited )
148 {
149 const QFileInfo fileInfo( filePath );
150 mTableNameEdit->setText( fileInfo.baseName() );
151 }
152 checkOk();
153 } );
154
155 QgsProviderConnectionModel *ogrProviderModel = new QgsProviderConnectionModel( u"ogr"_s, this );
156
157 QCompleter *completer = new QCompleter( this );
158 completer->setModel( ogrProviderModel );
159 completer->setCompletionRole( static_cast<int>( QgsProviderConnectionModel::CustomRole::Uri ) );
160 completer->setCompletionMode( QCompleter::PopupCompletion );
161 completer->setFilterMode( Qt::MatchContains );
162 mFileName->lineEdit()->setCompleter( completer );
163}
164
166{
167 mCrsSelector->setCrs( crs );
168}
169
171{
172 mFileName->setReadOnly( true );
173}
174
175void QgsNewGeoPackageLayerDialog::mFieldTypeBox_currentIndexChanged( int )
176{
177 const QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
178 mFieldLengthEdit->setEnabled( myType == "text"_L1 );
179 if ( myType != "text"_L1 )
180 mFieldLengthEdit->clear();
181}
182
183
184void QgsNewGeoPackageLayerDialog::mGeometryTypeBox_currentIndexChanged( int )
185{
186 const OGRwkbGeometryType geomType = static_cast<OGRwkbGeometryType>( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
187 const bool isSpatial = geomType != wkbNone;
188 mGeometryWithZCheckBox->setEnabled( isSpatial );
189 mGeometryWithMCheckBox->setEnabled( isSpatial );
190 mGeometryColumnEdit->setEnabled( isSpatial );
191 mCheckBoxCreateSpatialIndex->setEnabled( isSpatial );
192 mCrsSelector->setEnabled( isSpatial );
193}
194
195void QgsNewGeoPackageLayerDialog::mTableNameEdit_textChanged( const QString &text )
196{
197 mTableNameEdited = !text.isEmpty();
198 if ( !text.isEmpty() && !mLayerIdentifierEdited )
199 {
200 mLayerIdentifierEdit->setText( text );
201 }
202}
203
204void QgsNewGeoPackageLayerDialog::mTableNameEdit_textEdited( const QString &text )
205{
206 // Remember if the user explicitly defined a name
207 mTableNameEdited = !text.isEmpty();
208}
209
210void QgsNewGeoPackageLayerDialog::mLayerIdentifierEdit_textEdited( const QString &text )
211{
212 // Remember if the user explicitly defined a name
213 mLayerIdentifierEdited = !text.isEmpty();
214}
215
216void QgsNewGeoPackageLayerDialog::checkOk()
217{
218 const bool ok = !mFileName->filePath().isEmpty() && !mTableNameEdit->text().isEmpty() && mGeometryTypeBox->currentIndex() != -1;
219
220 mOkButton->setEnabled( ok );
221}
222
223void QgsNewGeoPackageLayerDialog::mAddAttributeButton_clicked()
224{
225 if ( !mFieldNameEdit->text().isEmpty() )
226 {
227 const QString myName = mFieldNameEdit->text();
228 const QString featureId = mFeatureIdColumnEdit->text().isEmpty() ? QStringLiteral( DEFAULT_OGR_FID_COLUMN_TITLE ) : mFeatureIdColumnEdit->text();
229 if ( myName.compare( featureId, Qt::CaseInsensitive ) == 0 )
230 {
231 QMessageBox::critical( this, tr( "Add Field" ), tr( "The field cannot have the same name as the feature identifier." ) );
232 return;
233 }
234
235 //use userrole to avoid translated type string
236 const QString myType = mFieldTypeBox->currentData( Qt::UserRole ).toString();
237 const QString length = mFieldLengthEdit->text();
238 mAttributeView->addTopLevelItem( new QTreeWidgetItem( QStringList() << myName << myType << length ) );
239
240 checkOk();
241
242 mFieldNameEdit->clear();
243
244 if ( !mFieldNameEdit->hasFocus() )
245 {
246 mFieldNameEdit->setFocus();
247 }
248 }
249}
250
251void QgsNewGeoPackageLayerDialog::mRemoveAttributeButton_clicked()
252{
253 delete mAttributeView->currentItem();
254
255 checkOk();
256}
257
258void QgsNewGeoPackageLayerDialog::fieldNameChanged( const QString &name )
259{
260 mAddAttributeButton->setDisabled( name.isEmpty() || !mAttributeView->findItems( name, Qt::MatchExactly ).isEmpty() );
261}
262
263void QgsNewGeoPackageLayerDialog::selectionChanged()
264{
265 mRemoveAttributeButton->setDisabled( mAttributeView->selectedItems().isEmpty() );
266 mButtonUp->setDisabled( mAttributeView->selectedItems().isEmpty() );
267 mButtonDown->setDisabled( mAttributeView->selectedItems().isEmpty() );
268}
269
270void QgsNewGeoPackageLayerDialog::buttonBox_accepted()
271{
272 if ( apply() )
273 accept();
274}
275
276void QgsNewGeoPackageLayerDialog::buttonBox_rejected()
277{
278 reject();
279}
280
281void QgsNewGeoPackageLayerDialog::moveFieldsUp()
282{
283 int currentRow = mAttributeView->currentIndex().row();
284 if ( currentRow == 0 )
285 return;
286
287 mAttributeView->insertTopLevelItem( currentRow - 1, mAttributeView->takeTopLevelItem( currentRow ) );
288 mAttributeView->setCurrentIndex( mAttributeView->model()->index( currentRow - 1, 0 ) );
289}
290
291void QgsNewGeoPackageLayerDialog::moveFieldsDown()
292{
293 int currentRow = mAttributeView->currentIndex().row();
294 if ( currentRow == mAttributeView->topLevelItemCount() - 1 )
295 return;
296
297 mAttributeView->insertTopLevelItem( currentRow + 1, mAttributeView->takeTopLevelItem( currentRow ) );
298 mAttributeView->setCurrentIndex( mAttributeView->model()->index( currentRow + 1, 0 ) );
299}
300
301bool QgsNewGeoPackageLayerDialog::apply()
302{
303 if ( !mFieldNameEdit->text().trimmed().isEmpty() )
304 {
305 const QString currentFieldName = mFieldNameEdit->text();
306 bool currentFound = false;
307 QTreeWidgetItemIterator it( mAttributeView );
308 while ( *it )
309 {
310 QTreeWidgetItem *item = *it;
311 if ( item->text( 0 ) == currentFieldName )
312 {
313 currentFound = true;
314 break;
315 }
316 ++it;
317 }
318
319 if ( !currentFound )
320 {
321 if ( QMessageBox::question( this, windowTitle(), tr( "The field “%1” has not been added to the fields list. Are you sure you want to proceed and discard this field?" ).arg( currentFieldName ), QMessageBox::Ok | QMessageBox::Cancel ) != QMessageBox::Ok )
322 {
323 return false;
324 }
325 }
326 }
327
328 QString fileName( mFileName->filePath() );
329 if ( !fileName.endsWith( ".gpkg"_L1, Qt::CaseInsensitive ) )
330 fileName += ".gpkg"_L1;
331
332 bool createNewDb = false;
333
334 if ( QFile::exists( fileName ) )
335 {
336 bool overwrite = false;
337
338 switch ( mBehavior )
339 {
340 case Prompt:
341 {
342 QMessageBox msgBox;
343 msgBox.setIcon( QMessageBox::Question );
344 msgBox.setWindowTitle( tr( "New GeoPackage Layer" ) );
345 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?" ) );
346 QPushButton *overwriteButton = msgBox.addButton( tr( "Overwrite" ), QMessageBox::ActionRole );
347 QPushButton *addNewLayerButton = msgBox.addButton( tr( "Add New Layer" ), QMessageBox::ActionRole );
348 msgBox.setStandardButtons( QMessageBox::Cancel );
349 msgBox.setDefaultButton( addNewLayerButton );
350 bool cancel = false;
351 if ( property( "hideDialogs" ).toBool() )
352 {
353 overwrite = property( "question_existing_db_answer_overwrite" ).toBool();
354 if ( !overwrite )
355 cancel = !property( "question_existing_db_answer_add_new_layer" ).toBool();
356 }
357 else
358 {
359 const int ret = msgBox.exec();
360 if ( ret == QMessageBox::Cancel )
361 cancel = true;
362 if ( msgBox.clickedButton() == overwriteButton )
363 overwrite = true;
364 }
365 if ( cancel )
366 {
367 return false;
368 }
369 break;
370 }
371
372 case Overwrite:
373 overwrite = true;
374 break;
375
376 case AddNewLayer:
377 overwrite = false;
378 break;
379 }
380
381 if ( overwrite )
382 {
383 QFile( fileName ).remove();
384 createNewDb = true;
385 }
386 }
387 else
388 {
389 createNewDb = true;
390 }
391
392 OGRSFDriverH hGpkgDriver = OGRGetDriverByName( "GPKG" );
393 if ( !hGpkgDriver )
394 {
395 if ( !property( "hideDialogs" ).toBool() )
396 QMessageBox::critical( this, tr( "New GeoPackage Layer" ), tr( "Layer creation failed. GeoPackage driver not found." ) );
397 return false;
398 }
399
401 if ( createNewDb )
402 {
403 hDS.reset( OGR_Dr_CreateDataSource( hGpkgDriver, fileName.toUtf8().constData(), nullptr ) );
404 if ( !hDS )
405 {
406 const QString msg( tr( "Creation of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
407 if ( !property( "hideDialogs" ).toBool() )
408 QMessageBox::critical( this, tr( "New GeoPackage Layer" ), msg );
409 return false;
410 }
411 }
412 else
413 {
414 OGRSFDriverH hDriver = nullptr;
415 hDS.reset( OGROpen( fileName.toUtf8().constData(), true, &hDriver ) );
416 if ( !hDS )
417 {
418 const QString msg( tr( "Opening of database failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
419 if ( !property( "hideDialogs" ).toBool() )
420 QMessageBox::critical( this, tr( "New GeoPackage Layer" ), msg );
421 return false;
422 }
423 if ( hDriver != hGpkgDriver )
424 {
425 const QString msg( tr( "Opening of file succeeded, but this is not a GeoPackage database." ) );
426 if ( !property( "hideDialogs" ).toBool() )
427 QMessageBox::critical( this, tr( "New GeoPackage Layer" ), msg );
428 return false;
429 }
430 }
431
432 const QString tableName( mTableNameEdit->text() );
433
434 bool overwriteTable = false;
435 if ( OGR_DS_GetLayerByName( hDS.get(), tableName.toUtf8().constData() ) )
436 {
437 if ( property( "hideDialogs" ).toBool() )
438 {
439 overwriteTable = property( "question_existing_layer_answer_overwrite" ).toBool();
440 }
441 else if ( QMessageBox::question( this, tr( "New GeoPackage Layer" ), tr( "A table with the same name already exists. Do you want to overwrite it?" ), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes )
442 {
443 overwriteTable = true;
444 }
445
446 if ( !overwriteTable )
447 {
448 return false;
449 }
450 }
451
452 const QString layerIdentifier( mLayerIdentifierEdit->text() );
453 const QString layerDescription( mLayerDescriptionEdit->text() );
454
455 OGRwkbGeometryType wkbType = static_cast<OGRwkbGeometryType>( mGeometryTypeBox->currentData( Qt::UserRole ).toInt() );
456
457 // z-coordinate & m-value.
458 if ( mGeometryWithZCheckBox->isChecked() )
459 wkbType = OGR_GT_SetZ( wkbType );
460
461 if ( mGeometryWithMCheckBox->isChecked() )
462 wkbType = OGR_GT_SetM( wkbType );
463
464 // Check for non-standard GeoPackage geometry types
465 const Qgis::WkbType qgisWkbType = static_cast<Qgis::WkbType>( wkbType );
466 bool isNonStandardGeomType = false;
467 if ( !QgsGuiUtils::warnAboutNonStandardGeoPackageGeometryType( qgisWkbType, this, tr( "New GeoPackage Layer" ), !property( "hideDialogs" ).toBool(), &isNonStandardGeomType ) )
468 {
469 return false;
470 }
471
472 OGRSpatialReferenceH hSRS = nullptr;
473 // consider spatial reference system of the layer
474 const QgsCoordinateReferenceSystem srs = mCrsSelector->crs();
475 if ( wkbType != wkbNone && srs.isValid() )
476 {
478 }
479
480 // Set options
481 char **options = nullptr;
482
483 if ( overwriteTable )
484 options = CSLSetNameValue( options, "OVERWRITE", "YES" );
485 if ( !layerIdentifier.isEmpty() )
486 options = CSLSetNameValue( options, "IDENTIFIER", layerIdentifier.toUtf8().constData() );
487 if ( !layerDescription.isEmpty() )
488 options = CSLSetNameValue( options, "DESCRIPTION", layerDescription.toUtf8().constData() );
489
490 const QString featureId( mFeatureIdColumnEdit->text() );
491 if ( !featureId.isEmpty() )
492 options = CSLSetNameValue( options, "FID", featureId.toUtf8().constData() );
493
494 const QString geometryColumn( mGeometryColumnEdit->text() );
495 if ( wkbType != wkbNone && !geometryColumn.isEmpty() )
496 options = CSLSetNameValue( options, "GEOMETRY_COLUMN", geometryColumn.toUtf8().constData() );
497
498 if ( wkbType != wkbNone )
499 options = CSLSetNameValue( options, "SPATIAL_INDEX", mCheckBoxCreateSpatialIndex->isChecked() ? "YES" : "NO" );
500
501 OGRLayerH hLayer = OGR_DS_CreateLayer( hDS.get(), tableName.toUtf8().constData(), hSRS, wkbType, options );
502 CSLDestroy( options );
503 if ( hSRS )
504 OSRRelease( hSRS );
505 if ( !hLayer )
506 {
507 const QString msg( tr( "Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
508 if ( !property( "hideDialogs" ).toBool() )
509 QMessageBox::critical( this, tr( "New GeoPackage Layer" ), msg );
510 return false;
511 }
512
513 QTreeWidgetItemIterator it( mAttributeView );
514 while ( *it )
515 {
516 const QString fieldName( ( *it )->text( 0 ) );
517 const QString fieldType( ( *it )->text( 1 ) );
518 const QString fieldWidth( ( *it )->text( 2 ) );
519
520 OGRFieldType ogrType( OFTString );
521 OGRFieldSubType ogrSubType = OFSTNone;
522 if ( fieldType == "text"_L1 )
523 ogrType = OFTString;
524 else if ( fieldType == "integer"_L1 )
525 ogrType = OFTInteger;
526 else if ( fieldType == "integer64"_L1 )
527 ogrType = OFTInteger64;
528 else if ( fieldType == "real"_L1 )
529 ogrType = OFTReal;
530 else if ( fieldType == "date"_L1 )
531 ogrType = OFTDate;
532 else if ( fieldType == "datetime"_L1 )
533 ogrType = OFTDateTime;
534 else if ( fieldType == "bool"_L1 )
535 {
536 ogrType = OFTInteger;
537 ogrSubType = OFSTBoolean;
538 }
539 else if ( fieldType == "binary"_L1 )
540 ogrType = OFTBinary;
541 else if ( fieldType == "json"_L1 )
542 {
543 ogrType = OFTString;
544 ogrSubType = OFSTJSON;
545 }
546
547 const int ogrWidth = fieldWidth.toInt();
548
549 const gdal::ogr_field_def_unique_ptr fld( OGR_Fld_Create( fieldName.toUtf8().constData(), ogrType ) );
550 if ( ogrSubType != OFSTNone )
551 OGR_Fld_SetSubType( fld.get(), ogrSubType );
552
553 if ( ogrType != OFTBinary )
554 OGR_Fld_SetWidth( fld.get(), ogrWidth );
555
556 if ( OGR_L_CreateField( hLayer, fld.get(), true ) != OGRERR_NONE )
557 {
558 if ( !property( "hideDialogs" ).toBool() )
559 {
560 QMessageBox::critical( this, tr( "New GeoPackage Layer" ), tr( "Creation of field %1 failed (OGR error: %2)" ).arg( fieldName, QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
561 }
562 return false;
563 }
564
565 ++it;
566 }
567
568 // In GDAL >= 2.0, the driver implements a deferred creation strategy, so
569 // issue a command that will force table creation
570 CPLErrorReset();
571 OGR_L_ResetReading( hLayer );
572 const CPLErr errorType = CPLGetLastErrorType();
573 if ( errorType == CE_Failure || errorType == CE_Fatal )
574 {
575 const QString msg( tr( "Creation of layer failed (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
576 if ( !property( "hideDialogs" ).toBool() )
577 QMessageBox::critical( this, tr( "New GeoPackage Layer" ), msg );
578 return false;
579 }
580 else if ( errorType == CE_Warning && !isNonStandardGeomType )
581 {
582 // Show OGR warning only if user was not already warned about non-standard geometry types
583 const QString msg( tr( "Layer created with warning (OGR warning: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) ) );
584 if ( !property( "hideDialogs" ).toBool() )
585 QMessageBox::warning( this, tr( "New GeoPackage Layer" ), msg );
586 }
587 hDS.reset();
588
589 const QString uri( u"%1|layername=%2"_s.arg( fileName, tableName ) );
590 const QString userVisiblelayerName( layerIdentifier.isEmpty() ? tableName : layerIdentifier );
591 const QgsVectorLayer::LayerOptions layerOptions { QgsProject::instance()->transformContext() };
592 auto layer = std::make_unique<QgsVectorLayer>( uri, userVisiblelayerName, u"ogr"_s, layerOptions );
593 if ( layer->isValid() )
594 {
595 if ( mAddToProject )
596 {
597 // register this layer with the central layers registry
598 QList<QgsMapLayer *> myList;
599 myList << layer.release();
600 //addMapLayers returns a list of all successfully added layers
601 //so we compare that to our original list.
602 if ( myList == QgsProject::instance()->addMapLayers( myList ) )
603 return true;
604 }
605 else
606 {
607 return true;
608 }
609 }
610 else
611 {
612 if ( !property( "hideDialogs" ).toBool() )
613 QMessageBox::critical( this, tr( "New GeoPackage Layer" ), tr( "%1 is an invalid layer and cannot be loaded." ).arg( tableName ) );
614 }
615
616 return false;
617}
618
620{
621 mBehavior = behavior;
622}
623
625{
626 mAddToProject = addToProject;
627}
628
629void QgsNewGeoPackageLayerDialog::showHelp()
630{
631 QgsHelp::openHelp( u"managing_data_source/create_layers.html#creating-a-new-geopackage-layer"_s );
632}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
static QIcon iconForFieldType(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType, const QString &typeString=QString())
Returns an icon corresponding to a field type.
@ SaveFile
Select a single new or pre-existing file.
void fileChanged(const QString &path)
Emitted whenever the current file or directory path is changed.
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...
Definition qgsgui.cpp:224
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Definition qgshelp.cpp:41
static QIcon iconForWkbType(Qgis::WkbType 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.
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 Qgis::WkbType ogrGeometryTypeToQgsWkbType(OGRwkbGeometryType ogrGeomType)
Converts a OGRwkbGeometryType to QgsWkbTypes::Type.
static OGRSpatialReferenceH crsToOGRSpatialReference(const QgsCoordinateReferenceSystem &crs)
Returns a OGRSpatialReferenceH corresponding to the specified crs object.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:120
A model containing registered connection names for a specific data provider.
Stores settings for use within QGIS.
Definition qgssettings.h:68
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 typeToDisplayString(QMetaType::Type type, QMetaType::Type subType=QMetaType::Type::UnknownType)
Returns a user-friendly translated string representing a QVariant type.
static Q_INVOKABLE QString translatedDisplayString(Qgis::WkbType type)
Returns a translated display string type for a WKB type, e.g., the geometry name used in WKT geometry...
bool warnAboutNonStandardGeoPackageGeometryType(Qgis::WkbType wkbType, QWidget *parent, const QString &dialogTitle, bool showDialog, bool *isNonStandard)
Checks if the given wkbType is a non-standard GeoPackage geometry type (PolyhedralSurface,...
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.
#define DEFAULT_OGR_FID_COLUMN_TITLE