QGIS API Documentation  3.2.0-Bonn (bc43194)
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" ), QLatin1String( "" ) );
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" ), QLatin1String( "" ) );
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 name
Definition: qgsfield.h:57
bool isReferenced() const
Is it a live layer or not ?
bool hasReferencedLayers() const
Convenience method to test whether the definition has referenced (live) layers.
void setFields(const QgsFields &fields)
Sets field definitions.
QString name() const
Name of the layer.
QString uid() const
Gets the name of the field with unique identifiers.
QString encoding() const
Optional encoding for the provider.
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.
QString toString() const
Convert into a QString that can be read by the virtual layer provider.
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 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.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:145
QUrl toUrl() const
Convert the definition into a QUrl.
QString filePath() const
Gets the file path. May be empty.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
QString reference() const
The reference (id) of the live layer.
void setQuery(const QString &query)
Sets the SQL query.
void setGeometryField(const QString &geometryField)
Sets the name of the geometry field.
bool isLazy() const
Returns the lazy mode.
QString geometryField() const
Gets the name of the geometry field. Empty if no geometry field.
QgsFields fields() const
Gets field definitions.
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Append a field. The field must have unique name, otherwise it is rejected (returns false) ...
Definition: qgsfields.cpp:59
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
bool hasSourceLayer(const QString &name) const
Convenience method to test if a given source layer is part of the definition.
const QgsVirtualLayerDefinition::SourceLayers & sourceLayers() const
Gets access to the source layers.
void setGeometryWkbType(QgsWkbTypes::Type t)
Sets the type of the geometry.
bool hasDefinedGeometry() const
Convenient method to test if the geometry is defined (not NoGeometry and not Unknown) ...
void setUid(const QString &uid)
Sets the name of the field with unique identifiers.
void setGeometrySrid(long srid)
Sets the SRID of the geometry.
QString query() const
Gets the SQL query.
void setLazy(bool lazy)
Sets the lazy mode.
QgsVirtualLayerDefinition(const QString &filePath="")
Constructor with an optional file path.
QgsWkbTypes::Type geometryWkbType() const
Gets the type of the geometry QgsWkbTypes::NoGeometry to hide any geometry QgsWkbTypes::Unknown for u...
QVariant::Type type
Definition: qgsfield.h:55
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.
long geometrySrid() const
Gets the SRID of the geometry.