Move import feature main code to QgsVectorLayerImport and keep just few methods in providers

This commit is contained in:
Giuseppe Sucameli 2011-09-12 21:08:13 +02:00
parent b6587d300e
commit 6eb406b122
15 changed files with 903 additions and 773 deletions

View File

@ -83,6 +83,7 @@
%Include qgsvectordataprovider.sip
%Include qgsvectorfilewriter.sip
%Include qgsvectorlayer.sip
%Include qgsvectorlayerimport.sip
%Include qgsvectoroverlay.sip
%Include qgsnetworkaccessmanager.sip

View File

@ -61,21 +61,6 @@ class QgsProviderRegistry
*/
virtual QString protocolDrivers() const;
/** allows to import a vector layer using the provider
* @note this method was added in QGIS 1.8
*/
int importVector( QgsVectorLayer* layer,
const QString& providerKey,
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected = FALSE,
QString *errorMessage /Out/ = 0,
bool skipAttributeCreation = FALSE,
const QMap<QString, QVariant> *options = 0
) const;
private:
/** ctor private since instance() creates it */

View File

@ -0,0 +1,65 @@
/**
There are two possibilities how to use this class:
1. static call to QgsVectorLayerImport::importLayer(...) which saves the whole vector layer
2. create an instance of the class and issue calls to addFeature(...)
*/
class QgsVectorLayerImport
{
%TypeHeaderCode
#include <qgsvectorlayerimport.h>
#include <qgsfield.h>
%End
public:
enum ImportError
{
NoError = 0,
ErrDriverNotFound,
ErrCreateDataSource,
ErrCreateLayer,
ErrAttributeTypeUnsupported,
ErrAttributeCreationFailed,
ErrProjection,
ErrFeatureWriteFailed,
ErrInvalidLayer,
ErrInvalidProvider,
ErrProviderUnsupportedFeature,
ErrConnectionFailed
};
/** Write contents of vector layer to a different datasource */
static ImportError importLayer( QgsVectorLayer* layer,
const QString& uri,
const QString& providerKey,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected = FALSE,
QString *errorMessage /Out/ = 0,
bool skipAttributeCreation = FALSE,
QMap<QString, QVariant> *options = 0
);
/** create a empty layer and add fields to it */
QgsVectorLayerImport( const QString &uri,
const QString &provider,
const QgsFieldMap& fields,
QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem* crs,
bool overwrite = false,
const QMap<QString, QVariant> *options = 0
);
/** checks whether there were any errors */
ImportError hasError();
/** retrieves error message */
QString errorMessage();
/** add feature to the new created layer */
bool addFeature( QgsFeature& feature );
/** close the new created layer */
~QgsVectorLayerImport();
};

View File

@ -97,6 +97,7 @@ SET(QGIS_CORE_SRCS
qgsvectordataprovider.cpp
qgsvectorfilewriter.cpp
qgsvectorlayer.cpp
qgsvectorlayerimport.cpp
qgsvectorlayerjoinbuffer.cpp
qgsvectorlayerundocommand.cpp
qgsvectoroverlay.cpp
@ -327,6 +328,7 @@ SET(QGIS_CORE_HDRS
qgsvectordataprovider.h
qgsvectorfilewriter.h
qgsvectorlayer.h
qgsvectorlayerimport.h
qgsvectoroverlay.h
qgstolerance.h

View File

@ -43,14 +43,6 @@ typedef QString directoryDrivers_t();
typedef QString protocolDrivers_t();
//typedef int dataCapabilities_t();
//typedef QgsDataItem * dataItem_t(QString);
typedef int importVector_t( QgsVectorLayer* layer,
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected = false,
QString *errorMessage = 0,
bool skipAttributeCreation = false,
const QMap<QString, QVariant> *options = 0
);
QgsProviderRegistry *QgsProviderRegistry::_instance = 0;
@ -540,35 +532,3 @@ QgsProviderRegistry::openVector( QString const & dataSource, QString const & pro
return getProvider( providerKey, dataSource );
} // QgsProviderRegistry::openVector
*/
int QgsProviderRegistry::importVector( QgsVectorLayer* layer,
const QString& providerKey,
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected,
QString *errorMessage,
bool skipAttributeCreation,
const QMap<QString, QVariant> *options
) const
{
QLibrary *myLib = providerLibrary( providerKey );
if ( !myLib )
{
if ( errorMessage )
*errorMessage = QObject::tr( "unable to load %1 provider" ).arg( providerKey );
return -1;
}
importVector_t * pImport = ( importVector_t * ) cast_to_fptr( myLib->resolve( "importVector" ) );
if ( !pImport )
{
delete myLib;
if ( errorMessage )
*errorMessage = QObject::tr( "provider %1 has no importVector feature" ).arg( providerKey );
return -2;
}
delete myLib;
return pImport( layer, uri, destCRS, onlySelected, errorMessage, skipAttributeCreation, options );
}

View File

@ -140,17 +140,6 @@ class CORE_EXPORT QgsProviderRegistry
/** type for data provider metadata associative container */
typedef std::map<QString, QgsProviderMetadata*> Providers;
/** allows to import a vector layer using the provider */
int importVector( QgsVectorLayer* layer,
const QString& providerKey,
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected = false,
QString *errorMessage = 0,
bool skipAttributeCreation = false,
const QMap<QString, QVariant> *options = 0
) const;
private:
/** ctor private since instance() creates it */

View File

@ -116,6 +116,8 @@ class CORE_EXPORT QgsVectorFileWriter
/** add feature to the currently opened shapefile */
bool addFeature( QgsFeature& feature );
QMap<int, int> attrIdxToOgrIdx() { return mAttrIdxToOgrIdx; }
/** close opened shapefile for writing */
~QgsVectorFileWriter();

View File

@ -0,0 +1,300 @@
/***************************************************************************
qgsvectorlayerimport.cpp
vector layer importer
-------------------
begin : Thu Aug 25 2011
copyright : (C) 2011 by Giuseppe Sucameli
email : brush.tyler at gmail.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsfield.h"
#include "qgsfeature.h"
#include "qgsgeometry.h"
#include "qgslogger.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsvectorlayerimport.h"
#include "qgsproviderregistry.h"
#include <QFile>
#include <QSettings>
#include <QFileInfo>
#include <QDir>
#include <QTextCodec>
#include <QTextStream>
#include <QSet>
#include <QMetaType>
#include <cassert>
#include <cstdlib> // size_t
#include <limits> // std::numeric_limits
typedef QgsVectorLayerImport::ImportError createEmptyLayer_t(
const QString &uri,
const QgsFieldMap &fields,
QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem *destCRS,
bool overwrite,
QMap<int, int> *oldToNewAttrIdx,
QString *errorMessage = 0,
const QMap<QString, QVariant> *options = 0
);
QgsVectorLayerImport::QgsVectorLayerImport(
const QString &uri,
const QString &providerKey,
const QgsFieldMap& fields,
QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem* crs,
bool overwrite,
const QMap<QString, QVariant> *options )
{
mProvider = NULL;
QgsProviderRegistry * pReg = QgsProviderRegistry::instance();
QLibrary *myLib = pReg->providerLibrary( providerKey );
if ( !myLib )
{
mError = ErrInvalidProvider;
mErrorMessage = QObject::tr( "Unable to load %1 provider" ).arg( providerKey );
return;
}
createEmptyLayer_t * pCreateEmpty = ( createEmptyLayer_t * ) cast_to_fptr( myLib->resolve( "createEmptyLayer" ) );
if ( !pCreateEmpty )
{
delete myLib;
mError = ErrProviderUnsupportedFeature;
mErrorMessage = QObject::tr( "Provider %1 has no createEmptyLayer method" ).arg( providerKey );
return;
}
delete myLib;
// create an empty layer
QString errMsg;
mError = pCreateEmpty( uri, fields, geometryType, crs, overwrite, &mOldToNewAttrIdx, &errMsg, options );
if ( hasError() )
{
mErrorMessage = errMsg;
return;
}
QgsDebugMsg( "Created empty layer" );
QgsVectorDataProvider *vectorProvider = ( QgsVectorDataProvider* ) pReg->provider( providerKey, uri );
if ( !vectorProvider || !vectorProvider->isValid() )
{
mError = ErrInvalidLayer;
mErrorMessage = QObject::tr( "Loading of layer failed" );
if ( vectorProvider )
delete vectorProvider;
return;
}
mProvider = vectorProvider;
mError = NoError;
}
QgsVectorLayerImport::~QgsVectorLayerImport()
{
if ( mProvider )
delete mProvider;
}
QgsVectorLayerImport::ImportError QgsVectorLayerImport::hasError()
{
return mError;
}
QString QgsVectorLayerImport::errorMessage()
{
return mErrorMessage;
}
bool QgsVectorLayerImport::addFeature( QgsFeature& feat )
{
const QgsAttributeMap &attrs = feat.attributeMap();
QgsAttributeMap newAttrs;
for ( QgsAttributeMap::const_iterator it = attrs.begin(); it != attrs.end(); it++ )
{
if ( mOldToNewAttrIdx.contains( it.key() ) )
{
QgsDebugMsgLevel( QString( "moving field from pos %1 to %2" ).arg( it.key() ).arg( mOldToNewAttrIdx.value( it.key() ) ), 3 );
newAttrs.insert( mOldToNewAttrIdx.value( it.key() ), *it );
}
else
{
QgsDebugMsgLevel( QString( "added attr pos %1" ).arg( it.key() ), 3 );
newAttrs.insert( it.key(), *it );
}
}
feat.setAttributeMap( newAttrs );
if ( !mProvider->addFeatures( QgsFeatureList() << feat ) )
{
mErrorMessage = QObject::tr( "Feature #%1 creation error" ).arg( feat.id() );
mError = ErrFeatureWriteFailed;
QgsDebugMsg( mErrorMessage );
return false;
}
return true;
}
QgsVectorLayerImport::ImportError
QgsVectorLayerImport::importLayer( QgsVectorLayer* layer,
const QString& uri,
const QString& providerKey,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected,
QString *errorMessage,
bool skipAttributeCreation,
QMap<QString, QVariant> *options )
{
const QgsCoordinateReferenceSystem* outputCRS;
QgsCoordinateTransform* ct = 0;
int shallTransform = false;
if ( layer == NULL )
{
return ErrInvalidLayer;
}
if ( destCRS && destCRS->isValid() )
{
// This means we should transform
outputCRS = destCRS;
shallTransform = true;
}
else
{
// This means we shouldn't transform, use source CRS as output (if defined)
outputCRS = &layer->crs();
}
QgsVectorLayerImport * writer =
new QgsVectorLayerImport( uri, providerKey, skipAttributeCreation ? QgsFieldMap() : layer->pendingFields(), layer->wkbType(), outputCRS, false, options );
// check whether file creation was successful
ImportError err = writer->hasError();
if ( err != NoError )
{
if ( errorMessage )
*errorMessage = writer->errorMessage();
delete writer;
return err;
}
if ( errorMessage )
{
errorMessage->clear();
}
QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->pendingAllAttributesList();
QgsFeature fet;
layer->select( allAttr, QgsRectangle(), layer->wkbType() != QGis::WKBNoGeometry );
const QgsFeatureIds& ids = layer->selectedFeaturesIds();
// Create our transform
if ( destCRS )
{
ct = new QgsCoordinateTransform( layer->crs(), *destCRS );
}
// Check for failure
if ( ct == NULL )
{
shallTransform = false;
}
int n = 0, errors = 0;
// write all features
while ( layer->nextFeature( fet ) )
{
if ( onlySelected && !ids.contains( fet.id() ) )
continue;
if ( shallTransform )
{
try
{
if ( fet.geometry() )
{
fet.geometry()->transform( *ct );
}
}
catch ( QgsCsException &e )
{
delete ct;
delete writer;
QString msg = QObject::tr( "Failed to transform a point while drawing a feature of type '%1'. Writing stopped. (Exception: %2)" )
.arg( fet.typeName() ).arg( e.what() );
QgsLogger::warning( msg );
if ( errorMessage )
*errorMessage = msg;
return ErrProjection;
}
}
if ( skipAttributeCreation )
{
fet.clearAttributeMap();
}
if ( !writer->addFeature( fet ) )
{
if ( writer->hasError() && 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;
if ( shallTransform )
{
delete ct;
}
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

@ -0,0 +1,99 @@
/***************************************************************************
qgsvectorlayerimport.cpp
vector layer importer
-------------------
begin : Thu Aug 25 2011
copyright : (C) 2011 by Giuseppe Sucameli
email : brush.tyler at gmail.com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef _QGSVECTORLAYERIMPORT_H_
#define _QGSVECTORLAYERIMPORT_H_
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"
/** \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
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 QgsVectorLayerImport
{
public:
enum ImportError
{
NoError = 0,
ErrDriverNotFound,
ErrCreateDataSource,
ErrCreateLayer,
ErrAttributeTypeUnsupported,
ErrAttributeCreationFailed,
ErrProjection,
ErrFeatureWriteFailed,
ErrInvalidLayer,
ErrInvalidProvider,
ErrProviderUnsupportedFeature,
ErrConnectionFailed
};
/** Write contents of vector layer to a different datasource */
static ImportError importLayer( QgsVectorLayer* layer,
const QString& uri,
const QString& providerKey,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected = false,
QString *errorMessage = 0,
bool skipAttributeCreation = false,
QMap<QString, QVariant> *options = 0
);
/** create a empty layer and add fields to it */
QgsVectorLayerImport( const QString &uri,
const QString &provider,
const QgsFieldMap& fields,
QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem* crs,
bool overwrite = false,
const QMap<QString, QVariant> *options = 0
);
/** checks whether there were any errors */
ImportError hasError();
/** retrieves error message */
QString errorMessage();
/** add feature to the new created layer */
bool addFeature( QgsFeature& feature );
/** close the new created layer */
~QgsVectorLayerImport();
protected:
/** contains error value */
ImportError mError;
QString mErrorMessage;
QgsVectorDataProvider *mProvider;
/** map attribute indexes to new field indexes */
QMap<int, int> mOldToNewAttrIdx;
};
#endif

View File

@ -42,7 +42,7 @@ email : sherman at mrcc.com
#include "qgsfield.h"
#include "qgsgeometry.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsvectorlayer.h"
#include "qgsvectorlayerimport.h"
static const QString TEXT_PROVIDER_KEY = "ogr";
static const QString TEXT_PROVIDER_DESCRIPTION =
@ -83,15 +83,54 @@ class QgsCPLErrorHandler
};
bool QgsOgrProvider::convertField( QgsField &field, const QTextCodec &encoding )
{
OGRFieldType ogrType = OFTString; //default to string
int ogrWidth = field.length();
int ogrPrecision = field.precision();
switch ( field.type() )
{
case QVariant::LongLong:
ogrType = OFTString;
ogrWidth = ogrWidth > 0 && ogrWidth <= 21 ? ogrWidth : 21;
ogrPrecision = -1;
break;
QgsVectorFileWriter::WriterError
QgsOgrProvider::importVector(
QgsVectorLayer *layer,
const QString& fileName,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected,
case QVariant::String:
ogrType = OFTString;
if ( ogrWidth < 0 || ogrWidth > 255 )
ogrWidth = 255;
break;
case QVariant::Int:
ogrType = OFTInteger;
ogrWidth = ogrWidth > 0 && ogrWidth <= 10 ? ogrWidth : 10;
ogrPrecision = 0;
break;
case QVariant::Double:
ogrType = OFTReal;
break;
default:
return false;
}
field.setTypeName( encoding.toUnicode( OGR_GetFieldTypeName( ogrType ) ) );
field.setLength( ogrWidth );
field.setPrecision( ogrPrecision );
return true;
}
QgsVectorLayerImport::ImportError QgsOgrProvider::createEmptyLayer(
const QString& uri,
const QgsFieldMap &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage,
bool skipAttributeCreation,
const QMap<QString,QVariant> *options )
{
QString encoding;
@ -113,9 +152,48 @@ QgsOgrProvider::importVector(
layerOptions << options->value( "layerOptions" ).toStringList();
}
return QgsVectorFileWriter::writeAsVectorFormat(
layer, fileName, encoding, destCRS, driverName, onlySelected,
errorMessage, dsOptions, layerOptions, skipAttributeCreation );
if ( oldToNewAttrIdxMap )
oldToNewAttrIdxMap->clear();
if ( errorMessage )
errorMessage->clear();
if ( !overwrite )
{
QFileInfo fi( uri );
if ( fi.exists() )
{
if ( errorMessage )
*errorMessage += QObject::tr( "Unable to create the datasource. %1 exists and overwrite flag is false." )
.arg( uri );
return QgsVectorLayerImport::ErrCreateDataSource;
}
}
QgsVectorFileWriter *writer = new QgsVectorFileWriter(
uri, encoding, fields, wkbType,
srs, driverName, dsOptions, layerOptions );
QgsVectorFileWriter::WriterError error = writer->hasError();
if ( error )
{
if ( errorMessage )
*errorMessage += writer->errorMessage();
delete writer;
return ( QgsVectorLayerImport::ImportError ) error;
}
if ( oldToNewAttrIdxMap )
{
QMap<int, int> attrIdxMap = writer->attrIdxToOgrIdx();
for ( QMap<int, int>::const_iterator attrIt = attrIdxMap.begin(); attrIt != attrIdxMap.end(); ++attrIt )
{
oldToNewAttrIdxMap->insert( attrIt.key(), *attrIt );
}
}
delete writer;
return QgsVectorLayerImport::NoError;
}
@ -2405,14 +2483,18 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem )
return 0;
}
QGISEXTERN int importVector(
QgsVectorLayer *layer,
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected,
QString *errorMessage,
bool skipAttributeCreation,
const QMap<QString,QVariant> *options )
QGISEXTERN QgsVectorLayerImport::ImportError createEmptyLayer(
const QString& uri,
const QgsFieldMap &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage,
const QMap<QString,QVariant> *options )
{
return QgsOgrProvider::importVector( layer, uri, destCRS, onlySelected, errorMessage, skipAttributeCreation, options );
return QgsOgrProvider::createEmptyLayer(
uri, fields, wkbType, srs, overwrite,
oldToNewAttrIdxMap, errorMessage, options
);
}

View File

@ -19,9 +19,10 @@ email : sherman at mrcc.com
#include "qgsrectangle.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorfilewriter.h"
#include "qgsvectorlayerimport.h"
class QgsFeature;
class QgsField;
class QgsVectorLayerImport;
#include <ogr_api.h>
@ -36,15 +37,16 @@ class QgsOgrProvider : public QgsVectorDataProvider
public:
/** convert a vector layer to a vector file */
static QgsVectorFileWriter::WriterError importVector(
QgsVectorLayer *layer,
const QString& fileName,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected,
QString *errorMessage,
bool skipAttributeCreation,
const QMap<QString,QVariant> *options
);
static QgsVectorLayerImport::ImportError createEmptyLayer(
const QString& uri,
const QgsFieldMap &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage = 0,
const QMap<QString,QVariant> *options = 0
);
/**
* Constructor of the vector provider
@ -266,6 +268,9 @@ class QgsOgrProvider : public QgsVectorDataProvider
/** tell OGR, which fields to fetch in nextFeature/featureAtId (ie. which not to ignore) */
void setRelevantFields( bool fetchGeometry, const QgsAttributeList& fetchAttributes );
/** convert a QgsField to work with OGR */
static bool convertField( QgsField &field, const QTextCodec &encoding );
private:
bool crsFromWkt( QgsCoordinateReferenceSystem &srs, const char *wkt );
unsigned char *getGeometryPointer( OGRFeatureH fet );

View File

@ -25,13 +25,13 @@
#include <qgis.h>
#include <qgsapplication.h>
#include <qgsvectorlayer.h>
#include <qgsfeature.h>
#include <qgsfield.h>
#include <qgsgeometry.h>
#include <qgsmessageoutput.h>
#include <qgsrectangle.h>
#include <qgscoordinatereferencesystem.h>
#include "qgsvectorlayerimport.h"
#include "qgsprovidercountcalcevent.h"
#include "qgsproviderextentcalcevent.h"
@ -81,7 +81,7 @@ bool QgsPostgresProvider::convertField( QgsField &field )
break;
case QVariant::Double:
if ( fieldSize <= 0 || fieldPrec < 0)
if ( fieldSize <= 0 || fieldPrec <= 0)
{
fieldType = "float";
fieldSize = -1;
@ -103,38 +103,17 @@ bool QgsPostgresProvider::convertField( QgsField &field )
return true;
}
QgsPostgresProvider::WriterError
QgsPostgresProvider::importVector(
QgsVectorLayer *layer,
QgsVectorLayerImport::ImportError QgsPostgresProvider::createEmptyLayer(
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected,
const QgsFieldMap &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage,
bool skipAttributeCreation,
const QMap<QString,QVariant> *options )
{
const QgsCoordinateReferenceSystem* outputCRS;
QgsCoordinateTransform* ct = 0;
int shallTransform = false;
if ( !layer )
{
return ErrInvalidLayer;
}
if ( destCRS && destCRS->isValid() )
{
// This means we should transform
outputCRS = destCRS;
shallTransform = true;
}
else
{
// This means we shouldn't transform, use source CRS as output (if defined)
outputCRS = &layer->crs();
}
QgsFieldMap fields = skipAttributeCreation ? QgsFieldMap() : layer->pendingFields();
Q_UNUSED( options );
// populate members from the uri structure
QgsDataSourceURI dsUri( uri );
@ -160,184 +139,199 @@ QgsPostgresProvider::importVector(
QgsDebugMsg( "Table name is: " + tableName );
// create the table
Conn *conn = Conn::connectDb( dsUri.connectionInfo(), false );
if ( conn == NULL )
{
Conn *conn = Conn::connectDb( dsUri.connectionInfo(), false );
if ( conn == NULL )
{
QgsDebugMsg( "Connection to database failed. Import of layer aborted." );
if ( errorMessage )
*errorMessage = QObject::tr( "Connection to database failed" );
return ErrConnectionFailed;
}
QgsDebugMsg( "Connection to database failed. Import of layer aborted." );
if ( errorMessage )
*errorMessage = QObject::tr( "Connection to database failed" );
return QgsVectorLayerImport::ErrConnectionFailed;
}
// get the pk's name and type
if ( primaryKey.isEmpty() )
// get the pk's name and type
// if no pk name was passed, define the new pk field name
if ( primaryKey.isEmpty() )
{
int index = 0;
QString pk = primaryKey = "pk";
for ( QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
{
// if no pk name was passed, define the new pk field name
int index = 0;
QString pk = primaryKey = "pk";
for ( QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
if ( fldIt.value().name() == pk )
{
if ( fldIt.value().name() == pk )
// it already exists, try again with a new name
primaryKey = QString( "%1_%2" ).arg( pk ).arg( index++ );
fldIt = fields.begin();
}
}
}
else
{
// search for the passed field
for ( QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
{
if ( fldIt.value().name() == primaryKey )
{
// found, get the field type
QgsField fld = fldIt.value();
if ( convertField( fld ) )
{
// it already exists, try again with a new name
primaryKey = QString( "%1_%2" ).arg( pk ).arg( index++ );
fldIt = fields.begin();
}
}
}
else
{
// search for the passed field
for ( QgsFieldMap::iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
{
if ( fldIt.value().name() == primaryKey )
{
// found, get the field type
QgsField &fld = fldIt.value();
if ( convertField( fld ) )
{
primaryKeyType = fld.typeName();
}
primaryKeyType = fld.typeName();
}
}
}
}
// if the field doesn't not exist yet, create it as a serial field
if ( primaryKeyType.isEmpty() )
// if the field doesn't not exist yet, create it as a serial field
if ( primaryKeyType.isEmpty() )
{
primaryKeyType = "serial";
/* TODO
// check the feature count to choose if create a serial8 pk field
if ( layer->featureCount() > 0xFFFFFF )
{
primaryKeyType = "serial";
// check the feature count to choose if create a serial8 pk field
if ( layer->featureCount() > 0xFFFFFF )
{
primaryKeyType = "serial8";
}
}
primaryKeyType = "serial8";
}*/
}
try
try
{
conn->PQexecNR( "BEGIN" );
bool exists = false;
QString sql = QString( "SELECT 1 "
"FROM pg_class AS cls JOIN pg_namespace AS nsp"
" ON nsp.oid = cls.relnamespace "
" WHERE cls.relname = %1 AND nsp.nspname = %2" )
.arg( quotedValue( tableName ) )
.arg( quotedValue( schemaName ) );
PGresult *result = conn->PQexec( sql );
if ( PQresultStatus( result ) == PGRES_FATAL_ERROR )
throw PGException( result );
if ( PQntuples( result ) > 0 )
exists = true;
PQclear( result );
if ( exists && overwrite )
{
conn->PQexecNR( "BEGIN" );
// delete the table if exists, then re-create it
QString sql = QString( "SELECT %1(%2, %3) "
QString sql = QString( "SELECT dropgeometrytable(%1, %2) "
"FROM pg_class AS cls JOIN pg_namespace AS nsp"
" ON nsp.oid = cls.relnamespace "
" WHERE cls.relname = %4 AND nsp.nspname = %5" )
.arg( conn->majorVersion() < 2 ? "dropgeometrytable" : "st_dropgeometrytable" )
" WHERE cls.relname = %3 AND nsp.nspname = %4" )
.arg( quotedValue( schemaName ) )
.arg( quotedValue( tableName ) )
.arg( quotedValue( tableName ) )
.arg( quotedValue( schemaName ) );
PGresult *result = conn->PQexec( sql );
if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
result = conn->PQexec( sql );
if ( PQresultStatus( result ) == PGRES_FATAL_ERROR )
throw PGException( result );
PQclear( result );
}
sql = QString( "CREATE TABLE %1 (%2 %3 PRIMARY KEY)" )
.arg( schemaTableName )
.arg( quotedIdentifier( primaryKey ) )
.arg( primaryKeyType );
sql = QString( "CREATE TABLE %1 (%2 %3 PRIMARY KEY)" )
.arg( schemaTableName )
.arg( quotedIdentifier( primaryKey ) )
.arg( primaryKeyType );
result = conn->PQexec( sql );
if ( PQresultStatus( result ) == PGRES_FATAL_ERROR )
throw PGException( result );
PQclear( result );
// get geometry type, dim and srid
int dim = 2;
long srid = srs->postgisSrid();
switch( wkbType )
{
case QGis::WKBPoint25D:
dim = 3;
case QGis::WKBPoint:
geometryType = "POINT";
break;
case QGis::WKBLineString25D:
dim = 3;
case QGis::WKBLineString:
geometryType = "LINESTRING";
break;
case QGis::WKBPolygon25D:
dim = 3;
case QGis::WKBPolygon:
geometryType = "POLYGON";
break;
case QGis::WKBMultiPoint25D:
dim = 3;
case QGis::WKBMultiPoint:
geometryType = "MULTIPOINT";
break;
case QGis::WKBMultiLineString25D:
dim = 3;
case QGis::WKBMultiLineString:
geometryType = "MULTILINESTRING";
break;
case QGis::WKBMultiPolygon25D:
dim = 3;
case QGis::WKBMultiPolygon:
geometryType = "MULTIPOLYGON";
break;
case QGis::WKBUnknown:
geometryType = "GEOMETRY";
break;
case QGis::WKBNoGeometry:
default:
dim = 0;
break;
}
// create geometry column
if ( !geometryType.isEmpty() )
{
sql = QString( "SELECT addgeometrycolumn(%1, %2, %3, %4, %5, %6)" )
.arg( quotedValue( schemaName ) )
.arg( QgsPostgresProvider::quotedValue( tableName ) )
.arg( QgsPostgresProvider::quotedValue( geometryColumn ) )
.arg( srid )
.arg( QgsPostgresProvider::quotedValue( geometryType ) )
.arg( dim );
result = conn->PQexec( sql );
if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
if ( PQresultStatus( result ) == PGRES_FATAL_ERROR )
throw PGException( result );
PQclear( result );
// get geometry type, dim and srid
int dim = 2;
long srid = outputCRS->postgisSrid();
switch( layer->wkbType() )
{
case QGis::WKBPoint25D:
dim = 3;
case QGis::WKBPoint:
geometryType = "POINT";
break;
case QGis::WKBLineString25D:
dim = 3;
case QGis::WKBLineString:
geometryType = "LINESTRING";
break;
case QGis::WKBPolygon25D:
dim = 3;
case QGis::WKBPolygon:
geometryType = "POLYGON";
break;
case QGis::WKBMultiPoint25D:
dim = 3;
case QGis::WKBMultiPoint:
geometryType = "MULTIPOINT";
break;
case QGis::WKBMultiLineString25D:
dim = 3;
case QGis::WKBMultiLineString:
geometryType = "MULTILINESTRING";
break;
case QGis::WKBMultiPolygon25D:
dim = 3;
case QGis::WKBMultiPolygon:
geometryType = "MULTIPOLYGON";
break;
case QGis::WKBUnknown:
geometryType = "GEOMETRY";
break;
case QGis::WKBNoGeometry:
default:
dim = 0;
break;
}
// create geometry column
if ( !geometryType.isEmpty() && dim > 0 )
{
sql = QString( "SELECT %1(%2, %3, %4, %5, %6, %7)" )
.arg( conn->majorVersion() < 2 ? "addgeometrycolumn" : "st_addgeometrycolumn" )
.arg( quotedValue( schemaName ) )
.arg( QgsPostgresProvider::quotedValue( tableName ) )
.arg( QgsPostgresProvider::quotedValue( geometryColumn ) )
.arg( srid )
.arg( QgsPostgresProvider::quotedValue( geometryType ) )
.arg( dim );
PGresult *result = conn->PQexec( sql );
if ( result == 0 || PQresultStatus( result ) == PGRES_FATAL_ERROR )
throw PGException( result );
PQclear( result );
}
else
{
geometryColumn = QString();
geometryType = QString();
dim = 0;
}
conn->PQexecNR( "COMMIT" );
}
catch ( PGException &e )
else
{
QgsDebugMsg( "creation of data source " + schemaTableName + " failed. " + e.errorMessage() );
if ( errorMessage )
*errorMessage = QObject::tr( "creation of data source %1 failed. %2" )
.arg( schemaTableName )
.arg( e.errorMessage() );
conn->PQexecNR( "ROLLBACK" );
Conn::disconnectRW( conn );
return ErrCreateLayer;
geometryColumn.clear();
}
Conn::disconnectRW( conn );
QgsDebugMsg( "layer " + schemaTableName + " created." );
conn->PQexecNR( "COMMIT" );
}
catch ( PGException &e )
{
QgsDebugMsg( "creation of data source " + schemaTableName + " failed. " + e.errorMessage() );
if ( errorMessage )
*errorMessage = QObject::tr( "Creation of data source %1 failed: \n%2" )
.arg( schemaTableName )
.arg( e.errorMessage() );
conn->PQexecNR( "ROLLBACK" );
Conn::disconnectRW( conn );
return QgsVectorLayerImport::ErrCreateLayer;
}
Conn::disconnectRW( conn );
QgsDebugMsg( "layer " + schemaTableName + " created." );
// use the provider to edit the table
dsUri.setDataSource( schemaName, tableName, geometryColumn, QString(), primaryKey );
@ -346,25 +340,33 @@ QgsPostgresProvider::importVector(
{
QgsDebugMsg( "The layer " + schemaTableName + " just created is not valid or not supported by the provider." );
if ( errorMessage )
*errorMessage = QObject::tr( "loading of the layer %1 failed" )
*errorMessage = QObject::tr( "Loading of the layer %1 failed" )
.arg( schemaTableName );
delete provider;
return ErrCreateLayer;
return QgsVectorLayerImport::ErrInvalidLayer;
}
QgsDebugMsg( "layer loaded" );
// add fields to the layer
if ( oldToNewAttrIdxMap )
oldToNewAttrIdxMap->clear();
if ( fields.size() > 0 )
{
int offset = geometryColumn.isEmpty() ? 1 : 2;
// get the list of fields
QList<QgsField> flist;
for ( QgsFieldMap::iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
for ( QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
{
QgsField &fld = fldIt.value();
QgsField fld = fldIt.value();
if ( fld.name() == primaryKey )
{
oldToNewAttrIdxMap->insert( fldIt.key(), 0 );
continue;
}
if ( fld.name() == geometryColumn )
{
@ -372,180 +374,43 @@ QgsPostgresProvider::importVector(
continue;
}
// convert field type only if the source provider is different from the destination one
// because if both source and destination use the same provider,
// the field type should be natively supported
if ( layer->dataProvider()->name() != provider->name() )
if ( !convertField( fld ) )
{
if ( !convertField( fld ) )
{
QgsDebugMsg( "error creating field " + fld.name() + ": unsupported type" );
if ( errorMessage )
*errorMessage = QObject::tr( "unsupported type for field %1" )
.arg( fld.name() );
QgsDebugMsg( "error creating field " + fld.name() + ": unsupported type" );
if ( errorMessage )
*errorMessage = QObject::tr( "Unsupported type for field %1" )
.arg( fld.name() );
delete provider;
return ErrAttributeTypeUnsupported;
}
delete provider;
return QgsVectorLayerImport::ErrAttributeTypeUnsupported;
}
flist.append( fld );
QgsDebugMsg( "creating field " + fld.name() +
QgsDebugMsg( "creating field #" + QString::number( fldIt.key() ) +
" -> #" + QString::number( offset ) +
" name " + fld.name() +
" type " + QString( QVariant::typeToName( fld.type() ) ) +
" typename " + fld.typeName() +
" width " + QString::number( fld.length() ) +
" precision " + QString::number( fld.precision() ) );
flist.append( fld );
if ( oldToNewAttrIdxMap )
oldToNewAttrIdxMap->insert( fldIt.key(), offset++ );
}
if ( !provider->addAttributes( flist ) )
{
QgsDebugMsg( "error creating fields " );
if ( errorMessage )
*errorMessage = QObject::tr( "creation of fields failed" );
*errorMessage = QObject::tr( "Creation of fields failed" );
delete provider;
return ErrAttributeCreationFailed;
return QgsVectorLayerImport::ErrAttributeCreationFailed;
}
QgsDebugMsg( "Done creating fields" );
}
QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->pendingAllAttributesList();
QgsFeature fet;
layer->select( allAttr, QgsRectangle(), layer->wkbType() != QGis::WKBNoGeometry );
const QgsFeatureIds& ids = layer->selectedFeaturesIds();
// Create our transform
if ( destCRS )
{
ct = new QgsCoordinateTransform( layer->crs(), *destCRS );
}
// Check for failure
if ( ct == NULL )
{
shallTransform = false;
}
int n = 0, errors = 0;
// make all the changes using one transaction only
if ( provider->connectRW() )
{
provider->connectionRW->PQexecNR( "BEGIN" );
}
else
{
QgsDebugMsg( "unable to estabilish a RW connection" );
if ( errorMessage )
*errorMessage = QObject::tr( "unable to estabilish a RW connection. Writing of features failed" );
delete provider;
return ErrFeatureWriteFailed;
}
// write all features
QgsFeatureList flist;
while (1)
{
while ( layer->nextFeature( fet ) )
{
if ( onlySelected && !ids.contains( fet.id() ) )
continue;
if ( shallTransform )
{
try
{
if ( fet.geometry() )
{
fet.geometry()->transform( *ct );
}
}
catch ( QgsCsException &e )
{
delete ct;
QString msg = QObject::tr( "Failed to transform a point while drawing a feature of type '%1'. Writing stopped. (Exception: %2)" )
.arg( fet.typeName() ).arg( e.what() );
QgsLogger::warning( msg );
if ( errorMessage )
*errorMessage = msg;
provider->connectionRW->PQexecNR( "ROLLBACK" );
delete provider;
return ErrProjection;
}
}
if ( skipAttributeCreation )
{
fet.clearAttributeMap();
}
else
{
// fix attributes position based on how the table was created.
// The first field is reserved to the primary key and the second one to
// the geometry column (if any), so move the other attributes after these fields
int offset = geometryColumn.isEmpty() ? 1 : 2;
const QgsAttributeMap &attrs = fet.attributeMap();
QgsAttributeMap newAttrs;
for ( QgsAttributeMap::const_iterator it = attrs.begin(); it != attrs.end(); it++ )
{
const QgsField &fld = fields[ it.key() ];
if ( fld.name() == primaryKey || fld.name() == geometryColumn )
continue;
newAttrs.insert( offset++, *it );
}
fet.setAttributeMap( newAttrs );
}
flist.append( fet );
// add 100 features at the same time
if ( flist.size() == 100 )
break;
}
if ( !provider->addFeatures( flist, false ) )
{
QgsDebugMsg( QString( "failed while adding features from %1 to %2" )
.arg( flist.first().id() )
.arg( flist.last().id() )
);
if ( errorMessage )
*errorMessage += QObject::tr( "failed while adding features from %1 to %2\n" )
.arg( flist.first().id() )
.arg( flist.last().id() );
}
n += flist.size();
if ( flist.size() < 100 )
break;
flist.clear();
}
provider->connectionRW->PQexecNR( "COMMIT" );
delete provider;
if ( shallTransform )
{
delete ct;
}
if ( errors > 0 && n > 0 )
{
if ( errorMessage )
{
*errorMessage += QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n );
}
return ErrFeatureWriteFailed;
}
return NoError;
return QgsVectorLayerImport::NoError;
}
@ -2897,11 +2762,6 @@ QString QgsPostgresProvider::paramValue( QString fieldValue, const QString &defa
}
bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist )
{
return addFeatures( flist, true );
}
bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, bool useNewTransaction )
{
if ( flist.size() == 0 )
return true;
@ -2909,10 +2769,6 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, bool useNewTransac
if ( isQuery )
return false;
// if there's no rw connection opened then create a new transaction
if ( !connectionRW )
useNewTransaction = true;
if ( !connectRW() )
return false;
@ -2920,8 +2776,7 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, bool useNewTransac
try
{
if ( useNewTransaction )
connectionRW->PQexecNR( "BEGIN" );
connectionRW->PQexecNR( "BEGIN" );
// Prepare the INSERT statement
QString insert = QString( "INSERT INTO %1 (" ).arg( mQuery );
@ -3094,22 +2949,19 @@ bool QgsPostgresProvider::addFeatures( QgsFeatureList &flist, bool useNewTransac
flist[i].setFeatureId( newIds[i] );
connectionRW->PQexecNR( "DEALLOCATE addfeatures" );
if ( useNewTransaction )
connectionRW->PQexecNR( "COMMIT" );
connectionRW->PQexecNR( "COMMIT" );
featuresCounted += flist.size();
}
catch ( PGException &e )
{
e.showErrorMessage( tr( "Error while adding features" ) );
if ( useNewTransaction )
connectionRW->PQexecNR( "ROLLBACK" );
connectionRW->PQexecNR( "ROLLBACK" );
connectionRW->PQexecNR( "DEALLOCATE addfeatures" );
returnvalue = false;
}
if ( useNewTransaction )
rewind();
rewind();
return returnvalue;
}
@ -4208,14 +4060,18 @@ QGISEXTERN bool isProvider()
return true;
}
QGISEXTERN int importVector(
QgsVectorLayer *layer,
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected,
QString *errorMessage,
bool skipAttributeCreation,
const QMap<QString,QVariant> *options )
QGISEXTERN QgsVectorLayerImport::ImportError createEmptyLayer(
const QString& uri,
const QgsFieldMap &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage,
const QMap<QString,QVariant> *options )
{
return QgsPostgresProvider::importVector( layer, uri, destCRS, onlySelected, errorMessage, skipAttributeCreation, options );
return QgsPostgresProvider::createEmptyLayer(
uri, fields, wkbType, srs, overwrite,
oldToNewAttrIdxMap, errorMessage, options
);
}

View File

@ -24,6 +24,7 @@ extern "C"
}
#include "qgsvectordataprovider.h"
#include "qgsrectangle.h"
#include "qgsvectorlayerimport.h"
#include <list>
#include <queue>
#include <fstream>
@ -32,7 +33,6 @@ extern "C"
class QgsFeature;
class QgsField;
class QgsGeometry;
class QgsVectorLayer;
#include "qgsdatasourceuri.h"
@ -50,27 +50,15 @@ class QgsPostgresProvider : public QgsVectorDataProvider
public:
enum WriterError
{
NoError = 0,
ErrDriverNotFound,
ErrCreateDataSource,
ErrCreateLayer,
ErrAttributeTypeUnsupported,
ErrAttributeCreationFailed,
ErrProjection,
ErrFeatureWriteFailed,
ErrInvalidLayer,
ErrConnectionFailed,
};
/** Import a vector layer into the database */
static WriterError importVector( QgsVectorLayer* layer,
static QgsVectorLayerImport::ImportError createEmptyLayer(
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected = false,
const QgsFieldMap &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage = 0,
bool skipAttributeCreation = false,
const QMap<QString,QVariant> *options = 0
);
@ -379,15 +367,9 @@ class QgsPostgresProvider : public QgsVectorDataProvider
*/
bool loadFields();
/** convert a QgsField to work with PG
*/
/** convert a QgsField to work with PG */
static bool convertField( QgsField &field );
/**Adds a list of features
@param useNewTransaction create a new transaction
@return true in case of success and false in case of failure*/
bool addFeatures( QgsFeatureList & flist, bool useNewTransaction );
/**Parses the enum_range of an attribute and inserts the possible values into a stringlist
@param enumValues the stringlist where the values are appended
@param attributeName the name of the enum attribute

View File

@ -16,13 +16,13 @@ email : a.furieri@lqt.it
#include <qgis.h>
#include <qgsapplication.h>
#include <qgsvectorlayer.h>
#include <qgsfeature.h>
#include <qgsfield.h>
#include <qgsgeometry.h>
#include <qgsmessageoutput.h>
#include <qgsrectangle.h>
#include <qgscoordinatereferencesystem.h>
#include "qgsvectorlayerimport.h"
#include "qgsspatialiteprovider.h"
@ -87,38 +87,18 @@ bool QgsSpatiaLiteProvider::convertField( QgsField &field )
return true;
}
QgsSpatiaLiteProvider::WriterError
QgsSpatiaLiteProvider::importVector(
QgsVectorLayer *layer,
QgsVectorLayerImport::ImportError
QgsSpatiaLiteProvider::createEmptyLayer(
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected,
const QgsFieldMap &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage,
bool skipAttributeCreation,
const QMap<QString,QVariant> *options )
{
const QgsCoordinateReferenceSystem* outputCRS;
QgsCoordinateTransform* ct = 0;
int shallTransform = false;
if ( !layer )
{
return ErrInvalidLayer;
}
if ( destCRS && destCRS->isValid() )
{
// This means we should transform
outputCRS = destCRS;
shallTransform = true;
}
else
{
// This means we shouldn't transform, use source CRS as output (if defined)
outputCRS = &layer->crs();
}
QgsFieldMap fields = skipAttributeCreation ? QgsFieldMap() : layer->pendingFields();
Q_UNUSED( options );
// populate members from the uri structure
QgsDataSourceURI dsUri( uri );
@ -141,6 +121,7 @@ QgsSpatiaLiteProvider::importVector(
sqlite3 *sqliteHandle = NULL;
char *errMsg = NULL;
int toCommit = false;
QString sql;
// trying to open the SQLite DB
spatialite_init( 0 );
@ -150,7 +131,7 @@ QgsSpatiaLiteProvider::importVector(
QgsDebugMsg( "Connection to database failed. Import of layer aborted." );
if ( errorMessage )
*errorMessage = QObject::tr( "Connection to database failed" );
return ErrConnectionFailed;
return QgsVectorLayerImport::ErrConnectionFailed;
}
sqliteHandle = handle->handle();
@ -174,12 +155,12 @@ QgsSpatiaLiteProvider::importVector(
else
{
// search for the passed field
for ( QgsFieldMap::iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
for ( QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
{
if ( fldIt.value().name() == primaryKey )
{
// found, get the field type
QgsField &fld = fldIt.value();
QgsField fld = fldIt.value();
if ( convertField( fld ) )
{
primaryKeyType = fld.typeName();
@ -192,11 +173,12 @@ QgsSpatiaLiteProvider::importVector(
if ( primaryKeyType.isEmpty() )
{
primaryKeyType = "INTEGER";
/* TODO
// check the feature count to choose if create a bigint pk field
if ( layer->featureCount() > 0xFFFFFF )
{
primaryKeyType = "BIGINT";
}
}*/
}
try
@ -207,21 +189,23 @@ QgsSpatiaLiteProvider::importVector(
toCommit = true;
// delete the table if exists and the related entry in geometry_columns, then re-create it
QString sql = QString( "DROP TABLE IF EXISTS %1" )
.arg( quotedIdentifier( tableName ) );
if ( overwrite )
{
// delete the table if exists and the related entry in geometry_columns, then re-create it
sql = QString( "DROP TABLE IF EXISTS %1" )
.arg( quotedIdentifier( tableName ) );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
sql = QString( "DELETE FROM geometry_columns WHERE f_table_name = %1" )
.arg( quotedValue( tableName ) );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
sql = QString( "DELETE FROM geometry_columns WHERE f_table_name = %1" )
.arg( quotedValue( tableName ) );
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
}
sql = QString( "CREATE TABLE %1 (%2 %3 PRIMARY KEY)" )
.arg( quotedIdentifier( tableName ) )
@ -234,9 +218,9 @@ QgsSpatiaLiteProvider::importVector(
// get geometry type, dim and srid
int dim = 2;
long srid = outputCRS->postgisSrid();
long srid = srs->postgisSrid();
switch( layer->wkbType() )
switch( wkbType )
{
case QGis::WKBPoint25D:
dim = 3;
@ -285,7 +269,7 @@ QgsSpatiaLiteProvider::importVector(
}
// create geometry column
if ( !geometryType.isEmpty() && dim > 0 )
if ( !geometryType.isEmpty() )
{
sql = QString( "SELECT AddGeometryColumn(%1, %2, %3, %4, %5)" )
.arg( QgsSpatiaLiteProvider::quotedValue( tableName ) )
@ -297,13 +281,10 @@ QgsSpatiaLiteProvider::importVector(
ret = sqlite3_exec( sqliteHandle, sql.toUtf8().constData(), NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
throw SLException( errMsg );
}
else
{
geometryColumn = QString();
geometryType = QString();
dim = 0;
}
ret = sqlite3_exec( sqliteHandle, "COMMIT", NULL, NULL, &errMsg );
@ -331,7 +312,7 @@ QgsSpatiaLiteProvider::importVector(
}
SqliteHandles::closeDb( handle );
return ErrCreateLayer;
return QgsVectorLayerImport::ErrCreateLayer;
}
SqliteHandles::closeDb( handle );
@ -349,19 +330,24 @@ QgsSpatiaLiteProvider::importVector(
.arg( tableName );
delete provider;
return ErrCreateLayer;
return QgsVectorLayerImport::ErrInvalidLayer;
}
QgsDebugMsg( "layer loaded" );
// add fields to the layer
if ( oldToNewAttrIdxMap )
oldToNewAttrIdxMap->clear();
if ( fields.size() > 0 )
{
int offset = 1;
// get the list of fields
QList<QgsField> flist;
for ( QgsFieldMap::iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
for ( QgsFieldMap::const_iterator fldIt = fields.begin(); fldIt != fields.end(); ++fldIt )
{
QgsField &fld = fldIt.value();
QgsField fld = fldIt.value();
if ( fld.name() == primaryKey )
continue;
@ -371,28 +357,28 @@ QgsSpatiaLiteProvider::importVector(
continue;
}
// convert field type only if the source provider is different from the destination one
// because if both source and destination use the same provider,
// the field type should be natively supported
if ( layer->dataProvider()->name() != provider->name() )
if ( !convertField( fld ) )
{
if ( !convertField( fld ) )
{
QgsDebugMsg( "error creating field " + fld.name() + ": unsupported type" );
if ( errorMessage )
*errorMessage = QObject::tr( "unsupported type for field %1" )
.arg( fld.name() );
QgsDebugMsg( "error creating field " + fld.name() + ": unsupported type" );
if ( errorMessage )
*errorMessage = QObject::tr( "unsupported type for field %1" )
.arg( fld.name() );
delete provider;
return ErrAttributeTypeUnsupported;
}
delete provider;
return QgsVectorLayerImport::ErrAttributeTypeUnsupported;
}
flist.append( fld );
QgsDebugMsg( "creating field " + fld.name() +
QgsDebugMsg( "creating field #" + QString::number( fldIt.key() ) +
" -> #" + QString::number( offset ) +
" name " + fld.name() +
" type " + QString( QVariant::typeToName( fld.type() ) ) +
" typename " + fld.typeName() +
" width " + QString::number( fld.length() ) +
" precision " + QString::number( fld.precision() ) );
flist.append( fld );
if ( oldToNewAttrIdxMap )
oldToNewAttrIdxMap->insert( fldIt.key(), offset++ );
}
if ( !provider->addAttributes( flist ) )
@ -402,171 +388,12 @@ QgsSpatiaLiteProvider::importVector(
*errorMessage = QObject::tr( "creation of fields failed" );
delete provider;
return ErrAttributeCreationFailed;
return QgsVectorLayerImport::ErrAttributeCreationFailed;
}
QgsDebugMsg( "Done creating fields" );
}
QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->pendingAllAttributesList();
QgsFeature fet;
layer->select( allAttr, QgsRectangle(), layer->wkbType() != QGis::WKBNoGeometry );
const QgsFeatureIds& ids = layer->selectedFeaturesIds();
// Create our transform
if ( destCRS )
{
ct = new QgsCoordinateTransform( layer->crs(), *destCRS );
}
// Check for failure
if ( ct == NULL )
{
shallTransform = false;
}
int n = 0, errors = 0;
char *errMsg = NULL;
// make all the changes using one transaction only
int ret = sqlite3_exec( provider->sqliteHandle, "BEGIN", NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
{
QgsDebugMsg( "unable to start a new transaction" );
if ( errorMessage )
*errorMessage = QObject::tr( "unable to start a new transaction. Writing of features failed" );
if ( errMsg )
sqlite3_free( errMsg );
delete provider;
return ErrFeatureWriteFailed;
}
if ( errorMessage )
errorMessage->clear();
// write all features
QgsFeatureList flist;
while (1)
{
while ( layer->nextFeature( fet ) )
{
if ( onlySelected && !ids.contains( fet.id() ) )
continue;
if ( shallTransform )
{
try
{
if ( fet.geometry() )
{
fet.geometry()->transform( *ct );
}
}
catch ( QgsCsException &e )
{
delete ct;
QString msg = QObject::tr( "Failed to transform a point while drawing a feature of type '%1'. Writing stopped. (Exception: %2)" )
.arg( fet.typeName() ).arg( e.what() );
QgsLogger::warning( msg );
if ( errorMessage )
*errorMessage = msg;
sqlite3_exec( provider->sqliteHandle, "ROLLBACK", NULL, NULL, NULL );
delete provider;
return ErrProjection;
}
}
if ( skipAttributeCreation )
{
fet.clearAttributeMap();
}
else
{
// fix attributes position based on how the table was created.
// The first field is reserved to the primary key, so move the
// other attributes after this field
int offset = 1;
const QgsAttributeMap &attrs = fet.attributeMap();
QgsAttributeMap newAttrs;
for ( QgsAttributeMap::const_iterator it = attrs.begin(); it != attrs.end(); it++ )
{
const QgsField &fld = fields[ it.key() ];
if ( fld.name() == primaryKey || fld.name() == geometryColumn )
continue;
newAttrs.insert( offset++, *it );
}
fet.setAttributeMap( newAttrs );
}
flist.append( fet );
// add 100 features at the same time
if ( flist.size() == 100 )
break;
}
if ( !provider->addFeatures( flist, false ) )
{
QgsDebugMsg( QString( "failed while adding features from %1 to %2" )
.arg( flist.first().id() )
.arg( flist.last().id() )
);
if ( errorMessage )
*errorMessage += QObject::tr( "failed while adding features from %1 to %2\n" )
.arg( flist.first().id() )
.arg( flist.last().id() );
}
n += flist.size();
if ( flist.size() < 100 )
break;
flist.clear();
}
ret = sqlite3_exec( provider->sqliteHandle, "COMMIT", NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
{
QgsDebugMsg( QString( "unable to write features on the database: failed to commit the transaction. %1" )
.arg( errMsg ? QString::fromUtf8( errMsg ) : "unknown cause" )
);
if ( errorMessage )
*errorMessage = QObject::tr( "unable to write features on the database: failed to commit the transaction. %1" )
.arg( errMsg ? QString::fromUtf8( errMsg ) : "unknown cause" );
if ( errMsg )
sqlite3_free( errMsg );
sqlite3_exec( provider->sqliteHandle, "ROLLBACK", NULL, NULL, NULL );
delete provider;
return ErrFeatureWriteFailed;
}
delete provider;
if ( shallTransform )
{
delete ct;
}
if ( errors > 0 && n > 0 )
{
if ( errorMessage )
{
*errorMessage += QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n );
}
return ErrFeatureWriteFailed;
}
return NoError;
return QgsVectorLayerImport::NoError;
}
@ -3718,11 +3545,6 @@ void QgsSpatiaLiteProvider::uniqueValues( int index, QList < QVariant > &uniqueV
bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList & flist )
{
return addFeatures( flist, true );
}
bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList & flist, bool useNewTransaction )
{
sqlite3_stmt *stmt = NULL;
char *errMsg = NULL;
@ -3736,16 +3558,13 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList & flist, bool useNewTran
return true;
const QgsAttributeMap & attributevec = flist[0].attributeMap();
if ( useNewTransaction )
ret = sqlite3_exec( sqliteHandle, "BEGIN", NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
{
ret = sqlite3_exec( sqliteHandle, "BEGIN", NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
toCommit = true;
// some error occurred
goto abort;
}
toCommit = true;
sql = QString( "INSERT INTO %1(" ).arg( quotedIdentifier( mTableName ) );
values = QString( ") VALUES (" );
@ -3877,14 +3696,11 @@ bool QgsSpatiaLiteProvider::addFeatures( QgsFeatureList & flist, bool useNewTran
}
sqlite3_finalize( stmt );
if ( useNewTransaction )
ret = sqlite3_exec( sqliteHandle, "COMMIT", NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
{
ret = sqlite3_exec( sqliteHandle, "COMMIT", NULL, NULL, &errMsg );
if ( ret != SQLITE_OK )
{
// some error occurred
goto abort;
}
// some error occurred
goto abort;
}
return true;
@ -5064,14 +4880,18 @@ QGISEXTERN bool isProvider()
return true;
}
QGISEXTERN int importVector(
QgsVectorLayer *layer,
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected,
QString *errorMessage,
bool skipAttributeCreation,
const QMap<QString,QVariant> *options )
QGISEXTERN QgsVectorLayerImport::ImportError createEmptyLayer(
const QString& uri,
const QgsFieldMap &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage,
const QMap<QString,QVariant> *options )
{
return QgsSpatiaLiteProvider::importVector( layer, uri, destCRS, onlySelected, errorMessage, skipAttributeCreation, options );
return QgsSpatiaLiteProvider::createEmptyLayer(
uri, fields, wkbType, srs, overwrite,
oldToNewAttrIdxMap, errorMessage, options
);
}

View File

@ -24,6 +24,7 @@ extern "C"
#include "qgsvectordataprovider.h"
#include "qgsrectangle.h"
#include "qgsvectorlayerimport.h"
#include <list>
#include <queue>
#include <fstream>
@ -31,7 +32,6 @@ extern "C"
class QgsFeature;
class QgsField;
class QgsVectorLayer;
#include "qgsdatasourceuri.h"
@ -47,29 +47,17 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider
{
Q_OBJECT public:
enum WriterError
{
NoError = 0,
ErrDriverNotFound,
ErrCreateDataSource,
ErrCreateLayer,
ErrAttributeTypeUnsupported,
ErrAttributeCreationFailed,
ErrProjection,
ErrFeatureWriteFailed,
ErrInvalidLayer,
ErrConnectionFailed,
};
/** Import a vector layer into the database */
static WriterError importVector( QgsVectorLayer* layer,
const QString& uri,
const QgsCoordinateReferenceSystem *destCRS,
bool onlySelected = false,
QString *errorMessage = 0,
bool skipAttributeCreation = false,
const QMap<QString,QVariant> *options = 0
);
static QgsVectorLayerImport::ImportError createEmptyLayer(
const QString& uri,
const QgsFieldMap &fields,
QGis::WkbType wkbType,
const QgsCoordinateReferenceSystem *srs,
bool overwrite,
QMap<int, int> *oldToNewAttrIdxMap,
QString *errorMessage = 0,
const QMap<QString,QVariant> *options = 0
);
/**
* Constructor of the vector provider
@ -288,8 +276,7 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider
/** loads fields from input file to member attributeFields */
void loadFields();
/** convert a QgsField to work with SL
*/
/** convert a QgsField to work with SL */
static bool convertField( QgsField &field );
QgsFieldMap attributeFields;
@ -395,11 +382,6 @@ class QgsSpatiaLiteProvider: public QgsVectorDataProvider
const QgsField & field( int index ) const;
/**Adds a list of features
@param useNewTransaction create a new transaction
@return true in case of success and false in case of failure*/
bool addFeatures( QgsFeatureList & flist, bool useNewTransaction = true );
/**
* internal utility functions used to handle common SQLite tasks
*/