Skip to content

Commit 119cd8a

Browse files
rouaultnyalldawson
authored andcommittedOct 7, 2018
QgisApp::addVectorLayer(): add |layername= to OGR datasets (fixes #20031)
Do it also in case of datasets that have a single layer, in case they might later be edited to have more layers (except for a few drivers known to be always single layer)
1 parent bd6c111 commit 119cd8a

File tree

3 files changed

+213
-44
lines changed

3 files changed

+213
-44
lines changed
 

‎src/app/qgisapp.cpp

Lines changed: 71 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4441,6 +4441,68 @@ QString QgisApp::crsAndFormatAdjustedLayerUri( const QString &uri, const QString
44414441
return newuri;
44424442
}
44434443

4444+
static QStringList splitSubLayerDef( const QString &subLayerDef )
4445+
{
4446+
QStringList elements = subLayerDef.split( QgsDataProvider::SUBLAYER_SEPARATOR );
4447+
// merge back parts of the name that may have been split
4448+
while ( elements.size() > 5 )
4449+
{
4450+
elements[1] += ":" + elements[2];
4451+
elements.removeAt( 2 );
4452+
}
4453+
return elements;
4454+
}
4455+
4456+
static void setupVectorLayer( const QString &vectorLayerPath,
4457+
const QStringList &sublayers,
4458+
QgsVectorLayer *&layer,
4459+
const QString &providerKey,
4460+
const QgsVectorLayer::LayerOptions &options )
4461+
{
4462+
//set friendly name for datasources with only one layer
4463+
QgsSettings settings;
4464+
QStringList elements = splitSubLayerDef( sublayers.at( 0 ) );
4465+
QString rawLayerName = elements.size() >= 2 ? elements.at( 1 ) : QString();
4466+
QString subLayerNameFormatted = rawLayerName;
4467+
if ( settings.value( QStringLiteral( "qgis/formatLayerName" ), false ).toBool() )
4468+
{
4469+
subLayerNameFormatted = QgsMapLayer::formatLayerName( subLayerNameFormatted );
4470+
}
4471+
4472+
if ( elements.size() >= 4 && layer->name().compare( rawLayerName, Qt::CaseInsensitive ) != 0
4473+
&& layer->name().compare( subLayerNameFormatted, Qt::CaseInsensitive ) != 0 )
4474+
{
4475+
layer->setName( QStringLiteral( "%1 %2" ).arg( layer->name(), rawLayerName ) );
4476+
}
4477+
4478+
// Systematically add a layername= option to OGR datasets in case
4479+
// the current single layer dataset becomes layer a multi-layer one.
4480+
// Except for a few select extensions, known to be always single layer dataset.
4481+
QFileInfo fi( vectorLayerPath );
4482+
QString ext = fi.suffix().toLower();
4483+
if ( providerKey == QLatin1String( "ogr" ) &&
4484+
ext != QLatin1String( "shp" ) &&
4485+
ext != QLatin1String( "mif" ) &&
4486+
ext != QLatin1String( "tab" ) &&
4487+
ext != QLatin1String( "csv" ) &&
4488+
ext != QLatin1String( "geojson" ) &&
4489+
! vectorLayerPath.contains( QStringLiteral( "layerid=" ) ) &&
4490+
! vectorLayerPath.contains( QStringLiteral( "layername=" ) ) )
4491+
{
4492+
auto uriParts = QgsProviderRegistry::instance()->decodeUri(
4493+
layer->providerType(), layer->dataProvider()->dataSourceUri() );
4494+
QString composedURI( uriParts.value( QLatin1String( "path" ) ).toString() );
4495+
composedURI += "|layername=" + rawLayerName;
4496+
4497+
auto newLayer = qgis::make_unique<QgsVectorLayer>( composedURI, layer->name(), QStringLiteral( "ogr" ), options );
4498+
if ( newLayer && newLayer->isValid() )
4499+
{
4500+
delete layer;
4501+
layer = newLayer.release();
4502+
}
4503+
}
4504+
}
4505+
44444506
bool QgisApp::addVectorLayers( const QStringList &layerQStringList, const QString &enc, const QString &dataSourceType )
44454507
{
44464508
bool wasfrozen = mMapCanvas->isFrozen();
@@ -4523,19 +4585,8 @@ bool QgisApp::addVectorLayers( const QStringList &layerQStringList, const QStrin
45234585
}
45244586
else if ( !sublayers.isEmpty() ) // there is 1 layer of data available
45254587
{
4526-
//set friendly name for datasources with only one layer
4527-
QStringList elements = sublayers.at( 0 ).split( QgsDataProvider::SUBLAYER_SEPARATOR );
4528-
QString subLayerNameFormatted = elements.size() >= 2 ? elements.at( 1 ) : QString();
4529-
if ( settings.value( QStringLiteral( "qgis/formatLayerName" ), false ).toBool() )
4530-
{
4531-
subLayerNameFormatted = QgsMapLayer::formatLayerName( subLayerNameFormatted );
4532-
}
4533-
4534-
if ( elements.size() >= 4 && layer->name().compare( elements.at( 1 ), Qt::CaseInsensitive ) != 0
4535-
&& layer->name().compare( subLayerNameFormatted, Qt::CaseInsensitive ) != 0 )
4536-
{
4537-
layer->setName( QStringLiteral( "%1 %2" ).arg( layer->name(), elements.at( 1 ) ) );
4538-
}
4588+
setupVectorLayer( src, sublayers, layer,
4589+
QStringLiteral( "ogr" ), options );
45394590

45404591
myList << layer;
45414592
}
@@ -4938,14 +4989,7 @@ void QgisApp::askUserForOGRSublayers( QgsVectorLayer *layer )
49384989
// OGR provider returns items in this format:
49394990
// <layer_index>:<name>:<feature_count>:<geom_type>
49404991

4941-
QStringList elements = sublayer.split( QgsDataProvider::SUBLAYER_SEPARATOR );
4942-
// merge back parts of the name that may have been split
4943-
while ( elements.size() > 5 )
4944-
{
4945-
elements[1] += ":" + elements[2];
4946-
elements.removeAt( 2 );
4947-
}
4948-
4992+
QStringList elements = splitSubLayerDef( sublayer );
49494993
if ( elements.count() >= 4 )
49504994
{
49514995
QgsSublayersDialog::LayerDefinition def;
@@ -4978,18 +5022,11 @@ void QgisApp::askUserForOGRSublayers( QgsVectorLayer *layer )
49785022
if ( !chooseSublayersDialog.exec() )
49795023
return;
49805024

4981-
QString uri = layer->source();
49825025
QString name = layer->name();
4983-
//the separator char & was changed to | to be compatible
4984-
//with url for protocol drivers
4985-
if ( uri.contains( '|', Qt::CaseSensitive ) )
4986-
{
4987-
// If we get here, there are some options added to the filename.
4988-
// A valid uri is of the form: filename&option1=value1&option2=value2,...
4989-
// We want only the filename here, so we get the first part of the split.
4990-
QStringList theURIParts = uri.split( '|' );
4991-
uri = theURIParts.at( 0 );
4992-
}
5026+
5027+
auto uriParts = QgsProviderRegistry::instance()->decodeUri(
5028+
layer->providerType(), layer->dataProvider()->dataSourceUri() );
5029+
QString uri( uriParts.value( QLatin1String( "path" ) ).toString() );
49935030

49945031
// The uri must contain the actual uri of the vectorLayer from which we are
49955032
// going to load the sublayers.
@@ -10679,18 +10716,8 @@ QgsVectorLayer *QgisApp::addVectorLayer( const QString &vectorLayerPath, const Q
1067910716
QStringList sublayers = layer->dataProvider()->subLayers();
1068010717
if ( !sublayers.isEmpty() )
1068110718
{
10682-
QStringList elements = sublayers.at( 0 ).split( QgsDataProvider::SUBLAYER_SEPARATOR );
10683-
QString subLayerNameFormatted = elements.size() >= 2 ? elements.at( 1 ) : QString();
10684-
if ( settings.value( QStringLiteral( "qgis/formatLayerName" ), false ).toBool() )
10685-
{
10686-
subLayerNameFormatted = QgsMapLayer::formatLayerName( subLayerNameFormatted );
10687-
}
10688-
10689-
if ( elements.size() >= 4 && layer->name().compare( elements.at( 1 ), Qt::CaseInsensitive ) != 0
10690-
&& layer->name().compare( subLayerNameFormatted, Qt::CaseInsensitive ) != 0 )
10691-
{
10692-
layer->setName( QStringLiteral( "%1 %2" ).arg( layer->name(), elements.at( 1 ) ) );
10693-
}
10719+
setupVectorLayer( vectorLayerPath, sublayers, layer,
10720+
providerKey, options );
1069410721
}
1069510722

1069610723
myList << layer;

‎tests/src/app/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ ENDMACRO (ADD_QGIS_TEST)
9292
IF (WITH_BINDINGS)
9393
ADD_QGIS_TEST(apppythontest testqgisapppython.cpp)
9494
ENDIF ()
95+
ADD_QGIS_TEST(qgisapp testqgisapp.cpp)
9596
ADD_QGIS_TEST(qgisappclipboard testqgisappclipboard.cpp)
9697
ADD_QGIS_TEST(attributetabletest testqgsattributetable.cpp)
9798
ADD_QGIS_TEST(applocatorfilters testqgsapplocatorfilters.cpp)

‎tests/src/app/testqgisapp.cpp

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/***************************************************************************
2+
testqgisapp.cpp
3+
--------------------------------------
4+
Date : 6 october 2018
5+
Copyright : (C) 2018 by Even Rouault
6+
Email : even.rouault at spatialys.com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
#include "qgstest.h"
16+
17+
#include <QApplication>
18+
#include "gdal.h"
19+
20+
#include "qgisapp.h"
21+
#include "qgsproject.h"
22+
#include "qgsmaplayerstore.h"
23+
24+
/**
25+
* \ingroup UnitTests
26+
* This is a unit test for the QgisApp
27+
*/
28+
class TestQgisApp : public QObject
29+
{
30+
Q_OBJECT
31+
32+
public:
33+
TestQgisApp();
34+
35+
private slots:
36+
void initTestCase();// will be called before the first testfunction is executed.
37+
void cleanupTestCase();// will be called after the last testfunction was executed.
38+
void init(); // will be called before each testfunction is executed.
39+
void cleanup(); // will be called after every testfunction.
40+
41+
void addVectorLayerShp();
42+
void addVectorLayerGeopackageSingleLayer();
43+
void addVectorLayerGeopackageSingleLayerAlreadyLayername();
44+
void addVectorLayerInvalid();
45+
46+
private:
47+
QgisApp *mQgisApp = nullptr;
48+
QString mTestDataDir;
49+
};
50+
51+
TestQgisApp::TestQgisApp() = default;
52+
53+
//runs before all tests
54+
void TestQgisApp::initTestCase()
55+
{
56+
// Set up the QgsSettings environment
57+
QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
58+
QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
59+
QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
60+
61+
qDebug() << "TestQgisApp::initTestCase()";
62+
// init QGIS's paths - true means that all path will be inited from prefix
63+
QgsApplication::init();
64+
QgsApplication::initQgis();
65+
mTestDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
66+
mQgisApp = new QgisApp();
67+
}
68+
69+
//runs after all tests
70+
void TestQgisApp::cleanupTestCase()
71+
{
72+
QgsApplication::exitQgis();
73+
}
74+
75+
void TestQgisApp::init()
76+
{
77+
GDALDriverH hDriver = GDALGetDriverByName( "GPKG" );
78+
QVERIFY( hDriver );
79+
GDALDatasetH hDS = GDALCreate( hDriver, "/vsimem/test.gpkg", 0, 0, 0, GDT_Unknown, nullptr );
80+
QVERIFY( hDS );
81+
GDALDatasetCreateLayer( hDS, "my_layer", nullptr, wkbPoint, nullptr );
82+
GDALClose( hDS );
83+
}
84+
85+
void TestQgisApp::cleanup()
86+
{
87+
GDALDriverH hDriver = GDALGetDriverByName( "GPKG" );
88+
QVERIFY( hDriver );
89+
GDALDeleteDataset( hDriver, "/vsimem/test.gpkg" );
90+
}
91+
92+
void TestQgisApp::addVectorLayerShp()
93+
{
94+
QString filePath = mTestDataDir + QStringLiteral( "points.shp" );
95+
QgsVectorLayer *layer = mQgisApp->addVectorLayer( filePath, "test", QStringLiteral( "ogr" ) );
96+
QVERIFY( layer->isValid() );
97+
98+
// No need for |layerName= for shapefiles
99+
QVERIFY( layer->source().endsWith( QLatin1String( "points.shp" ) ) );
100+
101+
// cleanup
102+
QgsProject::instance()->layerStore()->removeMapLayers( QStringList() << layer->id() );
103+
}
104+
105+
void TestQgisApp::addVectorLayerGeopackageSingleLayer()
106+
{
107+
QString filePath = QLatin1String( "/vsimem/test.gpkg" );
108+
QgsVectorLayer *layer = mQgisApp->addVectorLayer( filePath, "test", QStringLiteral( "ogr" ) );
109+
QVERIFY( layer->isValid() );
110+
111+
// Need for |layerName= for geopackage
112+
QVERIFY( layer->source().endsWith( QLatin1String( "/vsimem/test.gpkg|layername=my_layer" ) ) );
113+
114+
// cleanup
115+
QgsProject::instance()->layerStore()->removeMapLayers( QStringList() << layer->id() );
116+
}
117+
118+
void TestQgisApp::addVectorLayerGeopackageSingleLayerAlreadyLayername()
119+
{
120+
QString filePath = QLatin1String( "/vsimem/test.gpkg|layername=my_layer" );
121+
QgsVectorLayer *layer = mQgisApp->addVectorLayer( filePath, "test", QStringLiteral( "ogr" ) );
122+
QVERIFY( layer->isValid() );
123+
124+
// Need for |layerName= for geopackage
125+
QVERIFY( layer->source().endsWith( QLatin1String( "/vsimem/test.gpkg|layername=my_layer" ) ) );
126+
127+
// cleanup
128+
QgsProject::instance()->layerStore()->removeMapLayers( QStringList() << layer->id() );
129+
}
130+
131+
void TestQgisApp::addVectorLayerInvalid()
132+
{
133+
QgsVectorLayer *layer = mQgisApp->addVectorLayer( "i_do_not_exist", "test", QStringLiteral( "ogr" ) );
134+
QVERIFY( !layer );
135+
136+
layer = mQgisApp->addVectorLayer( "/vsimem/test.gpkg|layername=invalid_layer_name", "test", QStringLiteral( "ogr" ) );
137+
QVERIFY( !layer );
138+
}
139+
140+
QGSTEST_MAIN( TestQgisApp )
141+
#include "testqgisapp.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.