QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 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 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 }
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.
const QgsVirtualLayerDefinition::SourceLayers & sourceLayers() const
Gets access to the source layers.
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.
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)