diff --git a/resources/context_help/QgsDelimitedTextSourceSelect-en_US b/resources/context_help/QgsDelimitedTextSourceSelect-en_US index f3a8d9f066c..04f00a16113 100644 --- a/resources/context_help/QgsDelimitedTextSourceSelect-en_US +++ b/resources/context_help/QgsDelimitedTextSourceSelect-en_US @@ -26,9 +26,25 @@ describe points, lines, and polygons of arbitrary complexity. The file can also only table, which can then be joined to other tables in QGIS.

-In addition to the geometry definition the file can contain text, integer, and real number fields. QGIS -will choose the type of field based on its contents. +In addition to the geometry definition the file can contain text, integer, and real number fields. By default +QGIS will choose the type of field based on its the non blank values of the field. If all can be interpreted +as integer then the type will be integer, if all can be interpreted as real numbers then the type will +be double, otherwise the type will be text.

+

+QGIS can also read the types from an OGR CSV driver compatible "csvt" file. +This is a file alongside the data file, but with a "t" appended to the file name. +The file should just contain one linewhich lists the type of each field. +Valid types are "integer", "real", "string", "date", "time", and "datetime". The date, time, and datetime types are treated as strings in QGIS. +Each type may be followed by a width and precision, for example "real(10.4)". +The list of types are separated by commas, regardless of the delimiter used in the data file. An +example of a valid format file would be: +

+ +
+"integer","string","string(20)","real(20.4)"
+
+

Creating a delimited text layer

Creating a delimited text layer involves choosing the data file, defining the format (how each record is to be split into fields), and defining the geometry is represented. diff --git a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp index 541f28c7d01..4e8048b949a 100644 --- a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp +++ b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -164,6 +165,83 @@ QgsDelimitedTextProvider::QgsDelimitedTextProvider( QString uri ) } } +QStringList QgsDelimitedTextProvider::readCsvtFieldTypes( QString filename, QString *message ) +{ + // Look for a file with the same name as the data file, but an extra 't' or 'T' at the end + QStringList types; + QFileInfo csvtInfo( filename + 't' ); + if ( ! csvtInfo.exists() ) csvtInfo.setFile( filename + 'T' ); + if ( ! csvtInfo.exists() ) return types; + QFile csvtFile( csvtInfo.filePath() ); + if ( ! csvtFile.open( QIODevice::ReadOnly ) ) return types; + + + // If anything goes wrong here, just ignore it, as the file + // is not valid, so just ignore any exceptions. + + // For it to be valid, there must be just one non blank line at the beginning of the + // file. + + QString strTypeList; + try + { + QTextStream csvtStream( &csvtFile ); + strTypeList = csvtStream.readLine(); + if ( strTypeList.isEmpty() ) return types; + QString extra = csvtStream.readLine(); + while ( ! extra.isNull() ) + { + if ( ! extra.isEmpty() ) return types; + extra = csvtStream.readLine(); + } + } + catch ( ... ) + { + return types; + } + csvtFile.close(); + + // Is the type string valid? + // This is a slightly generous regular expression in that it allows spaces and unquoted field types + // not allowed in OGR CSVT files. Also doesn't care if int and string fields have + + strTypeList = strTypeList.toLower(); + QRegExp reTypeList( "^(?:\\s*(\\\"?)(?:integer|real|string|date|datetime|time)(?:\\(\\d+(?:\\.\\d+)?\\))?\\1\\s*(?:,|$))+" ); + if ( ! reTypeList.exactMatch( strTypeList ) ) + { + // Looks like this was supposed to be a CSVT file, so report bad formatted string + if ( message ) { *message = tr( "File type string in %1 is not correctly formatted" ).arg( csvtInfo.fileName() ); } + return types; + } + + // All good, so pull out the types from the string. Currently only returning integer, real, and string types + + QgsDebugMsg( QString( "Reading field types from %1" ).arg( csvtInfo.fileName() ) ); + QgsDebugMsg( QString( "Field type string: %1" ).arg( strTypeList ) ); + + int pos = 0; + QRegExp reType( "(integer|real|string|date|datetime|time)" ); + + while (( pos = reType.indexIn( strTypeList, pos ) ) != -1 ) + { + QgsDebugMsg( QString( "Found type: %1" ).arg( reType.cap( 1 ) ) ); + types << reType.cap( 1 ); + pos += reType.matchedLength(); + } + + if ( message ) + { + // Would be a useful info message, but don't want dialog to pop up every time... + // *message=tr("Reading field types from %1").arg(csvtInfo.fileName()); + } + + + return types; + + +} + + void QgsDelimitedTextProvider::resetCachedSubset() { mCachedSubsetString = QString(); @@ -482,6 +560,10 @@ void QgsDelimitedTextProvider::scanFile( bool buildIndexes ) mFieldCount = fieldNames.size(); attributeColumns.clear(); attributeFields.clear(); + + QString csvtMessage; + QStringList csvtTypes = readCsvtFieldTypes( mFile->fileName(), &csvtMessage ); + for ( int i = 0; i < fieldNames.size(); i++ ) { // Skip over WKT field ... don't want to display in attribute table @@ -490,8 +572,21 @@ void QgsDelimitedTextProvider::scanFile( bool buildIndexes ) // Add the field index lookup for the column attributeColumns.append( i ); QVariant::Type fieldType = QVariant::String; - QString typeName = "Text"; - if ( i < couldBeInt.size() ) + QString typeName = "text"; + if ( i < csvtTypes.size() ) + { + if ( csvtTypes[i] == "integer" ) + { + fieldType = QVariant::Int; + typeName = "integer"; + } + else if ( csvtTypes[i] == "real" ) + { + fieldType = QVariant::Double; + typeName = "double"; + } + } + else if ( i < couldBeInt.size() ) { if ( couldBeInt[i] ) { @@ -513,6 +608,7 @@ void QgsDelimitedTextProvider::scanFile( bool buildIndexes ) QgsDebugMsg( "feature count is: " + QString::number( mNumberFeatures ) ); QStringList warnings; + if ( ! csvtMessage.isEmpty() ) warnings.append( csvtMessage ); if ( nBadFormatRecords > 0 ) warnings.append( tr( "%1 records discarded due to invalid format" ).arg( nBadFormatRecords ) ); if ( nEmptyGeometry > 0 ) @@ -1104,25 +1200,41 @@ void QgsDelimitedTextProvider::fetchAttribute( QgsFeature& feature, int fieldIdx switch ( attributeFields[fieldIdx].type() ) { case QVariant::Int: - if ( value.isEmpty() ) - val = QVariant( attributeFields[fieldIdx].type() ); + { + int ivalue; + bool ok = false; + if ( ! value.isEmpty() ) ivalue = value.toInt( &ok ); + if ( ok ) + val = QVariant( ivalue ); else - val = QVariant( value ); + val = QVariant( attributeFields[fieldIdx].type() ); break; + } case QVariant::Double: - if ( value.isEmpty() ) + { + int dvalue; + bool ok = false; + if ( ! value.isEmpty() ) { - val = QVariant( attributeFields[fieldIdx].type() ); + if ( mDecimalPoint.isEmpty() ) + { + dvalue = value.toDouble( &ok ); + } + else + { + dvalue = QVariant( QString( value ).replace( mDecimalPoint, "." ) ).toDouble( &ok ); + } } - else if ( mDecimalPoint.isEmpty() ) + if ( ok ) { - val = QVariant( value.toDouble() ); + val = QVariant( dvalue ); } else { - val = QVariant( QString( value ).replace( mDecimalPoint, "." ).toDouble() ); + val = QVariant( attributeFields[fieldIdx].type() ); } break; + } default: val = QVariant( value ); break; diff --git a/src/providers/delimitedtext/qgsdelimitedtextprovider.h b/src/providers/delimitedtext/qgsdelimitedtextprovider.h index 32010f3013e..185060b357e 100644 --- a/src/providers/delimitedtext/qgsdelimitedtextprovider.h +++ b/src/providers/delimitedtext/qgsdelimitedtextprovider.h @@ -201,6 +201,14 @@ class QgsDelimitedTextProvider : public QgsVectorDataProvider */ bool boundsCheck( QgsGeometry *geom ); + /** + * Try to read field types from CSVT (or equialent xxxT) file. + * @param filename The name of the file from which to read the field types + * @param message Pointer to a string to receive a status message + * @return A list of field type strings, empty if not found or not valid + */ + QStringList readCsvtFieldTypes( QString filename, QString *message = 0 ); + private slots: void onFileUpdated(); diff --git a/tests/src/python/test_qgsdelimitedtextprovider.py b/tests/src/python/test_qgsdelimitedtextprovider.py index 84c9f5db3f9..f3d41c25e2c 100644 --- a/tests/src/python/test_qgsdelimitedtextprovider.py +++ b/tests/src/python/test_qgsdelimitedtextprovider.py @@ -94,6 +94,7 @@ def layerData( layer, request={}, offset=0 ): first = True data = {} fields = [] + fieldTypes = [] fr = QgsFeatureRequest() if request: if 'exact' in request and request['exact']: @@ -112,6 +113,7 @@ def layerData( layer, request={}, offset=0 ): first = False for field in f.fields(): fields.append(str(field.name())) + fieldTypes.append(str(field.typeName())) fielddata = dict ( (name, unicode(f[name].toString()) ) for name in fields ) g = f.geometry() if g: @@ -129,7 +131,7 @@ def layerData( layer, request={}, offset=0 ): if 'description' not in fields: fields.insert(1,'description') fields.append(fidkey) fields.append(geomkey) - return fields, data + return fields, fieldTypes, data # Retrieve the data for a delimited text url @@ -157,6 +159,7 @@ def delimitedTextData( testname, filename, requests, verbose, **params ): basename='file' uri = re.sub(r'^file\:\/\/[^\?]*','file://'+basename,uri) fields = [] + fieldTypes = [] data = {} if layer.isValid(): for nr,r in enumerate(requests): @@ -165,14 +168,16 @@ def delimitedTextData( testname, filename, requests, verbose, **params ): if callable(r): r( layer ) continue - rfields,rdata = layerData(layer,r,nr*1000) - if len(rfields) > len(fields): fields = rfields + rfields,rtypes, rdata = layerData(layer,r,nr*1000) + if len(rfields) > len(fields): + fields = rfields + fieldTypes=rtypes data.update(rdata) if not rdata: log.append("Request "+str(nr)+" did not return any data") for msg in logger.messages(): log.append(msg.replace(filepath,'file')) - return dict( fields=fields, data=data, log=log, uri=uri) + return dict( fields=fields, fieldTypes=fieldTypes, data=data, log=log, uri=uri) def printWanted( testname, result ): # Routine to export the result as a function definition @@ -186,6 +191,7 @@ def printWanted( testname, result ): # Dump the data for a layer - used to construct unit tests print prefix+"wanted={}" print prefix+"wanted['uri']="+repr(result['uri']) + print prefix+"wanted['fieldTypes']="+repr(result['fieldTypes']) print prefix+"wanted['data']={" for k in sorted(data.keys()): row = data[k] @@ -265,6 +271,10 @@ def runTest( file, requests, **params ): result['uri'],wanted['uri']) print ' '+msg failures.append(msg) + if result['fieldTypes'] != wanted['fieldTypes']: + msg = "Layer field types ({0}) doesn't match expected ({1})".format( + result['fieldTypes'],wanted['fieldTypes']) + failures.apend wanted_data = wanted['data'] for id in sorted(wanted_data.keys()): wrec = wanted_data[id] @@ -614,6 +624,34 @@ class TestQgsDelimitedTextProvider(TestCase): ] runTest(filename,requests,**params) + def test_034_csvt_file(self): + # CSVT field types + filename='testcsvt.csv' + params={'geomType': 'none', 'type': 'csv'} + requests=None + runTest(filename,requests,**params) + + def test_035_csvt_file2(self): + # CSV field types 2 + filename='testcsvt2.txt' + params={'geomType': 'none', 'type': 'csv', 'delimiter': '|'} + requests=None + runTest(filename,requests,**params) + + def test_036_csvt_file_invalid_types(self): + # CSV field types invalid string format + filename='testcsvt3.csv' + params={'geomType': 'none', 'type': 'csv'} + requests=None + runTest(filename,requests,**params) + + def test_037_csvt_file_invalid_file(self): + # CSV field types invalid file + filename='testcsvt4.csv' + params={'geomType': 'none', 'type': 'csv'} + requests=None + runTest(filename,requests,**params) + if __name__ == '__main__': unittest.main() diff --git a/tests/src/python/test_qgsdelimitedtextprovider_wanted.py b/tests/src/python/test_qgsdelimitedtextprovider_wanted.py index 329d663c318..f0ac5e174ed 100644 --- a/tests/src/python/test_qgsdelimitedtextprovider_wanted.py +++ b/tests/src/python/test_qgsdelimitedtextprovider_wanted.py @@ -2,6 +2,7 @@ def test_002_load_csv_file(): wanted={} wanted['uri']=u'file://test.csv?geomType=none&type=csv' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -66,6 +67,7 @@ def test_002_load_csv_file(): def test_003_field_naming(): wanted={} wanted['uri']=u'file://testfields.csv?geomType=none&type=csv' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -92,6 +94,7 @@ def test_003_field_naming(): def test_004_max_fields(): wanted={} wanted['uri']=u'file://testfields.csv?geomType=none&maxFields=7&type=csv' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -113,6 +116,7 @@ def test_004_max_fields(): def test_005_load_whitespace(): wanted={} wanted['uri']=u'file://test.space?geomType=none&type=whitespace' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -183,6 +187,7 @@ def test_005_load_whitespace(): def test_006_quote_escape(): wanted={} wanted['uri']=u'file://test.pipe?geomType=none"e="&delimiter=|&escape=\\' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -273,6 +278,7 @@ def test_006_quote_escape(): def test_007_multiple_quote(): wanted={} wanted['uri']=u'file://test.quote?geomType=none"e=\'"&type=csv&escape="\'' + wanted['fieldTypes']=['integer', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -337,6 +343,7 @@ def test_007_multiple_quote(): def test_008_badly_formed_quotes(): wanted={} wanted['uri']=u'file://test.badquote?geomType=none"e="&type=csv&escape="' + wanted['fieldTypes']=['integer', 'text', 'text', 'text'] wanted['data']={ 4L: { 'id': u'3', @@ -360,6 +367,7 @@ def test_008_badly_formed_quotes(): def test_009_skip_lines(): wanted={} wanted['uri']=u'file://test2.csv?geomType=none&skipLines=2&type=csv&useHeader=no' + wanted['fieldTypes']=['integer', 'text', 'text'] wanted['data']={ 3L: { 'id': u'3', @@ -379,6 +387,7 @@ def test_009_skip_lines(): def test_010_read_coordinates(): wanted={} wanted['uri']=u'file://testpt.csv?yField=geom_y&xField=geom_x&type=csv' + wanted['fieldTypes']=['integer', 'text', 'double', 'double'] wanted['data']={ 2L: { 'id': u'1', @@ -417,6 +426,7 @@ def test_010_read_coordinates(): def test_011_read_wkt(): wanted={} wanted['uri']=u'file://testwkt.csv?delimiter=|&type=csv&wktField=geom_wkt' + wanted['fieldTypes']=['integer', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -462,6 +472,7 @@ def test_011_read_wkt(): def test_012_read_wkt_point(): wanted={} wanted['uri']=u'file://testwkt.csv?geomType=point&delimiter=|&type=csv&wktField=geom_wkt' + wanted['fieldTypes']=['integer', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -507,6 +518,7 @@ def test_012_read_wkt_point(): def test_013_read_wkt_line(): wanted={} wanted['uri']=u'file://testwkt.csv?geomType=line&delimiter=|&type=csv&wktField=geom_wkt' + wanted['fieldTypes']=['integer', 'text'] wanted['data']={ 4L: { 'id': u'3', @@ -552,6 +564,7 @@ def test_013_read_wkt_line(): def test_014_read_wkt_polygon(): wanted={} wanted['uri']=u'file://testwkt.csv?geomType=polygon&delimiter=|&type=csv&wktField=geom_wkt' + wanted['fieldTypes']=['integer', 'text'] wanted['data']={ 6L: { 'id': u'5', @@ -579,6 +592,7 @@ def test_014_read_wkt_polygon(): def test_015_read_dms_xy(): wanted={} wanted['uri']=u'file://testdms.csv?yField=lat&xField=lon&type=csv&xyDms=yes' + wanted['fieldTypes']=['integer', 'text', 'text', 'text'] wanted['data']={ 3L: { 'id': u'1', @@ -749,6 +763,7 @@ def test_015_read_dms_xy(): def test_016_decimal_point(): wanted={} wanted['uri']=u'file://testdp.csv?yField=geom_y&xField=geom_x&type=csv&delimiter=;&decimalPoint=,' + wanted['fieldTypes']=['integer', 'text', 'double', 'double', 'double', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -764,8 +779,8 @@ def test_016_decimal_point(): 'id': u'2', 'description': u'Comma as decimal point 2', 'geom_x': u'12', - 'geom_y': u'25.003', - 'other': u'-38.55', + 'geom_y': u'25', + 'other': u'-38', 'text field': u'Plain text field', '#fid': 3L, '#geometry': 'POINT(12.0 25.003)', @@ -779,6 +794,7 @@ def test_016_decimal_point(): def test_017_regular_expression_1(): wanted={} wanted['uri']=u'file://testre.txt?geomType=none&trimFields=Y&delimiter=RE(?:GEXP)?&type=regexp' + wanted['fieldTypes']=['integer', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -805,6 +821,7 @@ def test_017_regular_expression_1(): def test_018_regular_expression_2(): wanted={} wanted['uri']=u'file://testre.txt?geomType=none&trimFields=Y&delimiter=(RE)(GEXP)?&type=regexp' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -843,6 +860,7 @@ def test_018_regular_expression_2(): def test_019_regular_expression_3(): wanted={} wanted['uri']=u'file://testre2.txt?geomType=none&trimFields=Y&delimiter=^(.{5})(.{30})(.{5,})&type=regexp' + wanted['fieldTypes']=['integer', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -871,6 +889,7 @@ def test_019_regular_expression_3(): def test_020_regular_expression_4(): wanted={} wanted['uri']=u'file://testre3.txt?geomType=none&delimiter=x?&type=regexp' + wanted['fieldTypes']=['text', 'text', 'text', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'f', @@ -894,6 +913,7 @@ def test_020_regular_expression_4(): def test_021_regular_expression_5(): wanted={} wanted['uri']=u'file://testre3.txt?geomType=none&delimiter=\\b&type=regexp' + wanted['fieldTypes']=['text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'fi', @@ -913,6 +933,7 @@ def test_021_regular_expression_5(): def test_022_utf8_encoded_file(): wanted={} wanted['uri']=u'file://testutf8.csv?geomType=none&delimiter=|&type=csv&encoding=utf-8' + wanted['fieldTypes']=['integer', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -930,6 +951,7 @@ def test_022_utf8_encoded_file(): def test_023_latin1_encoded_file(): wanted={} wanted['uri']=u'file://testlatin1.csv?geomType=none&delimiter=|&type=csv&encoding=latin1' + wanted['fieldTypes']=['integer', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -947,6 +969,7 @@ def test_023_latin1_encoded_file(): def test_024_filter_rect_xy(): wanted={} wanted['uri']=u'file://testextpt.txt?yField=y&delimiter=|&type=csv&xField=x' + wanted['fieldTypes']=['integer', 'text', 'integer', 'integer'] wanted['data']={ 2L: { 'id': u'1', @@ -990,6 +1013,7 @@ def test_024_filter_rect_xy(): def test_025_filter_rect_wkt(): wanted={} wanted['uri']=u'file://testextw.txt?delimiter=|&type=csv&wktField=wkt' + wanted['fieldTypes']=['integer', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -1049,6 +1073,7 @@ def test_025_filter_rect_wkt(): def test_026_filter_fid(): wanted={} wanted['uri']=u'file://test.csv?geomType=none&type=csv' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text'] wanted['data']={ 3L: { 'id': u'2', @@ -1087,6 +1112,7 @@ def test_026_filter_fid(): def test_027_filter_attributes(): wanted={} wanted['uri']=u'file://test.csv?geomType=none&type=csv' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'', @@ -1196,6 +1222,7 @@ def test_027_filter_attributes(): def test_028_substring_test(): wanted={} wanted['uri']=u'file://test.csv?geomType=none&type=csv&subset=id%20%25%202%20%3D%201' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -1233,6 +1260,7 @@ def test_028_substring_test(): def test_029_file_watcher(): wanted={} wanted['uri']=u'file://file?geomType=none&type=csv&watchFile=yes' + wanted['fieldTypes']=['integer', 'text'] wanted['data']={ 3L: { 'id': u'2', @@ -1355,6 +1383,7 @@ def test_029_file_watcher(): def test_030_filter_rect_xy_spatial_index(): wanted={} wanted['uri']=u'file://testextpt.txt?spatialIndex=Y&yField=y&delimiter=|&type=csv&xField=x' + wanted['fieldTypes']=['integer', 'text', 'integer', 'integer'] wanted['data']={ 2L: { 'id': u'1', @@ -1542,6 +1571,7 @@ def test_030_filter_rect_xy_spatial_index(): def test_031_filter_rect_wkt_spatial_index(): wanted={} wanted['uri']=u'file://testextw.txt?spatialIndex=Y&delimiter=|&type=csv&wktField=wkt' + wanted['fieldTypes']=['integer', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -1673,6 +1703,7 @@ def test_031_filter_rect_wkt_spatial_index(): def test_032_filter_rect_wkt_create_spatial_index(): wanted={} wanted['uri']=u'file://testextw.txt?delimiter=|&type=csv&wktField=wkt' + wanted['fieldTypes']=['integer', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -1866,9 +1897,11 @@ def test_032_filter_rect_wkt_create_spatial_index(): ] return wanted + def test_033_reset_subset_string(): wanted={} wanted['uri']=u'file://test.csv?geomType=none&type=csv' + wanted['fieldTypes']=['integer', 'text', 'text', 'text', 'text'] wanted['data']={ 2L: { 'id': u'1', @@ -2028,3 +2061,137 @@ def test_033_reset_subset_string(): ] return wanted + +def test_034_csvt_file(): + wanted={} + wanted['uri']=u'file://testcsvt.csv?geomType=none&type=csv' + wanted['fieldTypes']=['integer', 'text', 'integer', 'double', 'text', 'text', 'text'] + wanted['data']={ + 2L: { + 'id': u'1', + 'description': u'Test csvt 1', + 'f1': u'1', + 'f2': u'1', + 'f3': u'01', + 'f4': u'text', + 'f5': u'times', + '#fid': 2L, + '#geometry': 'None', + }, + 3L: { + 'id': u'2', + 'description': u'Test csvt 2', + 'f1': u'3', + 'f2': u'1', + 'f3': u'99', + 'f4': u'23.5', + 'f5': u'80', + '#fid': 3L, + '#geometry': 'None', + }, + } + wanted['log']=[ + ] + return wanted + + +def test_035_csvt_file2(): + wanted={} + wanted['uri']=u'file://testcsvt2.txt?geomType=none&delimiter=|&type=csv' + wanted['fieldTypes']=['integer', 'text', 'integer', 'double', 'integer', 'text', 'integer'] + wanted['data']={ + 2L: { + 'id': u'1', + 'description': u'Test csvt 1', + 'f1': u'1', + 'f2': u'1', + 'f3': u'1', + 'f4': u'text', + 'f5': u'0', + '#fid': 2L, + '#geometry': 'None', + }, + 3L: { + 'id': u'2', + 'description': u'Test csvt 2', + 'f1': u'3', + 'f2': u'1', + 'f3': u'99', + 'f4': u'23.5', + 'f5': u'80', + '#fid': 3L, + '#geometry': 'None', + }, + } + wanted['log']=[ + ] + return wanted + + +def test_036_csvt_file_invalid_types(): + wanted={} + wanted['uri']=u'file://testcsvt3.csv?geomType=none&type=csv' + wanted['fieldTypes']=['integer', 'text', 'integer', 'double', 'integer', 'text', 'text'] + wanted['data']={ + 2L: { + 'id': u'1', + 'description': u'Test csvt 1', + 'f1': u'1', + 'f2': u'1', + 'f3': u'1', + 'f4': u'text', + 'f5': u'times', + '#fid': 2L, + '#geometry': 'None', + }, + 3L: { + 'id': u'2', + 'description': u'Test csvt 2', + 'f1': u'3', + 'f2': u'1', + 'f3': u'99', + 'f4': u'23.5', + 'f5': u'80', + '#fid': 3L, + '#geometry': 'None', + }, + } + wanted['log']=[ + u'Errors in file file', + u'File type string in testcsvt3.csvt is not correctly formatted', + ] + return wanted + + +def test_037_csvt_file_invalid_file(): + wanted={} + wanted['uri']=u'file://testcsvt4.csv?geomType=none&type=csv' + wanted['fieldTypes']=['integer', 'text', 'integer', 'double', 'integer', 'text', 'text'] + wanted['data']={ + 2L: { + 'id': u'1', + 'description': u'Test csvt 1', + 'f1': u'1', + 'f2': u'1', + 'f3': u'1', + 'f4': u'text', + 'f5': u'times', + '#fid': 2L, + '#geometry': 'None', + }, + 3L: { + 'id': u'2', + 'description': u'Test csvt 2', + 'f1': u'3', + 'f2': u'1', + 'f3': u'99', + 'f4': u'23.5', + 'f5': u'80', + '#fid': 3L, + '#geometry': 'None', + }, + } + wanted['log']=[ + ] + return wanted + diff --git a/tests/testdata/delimitedtext/testcsvt.csv b/tests/testdata/delimitedtext/testcsvt.csv new file mode 100644 index 00000000000..f5298e4b616 --- /dev/null +++ b/tests/testdata/delimitedtext/testcsvt.csv @@ -0,0 +1,4 @@ +id,description,f1,f2,f3,f4,f5 +1,Test csvt 1,1,1.2,01,text,times +2,Test csvt 2,3,1.5,99,23.5,80 + diff --git a/tests/testdata/delimitedtext/testcsvt.csvt b/tests/testdata/delimitedtext/testcsvt.csvt new file mode 100644 index 00000000000..d075123564f --- /dev/null +++ b/tests/testdata/delimitedtext/testcsvt.csvt @@ -0,0 +1 @@ +integer,string,integer,real,string,string,datetime diff --git a/tests/testdata/delimitedtext/testcsvt2.txt b/tests/testdata/delimitedtext/testcsvt2.txt new file mode 100644 index 00000000000..2480b52016c --- /dev/null +++ b/tests/testdata/delimitedtext/testcsvt2.txt @@ -0,0 +1,4 @@ +id|description|f1|f2|f3|f4|f5 +1|Test csvt 1|1|1.2|01|text|times +2|Test csvt 2|3|1.5|99|23.5|80 + diff --git a/tests/testdata/delimitedtext/testcsvt2.txtt b/tests/testdata/delimitedtext/testcsvt2.txtt new file mode 100644 index 00000000000..87804b41223 --- /dev/null +++ b/tests/testdata/delimitedtext/testcsvt2.txtt @@ -0,0 +1 @@ +"integer(5)","string(30)","integer(2)","real","integer","string(20)","integer" diff --git a/tests/testdata/delimitedtext/testcsvt3.csv b/tests/testdata/delimitedtext/testcsvt3.csv new file mode 100644 index 00000000000..f5298e4b616 --- /dev/null +++ b/tests/testdata/delimitedtext/testcsvt3.csv @@ -0,0 +1,4 @@ +id,description,f1,f2,f3,f4,f5 +1,Test csvt 1,1,1.2,01,text,times +2,Test csvt 2,3,1.5,99,23.5,80 + diff --git a/tests/testdata/delimitedtext/testcsvt3.csvt b/tests/testdata/delimitedtext/testcsvt3.csvt new file mode 100644 index 00000000000..6a49646a24f --- /dev/null +++ b/tests/testdata/delimitedtext/testcsvt3.csvt @@ -0,0 +1,5 @@ +integer,string,integer,gotitwrong,string,string,datetime + + + + diff --git a/tests/testdata/delimitedtext/testcsvt4.csv b/tests/testdata/delimitedtext/testcsvt4.csv new file mode 100644 index 00000000000..f5298e4b616 --- /dev/null +++ b/tests/testdata/delimitedtext/testcsvt4.csv @@ -0,0 +1,4 @@ +id,description,f1,f2,f3,f4,f5 +1,Test csvt 1,1,1.2,01,text,times +2,Test csvt 2,3,1.5,99,23.5,80 + diff --git a/tests/testdata/delimitedtext/testcsvt4.csvt b/tests/testdata/delimitedtext/testcsvt4.csvt new file mode 100644 index 00000000000..efdd51d86ee --- /dev/null +++ b/tests/testdata/delimitedtext/testcsvt4.csvt @@ -0,0 +1,2 @@ +This is nothing like a CSVT file +Just happened to be given the same name