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