[FEATURE] add support for OGR creation options to and improve error handling of file writer

git-svn-id: http://svn.osgeo.org/qgis/trunk@14166 c8812cc2-4d05-0410-92ff-de0c093fc19c
This commit is contained in:
jef 2010-08-28 23:14:16 +00:00
parent cf578ef78e
commit c07d26fbf6
8 changed files with 209 additions and 35 deletions

View File

@ -29,7 +29,10 @@ public:
const QString& shapefileName, const QString& shapefileName,
const QString& fileEncoding, const QString& fileEncoding,
const QgsCoordinateReferenceSystem*, 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 /** Write contents of vector layer to an (OGR supported) vector formt
@note: this method was added in version 1.5*/ @note: this method was added in version 1.5*/
@ -39,7 +42,9 @@ public:
const QgsCoordinateReferenceSystem *destCRS, const QgsCoordinateReferenceSystem *destCRS,
const QString& driverName = "ESRI Shapefile", const QString& driverName = "ESRI Shapefile",
bool onlySelected = FALSE, bool onlySelected = FALSE,
QString *errorMessage = 0 ); QString *errorMessage = 0,
const QStringList &datasourceOptions = QStringList(),
const QStringList &layerOptions = QStringList() );
/** create shapefile and initialize it */ /** create shapefile and initialize it */
QgsVectorFileWriter(const QString& vectorFileName, QgsVectorFileWriter(const QString& vectorFileName,
@ -47,7 +52,9 @@ public:
const QMap<int, QgsField>& fields, const QMap<int, QgsField>& fields,
QGis::WkbType geometryType, QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem* srs, 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*/ /**Returns map with format filter string as key and OGR format key as value*/
static QMap< QString, QString> supportedFiltersAndFormats(); static QMap< QString, QString> supportedFiltersAndFormats();

View File

@ -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.

View File

@ -137,3 +137,13 @@ long QgsVectorLayerSaveAsDialog::crs() const
{ {
return mCRS; return mCRS;
} }
QStringList QgsVectorLayerSaveAsDialog::datasourceOptions() const
{
return mOgrDatasourceOptions->toPlainText().split( "\n" );
}
QStringList QgsVectorLayerSaveAsDialog::layerOptions() const
{
return mOgrLayerOptions->toPlainText().split( "\n" );
}

View File

@ -37,6 +37,8 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
QString format() const; QString format() const;
QString encoding() const; QString encoding() const;
QString filename() const; QString filename() const;
QStringList datasourceOptions() const;
QStringList layerOptions() const;
long crs() const; long crs() const;
private slots: private slots:

View File

@ -4000,7 +4000,11 @@ void QgisApp::saveAsVectorFileGeneral( bool saveOnlySelection )
QgsVectorFileWriter::WriterError error; QgsVectorFileWriter::WriterError error;
QString errorMessage; 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(); QApplication::restoreOverrideCursor();
@ -4010,7 +4014,10 @@ void QgisApp::saveAsVectorFileGeneral( bool saveOnlySelection )
} }
else 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();
} }
} }

View File

@ -40,6 +40,7 @@
#include <ogr_api.h> #include <ogr_api.h>
#include <ogr_srs_api.h> #include <ogr_srs_api.h>
#include <cpl_error.h> #include <cpl_error.h>
#include <cpl_conv.h>
QgsVectorFileWriter::QgsVectorFileWriter( QgsVectorFileWriter::QgsVectorFileWriter(
@ -48,7 +49,10 @@ QgsVectorFileWriter::QgsVectorFileWriter(
const QgsFieldMap& fields, const QgsFieldMap& fields,
QGis::WkbType geometryType, QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem* srs, const QgsCoordinateReferenceSystem* srs,
const QString& driverName ) const QString& driverName,
const QStringList &datasourceOptions,
const QStringList &layerOptions
)
: mDS( NULL ) : mDS( NULL )
, mLayer( NULL ) , mLayer( NULL )
, mGeom( NULL ) , mGeom( NULL )
@ -116,8 +120,26 @@ QgsVectorFileWriter::QgsVectorFileWriter(
QFile::remove( vectorFileName ); 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 // 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 ) if ( mDS == NULL )
{ {
mError = ErrCreateDataSource; mError = ErrCreateDataSource;
@ -155,7 +177,24 @@ QgsVectorFileWriter::QgsVectorFileWriter(
// datasource created, now create the output layer // datasource created, now create the output layer
QString layerName = QFileInfo( vectorFileName ).baseName(); QString layerName = QFileInfo( vectorFileName ).baseName();
OGRwkbGeometryType wkbType = static_cast<OGRwkbGeometryType>( geometryType ); 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 ) if ( srs )
{ {
@ -302,9 +341,6 @@ QString QgsVectorFileWriter::errorMessage()
bool QgsVectorFileWriter::addFeature( QgsFeature& feature ) bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
{ {
if ( hasError() != NoError )
return false;
QgsAttributeMap::const_iterator it; QgsAttributeMap::const_iterator it;
// create the feature // create the feature
@ -342,10 +378,13 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
OGR_F_SetFieldString( poFeature, ogrField, mCodec->fromUnicode( attrValue.toString() ).data() ); OGR_F_SetFieldString( poFeature, ogrField, mCodec->fromUnicode( attrValue.toString() ).data() );
break; break;
default: default:
QgsDebugMsg( "Invalid variant type for field " + QString( fldIt.value().name() ) + " " mErrorMessage = QObject::tr( "Invalid variant type for field %1[%2]: received %3 with type %4" )
+ QString::number( ogrField ) + ": Received Type " + QMetaType::typeName ( attrValue.type() ) .arg( fldIt.value().name() )
+ " : With Value : " + attrValue.toString() .arg( ogrField )
); .arg( QMetaType::typeName( attrValue.type() ) )
.arg( attrValue.toString() );
QgsDebugMsg( mErrorMessage );
mError = ErrFeatureWriteFailed;
return false; return false;
} }
} }
@ -355,6 +394,8 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
if ( !geom ) if ( !geom )
{ {
QgsDebugMsg( "invalid geometry" ); QgsDebugMsg( "invalid geometry" );
mErrorMessage = QObject::tr( "Invalid feature geometry" );
mError = ErrFeatureWriteFailed;
OGR_F_Destroy( poFeature ); OGR_F_Destroy( poFeature );
return false; return false;
} }
@ -375,7 +416,10 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
OGRErr err = OGR_G_ImportFromWkb( mGeom2, geom->asWkb(), geom->wkbSize() ); OGRErr err = OGR_G_ImportFromWkb( mGeom2, geom->asWkb(), geom->wkbSize() );
if ( err != OGRERR_NONE ) 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 ); OGR_F_Destroy( poFeature );
return false; return false;
} }
@ -388,7 +432,10 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
OGRErr err = OGR_G_ImportFromWkb( mGeom, geom->asWkb(), geom->wkbSize() ); OGRErr err = OGR_G_ImportFromWkb( mGeom, geom->asWkb(), geom->wkbSize() );
if ( err != OGRERR_NONE ) 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 ); OGR_F_Destroy( poFeature );
return false; return false;
} }
@ -400,7 +447,10 @@ bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
// put the created feature to layer // put the created feature to layer
if ( OGR_L_CreateFeature( mLayer, poFeature ) != OGRERR_NONE ) 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 ); OGR_F_Destroy( poFeature );
return false; return false;
} }
@ -432,9 +482,11 @@ QgsVectorFileWriter::writeAsShapefile( QgsVectorLayer* layer,
const QString& fileEncoding, const QString& fileEncoding,
const QgsCoordinateReferenceSystem* destCRS, const QgsCoordinateReferenceSystem* destCRS,
bool onlySelected, 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 QgsVectorFileWriter::WriterError
@ -444,7 +496,9 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
const QgsCoordinateReferenceSystem *destCRS, const QgsCoordinateReferenceSystem *destCRS,
const QString& driverName, const QString& driverName,
bool onlySelected, bool onlySelected,
QString *errorMessage ) QString *errorMessage,
const QStringList &datasourceOptions,
const QStringList &layerOptions )
{ {
const QgsCoordinateReferenceSystem* outputCRS; const QgsCoordinateReferenceSystem* outputCRS;
QgsCoordinateTransform* ct = 0; QgsCoordinateTransform* ct = 0;
@ -462,7 +516,7 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
outputCRS = &layer->srs(); outputCRS = &layer->srs();
} }
QgsVectorFileWriter* writer = 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 // check whether file creation was successful
WriterError err = writer->hasError(); WriterError err = writer->hasError();
@ -474,6 +528,11 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
return err; return err;
} }
if ( errorMessage )
{
errorMessage->clear();
}
QgsAttributeList allAttr = layer->pendingAllAttributesList(); QgsAttributeList allAttr = layer->pendingAllAttributesList();
QgsFeature fet; QgsFeature fet;
@ -493,6 +552,8 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
shallTransform = false; shallTransform = false;
} }
int n = 0, errors = 0;
// write all features // write all features
while ( layer->nextFeature( fet ) ) while ( layer->nextFeature( fet ) )
{ {
@ -519,7 +580,31 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
return ErrProjection; 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; delete writer;
@ -529,7 +614,12 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
delete ct; 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;
} }

View File

@ -52,7 +52,8 @@ class CORE_EXPORT QgsVectorFileWriter
ErrCreateLayer, ErrCreateLayer,
ErrAttributeTypeUnsupported, ErrAttributeTypeUnsupported,
ErrAttributeCreationFailed, ErrAttributeCreationFailed,
ErrProjection // added in 1.5 ErrProjection, // added in 1.5
ErrFeatureWriteFailed, // added in 1.6
}; };
/** Write contents of vector layer to a shapefile /** Write contents of vector layer to a shapefile
@ -62,7 +63,10 @@ class CORE_EXPORT QgsVectorFileWriter
const QString& fileEncoding, const QString& fileEncoding,
const QgsCoordinateReferenceSystem *destCRS, const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected = false, 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 /** Write contents of vector layer to an (OGR supported) vector formt
@note: this method was added in version 1.5*/ @note: this method was added in version 1.5*/
@ -72,7 +76,10 @@ class CORE_EXPORT QgsVectorFileWriter
const QgsCoordinateReferenceSystem *destCRS, const QgsCoordinateReferenceSystem *destCRS,
const QString& driverName = "ESRI Shapefile", const QString& driverName = "ESRI Shapefile",
bool onlySelected = false, 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 */ /** create shapefile and initialize it */
QgsVectorFileWriter( const QString& vectorFileName, QgsVectorFileWriter( const QString& vectorFileName,
@ -80,7 +87,10 @@ class CORE_EXPORT QgsVectorFileWriter
const QgsFieldMap& fields, const QgsFieldMap& fields,
QGis::WkbType geometryType, QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem* srs, 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*/ /**Returns map with format filter string as key and OGR format key as value*/
static QMap< QString, QString> supportedFiltersAndFormats(); static QMap< QString, QString> supportedFiltersAndFormats();

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>383</width>
<height>203</height> <height>348</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -38,7 +38,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0" colspan="3"> <item row="7" column="0" colspan="3">
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -101,6 +101,41 @@
<item row="0" column="1" colspan="2"> <item row="0" column="1" colspan="2">
<widget class="QComboBox" name="mFormatComboBox"/> <widget class="QComboBox" name="mFormatComboBox"/>
</item> </item>
<item row="6" column="0" colspan="3">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>OGR creation options</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<widget class="QTextEdit" name="mOgrDatasourceOptions"/>
</item>
<item row="1" column="1">
<widget class="QTextEdit" name="mOgrLayerOptions"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Data source</string>
</property>
<property name="buddy">
<cstring>mOgrDatasourceOptions</cstring>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Layer</string>
</property>
<property name="buddy">
<cstring>mOgrLayerOptions</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<tabstops> <tabstops>
@ -121,8 +156,8 @@
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>257</x> <x>266</x>
<y>181</y> <y>268</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>157</x> <x>157</x>
@ -137,8 +172,8 @@
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>325</x> <x>334</x>
<y>181</y> <y>268</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>286</x> <x>286</x>