Skip to content

Commit 251474a

Browse files
committed
Add support for attribute renaming to OGR provider
1 parent 58cc890 commit 251474a

File tree

4 files changed

+111
-12
lines changed

4 files changed

+111
-12
lines changed

src/providers/ogr/qgsogrprovider.cpp

+47
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,48 @@ bool QgsOgrProvider::deleteAttributes( const QgsAttributeIds &attributes )
13151315
#endif
13161316
}
13171317

1318+
bool QgsOgrProvider::renameAttributes( const QgsFieldNameMap& renamedAttributes )
1319+
{
1320+
if ( !doInitialActionsForEdition() )
1321+
return false;
1322+
1323+
#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1900
1324+
QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
1325+
bool result = true;
1326+
for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
1327+
{
1328+
int fieldIndex = renameIt.key();
1329+
if ( fieldIndex < 0 || fieldIndex >= mAttributeFields.count() )
1330+
{
1331+
pushError( tr( "Invalid attribute index" ) );
1332+
result = false;
1333+
continue;
1334+
}
1335+
if ( mAttributeFields.indexFromName( renameIt.value() ) >= 0 )
1336+
{
1337+
//field name already in use
1338+
pushError( tr( "Error renaming field %1: name '%2' already exists" ).arg( fieldIndex ).arg( renameIt.value() ) );
1339+
result = false;
1340+
continue;
1341+
}
1342+
1343+
//type does not matter, it will not be used
1344+
OGRFieldDefnH fld = OGR_Fld_Create( mEncoding->fromUnicode( renameIt.value() ), OFTReal );
1345+
if ( OGR_L_AlterFieldDefn( ogrLayer, fieldIndex, fld, ALTER_NAME_FLAG ) != OGRERR_NONE )
1346+
{
1347+
pushError( tr( "OGR error renaming field %1: %2" ).arg( fieldIndex ).arg( CPLGetLastErrorMsg() ) );
1348+
result = false;
1349+
}
1350+
}
1351+
loadFields();
1352+
return result;
1353+
#else
1354+
Q_UNUSED( attributes );
1355+
pushError( tr( "Renaming fields is not supported prior to GDAL 1.9.0" ) );
1356+
return false;
1357+
#endif
1358+
}
1359+
13181360

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

1769+
if ( mWriteAccessPossible && OGR_L_TestCapability( ogrLayer, "AlterFieldDefn" ) )
1770+
{
1771+
ability |= RenameAttributes;
1772+
}
1773+
17271774
#if defined(OLCStringsAsUTF8)
17281775
if ( !OGR_L_TestCapability( ogrLayer, OLCStringsAsUTF8 ) )
17291776
{

src/providers/ogr/qgsogrprovider.h

+1-11
Original file line numberDiff line numberDiff line change
@@ -137,19 +137,9 @@ class QgsOgrProvider : public QgsVectorDataProvider
137137
/** Deletes a feature*/
138138
virtual bool deleteFeatures( const QgsFeatureIds & id ) override;
139139

140-
/**
141-
* Adds new attributes
142-
* @param attributes list of new attributes
143-
* @return true in case of success and false in case of failure
144-
*/
145140
virtual bool addAttributes( const QList<QgsField> &attributes ) override;
146-
147-
/**
148-
* Deletes existing attributes
149-
* @param attributes a set containing names of attributes
150-
* @return true in case of success and false in case of failure
151-
*/
152141
virtual bool deleteAttributes( const QgsAttributeIds &attributes ) override;
142+
virtual bool renameAttributes( const QgsFieldNameMap& renamedAttributes ) override;
153143

154144
/** Changes attribute values of existing features */
155145
virtual bool changeAttributeValues( const QgsChangedAttributesMap &attr_map ) override;

tests/src/python/test_provider_shapefile.py

+46
Original file line numberDiff line numberDiff line change
@@ -294,5 +294,51 @@ def testreloadData(self):
294294
# And now check that fields are up-to-date
295295
self.assertEquals(len(vl1.fields()), len(vl2.fields()))
296296

297+
def testRenameAttributes(self):
298+
''' Test renameAttributes() '''
299+
300+
tmpdir = tempfile.mkdtemp()
301+
self.dirs_to_cleanup.append(tmpdir)
302+
srcpath = os.path.join(TEST_DATA_DIR, 'provider')
303+
for file in glob.glob(os.path.join(srcpath, 'shapefile.*')):
304+
shutil.copy(os.path.join(srcpath, file), tmpdir)
305+
datasource = os.path.join(tmpdir, 'shapefile.shp')
306+
307+
vl = QgsVectorLayer(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
308+
provider = vl.dataProvider()
309+
310+
# bad rename
311+
self.assertFalse(provider.renameAttributes({-1: 'not_a_field'}))
312+
self.assertFalse(provider.renameAttributes({100: 'not_a_field'}))
313+
# already exists
314+
self.assertFalse(provider.renameAttributes({2: 'cnt'}))
315+
316+
# rename one field
317+
self.assertTrue(provider.renameAttributes({2: 'newname'}))
318+
self.assertEqual(provider.fields().at(2).name(), 'newname')
319+
vl.updateFields()
320+
fet = next(vl.getFeatures())
321+
self.assertEqual(fet.fields()[2].name(), 'newname')
322+
323+
# rename two fields
324+
self.assertTrue(provider.renameAttributes({2: 'newname2', 3: 'another'}))
325+
self.assertEqual(provider.fields().at(2).name(), 'newname2')
326+
self.assertEqual(provider.fields().at(3).name(), 'another')
327+
vl.updateFields()
328+
fet = next(vl.getFeatures())
329+
self.assertEqual(fet.fields()[2].name(), 'newname2')
330+
self.assertEqual(fet.fields()[3].name(), 'another')
331+
332+
# close file and reopen, then recheck to confirm that changes were saved to file
333+
del vl
334+
vl = None
335+
vl = QgsVectorLayer(u'{}|layerid=0'.format(datasource), u'test', u'ogr')
336+
provider = vl.dataProvider()
337+
self.assertEqual(provider.fields().at(2).name(), 'newname2')
338+
self.assertEqual(provider.fields().at(3).name(), 'another')
339+
fet = next(vl.getFeatures())
340+
self.assertEqual(fet.fields()[2].name(), 'newname2')
341+
self.assertEqual(fet.fields()[3].name(), 'another')
342+
297343
if __name__ == '__main__':
298344
unittest.main()

tests/src/python/test_provider_tabfile.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
__revision__ = '$Format:%H$'
1414

1515
import os
16-
16+
import tempfile
1717
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsVectorDataProvider
1818
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant
1919
from qgis.testing import (
@@ -22,6 +22,9 @@
2222
)
2323
import osgeo.gdal
2424
from utilities import unitTestDataPath
25+
import tempfile
26+
import shutil
27+
import glob
2528

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

3235
class TestPyQgsTabfileProvider(unittest.TestCase):
3336

37+
@classmethod
38+
def setUpClass(cls):
39+
"""Run before all tests"""
40+
cls.basetestpath = tempfile.mkdtemp()
41+
cls.dirs_to_cleanup = [cls.basetestpath]
42+
43+
@classmethod
44+
def tearDownClass(cls):
45+
"""Run after all tests"""
46+
for dirname in cls.dirs_to_cleanup:
47+
shutil.rmtree(dirname, True)
48+
3449
def testDateTimeFormats(self):
3550
# check that date and time formats are correctly interpreted
3651
basetestfile = os.path.join(TEST_DATA_DIR, 'tab_file.tab')
@@ -75,5 +90,6 @@ def testUpdateMode(self):
7590
self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-only")
7691
self.assertTrue(vl.dataProvider().isValid())
7792

93+
7894
if __name__ == '__main__':
7995
unittest.main()

0 commit comments

Comments
 (0)