QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsvirtuallayerdefinition.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvirtuallayerdefinition.cpp
3 begin : December 2015
4 copyright : (C) 2015 Hugo Mercier, Oslandia
5 email : 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"
19 #include "qgsvectordataprovider.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 
254 void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &ref )
255 {
256  mSourceLayers.append( SourceLayer( name, ref ) );
257 }
258 
259 void 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 
264 bool 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 
295 void QgsVirtualLayerDefinition::setSubsetString( const QString &subsetString )
296 {
297  mSubsetString = subsetString;
298 }
QgsVirtualLayerDefinition::setGeometryWkbType
void setGeometryWkbType(QgsWkbTypes::Type t)
Sets the type of the geometry.
Definition: qgsvirtuallayerdefinition.h:177
qgsvirtuallayerdefinition.h
QgsVirtualLayerDefinition::setGeometrySrid
void setGeometrySrid(long srid)
Sets the SRID of the geometry.
Definition: qgsvirtuallayerdefinition.h:182
QgsVirtualLayerDefinition::setQuery
void setQuery(const QString &query)
Sets the SQL query.
Definition: qgsvirtuallayerdefinition.h:135
QgsVirtualLayerDefinition::geometryField
QString geometryField() const
Gets the name of the geometry field. Empty if no geometry field.
Definition: qgsvirtuallayerdefinition.h:166
QgsVirtualLayerDefinition::setLazy
void setLazy(bool lazy)
Sets the lazy mode.
Definition: qgsvirtuallayerdefinition.h:155
QgsVirtualLayerDefinition::setUid
void setUid(const QString &uid)
Sets the name of the field with unique identifiers.
Definition: qgsvirtuallayerdefinition.h:145
QgsVirtualLayerDefinition::sourceLayers
const QgsVirtualLayerDefinition::SourceLayers & sourceLayers() const
Gets access to the source layers.
Definition: qgsvirtuallayerdefinition.h:130
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:44
QgsVirtualLayerDefinition::setSubsetString
void setSubsetString(const QString &subsetString)
Sets the subsetString.
Definition: qgsvirtuallayerdefinition.cpp:295
QgsVirtualLayerDefinition::SourceLayer
A SourceLayer is either a reference to a live layer in the registry or all the parameters needed to l...
Definition: qgsvirtuallayerdefinition.h:52
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:69
fromEncodedComponent_helper
QString fromEncodedComponent_helper(const QByteArray &ba)
Definition: fromencodedcomponenthelper.cpp:57
QgsVirtualLayerDefinition::geometrySrid
long geometrySrid() const
Gets the SRID of the geometry.
Definition: qgsvirtuallayerdefinition.h:180
QgsFields::append
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
QgsWkbTypes::parseType
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
QgsVirtualLayerDefinition::setFields
void setFields(const QgsFields &fields)
Sets field definitions.
Definition: qgsvirtuallayerdefinition.h:187
QgsVirtualLayerDefinition::addSource
void addSource(const QString &name, const QString &ref)
Add a live layer source layer.
Definition: qgsvirtuallayerdefinition.cpp:254
QgsWkbTypes::Unknown
@ Unknown
Definition: qgswkbtypes.h:71
fromencodedcomponenthelper.h
QgsVirtualLayerDefinition::QgsVirtualLayerDefinition
QgsVirtualLayerDefinition(const QString &filePath="")
Constructor with an optional file path.
Definition: qgsvirtuallayerdefinition.cpp:28
qgsvectordataprovider.h
QgsVirtualLayerDefinition::setGeometryField
void setGeometryField(const QString &geometryField)
Sets the name of the geometry field.
Definition: qgsvirtuallayerdefinition.h:168
QgsVirtualLayerDefinition::fromUrl
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...
Definition: qgsvirtuallayerdefinition.cpp:33
QgsVirtualLayerDefinition::uid
QString uid() const
Gets the name of the field with unique identifiers.
Definition: qgsvirtuallayerdefinition.h:143
QgsVirtualLayerDefinition::hasReferencedLayers
bool hasReferencedLayers() const
Convenience method to test whether the definition has referenced (live) layers.
Definition: qgsvirtuallayerdefinition.cpp:277
qgsvectorlayer.h
QgsVirtualLayerDefinition::isLazy
bool isLazy() const
Returns the lazy mode.
Definition: qgsvirtuallayerdefinition.h:163
QgsVirtualLayerDefinition::subsetString
QString subsetString() const
Returns the subset string.
Definition: qgsvirtuallayerdefinition.cpp:290
QgsVirtualLayerDefinition::query
QString query() const
Gets the SQL query.
Definition: qgsvirtuallayerdefinition.h:133
QgsVirtualLayerDefinition::setFilePath
void setFilePath(const QString &filePath)
Sets the file path.
Definition: qgsvirtuallayerdefinition.h:140
QgsWkbTypes::NoGeometry
@ NoGeometry
Definition: qgswkbtypes.h:85
QgsVirtualLayerDefinition
Class to manipulate the definition of a virtual layer.
Definition: qgsvirtuallayerdefinition.h:31
QgsVirtualLayerDefinition::hasDefinedGeometry
bool hasDefinedGeometry() const
Convenient method to test if the geometry is defined (not NoGeometry and not Unknown)
Definition: qgsvirtuallayerdefinition.h:196
QgsVirtualLayerDefinition::toString
QString toString() const
Convert into a QString that can be read by the virtual layer provider.
Definition: qgsvirtuallayerdefinition.cpp:249
QgsVirtualLayerDefinition::geometryWkbType
QgsWkbTypes::Type geometryWkbType() const
Gets the type of the geometry QgsWkbTypes::NoGeometry to hide any geometry QgsWkbTypes::Unknown for u...
Definition: qgsvirtuallayerdefinition.h:175
QgsFields::at
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
QgsVirtualLayerDefinition::fields
QgsFields fields() const
Gets field definitions.
Definition: qgsvirtuallayerdefinition.h:185
QgsVirtualLayerDefinition::hasSourceLayer
bool hasSourceLayer(const QString &name) const
Convenience method to test if a given source layer is part of the definition.
Definition: qgsvirtuallayerdefinition.cpp:264
QgsVirtualLayerDefinition::toUrl
QUrl toUrl() const
Convert the definition into a QUrl.
Definition: qgsvirtuallayerdefinition.cpp:178
QgsVirtualLayerDefinition::filePath
QString filePath() const
Gets the file path. May be empty.
Definition: qgsvirtuallayerdefinition.h:138
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50