Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add QgsMemoryProviderUtils.createMemoryLayer for easy creation
of new memory layers from QgsFields/QgsCoordinateReferenceSystem/etc

Since memory provider cannot use QgsVectorLayerImport we need
another centeralized function for creating new memory layers
without the need to manually create the uri string.
  • Loading branch information
nyalldawson committed May 6, 2017
1 parent 5511b07 commit 767cb12
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 12 deletions.
2 changes: 2 additions & 0 deletions python/CMakeLists.txt
Expand Up @@ -107,6 +107,8 @@ INCLUDE_DIRECTORIES(
../src/core/layertree
../src/core/metadata
../src/core/processing
../src/core/providers
../src/core/providers/memory
../src/core/raster
../src/core/scalebar
../src/core/symbology-ng
Expand Down
2 changes: 2 additions & 0 deletions python/core/core.sip
Expand Up @@ -286,6 +286,8 @@
%Include processing/qgsprocessingregistry.sip
%Include processing/qgsprocessingutils.sip

%Include providers/memory/qgsmemoryproviderutils.sip

%Include raster/qgsbilinearrasterresampler.sip
%Include raster/qgsbrightnesscontrastfilter.sip
%Include raster/qgscliptominmaxenhancement.sip
Expand Down
14 changes: 14 additions & 0 deletions python/core/providers/memory/qgsmemoryproviderutils.sip
Expand Up @@ -9,6 +9,7 @@




class QgsMemoryProviderUtils
{
%Docstring
Expand All @@ -21,6 +22,19 @@ class QgsMemoryProviderUtils
%End
public:

static QgsVectorLayer *createMemoryLayer( const QString &name,
const QgsFields &fields,
QgsWkbTypes::Type geometryType = QgsWkbTypes::NoGeometry,
const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ) /Factory/;
%Docstring
Creates a new memory layer using the specified parameters. The caller takes responsibility
for deleting the newly created layer.
\param name layer name
\param fields fields for layer
\param geometryType optional layer geometry type
\param crs optional layer CRS for layers with geometry
:rtype: QgsVectorLayer
%End
};


Expand Down
1 change: 1 addition & 0 deletions src/app/CMakeLists.txt
Expand Up @@ -500,6 +500,7 @@ INCLUDE_DIRECTORIES(
../core/dxf
../core/geometry
../core/layertree
../core/providers/memory
../core/raster
../core/scalebar
../core/symbology-ng
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -94,6 +94,7 @@ SET(QGIS_CORE_SRCS

providers/memory/qgsmemoryfeatureiterator.cpp
providers/memory/qgsmemoryprovider.cpp
providers/memory/qgsmemoryproviderutils.cpp

scalebar/qgsdoubleboxscalebarrenderer.cpp
scalebar/qgsnumericscalebarrenderer.cpp
Expand Down Expand Up @@ -872,6 +873,7 @@ SET(QGIS_CORE_HDRS
processing/qgsprocessingutils.h

providers/memory/qgsmemoryfeatureiterator.h
providers/memory/qgsmemoryproviderutils.h

raster/qgsbilinearrasterresampler.h
raster/qgsbrightnesscontrastfilter.h
Expand Down
65 changes: 65 additions & 0 deletions src/core/providers/memory/qgsmemoryproviderutils.cpp
Expand Up @@ -16,3 +16,68 @@
***************************************************************************/

#include "qgsmemoryproviderutils.h"
#include "qgsfields.h"
#include "qgsvectorlayer.h"

QString memoryLayerFieldType( QVariant::Type type )
{
switch ( type )
{
case QVariant::Int:
return QStringLiteral( "integer" );

case QVariant::LongLong:
return QStringLiteral( "long" );

case QVariant::Double:
return QStringLiteral( "double" );

case QVariant::String:
return QStringLiteral( "string" );

case QVariant::Date:
return QStringLiteral( "date" );

case QVariant::Time:
return QStringLiteral( "time" );

case QVariant::DateTime:
return QStringLiteral( "datetime" );

default:
return QStringLiteral( "string" );
}
return QStringLiteral( "string" ); // no warnings
}

QgsVectorLayer *QgsMemoryProviderUtils::createMemoryLayer( const QString &name, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs )
{
QString geomType = QgsWkbTypes::displayString( geometryType );
if ( geomType.isNull() )
geomType = "none";

QString uri = geomType + '?';

bool first = true;
if ( crs.isValid() )
{
uri += QStringLiteral( "crs=" ) + crs.authid();
first = false;
}

QStringList fieldsStrings;
Q_FOREACH ( const QgsField &field, fields )
{
fieldsStrings << QStringLiteral( "field=%1:%2" ).arg( field.name(), memoryLayerFieldType( field.type() ) );
}

if ( !fieldsStrings.isEmpty() )
{
if ( !first )
uri += '&';
first = false;
uri += fieldsStrings.join( '&' );
}

return new QgsVectorLayer( uri, name, QStringLiteral( "memory" ) );
}
19 changes: 19 additions & 0 deletions src/core/providers/memory/qgsmemoryproviderutils.h
Expand Up @@ -19,6 +19,13 @@
#define QGSMEMORYPROVIDERUTILS_H

#include "qgis_core.h"
#include "qgis.h"
#include "qgscoordinatereferencesystem.h"
#include <QString>
#include <QVariant>

class QgsVectorLayer;
class QgsFields;

/**
* \class QgsMemoryProviderUtils
Expand All @@ -31,6 +38,18 @@ class CORE_EXPORT QgsMemoryProviderUtils

public:

/**
* Creates a new memory layer using the specified parameters. The caller takes responsibility
* for deleting the newly created layer.
* \param name layer name
* \param fields fields for layer
* \param geometryType optional layer geometry type
* \param crs optional layer CRS for layers with geometry
*/
static QgsVectorLayer *createMemoryLayer( const QString &name,
const QgsFields &fields,
QgsWkbTypes::Type geometryType = QgsWkbTypes::NoGeometry,
const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ) SIP_FACTORY;
};

#endif // QGSMEMORYPROVIDERUTILS_H
Expand Down
1 change: 1 addition & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -748,6 +748,7 @@ INCLUDE_DIRECTORIES(
../core/geometry
../core/layertree
../core/metadata
../core/providers/memory
../core/raster
../core/scalebar
../core/symbology-ng
Expand Down
13 changes: 2 additions & 11 deletions src/gui/qgsnewmemorylayerdialog.cpp
Expand Up @@ -23,6 +23,7 @@
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
#include "qgssettings.h"
#include "qgsmemoryproviderutils.h"

#include <QPushButton>
#include <QComboBox>
Expand All @@ -40,18 +41,8 @@ QgsVectorLayer *QgsNewMemoryLayerDialog::runAndCreateLayer( QWidget *parent, con
}

QgsWkbTypes::Type geometrytype = dialog.selectedType();

QString geomType = QgsWkbTypes::displayString( geometrytype );
if ( geomType.isNull() )
geomType = "none";

QString layerProperties = QStringLiteral( "%1?" ).arg( geomType );
if ( QgsWkbTypes::NoGeometry != geometrytype )
layerProperties.append( QStringLiteral( "crs=%1&" ).arg( dialog.crs().authid() ) );
layerProperties.append( QStringLiteral( "memoryid=%1" ).arg( QUuid::createUuid().toString() ) );

QString name = dialog.layerName().isEmpty() ? tr( "New scratch layer" ) : dialog.layerName();
QgsVectorLayer *newLayer = new QgsVectorLayer( layerProperties, name, QStringLiteral( "memory" ) );
QgsVectorLayer *newLayer = QgsMemoryProviderUtils::createMemoryLayer( name, QgsFields(), geometrytype, dialog.crs() );
return newLayer;
}

Expand Down
57 changes: 56 additions & 1 deletion tests/src/python/test_provider_memory.py
Expand Up @@ -15,6 +15,7 @@

from qgis.core import (
QgsField,
QgsFields,
QgsLayerDefinition,
QgsPoint,
QgsPathResolver,
Expand All @@ -23,7 +24,9 @@
QgsFeature,
QgsGeometry,
QgsWkbTypes,
NULL
NULL,
QgsMemoryProviderUtils,
QgsCoordinateReferenceSystem
)

from qgis.testing import (
Expand Down Expand Up @@ -335,6 +338,58 @@ def testRenameAttributes(self):
self.assertEqual(fet.fields()[1].name(), 'mapinfo_is_the_stone_age')
self.assertEqual(fet.fields()[2].name(), 'super_size')

def testCreateMemoryLayer(self):
"""
Test QgsMemoryProviderUtils.createMemoryLayer()
"""

# no fields
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', QgsFields())
self.assertTrue(layer.isValid())
self.assertEqual(layer.name(), 'my name')
self.assertTrue(layer.fields().isEmpty())

# geometry type
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', QgsFields(), QgsWkbTypes.Point)
self.assertTrue(layer.isValid())
self.assertEqual(layer.wkbType(), QgsWkbTypes.Point)
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', QgsFields(), QgsWkbTypes.PolygonZM)
self.assertTrue(layer.isValid())
self.assertEqual(layer.wkbType(), QgsWkbTypes.PolygonZM)

# crs
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', QgsFields(), QgsWkbTypes.PolygonZM, QgsCoordinateReferenceSystem.fromEpsgId(3111))
self.assertTrue(layer.isValid())
self.assertEqual(layer.wkbType(), QgsWkbTypes.PolygonZM)
self.assertTrue(layer.crs().isValid())
self.assertEqual(layer.crs().authid(), 'EPSG:3111')

# fields
fields = QgsFields()
fields.append(QgsField("string", QVariant.String))
fields.append(QgsField("long", QVariant.LongLong))
fields.append(QgsField("double", QVariant.Double))
fields.append(QgsField("integer", QVariant.Int))
fields.append(QgsField("date", QVariant.Date))
fields.append(QgsField("datetime", QVariant.DateTime))
fields.append(QgsField("time", QVariant.Time))
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', fields)
self.assertTrue(layer.isValid())
self.assertFalse(layer.fields().isEmpty())
self.assertEqual(len(layer.fields()), len(fields))
for i in range(len(fields)):
self.assertEqual(layer.fields()[i].name(), fields[i].name())
self.assertEqual(layer.fields()[i].type(), fields[i].type())

# unsupported field type
fields = QgsFields()
fields.append(QgsField("rect", QVariant.RectF))
layer = QgsMemoryProviderUtils.createMemoryLayer('my name', fields)
self.assertTrue(layer.isValid())
self.assertFalse(layer.fields().isEmpty())
self.assertEqual(layer.fields()[0].name(), 'rect')
self.assertEqual(layer.fields()[0].type(), QVariant.String) # should be mapped to string


class TestPyQgsMemoryProviderIndexed(unittest.TestCase, ProviderTestCase):

Expand Down

0 comments on commit 767cb12

Please sign in to comment.