Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Add a provider for virtual layers
- Loading branch information
Hugo Mercier
committed
Dec 18, 2015
1 parent
13f4081
commit e60712e
Showing
24 changed files
with
4,132 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
/** | ||
* Class to manipulate the definition of a virtual layer | ||
* | ||
* It is used to extract parameters from an initial virtual layer definition as well as | ||
* to store the complete, expanded definition once types have been detected. | ||
*/ | ||
class QgsVirtualLayerDefinition | ||
{ | ||
%TypeHeaderCode | ||
#include <qgsvirtuallayerdefinition.h> | ||
%End | ||
public: | ||
/** | ||
* A SourceLayer is either a reference to a live layer in the registry | ||
* or all the parameters needed to load it (provider key, source, etc.) | ||
*/ | ||
class SourceLayer | ||
{ | ||
public: | ||
//! Constructor variant to build a live layer reference | ||
SourceLayer( const QString& name, const QString& ref ); | ||
//! Constructor variant to build a layer with a provider and a source | ||
SourceLayer( const QString& name, const QString& source, const QString& provider, const QString& encoding ); | ||
|
||
//! Is it a live layer or not ? | ||
bool isReferenced() const; | ||
|
||
//! The reference (id) of the live layer | ||
QString reference() const; | ||
|
||
//! Name of the layer | ||
QString name() const; | ||
|
||
//! Provider key | ||
QString provider() const; | ||
|
||
//! The source url used by the provider to build the layer | ||
QString source() const; | ||
|
||
//! Optional encoding for the provider | ||
QString encoding() const; | ||
}; | ||
|
||
//! Constructor with an optional file path | ||
QgsVirtualLayerDefinition( const QString& filePath = "" ); | ||
|
||
//! Constructor to build a definition from a QUrl | ||
//! The path part of the URL is extracted as well as the following optional keys: | ||
//! layer_ref=layer_id[:name] represents a live layer referenced by its ID. An optional name can be given | ||
//! layer=provider:source[:name[:encoding]] represents a layer given by its provider key, its source url (URL-encoded). | ||
//! An optional name and encoding can be given | ||
//! geometry=column_name[:type:srid] gives the definition of the geometry column. | ||
//! Type can be either a WKB type code or a string (point, linestring, etc.) | ||
//! srid is an integer | ||
//! uid=column_name is the name of a column with unique integer values. | ||
//! nogeometry is a flag to force the layer to be a non-geometry layer | ||
//! query=sql represents the SQL query. Must be URL-encoded | ||
//! field=column_name:[int|real|text] represents a field with its name and its type | ||
static QgsVirtualLayerDefinition fromUrl( const QUrl& url ); | ||
|
||
//! Convert the definition into a QUrl | ||
QUrl toUrl() const; | ||
|
||
//! Convert into a QString that can be read by the virtual layer provider | ||
QString toString() const; | ||
|
||
//! Add a live layer source layer | ||
void addSource( const QString& name, const QString ref ); | ||
|
||
//! Add a layer with a source, a provider and an encoding | ||
void addSource( const QString& name, const QString source, const QString& provider, const QString& encoding = "" ); | ||
|
||
//! List of source layers | ||
typedef QList<QgsVirtualLayerDefinition::SourceLayer> SourceLayers; | ||
|
||
//! Get access to the source layers | ||
const SourceLayers& sourceLayers() const; | ||
|
||
//! Get the SQL query | ||
QString query() const; | ||
//! Set the SQL query | ||
void setQuery( const QString& query ); | ||
|
||
//! Get the file path. May be empty | ||
QString filePath() const; | ||
//! Set the file path | ||
void setFilePath( const QString& filePath ); | ||
|
||
//! Get the name of the field with unique identifiers | ||
QString uid() const; | ||
//! Set the name of the field with unique identifiers | ||
void setUid( const QString& uid ); | ||
|
||
//! Get the name of the geometry field. Empty if no geometry field | ||
QString geometryField() const; | ||
//! Set the name of the geometry field | ||
void setGeometryField( const QString& geometryField ); | ||
|
||
//! Get the type of the geometry | ||
//! QgsWKBTypes::NoGeometry to hide any geometry | ||
//! QgsWKBTypes::Unknown for unknown types | ||
QgsWKBTypes::Type geometryWkbType() const; | ||
//! Set the type of the geometry | ||
void setGeometryWkbType( QgsWKBTypes::Type t ); | ||
|
||
//! Get the SRID of the geometry | ||
long geometrySrid() const; | ||
//! Set the SRID of the geometry | ||
void setGeometrySrid( long srid ); | ||
|
||
//! Get field definitions | ||
const QgsFields& fields() const; | ||
//! Set field definitions | ||
void setFields( const QgsFields& fields ); | ||
|
||
//! Convenience method to test if a given source layer is part of the definition | ||
bool hasSourceLayer( QString name ) const; | ||
|
||
//! Convenience method to test whether the definition has referenced (live) layers | ||
bool hasReferencedLayers() const; | ||
|
||
//! Convenient method to test if the geometry is defined (not NoGeometry and not Unknown) | ||
bool hasDefinedGeometry() const; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,250 @@ | ||
/*************************************************************************** | ||
qgsvirtuallayerdefinition.cpp | ||
begin : December 2015 | ||
copyright : (C) 2015 Hugo Mercier, Oslandia | ||
email : hugo dot mercier at oslandia dot com | ||
***************************************************************************/ | ||
|
||
/*************************************************************************** | ||
* * | ||
* This program is free software; you can redistribute it and/or modify * | ||
* it under the terms of the GNU General Public License as published by * | ||
* the Free Software Foundation; either version 2 of the License, or * | ||
* (at your option) any later version. * | ||
* * | ||
***************************************************************************/ | ||
|
||
#include <QUrl> | ||
#include <QRegExp> | ||
#include <QStringList> | ||
|
||
#include "qgsvirtuallayerdefinition.h" | ||
|
||
QgsVirtualLayerDefinition::QgsVirtualLayerDefinition( const QString& filePath ) : | ||
mFilePath( filePath ), | ||
mGeometryWkbType( QgsWKBTypes::Unknown ), | ||
mGeometrySrid( 0 ) | ||
{ | ||
} | ||
|
||
QgsVirtualLayerDefinition QgsVirtualLayerDefinition::fromUrl( const QUrl& url ) | ||
{ | ||
QgsVirtualLayerDefinition def; | ||
|
||
def.setFilePath( url.path() ); | ||
|
||
// regexp for column name | ||
const QString columnNameRx( "[a-zA-Z_\x80-\xFF][a-zA-Z0-9_\x80-\xFF]*" ); | ||
|
||
QgsFields fields; | ||
|
||
int layerIdx = 0; | ||
QList<QPair<QString, QString> > items = url.queryItems(); | ||
for ( int i = 0; i < items.size(); i++ ) | ||
{ | ||
QString key = items.at( i ).first; | ||
QString value = items.at( i ).second; | ||
if ( key == "layer_ref" ) | ||
{ | ||
layerIdx++; | ||
// layer id, with optional layer_name | ||
int pos = value.indexOf( ':' ); | ||
QString layerId, vlayerName; | ||
if ( pos == -1 ) | ||
{ | ||
layerId = value; | ||
vlayerName = QString( "vtab%1" ).arg( layerIdx ); | ||
} | ||
else | ||
{ | ||
layerId = value.left( pos ); | ||
vlayerName = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() ); | ||
} | ||
// add the layer to the list | ||
def.addSource( vlayerName, layerId ); | ||
} | ||
else if ( key == "layer" ) | ||
{ | ||
layerIdx++; | ||
// syntax: layer=provider:url_encoded_source_URI(:name(:encoding)?)? | ||
int pos = value.indexOf( ':' ); | ||
if ( pos != -1 ) | ||
{ | ||
QString providerKey, source, vlayerName, encoding = "UTF-8"; | ||
|
||
providerKey = value.left( pos ); | ||
int pos2 = value.indexOf( ':', pos + 1 ); | ||
if ( pos2 != -1 ) | ||
{ | ||
source = QUrl::fromPercentEncoding( value.mid( pos + 1, pos2 - pos - 1 ).toUtf8() ); | ||
int pos3 = value.indexOf( ':', pos2 + 1 ); | ||
if ( pos3 != -1 ) | ||
{ | ||
vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1, pos3 - pos2 - 1 ).toUtf8() ); | ||
encoding = value.mid( pos3 + 1 ); | ||
} | ||
else | ||
{ | ||
vlayerName = QUrl::fromPercentEncoding( value.mid( pos2 + 1 ).toUtf8() ); | ||
} | ||
} | ||
else | ||
{ | ||
source = QUrl::fromPercentEncoding( value.mid( pos + 1 ).toUtf8() ); | ||
vlayerName = QString( "vtab%1" ).arg( layerIdx ); | ||
} | ||
|
||
def.addSource( vlayerName, source, providerKey, encoding ); | ||
} | ||
} | ||
else if ( key == "geometry" ) | ||
{ | ||
// geometry field definition, optional | ||
// geometry_column(:wkb_type:srid)? | ||
QRegExp reGeom( "(" + columnNameRx + ")(?::([a-zA-Z0-9]+):(\\d+))?" ); | ||
int pos = reGeom.indexIn( value ); | ||
if ( pos >= 0 ) | ||
{ | ||
def.setGeometryField( reGeom.cap( 1 ) ); | ||
if ( reGeom.captureCount() > 1 ) | ||
{ | ||
// not used by the spatialite provider for now ... | ||
QgsWKBTypes::Type wkbType = QgsWKBTypes::parseType( reGeom.cap( 2 ) ); | ||
if ( wkbType == QgsWKBTypes::Unknown ) | ||
{ | ||
wkbType = static_cast<QgsWKBTypes::Type>( reGeom.cap( 2 ).toLong() ); | ||
} | ||
def.setGeometryWkbType( wkbType ); | ||
def.setGeometrySrid( reGeom.cap( 3 ).toLong() ); | ||
} | ||
} | ||
} | ||
else if ( key == "nogeometry" ) | ||
{ | ||
def.setGeometryWkbType( QgsWKBTypes::NoGeometry ); | ||
} | ||
else if ( key == "uid" ) | ||
{ | ||
def.setUid( value ); | ||
} | ||
else if ( key == "query" ) | ||
{ | ||
// url encoded query | ||
def.setQuery( value ); | ||
} | ||
else if ( key == "field" ) | ||
{ | ||
// field_name:type (int, real, text) | ||
QRegExp reField( "(" + columnNameRx + "):(int|real|text)" ); | ||
int pos = reField.indexIn( value ); | ||
if ( pos >= 0 ) | ||
{ | ||
QString fieldName( reField.cap( 1 ) ); | ||
QString fieldType( reField.cap( 2 ) ); | ||
if ( fieldType == "int" ) | ||
{ | ||
fields.append( QgsField( fieldName, QVariant::Int, fieldType ) ); | ||
} | ||
else if ( fieldType == "real" ) | ||
{ | ||
fields.append( QgsField( fieldName, QVariant::Double, fieldType ) ); | ||
} | ||
if ( fieldType == "text" ) | ||
{ | ||
fields.append( QgsField( fieldName, QVariant::String, fieldType ) ); | ||
} | ||
} | ||
} | ||
} | ||
def.setFields( fields ); | ||
|
||
return def; | ||
} | ||
|
||
QUrl QgsVirtualLayerDefinition::toUrl() const | ||
{ | ||
QUrl url; | ||
url.setPath( filePath() ); | ||
|
||
foreach ( const QgsVirtualLayerDefinition::SourceLayer& l, sourceLayers() ) | ||
{ | ||
if ( l.isReferenced() ) | ||
url.addQueryItem( "layer_ref", QString( "%1:%2" ).arg( l.reference() ).arg( l.name() ) ); | ||
else | ||
url.addQueryItem( "layer", QString( "%1:%4:%2:%3" ) // the order is important, since the 4th argument may contain '%2' as well | ||
.arg( l.provider() ) | ||
.arg( QString( QUrl::toPercentEncoding( l.name() ) ) ) | ||
.arg( l.encoding() ) | ||
.arg( QString( QUrl::toPercentEncoding( l.source() ) ) ) ); | ||
} | ||
|
||
if ( !query().isEmpty() ) | ||
{ | ||
url.addQueryItem( "query", query() ); | ||
} | ||
|
||
if ( !uid().isEmpty() ) | ||
url.addQueryItem( "uid", uid() ); | ||
|
||
if ( geometryWkbType() == QgsWKBTypes::NoGeometry ) | ||
url.addQueryItem( "nogeometry", "" ); | ||
else if ( !geometryField().isEmpty() ) | ||
{ | ||
if ( hasDefinedGeometry() ) | ||
url.addQueryItem( "geometry", QString( "%1:%2:%3" ).arg( geometryField() ). arg( geometryWkbType() ).arg( geometrySrid() ).toUtf8() ); | ||
else | ||
url.addQueryItem( "geometry", geometryField() ); | ||
} | ||
|
||
for ( int i = 0; i < fields().count(); i++ ) | ||
{ | ||
const QgsField& f = fields()[i]; | ||
if ( f.type() == QVariant::Int ) | ||
url.addQueryItem( "field", f.name() + ":int" ); | ||
else if ( f.type() == QVariant::Double ) | ||
url.addQueryItem( "field", f.name() + ":real" ); | ||
else if ( f.type() == QVariant::String ) | ||
url.addQueryItem( "field", f.name() + ":text" ); | ||
} | ||
|
||
return url; | ||
} | ||
|
||
QString QgsVirtualLayerDefinition::toString() const | ||
{ | ||
return QString( toUrl().toEncoded() ); | ||
} | ||
|
||
void QgsVirtualLayerDefinition::addSource( const QString& name, const QString ref ) | ||
{ | ||
mSourceLayers.append( SourceLayer( name, ref ) ); | ||
} | ||
|
||
void QgsVirtualLayerDefinition::addSource( const QString& name, const QString source, const QString& provider, const QString& encoding ) | ||
{ | ||
mSourceLayers.append( SourceLayer( name, source, provider, encoding ) ); | ||
} | ||
|
||
bool QgsVirtualLayerDefinition::hasSourceLayer( QString name ) const | ||
{ | ||
foreach ( const QgsVirtualLayerDefinition::SourceLayer& l, sourceLayers() ) | ||
{ | ||
if ( l.name() == name ) | ||
{ | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
bool QgsVirtualLayerDefinition::hasReferencedLayers() const | ||
{ | ||
foreach ( const QgsVirtualLayerDefinition::SourceLayer& l, sourceLayers() ) | ||
{ | ||
if ( l.isReferenced() ) | ||
{ | ||
return true; | ||
} | ||
} | ||
return false; | ||
} |
Oops, something went wrong.