Skip to content
Permalink
Browse files

Add support for attribute renaming to OGR provider

  • Loading branch information
nyalldawson committed May 30, 2016
1 parent 58cc890 commit 251474a62e5ff66ccfb2fc28f286d2f27eb97dd2
@@ -1315,6 +1315,48 @@ bool QgsOgrProvider::deleteAttributes( const QgsAttributeIds &attributes )
#endif
}

bool QgsOgrProvider::renameAttributes( const QgsFieldNameMap& renamedAttributes )
{
if ( !doInitialActionsForEdition() )
return false;

#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1900
QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
bool result = true;
for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
{
int fieldIndex = renameIt.key();
if ( fieldIndex < 0 || fieldIndex >= mAttributeFields.count() )
{
pushError( tr( "Invalid attribute index" ) );
result = false;
continue;
}
if ( mAttributeFields.indexFromName( renameIt.value() ) >= 0 )
{
//field name already in use
pushError( tr( "Error renaming field %1: name '%2' already exists" ).arg( fieldIndex ).arg( renameIt.value() ) );
result = false;
continue;
}

//type does not matter, it will not be used
OGRFieldDefnH fld = OGR_Fld_Create( mEncoding->fromUnicode( renameIt.value() ), OFTReal );
if ( OGR_L_AlterFieldDefn( ogrLayer, fieldIndex, fld, ALTER_NAME_FLAG ) != OGRERR_NONE )
{
pushError( tr( "OGR error renaming field %1: %2" ).arg( fieldIndex ).arg( CPLGetLastErrorMsg() ) );
result = false;
}
}
loadFields();
return result;
#else
Q_UNUSED( attributes );
pushError( tr( "Renaming fields is not supported prior to GDAL 1.9.0" ) );
return false;
#endif
}


bool QgsOgrProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
{
@@ -1724,6 +1766,11 @@ void QgsOgrProvider::computeCapabilities()
ability |= DeleteAttributes;
}

if ( mWriteAccessPossible && OGR_L_TestCapability( ogrLayer, "AlterFieldDefn" ) )
{
ability |= RenameAttributes;
}

#if defined(OLCStringsAsUTF8)
if ( !OGR_L_TestCapability( ogrLayer, OLCStringsAsUTF8 ) )
{
@@ -137,19 +137,9 @@ class QgsOgrProvider : public QgsVectorDataProvider
/** Deletes a feature*/
virtual bool deleteFeatures( const QgsFeatureIds & id ) override;

/**
* Adds new attributes
* @param attributes list of new attributes
* @return true in case of success and false in case of failure
*/
virtual bool addAttributes( const QList<QgsField> &attributes ) override;

/**
* Deletes existing attributes
* @param attributes a set containing names of attributes
* @return true in case of success and false in case of failure
*/
virtual bool deleteAttributes( const QgsAttributeIds &attributes ) override;
virtual bool renameAttributes( const QgsFieldNameMap& renamedAttributes ) override;

/** Changes attribute values of existing features */
virtual bool changeAttributeValues( const QgsChangedAttributesMap &attr_map ) override;
@@ -294,5 +294,51 @@ def testreloadData(self):
# And now check that fields are up-to-date
self.assertEquals(len(vl1.fields()), len(vl2.fields()))

def testRenameAttributes(self):
''' Test renameAttributes() '''

tmpdir = tempfile.mkdtemp()
self.dirs_to_cleanup.append(tmpdir)
srcpath = os.path.join(TEST_DATA_DIR, 'provider')
for file in glob.glob(os.path.join(srcpath, 'shapefile.*')):
shutil.copy(os.path.join(srcpath, file), tmpdir)
datasource = os.path.join(tmpdir, 'shapefile.shp')

vl = QgsVectorLayer(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
provider = vl.dataProvider()

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

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

# rename two fields
self.assertTrue(provider.renameAttributes({2: 'newname2', 3: 'another'}))
self.assertEqual(provider.fields().at(2).name(), 'newname2')
self.assertEqual(provider.fields().at(3).name(), 'another')
vl.updateFields()
fet = next(vl.getFeatures())
self.assertEqual(fet.fields()[2].name(), 'newname2')
self.assertEqual(fet.fields()[3].name(), 'another')

# close file and reopen, then recheck to confirm that changes were saved to file
del vl
vl = None
vl = QgsVectorLayer(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
provider = vl.dataProvider()
self.assertEqual(provider.fields().at(2).name(), 'newname2')
self.assertEqual(provider.fields().at(3).name(), 'another')
fet = next(vl.getFeatures())
self.assertEqual(fet.fields()[2].name(), 'newname2')
self.assertEqual(fet.fields()[3].name(), 'another')

if __name__ == '__main__':
unittest.main()
@@ -13,7 +13,7 @@
__revision__ = '$Format:%H$'

import os

import tempfile
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsVectorDataProvider
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant
from qgis.testing import (
@@ -22,6 +22,9 @@
)
import osgeo.gdal
from utilities import unitTestDataPath
import tempfile
import shutil
import glob

start_app()
TEST_DATA_DIR = unitTestDataPath()
@@ -31,6 +34,18 @@

class TestPyQgsTabfileProvider(unittest.TestCase):

@classmethod
def setUpClass(cls):
"""Run before all tests"""
cls.basetestpath = tempfile.mkdtemp()
cls.dirs_to_cleanup = [cls.basetestpath]

@classmethod
def tearDownClass(cls):
"""Run after all tests"""
for dirname in cls.dirs_to_cleanup:
shutil.rmtree(dirname, True)

def testDateTimeFormats(self):
# check that date and time formats are correctly interpreted
basetestfile = os.path.join(TEST_DATA_DIR, 'tab_file.tab')
@@ -75,5 +90,6 @@ def testUpdateMode(self):
self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-only")
self.assertTrue(vl.dataProvider().isValid())


if __name__ == '__main__':
unittest.main()

0 comments on commit 251474a

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