QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 
43  const QList<QPair<QString, QString> > items = QUrlQuery( url ).queryItems( QUrl::FullyEncoded );
44  for ( int i = 0; i < items.size(); i++ )
45  {
46  QString key = items.at( i ).first;
47  QString value = items.at( i ).second;
48  if ( key == QLatin1String( "layer_ref" ) )
49  {
50  layerIdx++;
51  // layer id, with optional layer_name
52  int pos = value.indexOf( ':' );
53  QString layerId, vlayerName;
54  if ( pos == -1 )
55  {
56  layerId = value;
57  vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
58  }
59  else
60  {
61  layerId = value.left( pos );
62  vlayerName = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
63  }
64  // add the layer to the list
65  def.addSource( vlayerName, layerId );
66  }
67  else if ( key == QLatin1String( "layer" ) )
68  {
69  layerIdx++;
70  // syntax: layer=provider:url_encoded_source_URI(:name(:encoding)?)?
71  int pos = value.indexOf( ':' );
72  if ( pos != -1 )
73  {
74  QString providerKey, source, vlayerName, encoding = QStringLiteral( "UTF-8" );
75 
76  providerKey = value.left( pos );
77  int pos2 = value.indexOf( ':', pos + 1 );
78  if ( pos2 - pos == 2 )
79  pos2 = value.indexOf( ':', pos + 3 );
80  if ( pos2 != -1 )
81  {
82  source = QUrl::fromPercentEncoding( value.mid( pos + 1, pos2 - pos - 1 ).toUtf8() );
83  int pos3 = value.indexOf( ':', pos2 + 1 );
84  if ( pos3 != -1 )
85  {
86  vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1, pos3 - pos2 - 1 ).toUtf8() );
87  encoding = value.mid( pos3 + 1 );
88  }
89  else
90  {
91  vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1 ).toUtf8() );
92  }
93  }
94  else
95  {
96  source = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() );
97  vlayerName = QStringLiteral( "vtab%1" ).arg( layerIdx );
98  }
99 
100  def.addSource( vlayerName, source, providerKey, encoding );
101  }
102  }
103  else if ( key == QLatin1String( "geometry" ) )
104  {
105  // geometry field definition, optional
106  // geometry_column(:wkb_type:srid)?
107  QRegExp reGeom( "(" + columnNameRx + ")(?::([a-zA-Z0-9]+):(\\d+))?" );
108  int pos = reGeom.indexIn( value );
109  if ( pos >= 0 )
110  {
111  def.setGeometryField( reGeom.cap( 1 ) );
112  if ( reGeom.captureCount() > 1 )
113  {
114  // not used by the spatialite provider for now ...
115  QgsWkbTypes::Type wkbType = QgsWkbTypes::parseType( reGeom.cap( 2 ) );
116  if ( wkbType == QgsWkbTypes::Unknown )
117  {
118  wkbType = static_cast<QgsWkbTypes::Type>( reGeom.cap( 2 ).toLong() );
119  }
120  def.setGeometryWkbType( wkbType );
121  def.setGeometrySrid( reGeom.cap( 3 ).toLong() );
122  }
123  }
124  }
125  else if ( key == QLatin1String( "nogeometry" ) )
126  {
128  }
129  else if ( key == QLatin1String( "uid" ) )
130  {
131  def.setUid( value );
132  }
133  else if ( key == QLatin1String( "query" ) )
134  {
135  // url encoded query
136  def.setQuery( QUrl::fromPercentEncoding( value.toUtf8() ) );
137  }
138  else if ( key == QLatin1String( "field" ) )
139  {
140  // field_name:type (int, real, text)
141  QRegExp reField( "(" + columnNameRx + "):(int|real|text)" );
142  int pos = reField.indexIn( value );
143  if ( pos >= 0 )
144  {
145  QString fieldName( reField.cap( 1 ) );
146  QString fieldType( reField.cap( 2 ) );
147  if ( fieldType == QLatin1String( "int" ) )
148  {
149  fields.append( QgsField( fieldName, QVariant::Int, fieldType ) );
150  }
151  else if ( fieldType == QLatin1String( "real" ) )
152  {
153  fields.append( QgsField( fieldName, QVariant::Double, fieldType ) );
154  }
155  if ( fieldType == QLatin1String( "text" ) )
156  {
157  fields.append( QgsField( fieldName, QVariant::String, fieldType ) );
158  }
159  }
160  }
161  else if ( key == QLatin1String( "lazy" ) )
162  {
163  def.setLazy( true );
164  }
165  else if ( key == QLatin1String( "subsetstring" ) )
166  {
167  def.setSubsetString( QUrl::fromPercentEncoding( value.toUtf8() ) );
168  }
169  }
170  def.setFields( fields );
171 
172  return def;
173 }
174 
175 // Mega ewwww. all this is taken from Qt's QUrl::addEncodedQueryItem compatibility helper.
176 // (I can't see any way to port the below code to NOT require this without breaking
177 // existing projects.)
178 
179 inline char toHexUpper( uint value ) noexcept
180 {
181  return "0123456789ABCDEF"[value & 0xF];
182 }
183 
184 static inline ushort encodeNibble( ushort c )
185 {
186  return ushort( toHexUpper( c ) );
187 }
188 
189 bool qt_is_ascii( const char *&ptr, const char *end ) noexcept
190 {
191  while ( ptr + 4 <= end )
192  {
193  quint32 data = qFromUnaligned<quint32>( ptr );
194  if ( data &= 0x80808080U )
195  {
196 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
197  uint idx = qCountLeadingZeroBits( data );
198 #else
199  uint idx = qCountTrailingZeroBits( data );
200 #endif
201  ptr += idx / 8;
202  return false;
203  }
204  ptr += 4;
205  }
206  while ( ptr != end )
207  {
208  if ( quint8( *ptr ) & 0x80 )
209  return false;
210  ++ptr;
211  }
212  return true;
213 }
214 
215 QString fromEncodedComponent_helper( const QByteArray &ba )
216 {
217  if ( ba.isNull() )
218  return QString();
219  // scan ba for anything above or equal to 0x80
220  // control points below 0x20 are fine in QString
221  const char *in = ba.constData();
222  const char *const end = ba.constEnd();
223  if ( qt_is_ascii( in, end ) )
224  {
225  // no non-ASCII found, we're safe to convert to QString
226  return QString::fromLatin1( ba, ba.size() );
227  }
228  // we found something that we need to encode
229  QByteArray intermediate = ba;
230  intermediate.resize( ba.size() * 3 - ( in - ba.constData() ) );
231  uchar *out = reinterpret_cast<uchar *>( intermediate.data() + ( in - ba.constData() ) );
232  for ( ; in < end; ++in )
233  {
234  if ( *in & 0x80 )
235  {
236  // encode
237  *out++ = '%';
238  *out++ = encodeNibble( uchar( *in ) >> 4 );
239  *out++ = encodeNibble( uchar( *in ) & 0xf );
240  }
241  else
242  {
243  // keep
244  *out++ = uchar( *in );
245  }
246  }
247  // now it's safe to call fromLatin1
248  return QString::fromLatin1( intermediate, out - reinterpret_cast<uchar *>( intermediate.data() ) );
249 }
250 
252 {
253  QUrl url;
254  if ( !filePath().isEmpty() )
255  url = QUrl::fromLocalFile( filePath() );
256 
257  QUrlQuery urlQuery( url );
258 
259  const auto constSourceLayers = sourceLayers();
260  for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
261  {
262  if ( l.isReferenced() )
263  urlQuery.addQueryItem( QStringLiteral( "layer_ref" ), QStringLiteral( "%1:%2" ).arg( l.reference(), l.name() ) );
264  else
265  // if you can find a way to port this away from fromEncodedComponent_helper without breaking existing projects,
266  // please do so... this is GROSS!
267  urlQuery.addQueryItem( fromEncodedComponent_helper( "layer" ),
268  fromEncodedComponent_helper( QStringLiteral( "%1:%4:%2:%3" ) // the order is important, since the 4th argument may contain '%2' as well
269  .arg( l.provider(),
270  QString( QUrl::toPercentEncoding( l.name() ) ),
271  l.encoding(),
272  QString( QUrl::toPercentEncoding( l.source() ) ) ).toUtf8() ) );
273  }
274 
275  if ( !query().isEmpty() )
276  {
277  urlQuery.addQueryItem( QStringLiteral( "query" ), query() );
278  }
279 
280  if ( !uid().isEmpty() )
281  urlQuery.addQueryItem( QStringLiteral( "uid" ), uid() );
282 
284  urlQuery.addQueryItem( QStringLiteral( "nogeometry" ), QString() );
285  else if ( !geometryField().isEmpty() )
286  {
287  if ( hasDefinedGeometry() )
288  urlQuery.addQueryItem( QStringLiteral( "geometry" ), QStringLiteral( "%1:%2:%3" ).arg( geometryField() ). arg( geometryWkbType() ).arg( geometrySrid() ).toUtf8() );
289  else
290  urlQuery.addQueryItem( QStringLiteral( "geometry" ), geometryField() );
291  }
292 
293  const auto constFields = fields();
294  for ( const QgsField &f : constFields )
295  {
296  if ( f.type() == QVariant::Int )
297  urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":int" );
298  else if ( f.type() == QVariant::Double )
299  urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":real" );
300  else if ( f.type() == QVariant::String )
301  urlQuery.addQueryItem( QStringLiteral( "field" ), f.name() + ":text" );
302  }
303 
304  if ( isLazy() )
305  {
306  urlQuery.addQueryItem( QStringLiteral( "lazy" ), QString() );
307  }
308 
309  if ( ! subsetString().isEmpty() )
310  {
311  urlQuery.addQueryItem( QStringLiteral( "subsetstring" ), QUrl::toPercentEncoding( subsetString() ) );
312  }
313 
314  url.setQuery( urlQuery );
315 
316  return url;
317 }
318 
320 {
321  return QString( toUrl().toEncoded() );
322 }
323 
324 void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &ref )
325 {
326  mSourceLayers.append( SourceLayer( name, ref ) );
327 }
328 
329 void QgsVirtualLayerDefinition::addSource( const QString &name, const QString &source, const QString &provider, const QString &encoding )
330 {
331  mSourceLayers.append( SourceLayer( name, source, provider, encoding ) );
332 }
333 
334 bool QgsVirtualLayerDefinition::hasSourceLayer( const QString &name ) const
335 {
336  const auto constSourceLayers = sourceLayers();
337  for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
338  {
339  if ( l.name() == name )
340  {
341  return true;
342  }
343  }
344  return false;
345 }
346 
348 {
349  const auto constSourceLayers = sourceLayers();
350  for ( const QgsVirtualLayerDefinition::SourceLayer &l : constSourceLayers )
351  {
352  if ( l.isReferenced() )
353  {
354  return true;
355  }
356  }
357  return false;
358 }
359 
361 {
362  return mSubsetString;
363 }
364 
365 void QgsVirtualLayerDefinition::setSubsetString( const QString &subsetString )
366 {
367  mSubsetString = subsetString;
368 }
QgsVirtualLayerDefinition::setGeometryWkbType
void setGeometryWkbType(QgsWkbTypes::Type t)
Sets the type of the geometry.
Definition: qgsvirtuallayerdefinition.h:165
qgsvirtuallayerdefinition.h
QgsVirtualLayerDefinition::setGeometrySrid
void setGeometrySrid(long srid)
Sets the SRID of the geometry.
Definition: qgsvirtuallayerdefinition.h:170
QgsVirtualLayerDefinition::setQuery
void setQuery(const QString &query)
Sets the SQL query.
Definition: qgsvirtuallayerdefinition.h:123
QgsVirtualLayerDefinition::geometryField
QString geometryField() const
Gets the name of the geometry field. Empty if no geometry field.
Definition: qgsvirtuallayerdefinition.h:154
QgsVirtualLayerDefinition::setLazy
void setLazy(bool lazy)
Sets the lazy mode.
Definition: qgsvirtuallayerdefinition.h:143
QgsVirtualLayerDefinition::setUid
void setUid(const QString &uid)
Sets the name of the field with unique identifiers.
Definition: qgsvirtuallayerdefinition.h:133
QgsVirtualLayerDefinition::sourceLayers
const QgsVirtualLayerDefinition::SourceLayers & sourceLayers() const
Gets access to the source layers.
Definition: qgsvirtuallayerdefinition.h:118
QgsFields
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsVirtualLayerDefinition::setSubsetString
void setSubsetString(const QString &subsetString)
Sets the subsetString.
Definition: qgsvirtuallayerdefinition.cpp:365
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:41
QgsWkbTypes::Type
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
QgsVirtualLayerDefinition::geometrySrid
long geometrySrid() const
Gets the SRID of the geometry.
Definition: qgsvirtuallayerdefinition.h:168
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:175
QgsVirtualLayerDefinition::addSource
void addSource(const QString &name, const QString &ref)
Add a live layer source layer.
Definition: qgsvirtuallayerdefinition.cpp:324
QgsWkbTypes::Unknown
@ Unknown
Definition: qgswkbtypes.h:71
QgsVirtualLayerDefinition::QgsVirtualLayerDefinition
QgsVirtualLayerDefinition(const QString &filePath="")
Constructor with an optional file path.
Definition: qgsvirtuallayerdefinition.cpp:25
qgsvectordataprovider.h
QgsVirtualLayerDefinition::setGeometryField
void setGeometryField(const QString &geometryField)
Sets the name of the geometry field.
Definition: qgsvirtuallayerdefinition.h:156
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:30
toHexUpper
char toHexUpper(uint value) noexcept
Definition: qgsvirtuallayerdefinition.cpp:179
fromEncodedComponent_helper
QString fromEncodedComponent_helper(const QByteArray &ba)
Definition: qgsvirtuallayerdefinition.cpp:215
QgsVirtualLayerDefinition::uid
QString uid() const
Gets the name of the field with unique identifiers.
Definition: qgsvirtuallayerdefinition.h:131
QgsVirtualLayerDefinition::hasReferencedLayers
bool hasReferencedLayers() const
Convenience method to test whether the definition has referenced (live) layers.
Definition: qgsvirtuallayerdefinition.cpp:347
qgsvectorlayer.h
QgsVirtualLayerDefinition::isLazy
bool isLazy() const
Returns the lazy mode.
Definition: qgsvirtuallayerdefinition.h:151
QgsVirtualLayerDefinition::subsetString
QString subsetString() const
Returns the subset string.
Definition: qgsvirtuallayerdefinition.cpp:360
QgsVirtualLayerDefinition::query
QString query() const
Gets the SQL query.
Definition: qgsvirtuallayerdefinition.h:121
QgsVirtualLayerDefinition::setFilePath
void setFilePath(const QString &filePath)
Sets the file path.
Definition: qgsvirtuallayerdefinition.h:128
QgsWkbTypes::NoGeometry
@ NoGeometry
Definition: qgswkbtypes.h:85
c
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Definition: porting_processing.dox:1
QgsVirtualLayerDefinition
Class to manipulate the definition of a virtual layer.
Definition: qgsvirtuallayerdefinition.h:32
QgsVirtualLayerDefinition::hasDefinedGeometry
bool hasDefinedGeometry() const
Convenient method to test if the geometry is defined (not NoGeometry and not Unknown)
Definition: qgsvirtuallayerdefinition.h:184
QgsVirtualLayerDefinition::toString
QString toString() const
Convert into a QString that can be read by the virtual layer provider.
Definition: qgsvirtuallayerdefinition.cpp:319
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:163
QgsFields::at
QgsField at(int i) const
Gets 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:173
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:334
QgsVirtualLayerDefinition::toUrl
QUrl toUrl() const
Convert the definition into a QUrl.
Definition: qgsvirtuallayerdefinition.cpp:251
QgsVirtualLayerDefinition::filePath
QString filePath() const
Gets the file path. May be empty.
Definition: qgsvirtuallayerdefinition.h:126
qt_is_ascii
bool qt_is_ascii(const char *&ptr, const char *end) noexcept
Definition: qgsvirtuallayerdefinition.cpp:189
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50