Skip to content

Commit afdb6c7

Browse files
committed
Merge pull request #3031 from rouault/gpkg_improvements
GeoPackage support related improvements
2 parents 563d75c + 7703ad8 commit afdb6c7

27 files changed

+2262
-69
lines changed

cmake/FindGDAL.cmake

+9
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ ELSE(WIN32)
6464
IF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION EQUAL 1 AND GDAL_VERSION_MINOR LESS 4))
6565
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 1.4.0 or higher.")
6666
ENDIF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION EQUAL 1 AND GDAL_VERSION_MINOR LESS 4))
67+
68+
IF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 11))
69+
MESSAGE (WARNING "GDAL version is too old (${GDAL_VERSION}) to support GeoPackage. 1.11.0 or higher is recommended.")
70+
ENDIF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 11))
71+
6772
ENDIF (GDAL_LIBRARY)
6873
SET (CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_save} CACHE STRING "" FORCE)
6974
ENDIF ()
@@ -105,6 +110,10 @@ ELSE(WIN32)
105110
MESSAGE (FATAL_ERROR "GDAL version is too old (${GDAL_VERSION}). Use 1.4.0 or higher.")
106111
ENDIF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 4))
107112

113+
IF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 11))
114+
MESSAGE (WARNING "GDAL version is too old (${GDAL_VERSION}) to support GeoPackage. 1.11.0 or higher is recommended.")
115+
ENDIF (GDAL_VERSION_MAJOR LESS 1 OR (GDAL_VERSION_MAJOR EQUAL 1 AND GDAL_VERSION_MINOR LESS 11))
116+
108117
# set INCLUDE_DIR to prefix+include
109118
EXEC_PROGRAM(${GDAL_CONFIG}
110119
ARGS --prefix

images/images.qrc

+1
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@
259259
<file>themes/default/mActionNewComposer.svg</file>
260260
<file>themes/default/mActionNewFolder.png</file>
261261
<file>themes/default/mActionNewSpatiaLiteLayer.svg</file>
262+
<file>themes/default/mActionNewGeoPackageLayer.svg</file>
262263
<file>themes/default/mActionNewVectorLayer.svg</file>
263264
<file>themes/default/mActionNodeTool.png</file>
264265
<file>themes/default/mActionOffsetCurve.png</file>

images/themes/default/mActionNewGeoPackageLayer.svg

+394
Loading

python/gui/gui.sip

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
%Include qgsnewmemorylayerdialog.sip
122122
%Include qgsnewnamedialog.sip
123123
%Include qgsnewvectorlayerdialog.sip
124+
%Include qgsnewgeopackagelayerdialog.sip
124125
%Include qgsnumericsortlistviewitem.sip
125126
%Include qgsoptionsdialogbase.sip
126127
%Include qgsorderbydialog.sip
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/** Dialog to set up parameters to create a new GeoPackage layer, and on accept() to create it and add it to the layers */
2+
class QgsNewGeoPackageLayerDialog : QDialog
3+
{
4+
%TypeHeaderCode
5+
#include <qgsnewgeopackagelayerdialog.h>
6+
%End
7+
8+
public:
9+
/** Constructor */
10+
QgsNewGeoPackageLayerDialog( QWidget *parent /TransferThis/ = 0, const Qt::WindowFlags& fl = QgisGui::ModalDialogFlags );
11+
12+
~QgsNewGeoPackageLayerDialog();
13+
14+
};

src/app/qgisapp.cpp

+36-7
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,13 @@
254254
// GDAL/OGR includes
255255
//
256256
#include <ogr_api.h>
257+
#include <gdal_version.h>
257258
#include <proj_api.h>
258259

260+
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(1,11,0)
261+
#define SUPPORT_GEOPACKAGE
262+
#endif
263+
259264
//
260265
// Other includes
261266
//
@@ -334,6 +339,7 @@ extern "C"
334339
#include <spatialite.h>
335340
}
336341
#include "qgsnewspatialitelayerdialog.h"
342+
#include "qgsnewgeopackagelayerdialog.h"
337343

338344
#include "qgspythonutils.h"
339345

@@ -1431,6 +1437,7 @@ void QgisApp::createActions()
14311437

14321438
connect( mActionNewVectorLayer, SIGNAL( triggered() ), this, SLOT( newVectorLayer() ) );
14331439
connect( mActionNewSpatiaLiteLayer, SIGNAL( triggered() ), this, SLOT( newSpatialiteLayer() ) );
1440+
connect( mActionNewGeoPackageLayer, SIGNAL( triggered() ), this, SLOT( newGeoPackageLayer() ) );
14341441
connect( mActionNewMemoryLayer, SIGNAL( triggered() ), this, SLOT( newMemoryLayer() ) );
14351442
connect( mActionShowRasterCalculator, SIGNAL( triggered() ), this, SLOT( showRasterCalculator() ) );
14361443
connect( mActionShowAlignRasterTool, SIGNAL( triggered() ), this, SLOT( showAlignRasterTool() ) );
@@ -1725,6 +1732,11 @@ void QgisApp::createMenus()
17251732
* For Mac, About and Exit are also automatically moved by Qt to the Application menu.
17261733
*/
17271734

1735+
// Layer menu
1736+
#ifndef SUPPORT_GEOPACKAGE
1737+
mNewLayerMenu->removeAction( mActionNewGeoPackageLayer );
1738+
#endif
1739+
17281740
// Panel and Toolbar Submenus
17291741
mPanelMenu = new QMenu( tr( "Panels" ), this );
17301742
mPanelMenu->setObjectName( "mPanelMenu" );
@@ -1980,6 +1992,9 @@ void QgisApp::createToolBars()
19801992
bt->setPopupMode( QToolButton::MenuButtonPopup );
19811993
bt->addAction( mActionNewVectorLayer );
19821994
bt->addAction( mActionNewSpatiaLiteLayer );
1995+
#ifdef SUPPORT_GEOPACKAGE
1996+
bt->addAction( mActionNewGeoPackageLayer );
1997+
#endif
19831998
bt->addAction( mActionNewMemoryLayer );
19841999

19852000
QAction* defNewLayerAction = mActionNewVectorLayer;
@@ -1994,6 +2009,11 @@ void QgisApp::createToolBars()
19942009
case 2:
19952010
defNewLayerAction = mActionNewMemoryLayer;
19962011
break;
2012+
#ifdef SUPPORT_GEOPACKAGE
2013+
case 3:
2014+
defNewLayerAction = mActionNewGeoPackageLayer;
2015+
break;
2016+
#endif
19972017
}
19982018
bt->setDefaultAction( defNewLayerAction );
19992019
QAction* newLayerAction = mLayerToolBar->addWidget( bt );
@@ -3668,10 +3688,10 @@ void QgisApp::loadOGRSublayers( const QString& layertype, const QString& uri, co
36683688
}
36693689

36703690
QString layerName = elements.value( 0 );
3671-
QString layerType = elements.value( 1 );
3672-
if ( layerType == "any" )
3691+
QString layerGeometryType = elements.value( 1 );
3692+
if ( layerGeometryType == "any" )
36733693
{
3674-
layerType = "";
3694+
layerGeometryType = "";
36753695
elements.removeAt( 1 );
36763696
}
36773697

@@ -3684,16 +3704,17 @@ void QgisApp::loadOGRSublayers( const QString& layertype, const QString& uri, co
36843704
composedURI = uri + "|layerindex=" + layerName;
36853705
}
36863706

3687-
if ( !layerType.isEmpty() )
3707+
if ( !layerGeometryType.isEmpty() )
36883708
{
3689-
composedURI += "|geometrytype=" + layerType;
3709+
composedURI += "|geometrytype=" + layerGeometryType;
36903710
}
36913711

36923712
// addVectorLayer( composedURI, list.at( i ), "ogr" );
36933713

36943714
QgsDebugMsg( "Creating new vector layer using " + composedURI );
3695-
QString name = list.at( i );
3696-
name.replace( ':', ' ' );
3715+
QString name = layerName;
3716+
if ( !layerGeometryType.isEmpty() )
3717+
name += " " + layerGeometryType;
36973718
QgsVectorLayer *layer = new QgsVectorLayer( composedURI, fileName + " " + name, "ogr", false );
36983719
if ( layer && layer->isValid() )
36993720
{
@@ -4366,6 +4387,12 @@ void QgisApp::newSpatialiteLayer()
43664387
spatialiteDialog.exec();
43674388
}
43684389

4390+
void QgisApp::newGeoPackageLayer()
4391+
{
4392+
QgsNewGeoPackageLayerDialog dialog( this );
4393+
dialog.exec();
4394+
}
4395+
43694396
void QgisApp::showRasterCalculator()
43704397
{
43714398
QgsRasterCalcDialog d( this );
@@ -11227,6 +11254,8 @@ void QgisApp::toolButtonActionTriggered( QAction *action )
1122711254
settings.setValue( "/UI/defaultNewLayer", 1 );
1122811255
else if ( action == mActionNewMemoryLayer )
1122911256
settings.setValue( "/UI/defaultNewLayer", 2 );
11257+
else if ( action == mActionNewGeoPackageLayer )
11258+
settings.setValue( "/UI/defaultNewLayer", 3 );
1123011259
bt->setDefaultAction( action );
1123111260
}
1123211261

src/app/qgisapp.h

+2
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
948948
void newMemoryLayer();
949949
//! Create a new empty spatialite layer
950950
void newSpatialiteLayer();
951+
//! Create a new empty GeoPackage layer
952+
void newGeoPackageLayer();
951953
//! Print the current map view frame
952954
void newPrintComposer();
953955
void showComposerManager();

src/core/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ SET(QGIS_CORE_SRCS
188188
qgssnappingutils.cpp
189189
qgsspatialindex.cpp
190190
qgssqlexpressioncompiler.cpp
191+
qgssqliteexpressioncompiler.cpp
191192
qgsstatisticalsummary.cpp
192193
qgsstringutils.cpp
193194
qgstextlabelfeature.cpp

src/core/qgsogrutils.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ QgsFields QgsOgrUtils::readOgrFields( OGRFeatureH ogrFet, QTextCodec* encoding )
7272
continue;
7373
}
7474

75-
QString name = encoding->toUnicode( OGR_Fld_GetNameRef( fldDef ) );
75+
QString name = encoding ? encoding->toUnicode( OGR_Fld_GetNameRef( fldDef ) ) : QString::fromUtf8( OGR_Fld_GetNameRef( fldDef ) );
7676
QVariant::Type varType;
7777
switch ( OGR_Fld_GetType( fldDef ) )
7878
{
@@ -137,8 +137,13 @@ QVariant QgsOgrUtils::getOgrFeatureAttribute( OGRFeatureH ogrFet, const QgsField
137137
switch ( fields.at( attIndex ).type() )
138138
{
139139
case QVariant::String:
140-
value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
140+
{
141+
if ( encoding )
142+
value = QVariant( encoding->toUnicode( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
143+
else
144+
value = QVariant( QString::fromUtf8( OGR_F_GetFieldAsString( ogrFet, attIndex ) ) );
141145
break;
146+
}
142147
case QVariant::Int:
143148
value = QVariant( OGR_F_GetFieldAsInteger( ogrFet, attIndex ) );
144149
break;

src/providers/spatialite/qgsspatialiteexpressioncompiler.cpp renamed to src/core/qgssqliteexpressioncompiler.cpp

+19-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/***************************************************************************
2-
qgsspatialiteexpressioncompiler.cpp
2+
qgssqliteexpressioncompiler.cpp
33
-----------------------------------
44
begin : November 2015
55
copyright : (C) 2015 Nyall Dawson
@@ -13,16 +13,17 @@
1313
* *
1414
***************************************************************************/
1515

16-
#include "qgsspatialiteexpressioncompiler.h"
16+
///@cond PRIVATE
17+
18+
#include "qgssqliteexpressioncompiler.h"
1719
#include "qgssqlexpressioncompiler.h"
18-
#include "qgsspatialiteprovider.h"
1920

20-
QgsSpatiaLiteExpressionCompiler::QgsSpatiaLiteExpressionCompiler( QgsSpatiaLiteFeatureSource* source )
21-
: QgsSqlExpressionCompiler( source->mFields, QgsSqlExpressionCompiler::LikeIsCaseInsensitive )
21+
QgsSQLiteExpressionCompiler::QgsSQLiteExpressionCompiler( const QgsFields& fields )
22+
: QgsSqlExpressionCompiler( fields, QgsSqlExpressionCompiler::LikeIsCaseInsensitive )
2223
{
2324
}
2425

25-
QgsSqlExpressionCompiler::Result QgsSpatiaLiteExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
26+
QgsSqlExpressionCompiler::Result QgsSQLiteExpressionCompiler::compileNode( const QgsExpression::Node* node, QString& result )
2627
{
2728
switch ( node->nodeType() )
2829
{
@@ -47,12 +48,14 @@ QgsSqlExpressionCompiler::Result QgsSpatiaLiteExpressionCompiler::compileNode( c
4748
return QgsSqlExpressionCompiler::compileNode( node, result );
4849
}
4950

50-
QString QgsSpatiaLiteExpressionCompiler::quotedIdentifier( const QString& identifier )
51+
QString QgsSQLiteExpressionCompiler::quotedIdentifier( const QString& identifier )
5152
{
52-
return QgsSpatiaLiteProvider::quotedIdentifier( identifier );
53+
QString id( identifier );
54+
id.replace( '\"', "\"\"" );
55+
return id.prepend( '\"' ).append( '\"' );
5356
}
5457

55-
QString QgsSpatiaLiteExpressionCompiler::quotedValue( const QVariant& value, bool& ok )
58+
QString QgsSQLiteExpressionCompiler::quotedValue( const QVariant& value, bool& ok )
5659
{
5760
ok = true;
5861

@@ -73,10 +76,12 @@ QString QgsSpatiaLiteExpressionCompiler::quotedValue( const QVariant& value, boo
7376
default:
7477
case QVariant::String:
7578
QString v = value.toString();
76-
v.replace( '\'', "''" );
77-
if ( v.contains( '\\' ) )
78-
return v.replace( '\\', "\\\\" ).prepend( "E'" ).append( '\'' );
79-
else
80-
return v.prepend( '\'' ).append( '\'' );
79+
// https://www.sqlite.org/lang_expr.html :
80+
// """A string constant is formed by enclosing the string in single quotes (').
81+
// A single quote within the string can be encoded by putting two single quotes
82+
// in a row - as in Pascal. C-style escapes using the backslash character are not supported because they are not standard SQL. """
83+
return v.replace( '\'', "''" ).prepend( '\'' ).append( '\'' );
8184
}
8285
}
86+
87+
///@endcond

src/providers/spatialite/qgsspatialiteexpressioncompiler.h renamed to src/core/qgssqliteexpressioncompiler.h

+23-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/***************************************************************************
2-
qgsspatialiteexpressioncompiler.h
2+
qgssqliteexpressioncompiler.h
33
---------------------------------
44
begin : November 2015
55
copyright : (C) 2015 Nyall Dawson
@@ -13,18 +13,32 @@
1313
* *
1414
***************************************************************************/
1515

16-
#ifndef QGSSPATIALITEEXPRESSIONCOMPILER_H
17-
#define QGSSPATIALITEEXPRESSIONCOMPILER_H
16+
#ifndef QGSSQLITEEXPRESSIONCOMPILER_H
17+
#define QGSSQLITEEXPRESSIONCOMPILER_H
18+
19+
///@cond PRIVATE
1820

1921
#include "qgssqlexpressioncompiler.h"
2022
#include "qgsexpression.h"
21-
#include "qgsspatialitefeatureiterator.h"
2223

23-
class QgsSpatiaLiteExpressionCompiler : public QgsSqlExpressionCompiler
24+
/** \ingroup core
25+
* \class QgsSQLiteExpressionCompiler
26+
* \brief Expression compiler for translation to SQlite SQL WHERE clauses.
27+
*
28+
* This class is designed to be used by spatialite and OGR providers.
29+
* \note Added in version 2.16
30+
* \note Not part of stable API, may change in future versions of QGIS
31+
* \note Not available in Python bindings
32+
*/
33+
34+
class CORE_EXPORT QgsSQLiteExpressionCompiler : public QgsSqlExpressionCompiler
2435
{
2536
public:
2637

27-
explicit QgsSpatiaLiteExpressionCompiler( QgsSpatiaLiteFeatureSource* source );
38+
/** Constructor for expression compiler.
39+
* @param fields fields from provider
40+
*/
41+
explicit QgsSQLiteExpressionCompiler( const QgsFields& fields );
2842

2943
protected:
3044

@@ -34,4 +48,6 @@ class QgsSpatiaLiteExpressionCompiler : public QgsSqlExpressionCompiler
3448

3549
};
3650

37-
#endif // QGSSPATIALITEEXPRESSIONCOMPILER_H
51+
///@endcond
52+
53+
#endif // QGSSQLITEEXPRESSIONCOMPILER_H

src/core/qgsvectorfilewriter.cpp

+42
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,48 @@ QMap<QString, QgsVectorFileWriter::MetaData> QgsVectorFileWriter::initMetaData()
982982
)
983983
);
984984

985+
// GeoPackage
986+
datasetOptions.clear();
987+
layerOptions.clear();
988+
989+
layerOptions.insert( "IDENTIFIER", new StringOption(
990+
QObject::tr( "Human-readable identifier (e.g. short name) for the layer content" ),
991+
"" // Default value
992+
) );
993+
994+
layerOptions.insert( "DESCRIPTION", new StringOption(
995+
QObject::tr( "Human-readable description for the layer content" ),
996+
"" // Default value
997+
) );
998+
999+
layerOptions.insert( "FID", new StringOption(
1000+
QObject::tr( "Name for the feature identifier column" ),
1001+
"fid" // Default value
1002+
) );
1003+
1004+
layerOptions.insert( "GEOMETRY_NAME", new StringOption(
1005+
QObject::tr( "Name for the geometry column" ),
1006+
"geometry" // Default value
1007+
) );
1008+
1009+
#if defined(GDAL_COMPUTE_VERSION) && GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2,0,0)
1010+
layerOptions.insert( "SPATIAL_INDEX", new BoolOption(
1011+
QObject::tr( "If a spatial index must be created." ),
1012+
true // Default value
1013+
) );
1014+
#endif
1015+
1016+
driverMetadata.insert( "GPKG",
1017+
MetaData(
1018+
"GeoPackage",
1019+
QObject::tr( "GeoPackage" ),
1020+
"*.gpkg",
1021+
"gpkg",
1022+
datasetOptions,
1023+
layerOptions
1024+
)
1025+
);
1026+
9851027
// Generic Mapping Tools [GMT]
9861028
datasetOptions.clear();
9871029
layerOptions.clear();

0 commit comments

Comments
 (0)