QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 
17 #include <QUrl>
18 #include <QRegExp>
19 #include <QStringList>
20 
22 #include "qgsvectorlayer.h"
23 #include "qgsvectordataprovider.h"
24 
26  : mFilePath( filePath )
27 {
28 }
29 
31 {
33 
34  def.setFilePath( url.toLocalFile() );
35 
36  // regexp for column name
37  const QString columnNameRx( QStringLiteral( "[a-zA-Z_\x80-\xFF][a-zA-Z0-9_\x80-\xFF]*" ) );
38 
40 
41  int layerIdx = 0;
42  QList<QPair<QByteArray, QByteArray> > items = url.encodedQueryItems();
43  for ( int i = 0; i < items.size(); i++ )
44  {
45  QString key = items.at( i ).first;
46  QString value = items.at( i ).second;
47  if ( key == QLatin1String( "layer_ref" ) )
48  {
49  layerIdx++;
50  // layer id, with optional layer_name
51  int pos = value.indexOf( ':' );
52  QString layerId, vlayerName;
53  if ( pos == -1 )
54  {
55  layerId = value;
56  vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
57  }
58  else
59  {
60  layerId = value.left( pos );
61  vlayerName = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
62  }
63  // add the layer to the list
64  def.addSource( vlayerName, layerId );
65  }
66  else if ( key == QLatin1String( "layer" ) )
67  {
68  layerIdx++;
69  // syntax: layer=provider:url_encoded_source_URI(:name(:encoding)?)?
70  int pos = value.indexOf( ':' );
71  if ( pos != -1 )
72  {
73  QString providerKey, source, vlayerName, encoding = QStringLiteral( "UTF-8" );
74 
75  providerKey = value.left( pos );
76  int pos2 = value.indexOf( ':', pos + 1 );
77  if ( pos2 - pos == 2 )
78  pos2 = value.indexOf( ':', pos + 3 );
79  if ( pos2 != -1 )
80  {
81  source = QUrl::fromPercentEncoding( value.mid( pos + 1, pos2 - pos - 1 ).toUtf8() );
82  int pos3 = value.indexOf( ':', pos2 + 1 );
83  if ( pos3 != -1 )
84  {
85  vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1, pos3 - pos2 - 1 ).toUtf8() );
86  encoding = value.mid( pos3 + 1 );
87  }
88  else
89  {
90  vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1 ).toUtf8() );
91  }
92  }
93  else
94  {
95  source = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
96  vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
97  }
98 
99  def.addSource( vlayerName, source, providerKey, encoding );
100  }
101  }
102  else if ( key == QLatin1String( "geometry" ) )
103  {
104  // geometry field definition, optional
105  // geometry_column(:wkb_type:srid)?
106  QRegExp reGeom( "(" + columnNameRx + ")(?::([a-zA-Z0-9]+):(\\d+))?" );
107  int pos = reGeom.indexIn( value );
108  if ( pos >= 0 )
109  {
110  def.setGeometryField( reGeom.cap( 1 ) );
111  if ( reGeom.captureCount() > 1 )
112  {
113  // not used by the spatialite provider for now ...
114  QgsWkbTypes::Type wkbType = QgsWkbTypes::parseType( reGeom.cap( 2 ) );
115  if ( wkbType == QgsWkbTypes::Unknown )
116  {
117  wkbType = static_cast<QgsWkbTypes::Type>( reGeom.cap( 2 ).toLong() );
118  }
119  def.setGeometryWkbType( wkbType );
120  def.setGeometrySrid( reGeom.cap( 3 ).toLong() );
121  }
122  }
123  }
124  else if ( key == QLatin1String( "nogeometry" ) )
125  {
127  }
128  else if ( key == QLatin1String( "uid" ) )
129  {
130  def.setUid( value );
131  }
132  else if ( key == QLatin1String( "query" ) )
133  {
134  // url encoded query
135  def.setQuery( QUrl::fromPercentEncoding( value.toUtf8() ) );
136  }
137  else if ( key == QLatin1String( "field" ) )
138  {
139  // field_name:type (int, real, text)
140  QRegExp reField( "(" + columnNameRx + "):(int|real|text)" );
141  int pos = reField.indexIn( value );
142  if ( pos >= 0 )
143  {
144  QString fieldName( reField.cap( 1 ) );
145  QString fieldType( reField.cap( 2 ) );
146  if ( fieldType == QLatin1String( "int" ) )
147  {
148  fields.append( QgsField( fieldName, QVariant::Int, fieldType ) );
149  }
150  else if ( fieldType == QLatin1String( "real" ) )
151  {
152  fields.append( QgsField( fieldName, QVariant::Double, fieldType ) );
153  }
154  if ( fieldType == QLatin1String( "text" ) )
155  {
156  fields.append( QgsField( fieldName, QVariant::String, fieldType ) );
157  }
158  }
159  }
160  else if ( key == QLatin1String( "lazy" ) )
161  {
162  def.setLazy( true );
163  }
164  }
165  def.setFields( fields );
166 
167  return def;
168 }
169 
171 {
172  QUrl url;
173  if ( !filePath().isEmpty() )
174  url = QUrl::fromLocalFile( filePath() );
175 
176  Q_FOREACH ( const QgsVirtualLayerDefinition::SourceLayer &l, sourceLayers() )
177  {
178  if ( l.isReferenced() )
179  url.addQueryItem( QStringLiteral( "layer_ref" ), QStringLiteral( "%1:%2" ).arg( l.reference(), l.name() ) );
180  else
181  url.addEncodedQueryItem( "layer", QStringLiteral( "%1:%4:%2:%3" ) // the order is important, since the 4th argument may contain '%2' as well
182  .arg( l.provider(),
183  QString( QUrl::toPercentEncoding( l.name() ) ),
184  l.encoding(),
185  QString( QUrl::toPercentEncoding( l.source() ) ) ).toUtf8() );
186  }
187 
188  if ( !query().isEmpty() )
189  {
190  url.addQueryItem( QStringLiteral( "query" ), query() );
191  }
192 
193  if ( !uid().isEmpty() )
194  url.addQueryItem( QStringLiteral( "uid" ), uid() );
195 
197  url.addQueryItem( QStringLiteral( "nogeometry" ), QString() );
198  else if ( !geometryField().isEmpty() )
199  {
200  if ( hasDefinedGeometry() )
201  url.addQueryItem( QStringLiteral( "geometry" ), QStringLiteral( "%1:%2:%3" ).arg( geometryField() ). arg( geometryWkbType() ).arg( geometrySrid() ).toUtf8() );
202  else
203  url.addQueryItem( QStringLiteral( "geometry" ), geometryField() );
204  }
205 
206  Q_FOREACH ( const QgsField &f, fields() )
207  {
208  if ( f.type() == QVariant::Int )
209  url.addQueryItem( QStringLiteral( "field" ), f.name() + ":int" );
210  else if ( f.type() == QVariant::Double )
211  url.addQueryItem( QStringLiteral( "field" ), f.name() + ":real" );
212  else if ( f.type() == QVariant::String )
213  url.addQueryItem( QStringLiteral( "field" ), f.name() + ":text" );
214  }
215 
216  if ( isLazy() )
217  {
218  url.addQueryItem( QStringLiteral( "lazy" ), QString() );
219  }
220 
221  return url;
222 }
223 
225 {
226  return QString( toUrl().toEncoded() );
227 }
228 
229 void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &ref )
230 {
231  mSourceLayers.append( SourceLayer( name, ref ) );
232 }
233 
234 void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &source, const QString &provider, const QString &encoding )
235 {
236  mSourceLayers.append( SourceLayer( name, source, provider, encoding ) );
237 }
238 
239 bool QgsVirtualLayerDefinition::hasSourceLayer( const QString &name ) const
240 {
241  Q_FOREACH ( const QgsVirtualLayerDefinition::SourceLayer &l, sourceLayers() )
242  {
243  if ( l.name() == name )
244  {
245  return true;
246  }
247  }
248  return false;
249 }
250 
252 {
253  Q_FOREACH ( const QgsVirtualLayerDefinition::SourceLayer &l, sourceLayers() )
254  {
255  if ( l.isReferenced() )
256  {
257  return true;
258  }
259  }
260  return false;
261 }
QString geometryField() const
Gets the name of the geometry field. Empty if no geometry field.
QString name
Definition: qgsfield.h:57
void setFields(const QgsFields &fields)
Sets field definitions.
QString query() const
Gets the SQL query.
Container of fields for a vector layer.
Definition: qgsfields.h:42
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
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...
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QString source() const
The source url used by the provider to build the layer.
void addSource(const QString &name, const QString &ref)
Add a live layer source layer.
void setFilePath(const QString &filePath)
Sets the file path.
bool isLazy() const
Returns the lazy mode.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QString uid() const
Gets the name of the field with unique identifiers.
void setQuery(const QString &query)
Sets the SQL query.
QgsFields fields() const
Gets field definitions.
void setGeometryField(const QString &geometryField)
Sets the name of the geometry field.
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
bool hasDefinedGeometry() const
Convenient method to test if the geometry is defined (not NoGeometry and not Unknown) ...
QString toString() const
Convert into a QString that can be read by the virtual layer provider.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
QString name() const
Name of the layer.
void setGeometryWkbType(QgsWkbTypes::Type t)
Sets the type of the geometry.
QUrl toUrl() const
Convert the definition into a QUrl.
bool hasSourceLayer(const QString &name) const
Convenience method to test if a given source layer is part of the definition.
void setUid(const QString &uid)
Sets the name of the field with unique identifiers.
void setGeometrySrid(long srid)
Sets the SRID of the geometry.
QgsWkbTypes::Type geometryWkbType() const
Gets the type of the geometry QgsWkbTypes::NoGeometry to hide any geometry QgsWkbTypes::Unknown for u...
bool isReferenced() const
Is it a live layer or not ?
long geometrySrid() const
Gets the SRID of the geometry.
void setLazy(bool lazy)
Sets the lazy mode.
QgsVirtualLayerDefinition(const QString &filePath="")
Constructor with an optional file path.
QString reference() const
The reference (id) of the live layer.
const QgsVirtualLayerDefinition::SourceLayers & sourceLayers() const
Gets access to the source layers.
QVariant::Type type
Definition: qgsfield.h:55
QString filePath() const
Gets the file path. May be empty.
QString encoding() const
Optional encoding for the provider.
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.
bool hasReferencedLayers() const
Convenience method to test whether the definition has referenced (live) layers.