Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE] add support for OGR creation options to and improve error h…
…andling of file writer

git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@14166 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
jef committed Aug 28, 2010
1 parent b4462a8 commit e752db7
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 35 deletions.
13 changes: 10 additions & 3 deletions python/core/qgsvectorfilewriter.sip
Expand Up @@ -29,7 +29,10 @@ public:
const QString& shapefileName,
const QString& fileEncoding,
const QgsCoordinateReferenceSystem*,
bool onlySelected = FALSE);
bool onlySelected = FALSE,
QString *errorMessage = 0,
const QStringList &datasourceOptions = QStringList(),
const QStringList &layerOptions = QStringList() );

/** Write contents of vector layer to an (OGR supported) vector formt
@note: this method was added in version 1.5*/
Expand All @@ -39,15 +42,19 @@ public:
const QgsCoordinateReferenceSystem *destCRS,
const QString& driverName = "ESRI Shapefile",
bool onlySelected = FALSE,
QString *errorMessage = 0 );
QString *errorMessage = 0,
const QStringList &datasourceOptions = QStringList(),
const QStringList &layerOptions = QStringList() );

/** create shapefile and initialize it */
QgsVectorFileWriter(const QString& vectorFileName,
const QString& fileEncoding,
const QMap<int, QgsField>& fields,
QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem* srs,
const QString& driverName = "ESRI Shapefile" );
const QString& driverName = "ESRI Shapefile",
const QStringList &datasourceOptions = QStringList(),
const QStringList &layerOptions = QStringList() );

/**Returns map with format filter string as key and OGR format key as value*/
static QMap< QString, QString> supportedFiltersAndFormats();
Expand Down
13 changes: 13 additions & 0 deletions resources/context_help/QgsVectorLayerSaveAsDialog-en_US
@@ -0,0 +1,13 @@
<h3>Save vector layer as...</h3>

<p>This dialog allows you to save vector data in various formats using GDAL/OGR.

<ul>
<li>From the <label>Format</label> list you can select the destination format (as advertised by OGR).
<li>At <label>Save as</label> you can enter a destination files name or select one using the <label>Browse</label> button.
<li>In the <label>Encoding</label> list you can define in which encoding the data should be saved.
<li>Using the <label>CRS</label> you can select a CRS into which the data about to be saved should be reprojected.
<li>OGR also has various options for the different formats it supports. Use the <label>datasource</label> creation field to set the datasource options and the <label>layer</label> creation options. Enter one options per line (e.g. <code>SPATIALITE=yes</code> in the <label>datasource</label> to create a spatialite database using the SQLite driver).
</ul>

See <a href="http://gdal.org/ogr/ogr_formats.html">OGR Vector formats</a> for a list of supported formats and the available options.
10 changes: 10 additions & 0 deletions src/app/ogr/qgsvectorlayersaveasdialog.cpp
Expand Up @@ -137,3 +137,13 @@ long QgsVectorLayerSaveAsDialog::crs() const
{
return mCRS;
}

QStringList QgsVectorLayerSaveAsDialog::datasourceOptions() const
{
return mOgrDatasourceOptions->toPlainText().split( "\n" );
}

QStringList QgsVectorLayerSaveAsDialog::layerOptions() const
{
return mOgrLayerOptions->toPlainText().split( "\n" );
}
2 changes: 2 additions & 0 deletions src/app/ogr/qgsvectorlayersaveasdialog.h
Expand Up @@ -37,6 +37,8 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
QString format() const;
QString encoding() const;
QString filename() const;
QStringList datasourceOptions() const;
QStringList layerOptions() const;
long crs() const;

private slots:
Expand Down
11 changes: 9 additions & 2 deletions src/app/qgisapp.cpp
Expand Up @@ -4000,7 +4000,11 @@ void QgisApp::saveAsVectorFileGeneral( bool saveOnlySelection )

QgsVectorFileWriter::WriterError error;
QString errorMessage;
error = QgsVectorFileWriter::writeAsVectorFormat( vlayer, vectorFilename, encoding, &destCRS, format, saveOnlySelection, &errorMessage );
error = QgsVectorFileWriter::writeAsVectorFormat(
vlayer, vectorFilename, encoding, &destCRS, format,
saveOnlySelection,
&errorMessage,
dialog->datasourceOptions(), dialog->layerOptions() );

QApplication::restoreOverrideCursor();

Expand All @@ -4010,7 +4014,10 @@ void QgisApp::saveAsVectorFileGeneral( bool saveOnlySelection )
}
else
{
QMessageBox::warning( 0, tr( "Save error" ), tr( "Export to vector file failed.\nError: %1" ).arg( errorMessage ) );
QgsMessageViewer *m = new QgsMessageViewer( 0 );
m->setWindowTitle( tr( "Save error" ) );
m->setMessageAsPlainText( tr( "Export to vector file failed.\nError: %1" ).arg( errorMessage ) );
m->exec();
}
}

Expand Down
128 changes: 109 additions & 19 deletions src/core/qgsvectorfilewriter.cpp
Expand Up @@ -40,6 +40,7 @@
#include <ogr_api.h>
#include <ogr_srs_api.h>
#include <cpl_error.h>
#include <cpl_conv.h>


QgsVectorFileWriter::QgsVectorFileWriter(
Expand All @@ -48,7 +49,10 @@ QgsVectorFileWriter::QgsVectorFileWriter(
const QgsFieldMap& fields,
QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem* srs,
const QString& driverName )
const QString& driverName,
const QStringList &datasourceOptions,
const QStringList &layerOptions
)
: mDS( NULL )
, mLayer( NULL )
, mGeom( NULL )
Expand Down Expand Up @@ -116,8 +120,26 @@ QgsVectorFileWriter::QgsVectorFileWriter(
QFile::remove( vectorFileName );
}

char **options = NULL;
if ( !datasourceOptions.isEmpty() )
{
options = new char *[ datasourceOptions.size()+1 ];
for ( int i = 0; i < datasourceOptions.size(); i++ )
{
options[i] = CPLStrdup( datasourceOptions[i].toLocal8Bit().data() );
}
options[ datasourceOptions.size()] = NULL;
}

// create the data source
mDS = OGR_Dr_CreateDataSource( poDriver, vectorFileName.toLocal8Bit().data(), NULL );
mDS = OGR_Dr_CreateDataSource( poDriver, vectorFileName.toLocal8Bit().data(), options );

if ( options )
{
for ( int i = 0; i < datasourceOptions.size(); i++ )
CPLFree( options[i] );
}

if ( mDS == NULL )
{
mError = ErrCreateDataSource;
Expand Down Expand Up @@ -155,7 +177,24 @@ QgsVectorFileWriter::QgsVectorFileWriter(
// datasource created, now create the output layer
QString layerName = QFileInfo( vectorFileName ).baseName();
OGRwkbGeometryType wkbType = static_cast<OGRwkbGeometryType>( geometryType );
mLayer = OGR_DS_CreateLayer( mDS, QFile::encodeName( layerName ).data(), ogrRef, wkbType, NULL );

if ( !layerOptions.isEmpty() )
{
options = new char *[ layerOptions.size()+1 ];
for ( int i = 0; i < layerOptions.size(); i++ )
{
options[i] = CPLStrdup( layerOptions[i].toLocal8Bit().data() );
}
options[ layerOptions.size()] = NULL;
}

mLayer = OGR_DS_CreateLayer( mDS, QFile::encodeName( layerName ).data(), ogrRef, wkbType, options );

if ( options )
{
for ( int i = 0; i < layerOptions.size(); i++ )
CPLFree( options[i] );
}

if ( srs )
{
Expand Down Expand Up @@ -302,9 +341,6 @@ QString QgsVectorFileWriter::errorMessage()

bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
{
if ( hasError() != NoError )
return false;

QgsAttributeMap::const_iterator it;

// create the feature
Expand Down Expand Up @@ -342,10 +378,13 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
OGR_F_SetFieldString( poFeature, ogrField, mCodec->fromUnicode( attrValue.toString() ).data() );
break;
default:
QgsDebugMsg( "Invalid variant type for field " + QString( fldIt.value().name() ) + " "
+ QString::number( ogrField ) + ": Received Type " + QMetaType::typeName ( attrValue.type() )
+ " : With Value : " + attrValue.toString()
);
mErrorMessage = QObject::tr( "Invalid variant type for field %1[%2]: received %3 with type %4" )
.arg( fldIt.value().name() )
.arg( ogrField )
.arg( QMetaType::typeName( attrValue.type() ) )
.arg( attrValue.toString() );
QgsDebugMsg( mErrorMessage );
mError = ErrFeatureWriteFailed;
return false;
}
}
Expand All @@ -355,6 +394,8 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
if ( !geom )
{
QgsDebugMsg( "invalid geometry" );
mErrorMessage = QObject::tr( "Invalid feature geometry" );
mError = ErrFeatureWriteFailed;
OGR_F_Destroy( poFeature );
return false;
}
Expand All @@ -375,7 +416,10 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
OGRErr err = OGR_G_ImportFromWkb( mGeom2, geom->asWkb(), geom->wkbSize() );
if ( err != OGRERR_NONE )
{
QgsDebugMsg( "Failed to import geometry from WKB: " + QString::number( err ) );
QgsDebugMsg( QString( "Failed to import geometry from WKB: %1 (OGR error: %2)" ).arg( err ).arg( CPLGetLastErrorMsg() ) );
mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
.arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
mError = ErrFeatureWriteFailed;
OGR_F_Destroy( poFeature );
return false;
}
Expand All @@ -388,7 +432,10 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
OGRErr err = OGR_G_ImportFromWkb( mGeom, geom->asWkb(), geom->wkbSize() );
if ( err != OGRERR_NONE )
{
QgsDebugMsg( "Failed to import geometry from WKB: " + QString::number( err ) );
QgsDebugMsg( QString( "Failed to import geometry from WKB: %1 (OGR error: %2)" ).arg( err ).arg( CPLGetLastErrorMsg() ) );
mErrorMessage = QObject::tr( "Feature geometry not imported (OGR error: %1)" )
.arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
mError = ErrFeatureWriteFailed;
OGR_F_Destroy( poFeature );
return false;
}
Expand All @@ -400,7 +447,10 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
// put the created feature to layer
if ( OGR_L_CreateFeature( mLayer, poFeature ) != OGRERR_NONE )
{
QgsDebugMsg( "Failed to create feature in shapefile" );
mErrorMessage = QObject::tr( "Feature creation error (OGR error: %1)" ).arg( QString::fromUtf8( CPLGetLastErrorMsg() ) );
mError = ErrFeatureWriteFailed;

QgsDebugMsg( mErrorMessage );
OGR_F_Destroy( poFeature );
return false;
}
Expand Down Expand Up @@ -432,9 +482,11 @@ QgsVectorFileWriter::writeAsShapefile( QgsVectorLayer* layer,
const QString& fileEncoding,
const QgsCoordinateReferenceSystem* destCRS,
bool onlySelected,
QString *errorMessage )
QString *errorMessage,
const QStringList &datasourceOptions,
const QStringList &layerOptions )
{
return writeAsVectorFormat( layer, shapefileName, fileEncoding, destCRS, "ESRI Shapefile", onlySelected, errorMessage );
return writeAsVectorFormat( layer, shapefileName, fileEncoding, destCRS, "ESRI Shapefile", onlySelected, errorMessage, datasourceOptions, layerOptions );
}

QgsVectorFileWriter::WriterError
Expand All @@ -444,7 +496,9 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
const QgsCoordinateReferenceSystem *destCRS,
const QString& driverName,
bool onlySelected,
QString *errorMessage )
QString *errorMessage,
const QStringList &datasourceOptions,
const QStringList &layerOptions )
{
const QgsCoordinateReferenceSystem* outputCRS;
QgsCoordinateTransform* ct = 0;
Expand All @@ -462,7 +516,7 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
outputCRS = &layer->srs();
}
QgsVectorFileWriter* writer =
new QgsVectorFileWriter( fileName, fileEncoding, layer->pendingFields(), layer->wkbType(), outputCRS, driverName );
new QgsVectorFileWriter( fileName, fileEncoding, layer->pendingFields(), layer->wkbType(), outputCRS, driverName, datasourceOptions, layerOptions );

// check whether file creation was successful
WriterError err = writer->hasError();
Expand All @@ -474,6 +528,11 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
return err;
}

if ( errorMessage )
{
errorMessage->clear();
}

QgsAttributeList allAttr = layer->pendingAllAttributesList();
QgsFeature fet;

Expand All @@ -493,6 +552,8 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
shallTransform = false;
}

int n = 0, errors = 0;

// write all features
while ( layer->nextFeature( fet ) )
{
Expand All @@ -519,7 +580,31 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
return ErrProjection;
}
}
writer->addFeature( fet );
if ( !writer->addFeature( fet ) )
{
WriterError err = writer->hasError();
if ( err != NoError && errorMessage )
{
if ( errorMessage->isEmpty() )
{
*errorMessage = QObject::tr( "Feature write errors:" );
}
*errorMessage += "\n" + writer->errorMessage();
}
errors++;

if ( errors > 1000 )
{
if ( errorMessage )
{
*errorMessage += QObject::tr( "Stopping after %1 errors" ).arg( errors );
}

n = -1;
break;
}
}
n++;
}

delete writer;
Expand All @@ -529,7 +614,12 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
delete ct;
}

return NoError;
if ( errors > 0 && errorMessage && n > 0 )
{
*errorMessage += QObject::tr( "\nOnly %1 of %2 features written." ).arg( n - errors ).arg( n );
}

return errors == 0 ? NoError : ErrFeatureWriteFailed;
}


Expand Down
18 changes: 14 additions & 4 deletions src/core/qgsvectorfilewriter.h
Expand Up @@ -52,7 +52,8 @@ class CORE_EXPORT QgsVectorFileWriter
ErrCreateLayer,
ErrAttributeTypeUnsupported,
ErrAttributeCreationFailed,
ErrProjection // added in 1.5
ErrProjection, // added in 1.5
ErrFeatureWriteFailed, // added in 1.6
};

/** Write contents of vector layer to a shapefile
Expand All @@ -62,7 +63,10 @@ class CORE_EXPORT QgsVectorFileWriter
const QString& fileEncoding,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected = false,
QString *errorMessage = 0 );
QString *errorMessage = 0,
const QStringList &datasourceOptions = QStringList(), // added in 1.6
const QStringList &layerOptions = QStringList() // added in 1.6
);

/** Write contents of vector layer to an (OGR supported) vector formt
@note: this method was added in version 1.5*/
Expand All @@ -72,15 +76,21 @@ class CORE_EXPORT QgsVectorFileWriter
const QgsCoordinateReferenceSystem *destCRS,
const QString& driverName = "ESRI Shapefile",
bool onlySelected = false,
QString *errorMessage = 0 );
QString *errorMessage = 0,
const QStringList &datasourceOptions = QStringList(), // added in 1.6
const QStringList &layerOptions = QStringList() // added in 1.6
);

/** create shapefile and initialize it */
QgsVectorFileWriter( const QString& vectorFileName,
const QString& fileEncoding,
const QgsFieldMap& fields,
QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem* srs,
const QString& driverName = "ESRI Shapefile" );
const QString& driverName = "ESRI Shapefile",
const QStringList &datasourceOptions = QStringList(), // added in 1.6
const QStringList &layerOptions = QStringList() // added in 1.6
);

/**Returns map with format filter string as key and OGR format key as value*/
static QMap< QString, QString> supportedFiltersAndFormats();
Expand Down

0 comments on commit e752db7

Please sign in to comment.