[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& 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*/
@ -39,7 +42,9 @@ 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,
@ -47,7 +52,9 @@ public:
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();

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;
}
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 encoding() const;
QString filename() const;
QStringList datasourceOptions() const;
QStringList layerOptions() const;
long crs() const;
private slots:

View File

@ -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();
@ -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();
}
}

View File

@ -40,6 +40,7 @@
#include <ogr_api.h>
#include <ogr_srs_api.h>
#include <cpl_error.h>
#include <cpl_conv.h>
QgsVectorFileWriter::QgsVectorFileWriter(
@ -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 )
@ -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;
@ -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 )
{
@ -302,9 +341,6 @@ QString QgsVectorFileWriter::errorMessage()
bool QgsVectorFileWriter::addFeature( QgsFeature& feature )
{
if ( hasError() != NoError )
return false;
QgsAttributeMap::const_iterator it;
// create the feature
@ -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;
}
}
@ -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;
}
@ -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;
}
@ -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;
}
@ -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;
}
@ -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
@ -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;
@ -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();
@ -474,6 +528,11 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
return err;
}
if ( errorMessage )
{
errorMessage->clear();
}
QgsAttributeList allAttr = layer->pendingAllAttributesList();
QgsFeature fet;
@ -493,6 +552,8 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
shallTransform = false;
}
int n = 0, errors = 0;
// write all features
while ( layer->nextFeature( fet ) )
{
@ -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;
@ -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;
}

View File

@ -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
@ -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*/
@ -72,7 +76,10 @@ 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,
@ -80,7 +87,10 @@ class CORE_EXPORT QgsVectorFileWriter
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();

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>203</height>
<width>383</width>
<height>348</height>
</rect>
</property>
<property name="windowTitle">
@ -38,7 +38,7 @@
</property>
</widget>
</item>
<item row="6" column="0" colspan="3">
<item row="7" column="0" colspan="3">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -101,6 +101,41 @@
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="mFormatComboBox"/>
</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>
</widget>
<tabstops>
@ -121,8 +156,8 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>181</y>
<x>266</x>
<y>268</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@ -137,8 +172,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>325</x>
<y>181</y>
<x>334</x>
<y>268</y>
</hint>
<hint type="destinationlabel">
<x>286</x>