Skip to content

Commit

Permalink
QgsVectorFileWriter: add capability to export displayed values of fie…
Browse files Browse the repository at this point in the history
…lds (typically coming from edit widgets) instead of their raw values
  • Loading branch information
rouault committed May 14, 2016
1 parent b38a16f commit 038b3b7
Show file tree
Hide file tree
Showing 4 changed files with 252 additions and 23 deletions.
39 changes: 33 additions & 6 deletions python/core/qgsvectorfilewriter.sip
@@ -1,11 +1,8 @@
/** \ingroup core
* A convenience class for writing vector files to disk.
There are two possibilities how to use this class:
1. static call to QgsVectorFileWriter::writeAsShapefile(...) which saves the whole vector layer
1. static call to QgsVectorFileWriter::writeAsVectorFormat(...) which saves the whole vector layer
2. create an instance of the class and issue calls to addFeature(...)

Currently supports only writing to shapefiles, but shouldn't be a problem to add capability
to support other OGR-writable formats.
*/
class QgsVectorFileWriter
{
Expand Down Expand Up @@ -107,6 +104,32 @@ class QgsVectorFileWriter
SymbolLayerSymbology //Exports one feature per symbol layer (considering symbol levels)
};

/** Interface to convert raw field values to their user-friendly value.
* @note Added in QGIS 2.16
*/
class FieldValueConverter
{
public:
/** Constructor */
FieldValueConverter();

/** Destructor */
virtual ~FieldValueConverter();

/** Return a possibly modified field definition. Default implementation will return provided field unmodified.
* @param field original field definition
* @return possibly modified field definition
*/
virtual QgsField fieldDefinition( const QgsField& field );

/** Convert the provided value, for field fieldIdxInLayer. Default implementation will return provided value unmodified.
* @param fieldIdxInLayer field index
* @param value original raw value
* @return possibly modified value.
*/
virtual QVariant convert( int fieldIdxInLayer, const QVariant& value );
};

/** Write contents of vector layer to an (OGR supported) vector formt
* @param layer layer to write
* @param fileName file name to write to
Expand All @@ -127,6 +150,7 @@ class QgsVectorFileWriter
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
* @param includeZ set to true to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
* @param fieldValueConverter field value converter (added in QGIS 2.16)
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
Expand All @@ -145,7 +169,8 @@ class QgsVectorFileWriter
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false,
QgsAttributeList attributes = QgsAttributeList()
QgsAttributeList attributes = QgsAttributeList(),
FieldValueConverter* fieldValueConverter = nullptr
);

/** Writes a layer out to a vector file.
Expand All @@ -168,6 +193,7 @@ class QgsVectorFileWriter
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
* @param includeZ set to true to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
* @param fieldValueConverter field value converter (added in QGIS 2.16)
* @note added in 2.2
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
Expand All @@ -187,7 +213,8 @@ class QgsVectorFileWriter
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false,
QgsAttributeList attributes = QgsAttributeList()
QgsAttributeList attributes = QgsAttributeList(),
FieldValueConverter* fieldValueConverter = nullptr
);

/** Create a new vector file writer */
Expand Down
98 changes: 88 additions & 10 deletions src/core/qgsvectorfilewriter.cpp
Expand Up @@ -52,6 +52,23 @@
#define TO8F(x) QFile::encodeName( x ).constData()
#endif

QgsVectorFileWriter::FieldValueConverter::FieldValueConverter()
{
}

QgsVectorFileWriter::FieldValueConverter::~FieldValueConverter()
{
}

QgsField QgsVectorFileWriter::FieldValueConverter::fieldDefinition( const QgsField& field )
{
return field;
}

QVariant QgsVectorFileWriter::FieldValueConverter::convert( int /*fieldIdxInLayer*/, const QVariant& value )
{
return value;
}

QgsVectorFileWriter::QgsVectorFileWriter(
const QString &theVectorFileName,
Expand All @@ -74,8 +91,10 @@ QgsVectorFileWriter::QgsVectorFileWriter(
, mWkbType( QGis::fromOldWkbType( geometryType ) )
, mSymbologyExport( symbologyExport )
, mSymbologyScaleDenominator( 1.0 )
, mFieldValueConverter( nullptr )
{
init( theVectorFileName, theFileEncoding, fields, QGis::fromOldWkbType( geometryType ), srs, driverName, datasourceOptions, layerOptions, newFilename );
init( theVectorFileName, theFileEncoding, fields, QGis::fromOldWkbType( geometryType ),
srs, driverName, datasourceOptions, layerOptions, newFilename, nullptr );
}

QgsVectorFileWriter::QgsVectorFileWriter( const QString& vectorFileName, const QString& fileEncoding, const QgsFields& fields, QgsWKBTypes::Type geometryType, const QgsCoordinateReferenceSystem* srs, const QString& driverName, const QStringList& datasourceOptions, const QStringList& layerOptions, QString* newFilename, QgsVectorFileWriter::SymbologyExport symbologyExport )
Expand All @@ -88,11 +107,48 @@ QgsVectorFileWriter::QgsVectorFileWriter( const QString& vectorFileName, const Q
, mWkbType( geometryType )
, mSymbologyExport( symbologyExport )
, mSymbologyScaleDenominator( 1.0 )
, mFieldValueConverter( nullptr )
{
init( vectorFileName, fileEncoding, fields, geometryType, srs, driverName,
datasourceOptions, layerOptions, newFilename, nullptr );
}

QgsVectorFileWriter::QgsVectorFileWriter( const QString& vectorFileName,
const QString& fileEncoding,
const QgsFields& fields,
QgsWKBTypes::Type geometryType,
const QgsCoordinateReferenceSystem* srs,
const QString& driverName,
const QStringList& datasourceOptions,
const QStringList& layerOptions,
QString* newFilename,
QgsVectorFileWriter::SymbologyExport symbologyExport,
FieldValueConverter* fieldValueConverter )
: mDS( nullptr )
, mLayer( nullptr )
, mOgrRef( nullptr )
, mGeom( nullptr )
, mError( NoError )
, mCodec( nullptr )
, mWkbType( geometryType )
, mSymbologyExport( symbologyExport )
, mSymbologyScaleDenominator( 1.0 )
, mFieldValueConverter( nullptr )
{
init( vectorFileName, fileEncoding, fields, geometryType, srs, driverName, datasourceOptions, layerOptions, newFilename );
init( vectorFileName, fileEncoding, fields, geometryType, srs, driverName,
datasourceOptions, layerOptions, newFilename, fieldValueConverter );
}

void QgsVectorFileWriter::init( QString vectorFileName, QString fileEncoding, const QgsFields& fields, QgsWKBTypes::Type geometryType, const QgsCoordinateReferenceSystem* srs, const QString& driverName, QStringList datasourceOptions, QStringList layerOptions, QString* newFilename )
void QgsVectorFileWriter::init( QString vectorFileName,
QString fileEncoding,
const QgsFields& fields,
QgsWKBTypes::Type geometryType,
const QgsCoordinateReferenceSystem* srs,
const QString& driverName,
QStringList datasourceOptions,
QStringList layerOptions,
QString* newFilename,
FieldValueConverter* fieldValueConverter )
{
mRenderContext.setRendererScale( mSymbologyScaleDenominator );

Expand Down Expand Up @@ -350,11 +406,19 @@ void QgsVectorFileWriter::init( QString vectorFileName, QString fileEncoding, co
mAttrIdxToOgrIdx.clear();
QSet<int> existingIdxs;

mFieldValueConverter = fieldValueConverter;

for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
{
const QgsField& attrField = fields[fldIdx];
QgsField attrField = fields[fldIdx];

OGRFieldType ogrType = OFTString; //default to string

if ( fieldValueConverter )
{
attrField = fieldValueConverter->fieldDefinition( fields[fldIdx] );
}

int ogrWidth = attrField.length();
int ogrPrecision = attrField.precision();
if ( ogrPrecision > 0 )
Expand Down Expand Up @@ -1806,11 +1870,16 @@ OGRFeatureH QgsVectorFileWriter::createFeature( QgsFeature& feature )
int fldIdx = it.key();
int ogrField = it.value();

const QVariant& attrValue = feature.attribute( fldIdx );
QVariant attrValue = feature.attribute( fldIdx );

if ( !attrValue.isValid() || attrValue.isNull() )
continue;

if ( mFieldValueConverter )
{
attrValue = mFieldValueConverter->convert( fldIdx, attrValue );
}

switch ( attrValue.type() )
{
#if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 2000000
Expand Down Expand Up @@ -2047,7 +2116,8 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
QgsWKBTypes::Type overrideGeometryType,
bool forceMulti,
bool includeZ,
QgsAttributeList attributes )
QgsAttributeList attributes,
FieldValueConverter* fieldValueConverter )
{
QgsCoordinateTransform* ct = nullptr;
if ( destCRS && layer )
Expand All @@ -2056,7 +2126,10 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
}

QgsVectorFileWriter::WriterError error = writeAsVectorFormat( layer, fileName, fileEncoding, ct, driverName, onlySelected,
errorMessage, datasourceOptions, layerOptions, skipAttributeCreation, newFilename, symbologyExport, symbologyScale, filterExtent, overrideGeometryType, forceMulti, includeZ, attributes );
errorMessage, datasourceOptions, layerOptions, skipAttributeCreation,
newFilename, symbologyExport, symbologyScale, filterExtent,
overrideGeometryType, forceMulti, includeZ, attributes,
fieldValueConverter );
delete ct;
return error;
}
Expand All @@ -2078,7 +2151,8 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
QgsWKBTypes::Type overrideGeometryType,
bool forceMulti,
bool includeZ,
QgsAttributeList attributes )
QgsAttributeList attributes,
FieldValueConverter* fieldValueConverter )
{
if ( !layer )
{
Expand Down Expand Up @@ -2183,10 +2257,12 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
}

QgsVectorFileWriter* writer =
new QgsVectorFileWriter( fileName, fileEncoding, fields, QGis::fromNewWkbType( destWkbType ), outputCRS, driverName, datasourceOptions, layerOptions, newFilename, symbologyExport );
new QgsVectorFileWriter( fileName, fileEncoding, fields, destWkbType,
outputCRS, driverName, datasourceOptions, layerOptions,
newFilename, symbologyExport,
fieldValueConverter );
writer->setSymbologyScaleDenominator( symbologyScale );


if ( newFilename )
{
QgsDebugMsg( "newFilename = " + *newFilename );
Expand Down Expand Up @@ -2261,6 +2337,8 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
}

writer->resetMap( attributes );
// Reset mFields to layer fields, and not just exported fields
writer->mFields = layer->fields();

// write all features
while ( fit.nextFeature( fet ) )
Expand Down
75 changes: 68 additions & 7 deletions src/core/qgsvectorfilewriter.h
Expand Up @@ -33,11 +33,8 @@ class QTextCodec;
/** \ingroup core
* A convenience class for writing vector files to disk.
There are two possibilities how to use this class:
1. static call to QgsVectorFileWriter::writeAsShapefile(...) which saves the whole vector layer
1. static call to QgsVectorFileWriter::writeAsVectorFormat(...) which saves the whole vector layer
2. create an instance of the class and issue calls to addFeature(...)
Currently supports only writing to shapefiles, but shouldn't be a problem to add capability
to support other OGR-writable formats.
*/
class CORE_EXPORT QgsVectorFileWriter
{
Expand Down Expand Up @@ -160,6 +157,32 @@ class CORE_EXPORT QgsVectorFileWriter
SymbolLayerSymbology //Exports one feature per symbol layer (considering symbol levels)
};

/** Interface to convert raw field values to their user-friendly value.
* @note Added in QGIS 2.16
*/
class FieldValueConverter
{
public:
/** Constructor */
FieldValueConverter();

/** Destructor */
virtual ~FieldValueConverter();

/** Return a possibly modified field definition. Default implementation will return provided field unmodified.
* @param field original field definition
* @return possibly modified field definition
*/
virtual QgsField fieldDefinition( const QgsField& field );

/** Convert the provided value, for field fieldIdxInLayer. Default implementation will return provided value unmodified.
* @param fieldIdxInLayer field index
* @param value original raw value
* @return possibly modified value.
*/
virtual QVariant convert( int fieldIdxInLayer, const QVariant& value );
};

/** Write contents of vector layer to an (OGR supported) vector formt
* @param layer layer to write
* @param fileName file name to write to
Expand All @@ -180,6 +203,7 @@ class CORE_EXPORT QgsVectorFileWriter
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
* @param includeZ set to true to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
* @param fieldValueConverter field value converter (added in QGIS 2.16)
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
Expand All @@ -198,7 +222,8 @@ class CORE_EXPORT QgsVectorFileWriter
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false,
QgsAttributeList attributes = QgsAttributeList()
QgsAttributeList attributes = QgsAttributeList(),
FieldValueConverter* fieldValueConverter = nullptr
);

/** Writes a layer out to a vector file.
Expand All @@ -221,6 +246,7 @@ class CORE_EXPORT QgsVectorFileWriter
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
* @param includeZ set to true to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
* @param fieldValueConverter field value converter (added in QGIS 2.16)
* @note added in 2.2
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
Expand All @@ -240,7 +266,8 @@ class CORE_EXPORT QgsVectorFileWriter
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false,
QgsAttributeList attributes = QgsAttributeList()
QgsAttributeList attributes = QgsAttributeList(),
FieldValueConverter* fieldValueConverter = nullptr
);

/** Create a new vector file writer */
Expand Down Expand Up @@ -358,8 +385,42 @@ class CORE_EXPORT QgsVectorFileWriter

QString mOgrDriverName;

/** Field value converter */
FieldValueConverter* mFieldValueConverter;

private:
void init( QString vectorFileName, QString fileEncoding, const QgsFields& fields, QgsWKBTypes::Type geometryType, const QgsCoordinateReferenceSystem* srs, const QString& driverName, QStringList datasourceOptions, QStringList layerOptions, QString* newFilename );

/** Create a new vector file writer.
* @param vectorFileName file name to write to
* @param fileEncoding encoding to use
* @param fields fields to write
* @param geometryType geometry type of output file
* @param srs spatial reference system of output file
* @param driverName OGR driver to use
* @param datasourceOptions list of OGR data source creation options
* @param layerOptions list of OGR layer creation options
* @param newFilename potentially modified file name (output parameter)
* @param symbologyExport symbology to export
* @param fieldValueConverter field value converter (added in QGIS 2.16)
*/
QgsVectorFileWriter( const QString& vectorFileName,
const QString& fileEncoding,
const QgsFields& fields,
QgsWKBTypes::Type geometryType,
const QgsCoordinateReferenceSystem* srs,
const QString& driverName,
const QStringList &datasourceOptions,
const QStringList &layerOptions,
QString *newFilename,
SymbologyExport symbologyExport,
FieldValueConverter* fieldValueConverter
);

void init( QString vectorFileName, QString fileEncoding, const QgsFields& fields,
QgsWKBTypes::Type geometryType, const QgsCoordinateReferenceSystem* srs,
const QString& driverName, QStringList datasourceOptions,
QStringList layerOptions, QString* newFilename,
FieldValueConverter* fieldValueConverter );
void resetMap( const QgsAttributeList &attributes );

QgsRenderContext mRenderContext;
Expand Down

0 comments on commit 038b3b7

Please sign in to comment.