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