Skip to content
Permalink
Browse files

[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 a0e3e76 commit 7c81384adbc39c260b659281e40054209f20cf6e
@@ -45,6 +45,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()
@@ -435,10 +436,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;
@@ -1276,6 +1276,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;
@@ -1287,8 +1289,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;
@@ -1326,6 +1337,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;
}

@@ -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
@@ -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()
@@ -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):
@@ -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 7c81384

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