QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsvirtuallayerdefinition.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvirtuallayerdefinition.cpp
3begin : December 2015
4copyright : (C) 2015 Hugo Mercier, Oslandia
5email : hugo dot mercier at oslandia dot com
6 ***************************************************************************/
7
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
19
20#include <QUrl>
21#include <QRegularExpression>
22#include <QStringList>
23#include <QUrlQuery>
24
25
27 : mFilePath( filePath )
28{
29}
30
32{
34
35 def.setFilePath( url.toLocalFile() );
36
37 // regexp for column name
38 const QString columnNameRx( QStringLiteral( "[a-zA-Z_\\x80-\\xFF][a-zA-Z0-9_\\x80-\\xFF]*" ) );
39
41
42 int layerIdx = 0;
43
44 const QList<QPair<QString, QString> > items = QUrlQuery( url ).queryItems( QUrl::FullyEncoded );
45 for ( int i = 0; i < items.size(); i++ )
46 {
47 const QString key = items.at( i ).first;
48 const QString value = items.at( i ).second;
49 if ( key == QLatin1String( "layer_ref" ) )
50 {
51 layerIdx++;
52 // layer id, with optional layer_name
53 const int pos = value.indexOf( ':' );
54 QString layerId, vlayerName;
55 if ( pos == -1 )
56 {
57 layerId = QUrl::fromPercentEncoding( value.toUtf8() );
58 vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
59 }
60 else
61 {
62 layerId = QUrl::fromPercentEncoding( value.left( pos ).toUtf8() );
63 vlayerName = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
64 }
65 // add the layer to the list
66 def.addSource( vlayerName, layerId );
67 }
68 else if ( key == QLatin1String( "layer" ) )
69 {
70 layerIdx++;
71 // syntax: layer=provider:url_encoded_source_URI(:name(:encoding)?)?
72 const int pos = value.indexOf( ':' );
73 if ( pos != -1 )
74 {
75 QString providerKey, source, vlayerName, encoding = QStringLiteral( "UTF-8" );
76
77 providerKey = value.left( pos );
78 int pos2 = value.indexOf( ':', pos + 1 );
79 if ( pos2 - pos == 2 )
80 pos2 = value.indexOf( ':', pos + 3 );
81 if ( pos2 != -1 )
82 {
83 source = QUrl::fromPercentEncoding( value.mid( pos + 1, pos2 - pos - 1 ).toUtf8() );
84 const int pos3 = value.indexOf( ':', pos2 + 1 );
85 if ( pos3 != -1 )
86 {
87 vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1, pos3 - pos2 - 1 ).toUtf8() );
88 encoding = value.mid( pos3 + 1 );
89 }
90 else
91 {
92 vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1 ).toUtf8() );
93 }
94 }
95 else
96 {
97 source = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
98 vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
99 }
100
101 def.addSource( vlayerName, source, providerKey, encoding );
102 }
103 }
104 else if ( key == QLatin1String( "geometry" ) )
105 {
106 // geometry field definition, optional
107 // geometry_column(:wkb_type:srid)?
108 const thread_local QRegularExpression reGeom( "(" + columnNameRx + ")(?::([a-zA-Z0-9]+):(\\d+))?" );
109 const QRegularExpressionMatch match = reGeom.match( value );
110 if ( match.hasMatch() )
111 {
112 def.setGeometryField( match.captured( 1 ) );
113 if ( match.capturedTexts().size() > 2 )
114 {
115 // not used by the spatialite provider for now ...
116 Qgis::WkbType wkbType = QgsWkbTypes::parseType( match.captured( 2 ) );
117 if ( wkbType == Qgis::WkbType::Unknown )
118 {
119 wkbType = static_cast<Qgis::WkbType>( match.captured( 2 ).toLong() );
120 }
121 def.setGeometryWkbType( wkbType );
122 def.setGeometrySrid( match.captured( 3 ).toLong() );
123 }
124 }
125 }
126 else if ( key == QLatin1String( "nogeometry" ) )
127 {
129 }
130 else if ( key == QLatin1String( "uid" ) )
131 {
132 def.setUid( value );
133 }
134 else if ( key == QLatin1String( "query" ) )
135 {
136 // url encoded query
137 def.setQuery( QUrl::fromPercentEncoding( value.toUtf8() ) );
138 }
139 else if ( key == QLatin1String( "field" ) )
140 {
141 // field_name:type (int, real, text)
142 const thread_local QRegularExpression reField( "(" + columnNameRx + "):(int|real|text)" );
143 const QRegularExpressionMatch match = reField.match( value );
144 if ( match.hasMatch() )
145 {
146 const QString fieldName( match.captured( 1 ) );
147 const QString fieldType( match.captured( 2 ) );
148 if ( fieldType == QLatin1String( "int" ) )
149 {
150 fields.append( QgsField( fieldName, QVariant::LongLong, fieldType ) );
151 }
152 else if ( fieldType == QLatin1String( "real" ) )
153 {
154 fields.append( QgsField( fieldName, QVariant::Double, fieldType ) );
155 }
156 if ( fieldType == QLatin1String( "text" ) )
157 {
158 fields.append( QgsField( fieldName, QVariant::String, fieldType ) );
159 }
160 }
161 }
162 else if ( key == QLatin1String( "lazy" ) )
163 {
164 def.setLazy( true );
165 }
166 else if ( key == QLatin1String( "subsetstring" ) )
167 {
168 def.setSubsetString( QUrl::fromPercentEncoding( value.toUtf8() ) );
169 }
170 }
171 def.setFields( fields );
172
173 return def;
174}
175
177{
178 QUrl url;
179 if ( !filePath().isEmpty() )
180 url = QUrl::fromLocalFile( filePath() );
181
182 QUrlQuery urlQuery( url );
183
184 const auto constSourceLayers = sourceLayers();
185 for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
186 {
187 if ( l.isReferenced() )
188 urlQuery.addQueryItem( QStringLiteral( "layer_ref" ), QStringLiteral( "%1:%2" ).arg( l.reference(), l.name() ) );
189 else
190 // if you can find a way to port this away from fromEncodedComponent_helper without breaking existing projects,
191 // please do so... this is GROSS!
192 urlQuery.addQueryItem( fromEncodedComponent_helper( "layer" ),
193 fromEncodedComponent_helper( QStringLiteral( "%1:%4:%2:%3" ) // the order is important, since the 4th argument may contain '%2' as well
194 .arg( l.provider(),
195 QString( QUrl::toPercentEncoding( l.name() ) ),
196 l.encoding(),
197 QString( QUrl::toPercentEncoding( l.source() ) ) ).toUtf8() ) );
198 }
199
200 if ( !query().isEmpty() )
201 {
202 urlQuery.addQueryItem( QStringLiteral( "query" ), query() );
203 }
204
205 if ( !uid().isEmpty() )
206 urlQuery.addQueryItem( QStringLiteral( "uid" ), uid() );
207
209 urlQuery.addQueryItem( QStringLiteral( "nogeometry" ), QString() );
210 else if ( !geometryField().isEmpty() )
211 {
212 if ( hasDefinedGeometry() )
213 urlQuery.addQueryItem( QStringLiteral( "geometry" ), QStringLiteral( "%1:%2:%3" ).arg( geometryField() ). arg( qgsEnumValueToKey( geometryWkbType() ) ).arg( geometrySrid() ).toUtf8() );
214 else
215 urlQuery.addQueryItem( QStringLiteral( "geometry" ), geometryField() );
216 }
217
218 const auto constFields = fields();
219 for ( const QgsField &f : constFields )
220 {
221 if ( f.type() == QVariant::Int
222 || f.type() == QVariant::UInt
223 || f.type() == QVariant::Bool
224 || f.type() == QVariant::LongLong )
225 urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":int" );
226 else if ( f.type() == QVariant::Double )
227 urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":real" );
228 else if ( f.type() == QVariant::String )
229 urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":text" );
230 }
231
232 if ( isLazy() )
233 {
234 urlQuery.addQueryItem( QStringLiteral( "lazy" ), QString() );
235 }
236
237 if ( ! subsetString().isEmpty() )
238 {
239 urlQuery.addQueryItem( QStringLiteral( "subsetstring" ), QUrl::toPercentEncoding( subsetString() ) );
240 }
241
242 url.setQuery( urlQuery );
243
244 return url;
245}
246
248{
249 return QString( toUrl().toEncoded() );
250}
251
252void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &ref )
253{
254 mSourceLayers.append( SourceLayer( name, ref ) );
255}
256
257void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &source, const QString &provider, const QString &encoding )
258{
259 mSourceLayers.append( SourceLayer( name, source, provider, encoding ) );
260}
261
262bool QgsVirtualLayerDefinition::hasSourceLayer( const QString &name ) const
263{
264 const auto constSourceLayers = sourceLayers();
265 for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
266 {
267 if ( l.name() == name )
268 {
269 return true;
270 }
271 }
272 return false;
273}
274
276{
277 const auto constSourceLayers = sourceLayers();
278 for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
279 {
280 if ( l.isReferenced() )
281 {
282 return true;
283 }
284 }
285 return false;
286}
287
289{
290 return mSubsetString;
291}
292
293void QgsVirtualLayerDefinition::setSubsetString( const QString &subsetString )
294{
295 mSubsetString = subsetString;
296}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition: qgis.h:182
@ NoGeometry
No geometry.
@ Unknown
Unknown.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:53
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
A SourceLayer is either a reference to a live layer in the registry or all the parameters needed to l...
Class to manipulate the definition of a virtual layer.
QString geometryField() const
Gets the name of the geometry field. Empty if no geometry field.
QString query() const
Gets the SQL query.
Qgis::WkbType geometryWkbType() const
Gets the type of the geometry QgsWkbTypes::NoGeometry to hide any geometry QgsWkbTypes::Unknown for u...
QgsFields fields() const
Gets field definitions.
long geometrySrid() const
Gets the SRID of the geometry.
bool hasSourceLayer(const QString &name) const
Convenience method to test if a given source layer is part of the definition.
QgsVirtualLayerDefinition(const QString &filePath="")
Constructor with an optional file path.
void setUid(const QString &uid)
Sets the name of the field with unique identifiers.
bool hasDefinedGeometry() const
Convenient method to test if the geometry is defined (not NoGeometry and not Unknown)
void setSubsetString(const QString &subsetString)
Sets the subsetString.
void setLazy(bool lazy)
Sets the lazy mode.
bool hasReferencedLayers() const
Convenience method to test whether the definition has referenced (live) layers.
void setFilePath(const QString &filePath)
Sets the file path.
QString subsetString() const
Returns the subset string.
QUrl toUrl() const
Convert the definition into a QUrl.
void setGeometrySrid(long srid)
Sets the SRID of the geometry.
void addSource(const QString &name, const QString &ref)
Add a live layer source layer.
void setGeometryField(const QString &geometryField)
Sets the name of the geometry field.
const QgsVirtualLayerDefinition::SourceLayers & sourceLayers() const
Gets access to the source layers.
QString uid() const
Gets the name of the field with unique identifiers.
QString filePath() const
Gets the file path. May be empty.
static QgsVirtualLayerDefinition fromUrl(const QUrl &url)
Constructor to build a definition from a QUrl The path part of the URL is extracted as well as the fo...
QString toString() const
Convert into a QString that can be read by the virtual layer provider.
void setFields(const QgsFields &fields)
Sets field definitions.
void setGeometryWkbType(Qgis::WkbType t)
Sets the type of the geometry.
void setQuery(const QString &query)
Sets the SQL query.
bool isLazy() const
Returns the lazy mode.
static Qgis::WkbType parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
QString fromEncodedComponent_helper(const QByteArray &ba)
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:5398