QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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
18
20
21#include <QRegularExpression>
22#include <QString>
23#include <QStringList>
24#include <QUrl>
25#include <QUrlQuery>
26
27using namespace Qt::StringLiterals;
28
33
35{
37
38 def.setFilePath( url.toLocalFile() );
39
40 // regexp for column name
41 const QString columnNameRx( u"[a-zA-Z_\\x80-\\xFF][a-zA-Z0-9_\\x80-\\xFF]*"_s );
42
44
45 int layerIdx = 0;
46
47 const QList<QPair<QString, QString> > items = QUrlQuery( url ).queryItems( QUrl::FullyEncoded );
48 for ( int i = 0; i < items.size(); i++ )
49 {
50 const QString key = items.at( i ).first;
51 const QString value = items.at( i ).second;
52 if ( key == "layer_ref"_L1 )
53 {
54 layerIdx++;
55 // layer id, with optional layer_name
56 const int pos = value.indexOf( ':' );
57 QString layerId, vlayerName;
58 if ( pos == -1 )
59 {
60 layerId = QUrl::fromPercentEncoding( value.toUtf8() );
61 vlayerName = u"vtab%1"_s.arg( layerIdx );
62 }
63 else
64 {
65 layerId = QUrl::fromPercentEncoding( value.left( pos ).toUtf8() );
66 vlayerName = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
67 }
68 // add the layer to the list
69 def.addSource( vlayerName, layerId );
70 }
71 else if ( key == "layer"_L1 )
72 {
73 layerIdx++;
74 // syntax: layer=provider:url_encoded_source_URI(:name(:encoding)?)?
75 const int pos = value.indexOf( ':' );
76 if ( pos != -1 )
77 {
78 QString providerKey, source, vlayerName, encoding = u"UTF-8"_s;
79
80 providerKey = value.left( pos );
81 int pos2 = value.indexOf( ':', pos + 1 );
82 if ( pos2 - pos == 2 )
83 pos2 = value.indexOf( ':', pos + 3 );
84 if ( pos2 != -1 )
85 {
86 source = QUrl::fromPercentEncoding( value.mid( pos + 1, pos2 - pos - 1 ).toUtf8() );
87 const int pos3 = value.indexOf( ':', pos2 + 1 );
88 if ( pos3 != -1 )
89 {
90 vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1, pos3 - pos2 - 1 ).toUtf8() );
91 encoding = value.mid( pos3 + 1 );
92 }
93 else
94 {
95 vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1 ).toUtf8() );
96 }
97 }
98 else
99 {
100 source = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
101 vlayerName = u"vtab%1"_s.arg( layerIdx );
102 }
103
104 def.addSource( vlayerName, source, providerKey, encoding );
105 }
106 }
107 else if ( key == "geometry"_L1 )
108 {
109 // geometry field definition, optional
110 // geometry_column(:wkb_type:srid)?
111 const thread_local QRegularExpression reGeom( "(" + columnNameRx + ")(?::([a-zA-Z0-9]+):(\\d+))?" );
112 const QRegularExpressionMatch match = reGeom.match( value );
113 if ( match.hasMatch() )
114 {
115 def.setGeometryField( match.captured( 1 ) );
116 if ( match.capturedTexts().size() > 2 )
117 {
118 // not used by the spatialite provider for now ...
119 Qgis::WkbType wkbType = QgsWkbTypes::parseType( match.captured( 2 ) );
120 if ( wkbType == Qgis::WkbType::Unknown )
121 {
122 wkbType = static_cast<Qgis::WkbType>( match.captured( 2 ).toLong() );
123 }
124 def.setGeometryWkbType( wkbType );
125 def.setGeometrySrid( match.captured( 3 ).toLong() );
126 }
127 }
128 }
129 else if ( key == "nogeometry"_L1 )
130 {
132 }
133 else if ( key == "uid"_L1 )
134 {
135 def.setUid( value );
136 }
137 else if ( key == "query"_L1 )
138 {
139 // url encoded query
140 def.setQuery( QUrl::fromPercentEncoding( value.toUtf8() ) );
141 }
142 else if ( key == "field"_L1 )
143 {
144 // field_name:type (int, real, text)
145 const thread_local QRegularExpression reField( "(" + columnNameRx + "):(int|real|text)" );
146 const QRegularExpressionMatch match = reField.match( value );
147 if ( match.hasMatch() )
148 {
149 const QString fieldName( match.captured( 1 ) );
150 const QString fieldType( match.captured( 2 ) );
151 if ( fieldType == "int"_L1 )
152 {
153 fields.append( QgsField( fieldName, QMetaType::Type::LongLong, fieldType ) );
154 }
155 else if ( fieldType == "real"_L1 )
156 {
157 fields.append( QgsField( fieldName, QMetaType::Type::Double, fieldType ) );
158 }
159 if ( fieldType == "text"_L1 )
160 {
161 fields.append( QgsField( fieldName, QMetaType::Type::QString, fieldType ) );
162 }
163 }
164 }
165 else if ( key == "lazy"_L1 )
166 {
167 def.setLazy( true );
168 }
169 else if ( key == "subsetstring"_L1 )
170 {
171 def.setSubsetString( QUrl::fromPercentEncoding( value.toUtf8() ) );
172 }
173 }
174 def.setFields( fields );
175
176 return def;
177}
178
180{
181 QUrl url;
182 if ( !filePath().isEmpty() )
183 url = QUrl::fromLocalFile( filePath() );
184
185 QUrlQuery urlQuery( url );
186
187 const auto constSourceLayers = sourceLayers();
188 for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
189 {
190 if ( l.isReferenced() )
191 urlQuery.addQueryItem( u"layer_ref"_s, u"%1:%2"_s.arg( l.reference(), l.name() ) );
192 else
193 // if you can find a way to port this away from fromEncodedComponent_helper without breaking existing projects,
194 // please do so... this is GROSS!
195 urlQuery.addQueryItem( fromEncodedComponent_helper( "layer" ),
196 fromEncodedComponent_helper( u"%1:%4:%2:%3"_s // the order is important, since the 4th argument may contain '%2' as well
197 .arg( l.provider(),
198 QString( QUrl::toPercentEncoding( l.name() ) ),
199 l.encoding(),
200 QString( QUrl::toPercentEncoding( l.source() ) ) ).toUtf8() ) );
201 }
202
203 if ( !query().isEmpty() )
204 {
205 urlQuery.addQueryItem( u"query"_s, query() );
206 }
207
208 if ( !uid().isEmpty() )
209 urlQuery.addQueryItem( u"uid"_s, uid() );
210
212 urlQuery.addQueryItem( u"nogeometry"_s, QString() );
213 else if ( !geometryField().isEmpty() )
214 {
215 if ( hasDefinedGeometry() )
216 urlQuery.addQueryItem( u"geometry"_s, u"%1:%2:%3"_s.arg( geometryField() ). arg( qgsEnumValueToKey( geometryWkbType() ) ).arg( geometrySrid() ).toUtf8() );
217 else
218 urlQuery.addQueryItem( u"geometry"_s, geometryField() );
219 }
220
221 const auto constFields = fields();
222 for ( const QgsField &f : constFields )
223 {
224 if ( f.type() == QMetaType::Type::Int
225 || f.type() == QMetaType::Type::UInt
226 || f.type() == QMetaType::Type::Bool
227 || f.type() == QMetaType::Type::LongLong )
228 urlQuery.addQueryItem( u"field"_s, f.name() + ":int" );
229 else if ( f.type() == QMetaType::Type::Double )
230 urlQuery.addQueryItem( u"field"_s, f.name() + ":real" );
231 else if ( f.type() == QMetaType::Type::QString )
232 urlQuery.addQueryItem( u"field"_s, f.name() + ":text" );
233 }
234
235 if ( isLazy() )
236 {
237 urlQuery.addQueryItem( u"lazy"_s, QString() );
238 }
239
240 if ( ! subsetString().isEmpty() )
241 {
242 urlQuery.addQueryItem( u"subsetstring"_s, QUrl::toPercentEncoding( subsetString() ) );
243 }
244
245 url.setQuery( urlQuery );
246
247 return url;
248}
249
251{
252 return QString( toUrl().toEncoded() );
253}
254
255void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &ref )
256{
257 mSourceLayers.append( SourceLayer( name, ref ) );
258}
259
260void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &source, const QString &provider, const QString &encoding )
261{
262 mSourceLayers.append( SourceLayer( name, source, provider, encoding ) );
263}
264
265bool QgsVirtualLayerDefinition::hasSourceLayer( const QString &name ) const
266{
267 const auto constSourceLayers = sourceLayers();
268 for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
269 {
270 if ( l.name() == name )
271 {
272 return true;
273 }
274 }
275 return false;
276}
277
279{
280 const auto constSourceLayers = sourceLayers();
281 for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
282 {
283 if ( l.isReferenced() )
284 {
285 return true;
286 }
287 }
288 return false;
289}
290
292{
293 return mSubsetString;
294}
295
297{
298 mSubsetString = subsetString;
299}
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ NoGeometry
No geometry.
Definition qgis.h:298
@ Unknown
Unknown.
Definition qgis.h:281
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
Container of fields for a vector layer.
Definition qgsfields.h:46
Either a reference to a live layer in the registry or all the parameters needed to load it (provider ...
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
Converts the definition 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:7091