Skip to content
Permalink
Browse files

[FEATURE] Add support for attribute renaming to QgsVectorDataProvider

Implemented in memory layer provider only, not yet supported
in edit buffer or configurable in GUI
  • Loading branch information
nyalldawson committed May 30, 2016
1 parent d40554b commit 58cc89019c878646e9bf010eb00d77f2865af464
@@ -54,7 +54,9 @@ class QgsVectorDataProvider : QgsDataProvider
/** Supports joint updates for attributes and geometry
* Providers supporting this should still define ChangeGeometries | ChangeAttributeValues
*/
ChangeFeatures
ChangeFeatures,
/** Supports renaming attributes (fields). Added in QGIS 2.16 */
RenameAttributes,
};

/** Bitmask of all provider's editing capabilities */
@@ -205,6 +207,14 @@ class QgsVectorDataProvider : QgsDataProvider
*/
virtual bool deleteAttributes( const QSet<int> &attributes );

/**
* Renames existing attributes.
* @param renamedAttributes map of attribute index to new attribute name
* @return true in case of success and false in case of failure
* @note added in QGIS 2.16
*/
virtual bool renameAttributes( const QgsFieldNameMap& renamedAttributes );

/**
* Changes attribute values of existing features.
* @param attr_map a map containing changed attributes
@@ -81,6 +81,12 @@ bool QgsVectorDataProvider::deleteAttributes( const QgsAttributeIds &attributes
return false;
}

bool QgsVectorDataProvider::renameAttributes( const QgsFieldNameMap& renamedAttributes )
{
Q_UNUSED( renamedAttributes );
return false;
}

bool QgsVectorDataProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
{
Q_UNUSED( attr_map );
@@ -102,12 +102,15 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
/** Supports joint updates for attributes and geometry
* Providers supporting this should still define ChangeGeometries | ChangeAttributeValues
*/
ChangeFeatures = 1 << 18
ChangeFeatures = 1 << 18,
/** Supports renaming attributes (fields). Added in QGIS 2.16 */
RenameAttributes = 1 << 19,
};

/** Bitmask of all provider's editing capabilities */
const static int EditingCapabilities = AddFeatures | DeleteFeatures |
ChangeAttributeValues | ChangeGeometries | AddAttributes | DeleteAttributes;
ChangeAttributeValues | ChangeGeometries | AddAttributes | DeleteAttributes |
RenameAttributes;

/**
* Constructor of the vector provider
@@ -254,6 +257,14 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider
*/
virtual bool deleteAttributes( const QgsAttributeIds &attributes );

/**
* Renames existing attributes.
* @param renamedAttributes map of attribute index to new attribute name
* @return true in case of success and false in case of failure
* @note added in QGIS 2.16
*/
virtual bool renameAttributes( const QgsFieldNameMap& renamedAttributes );

/**
* Changes attribute values of existing features.
* @param attr_map a map containing changed attributes
@@ -375,7 +375,6 @@ bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
{
for ( QList<QgsField>::const_iterator it = attributes.begin(); it != attributes.end(); ++it )
{
// Why are attributes restricted to int,double and string only?
switch ( it->type() )
{
case QVariant::Int:
@@ -404,6 +403,30 @@ bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
return true;
}

bool QgsMemoryProvider::renameAttributes( const QgsFieldNameMap& renamedAttributes )
{
QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
bool result = true;
for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
{
int fieldIndex = renameIt.key();
if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
{
result = false;
continue;
}
if ( mFields.indexFromName( renameIt.value() ) >= 0 )
{
//field name already in use
result = false;
continue;
}

mFields[ fieldIndex ].setName( renameIt.value() );
}
return result;
}

bool QgsMemoryProvider::deleteAttributes( const QgsAttributeIds& attributes )
{
QList<int> attrIdx = attributes.toList();
@@ -506,7 +529,7 @@ bool QgsMemoryProvider::createSpatialIndex()
int QgsMemoryProvider::capabilities() const
{
return AddFeatures | DeleteFeatures | ChangeGeometries |
ChangeAttributeValues | AddAttributes | DeleteAttributes | CreateSpatialIndex |
ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
SelectAtId | SelectGeometryAtId | CircularGeometries;
}

@@ -89,6 +89,8 @@ class QgsMemoryProvider : public QgsVectorDataProvider
*/
virtual bool addAttributes( const QList<QgsField> &attributes ) override;

virtual bool renameAttributes( const QgsFieldNameMap& renamedAttributes ) override;

/**
* Deletes existing attributes
* @param attributes a set containing names of attributes
@@ -132,7 +134,6 @@ class QgsMemoryProvider : public QgsVectorDataProvider
*/
virtual int capabilities() const override;


/* Implementation of functions from QgsDataProvider */

/**
@@ -262,6 +262,44 @@ def testSaveFields(self):
for f in myFields:
assert f == importedFields.field(f.name())

def testRenameAttributes(self):
layer = QgsVectorLayer("Point", "test", "memory")
provider = layer.dataProvider()

res = provider.addAttributes([QgsField("name", QVariant.String, ),
QgsField("age", QVariant.Int),
QgsField("size", QVariant.Double)])
layer.updateFields()
assert res, "Failed to add attributes"
ft = QgsFeature()
ft.setGeometry(QgsGeometry.fromPoint(QgsPoint(10, 10)))
ft.setAttributes(["Johny",
20,
0.3])
res, t = provider.addFeatures([ft])

# bad rename
self.assertFalse(provider.renameAttributes({-1: 'not_a_field'}))
self.assertFalse(provider.renameAttributes({100: 'not_a_field'}))
# already exists
self.assertFalse(provider.renameAttributes({1: 'name'}))

# rename one field
self.assertTrue(provider.renameAttributes({1: 'this_is_the_new_age'}))
self.assertEqual(provider.fields().at(1).name(), 'this_is_the_new_age')
layer.updateFields()
fet = next(layer.getFeatures())
self.assertEqual(fet.fields()[1].name(), 'this_is_the_new_age')

# rename two fields
self.assertTrue(provider.renameAttributes({1: 'mapinfo_is_the_stone_age', 2: 'super_size'}))
self.assertEqual(provider.fields().at(1).name(), 'mapinfo_is_the_stone_age')
self.assertEqual(provider.fields().at(2).name(), 'super_size')
layer.updateFields()
fet = next(layer.getFeatures())
self.assertEqual(fet.fields()[1].name(), 'mapinfo_is_the_stone_age')
self.assertEqual(fet.fields()[2].name(), 'super_size')


class TestPyQgsMemoryProviderIndexed(unittest.TestCase, ProviderTestCase):

0 comments on commit 58cc890

Please sign in to comment.
You can’t perform that action at this time.