Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[QgsVectorFileWriter + OGR provider] Create Integer64 fields as Real …
…fields when output driver doesn't support Integer64

Fix #15405
  • Loading branch information
rouault committed Aug 8, 2016
1 parent 4bcbc1e commit 400a4d7
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 4 deletions.
9 changes: 8 additions & 1 deletion src/core/qgsvectorfilewriter.cpp
Expand Up @@ -48,6 +48,7 @@
#include <ogr_srs_api.h>
#include <cpl_error.h>
#include <cpl_conv.h>
#include <gdal.h>

#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1800
#define TO8F(x) (x).toUtf8().constData()
Expand Down Expand Up @@ -422,10 +423,16 @@ void QgsVectorFileWriter::init( QString vectorFileName,
break;
#else
case QVariant::LongLong:
ogrType = OFTInteger64;
{
const char* pszDataTypes = GDALGetMetadataItem( poDriver, GDAL_DMD_CREATIONFIELDDATATYPES, NULL );
if ( pszDataTypes && strstr( pszDataTypes, "Integer64" ) )
ogrType = OFTInteger64;
else
ogrType = OFTReal;
ogrWidth = ogrWidth > 0 && ogrWidth <= 20 ? ogrWidth : 20;
ogrPrecision = 0;
break;
}
#endif
case QVariant::String:
ogrType = OFTString;
Expand Down
23 changes: 22 additions & 1 deletion src/providers/ogr/qgsogrprovider.cpp
Expand Up @@ -1274,6 +1274,8 @@ bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )

bool returnvalue = true;

QMap< QString, QVariant::Type > mapFieldTypesToPatch;

for ( QList<QgsField>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter )
{
OGRFieldType type;
Expand All @@ -1285,8 +1287,17 @@ bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )
break;
#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 2000000
case QVariant::LongLong:
type = OFTInteger64;
{
const char* pszDataTypes = GDALGetMetadataItem( ogrDriver, GDAL_DMD_CREATIONFIELDDATATYPES, NULL );
if ( pszDataTypes && strstr( pszDataTypes, "Integer64" ) )
type = OFTInteger64;
else
{
mapFieldTypesToPatch[ iter->name()] = iter->type();
type = OFTReal;
}
break;
}
#endif
case QVariant::Double:
type = OFTReal;
Expand Down Expand Up @@ -1324,6 +1335,16 @@ bool QgsOgrProvider::addAttributes( const QList<QgsField> &attributes )
OGR_Fld_Destroy( fielddefn );
}
loadFields();

// Patch field type in case of Integer64->Real mapping so that QVariant::LongLong
// is still returned to the caller
for ( QMap< QString, QVariant::Type >::const_iterator it = mapFieldTypesToPatch.begin(); it != mapFieldTypesToPatch.end(); ++it )
{
int idx = mAttributeFields.fieldNameIndex( it.key() );
if ( idx >= 0 )
mAttributeFields[ idx ].setType( *it );
}

return returnvalue;
}

Expand Down
18 changes: 16 additions & 2 deletions tests/src/python/test_provider_tabfile.py
Expand Up @@ -14,8 +14,8 @@

import os
import tempfile
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsVectorDataProvider
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant
from qgis.core import QgsVectorLayer, QgsFeatureRequest, QgsVectorDataProvider, QgsField
from qgis.PyQt.QtCore import QDate, QTime, QDateTime, QVariant, QDir
from qgis.testing import (
start_app,
unittest
Expand Down Expand Up @@ -90,6 +90,20 @@ def testUpdateMode(self):
self.assertEquals(vl.dataProvider().property("_debug_open_mode"), "read-only")
self.assertTrue(vl.dataProvider().isValid())

@unittest.expectedFailure(int(osgeo.gdal.VersionInfo()[:1]) < 2)
def testInteger64WriteTabfile(self):
"""Check writing Integer64 fields to an MapInfo tabfile (which does not support that type)."""

base_dest_file_name = os.path.join(str(QDir.tempPath()), 'integer64')
dest_file_name = base_dest_file_name + '.tab'
shutil.copy(os.path.join(TEST_DATA_DIR, 'tab_file.tab'), base_dest_file_name + '.tab')
shutil.copy(os.path.join(TEST_DATA_DIR, 'tab_file.dat'), base_dest_file_name + '.dat')
shutil.copy(os.path.join(TEST_DATA_DIR, 'tab_file.map'), base_dest_file_name + '.map')
shutil.copy(os.path.join(TEST_DATA_DIR, 'tab_file.id'), base_dest_file_name + '.id')
vl = QgsVectorLayer(u'{}|layerid=0'.format(dest_file_name), u'test', u'ogr')
self.assertTrue(vl.isValid())
self.assertTrue(vl.dataProvider().addAttributes([QgsField("int8", QVariant.LongLong, "integer64")]))


if __name__ == '__main__':
unittest.main()
46 changes: 46 additions & 0 deletions tests/src/python/test_qgsvectorfilewriter.py
Expand Up @@ -36,6 +36,10 @@
start_app()


def GDAL_COMPUTE_VERSION(maj, min, rev):
return ((maj) * 1000000 + (min) * 10000 + (rev) * 100)


class TestFieldValueConverter(QgsVectorFileWriter.FieldValueConverter):

def __init__(self, layer):
Expand Down Expand Up @@ -416,5 +420,47 @@ def testValueConverter(self):
self.assertEqual(f['nonconv'], 1)
self.assertEqual(f['conv_attr'], 'converted_val')

@unittest.expectedFailure(int(osgeo.gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 0))
def testInteger64WriteTabfile(self):
"""Check writing Integer64 fields to an MapInfo tabfile (which does not support that type)."""
ml = QgsVectorLayer(
('Point?crs=epsg:4326&field=int8:int8'),
'test',
'memory')

self.assertIsNotNone(ml, 'Provider not initialized')
self.assertTrue(ml.isValid(), 'Source layer not valid')
provider = ml.dataProvider()
self.assertIsNotNone(provider)

ft = QgsFeature()
ft.setAttributes([2123456789])
res, features = provider.addFeatures([ft])
self.assertTrue(res)
self.assertTrue(features)

dest_file_name = os.path.join(str(QDir.tempPath()), 'integer64.tab')
crs = QgsCoordinateReferenceSystem()
crs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId)
write_result = QgsVectorFileWriter.writeAsVectorFormat(
ml,
dest_file_name,
'utf-8',
crs,
'MapInfo File')
self.assertEqual(write_result, QgsVectorFileWriter.NoError)

# Open result and check
created_layer = QgsVectorLayer(u'{}|layerid=0'.format(dest_file_name), u'test', u'ogr')

fields = created_layer.dataProvider().fields()
self.assertEqual(fields.at(fields.indexFromName('int8')).type(), QVariant.Double)

f = next(created_layer.getFeatures(QgsFeatureRequest()))

int8_idx = created_layer.fieldNameIndex('int8')
self.assertEqual(f.attributes()[int8_idx], 2123456789)


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

0 comments on commit 400a4d7

Please sign in to comment.