Skip to content

Commit f8ede27

Browse files
committedAug 8, 2016
[QgsVectorFileWriter + OGR provider] Create Integer64 fields as Real fields when output driver doesn't support Integer64
Fix #15405
1 parent f17f6ac commit f8ede27

File tree

4 files changed

+92
-4
lines changed

4 files changed

+92
-4
lines changed
 

‎src/core/qgsvectorfilewriter.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#include <ogr_srs_api.h>
4646
#include <cpl_error.h>
4747
#include <cpl_conv.h>
48+
#include <gdal.h>
4849

4950
#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1800
5051
#define TO8F(x) (x).toUtf8().constData()
@@ -435,10 +436,16 @@ void QgsVectorFileWriter::init( QString vectorFileName,
435436
break;
436437
#else
437438
case QVariant::LongLong:
438-
ogrType = OFTInteger64;
439+
{
440+
const char* pszDataTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATATYPES, NULL );
441+
if ( pszDataTypes && strstr( pszDataTypes, "Integer64" ) )
442+
ogrType = OFTInteger64;
443+
else
444+
ogrType = OFTReal;
439445
ogrWidth = ogrWidth > 0 && ogrWidth <= 20 ? ogrWidth : 20;
440446
ogrPrecision = 0;
441447
break;
448+
}
442449
#endif
443450
case QVariant::String:
444451
ogrType = OFTString;

‎src/providers/ogr/qgsogrprovider.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,8 @@ bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )
12761276

12771277
bool returnvalue = true;
12781278

1279+
QMap< QString, QVariant::Type > mapFieldTypesToPatch;
1280+
12791281
for ( QList<QgsField>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter )
12801282
{
12811283
OGRFieldType type;
@@ -1287,8 +1289,17 @@ bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )
12871289
break;
12881290
#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 2000000
12891291
case QVariant::LongLong:
1290-
type = OFTInteger64;
1292+
{
1293+
const char* pszDataTypes = GDALGetMetadataItem( ogrDriver, GDAL_DMD_CREATIONFIELDDATATYPES, NULL );
1294+
if ( pszDataTypes && strstr( pszDataTypes, "Integer64" ) )
1295+
type = OFTInteger64;
1296+
else
1297+
{
1298+
mapFieldTypesToPatch[ iter->name()] = iter->type();
1299+
type = OFTReal;
1300+
}
12911301
break;
1302+
}
12921303
#endif
12931304
case QVariant::Double:
12941305
type = OFTReal;
@@ -1326,6 +1337,16 @@ bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )
13261337
OGR_Fld_Destroy( fielddefn );
13271338
}
13281339
loadFields();
1340+
1341+
// Patch field type in case of Integer64->Real mapping so that QVariant::LongLong
1342+
// is still returned to the caller
1343+
for ( QMap< QString, QVariant::Type >::const_iterator it = mapFieldTypesToPatch.begin(); it != mapFieldTypesToPatch.end(); ++it )
1344+
{
1345+
int idx = mAttributeFields.fieldNameIndex( it.key() );
1346+
if ( idx >= 0 )
1347+
mAttributeFields[ idx ].setType( *it );
1348+
}
1349+
13291350
return returnvalue;
13301351
}
13311352

‎tests/src/python/test_provider_tabfile.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
import os
1616
import tempfile
17-
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsVectorDataProvider
18-
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant
17+
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsVectorDataProvider, QgsField
18+
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant, QDir
1919
from qgis.testing import (
2020
start_app,
2121
unittest
@@ -90,6 +90,20 @@ def testUpdateMode(self):
9090
self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-only")
9191
self.assertTrue(vl.dataProvider().isValid())
9292

93+
@unittest.expectedFailure(int(osgeo.gdal.VersionInfo()[:1]) < 2)
94+
def testInteger64WriteTabfile(self):
95+
"""Check writing Integer64 fields to an MapInfo tabfile (which does not support that type)."""
96+
97+
base_dest_file_name = os.path.join(str(QDir.tempPath()), 'integer64')
98+
dest_file_name = base_dest_file_name + '.tab'
99+
shutil.copy(os.path.join(TEST_DATA_DIR, 'tab_file.tab'), base_dest_file_name + '.tab')
100+
shutil.copy(os.path.join(TEST_DATA_DIR, 'tab_file.dat'), base_dest_file_name + '.dat')
101+
shutil.copy(os.path.join(TEST_DATA_DIR, 'tab_file.map'), base_dest_file_name + '.map')
102+
shutil.copy(os.path.join(TEST_DATA_DIR, 'tab_file.id'), base_dest_file_name + '.id')
103+
vl = QgsVectorLayer(u'{}|layerid=0'.format(dest_file_name), u'test', u'ogr')
104+
self.assertTrue(vl.isValid())
105+
self.assertTrue(vl.dataProvider().addAttributes([QgsField("int8", QVariant.LongLong, "integer64")]))
106+
93107

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

‎tests/src/python/test_qgsvectorfilewriter.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
start_app()
3737

3838

39+
def GDAL_COMPUTE_VERSION(maj, min, rev):
40+
return ((maj) * 1000000 + (min) * 10000 + (rev) * 100)
41+
42+
3943
class TestFieldValueConverter(QgsVectorFileWriter.FieldValueConverter):
4044

4145
def __init__(self, layer):
@@ -416,5 +420,47 @@ def testValueConverter(self):
416420
self.assertEqual(f['nonconv'], 1)
417421
self.assertEqual(f['conv_attr'], 'converted_val')
418422

423+
@unittest.expectedFailure(int(osgeo.gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 0))
424+
def testInteger64WriteTabfile(self):
425+
"""Check writing Integer64 fields to an MapInfo tabfile (which does not support that type)."""
426+
ml = QgsVectorLayer(
427+
('Point?crs=epsg:4326&field=int8:int8'),
428+
'test',
429+
'memory')
430+
431+
self.assertIsNotNone(ml, 'Provider not initialized')
432+
self.assertTrue(ml.isValid(), 'Source layer not valid')
433+
provider = ml.dataProvider()
434+
self.assertIsNotNone(provider)
435+
436+
ft = QgsFeature()
437+
ft.setAttributes([2123456789])
438+
res, features = provider.addFeatures([ft])
439+
self.assertTrue(res)
440+
self.assertTrue(features)
441+
442+
dest_file_name = os.path.join(str(QDir.tempPath()), 'integer64.tab')
443+
crs = QgsCoordinateReferenceSystem()
444+
crs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId)
445+
write_result = QgsVectorFileWriter.writeAsVectorFormat(
446+
ml,
447+
dest_file_name,
448+
'utf-8',
449+
crs,
450+
'MapInfo File')
451+
self.assertEqual(write_result, QgsVectorFileWriter.NoError)
452+
453+
# Open result and check
454+
created_layer = QgsVectorLayer(u'{}|layerid=0'.format(dest_file_name), u'test', u'ogr')
455+
456+
fields = created_layer.dataProvider().fields()
457+
self.assertEqual(fields.at(fields.indexFromName('int8')).type(), QVariant.Double)
458+
459+
f = next(created_layer.getFeatures(QgsFeatureRequest()))
460+
461+
int8_idx = created_layer.fieldNameIndex('int8')
462+
self.assertEqual(f.attributes()[int8_idx], 2123456789)
463+
464+
419465
if __name__ == '__main__':
420466
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.