Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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 Jun 2, 2016
1 parent d40554b commit 58cc890
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 6 deletions.
12 changes: 11 additions & 1 deletion python/core/qgsvectordataprovider.sip
Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgsvectordataprovider.cpp
Expand Up @@ -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 );
Expand Down
15 changes: 13 additions & 2 deletions src/core/qgsvectordataprovider.h
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
27 changes: 25 additions & 2 deletions src/providers/memory/qgsmemoryprovider.cpp
Expand Up @@ -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:
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}

Expand Down
3 changes: 2 additions & 1 deletion src/providers/memory/qgsmemoryprovider.h
Expand Up @@ -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
Expand Down Expand Up @@ -132,7 +134,6 @@ class QgsMemoryProvider : public QgsVectorDataProvider
*/
virtual int capabilities() const override;


/* Implementation of functions from QgsDataProvider */

/**
Expand Down
38 changes: 38 additions & 0 deletions tests/src/python/test_provider_memory.py
Expand Up @@ -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):

Expand Down

0 comments on commit 58cc890

Please sign in to comment.