1
0
mirror of https://github.com/qgis/QGIS.git synced 2025-04-28 00:05:04 -04:00
QGIS/src/core/qgsvectorlayerimport.cpp
2016-07-15 06:00:11 +10:00

449 lines
11 KiB
C++

/***************************************************************************
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 "qgsmessagelog.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsvectorlayerimport.h"
#include "qgsproviderregistry.h"
#include "qgsdatasourceuri.h"
#include <QProgressDialog>
#define FEATURE_BUFFER_SIZE 200
typedef QgsVectorLayerImport::ImportError createEmptyLayer_t(
const QString &uri,
const QgsFields &fields,
QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem &destCRS,
bool overwrite,
QMap<int, int> *oldToNewAttrIdx,
QString *errorMessage,
const QMap<QString, QVariant> *options
);
QgsVectorLayerImport::QgsVectorLayerImport( const QString &uri,
const QString &providerKey,
const QgsFields& fields,
QGis::WkbType geometryType,
const QgsCoordinateReferenceSystem& crs,
bool overwrite,
const QMap<QString, QVariant> *options,
QProgressDialog *progress )
: mErrorCount( 0 )
, mAttributeCount( -1 )
, mProgress( progress )
{
mProvider = nullptr;
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 = reinterpret_cast< createEmptyLayer_t * >( cast_to_fptr( myLib->resolve( "createEmptyLayer" ) ) );
if ( !pCreateEmpty )
{
delete myLib;
mError = ErrProviderUnsupportedFeature;
mErrorMessage = QObject::tr( "Provider %1 has no %2 method" ).arg( providerKey, "createEmptyLayer" );
return;
}
delete myLib;
// create an empty layer
QString errMsg;
mError = pCreateEmpty( uri, fields, geometryType, crs, overwrite, &mOldToNewAttrIdx, &errMsg, options );
if ( hasError() )
{
mErrorMessage = errMsg;
return;
}
Q_FOREACH ( int idx, mOldToNewAttrIdx )
{
if ( idx > mAttributeCount )
mAttributeCount = idx;
}
mAttributeCount++;
QgsDebugMsg( "Created empty layer" );
QgsVectorDataProvider *vectorProvider = dynamic_cast< QgsVectorDataProvider* >( pReg->provider( providerKey, uri ) );
if ( !vectorProvider || !vectorProvider->isValid() || ( vectorProvider->capabilities() & QgsVectorDataProvider::AddFeatures ) == 0 )
{
mError = ErrInvalidLayer;
mErrorMessage = QObject::tr( "Loading of layer failed" );
if ( vectorProvider )
delete vectorProvider;
return;
}
mProvider = vectorProvider;
mError = NoError;
}
QgsVectorLayerImport::~QgsVectorLayerImport()
{
flushBuffer();
if ( mProvider )
delete mProvider;
}
QgsVectorLayerImport::ImportError QgsVectorLayerImport::hasError()
{
return mError;
}
QString QgsVectorLayerImport::errorMessage()
{
return mErrorMessage;
}
bool QgsVectorLayerImport::addFeature( QgsFeature& feat )
{
QgsAttributes attrs = feat.attributes();
QgsFeature newFeat;
if ( feat.constGeometry() )
newFeat.setGeometry( *feat.constGeometry() );
newFeat.initAttributes( mAttributeCount );
for ( int i = 0; i < attrs.count(); ++i )
{
// add only mapped attributes (un-mapped ones will not be present in the
// destination layer)
int dstIdx = mOldToNewAttrIdx.value( i, -1 );
if ( dstIdx < 0 )
continue;
QgsDebugMsgLevel( QString( "moving field from pos %1 to %2" ).arg( i ).arg( dstIdx ), 3 );
newFeat.setAttribute( dstIdx, attrs.at( i ) );
}
mFeatureBuffer.append( newFeat );
if ( mFeatureBuffer.count() >= FEATURE_BUFFER_SIZE )
{
return flushBuffer();
}
return true;
}
bool QgsVectorLayerImport::flushBuffer()
{
if ( mFeatureBuffer.count() <= 0 )
return true;
if ( !mProvider->addFeatures( mFeatureBuffer ) )
{
QStringList errors = mProvider->errors();
mProvider->clearErrors();
mErrorMessage = QObject::tr( "Creation error for features from #%1 to #%2. Provider errors was: \n%3" )
.arg( mFeatureBuffer.first().id() )
.arg( mFeatureBuffer.last().id() )
.arg( errors.join( "\n" ) );
mError = ErrFeatureWriteFailed;
mErrorCount += mFeatureBuffer.count();
mFeatureBuffer.clear();
QgsDebugMsg( mErrorMessage );
return false;
}
mFeatureBuffer.clear();
return true;
}
bool QgsVectorLayerImport::createSpatialIndex()
{
if ( mProvider && ( mProvider->capabilities() & QgsVectorDataProvider::CreateSpatialIndex ) != 0 )
{
return mProvider->createSpatialIndex();
}
else
{
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,
QProgressDialog *progress )
{
QgsCoordinateReferenceSystem outputCRS;
QgsCoordinateTransform* ct = nullptr;
bool shallTransform = false;
if ( !layer )
return ErrInvalidLayer;
if ( 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();
}
bool overwrite = false;
bool forceSinglePartGeom = false;
if ( options )
{
overwrite = options->take( "overwrite" ).toBool();
forceSinglePartGeom = options->take( "forceSinglePartGeometryType" ).toBool();
}
QgsFields fields = skipAttributeCreation ? QgsFields() : layer->fields();
QGis::WkbType wkbType = layer->wkbType();
// Special handling for Shapefiles
if ( layer->providerType() == "ogr" && layer->storageType() == "ESRI Shapefile" )
{
// convert field names to lowercase
for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
{
fields[fldIdx].setName( fields.at( fldIdx ).name().toLower() );
}
if ( !forceSinglePartGeom )
{
// convert wkbtype to multipart (see #5547)
switch ( wkbType )
{
case QGis::WKBPoint:
wkbType = QGis::WKBMultiPoint;
break;
case QGis::WKBLineString:
wkbType = QGis::WKBMultiLineString;
break;
case QGis::WKBPolygon:
wkbType = QGis::WKBMultiPolygon;
break;
case QGis::WKBPoint25D:
wkbType = QGis::WKBMultiPoint25D;
break;
case QGis::WKBLineString25D:
wkbType = QGis::WKBMultiLineString25D;
break;
case QGis::WKBPolygon25D:
wkbType = QGis::WKBMultiPolygon25D;
break;
default:
break;
}
}
}
QgsVectorLayerImport * writer =
new QgsVectorLayerImport( uri, providerKey, fields, wkbType, outputCRS, overwrite, options, progress );
// 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->attributeList();
QgsFeature fet;
QgsFeatureRequest req;
if ( wkbType == QGis::WKBNoGeometry )
req.setFlags( QgsFeatureRequest::NoGeometry );
if ( skipAttributeCreation )
req.setSubsetOfAttributes( QgsAttributeList() );
QgsFeatureIterator fit = layer->getFeatures( req );
const QgsFeatureIds& ids = layer->selectedFeaturesIds();
// Create our transform
if ( destCRS.isValid() )
ct = new QgsCoordinateTransform( layer->crs(), destCRS );
// Check for failure
if ( !ct )
shallTransform = false;
int n = 0;
if ( errorMessage )
{
*errorMessage = QObject::tr( "Feature write errors:" );
}
if ( progress )
{
progress->setRange( 0, layer->featureCount() );
}
bool cancelled = false;
// write all features
while ( fit.nextFeature( fet ) )
{
if ( progress && progress->wasCanceled() )
{
cancelled = true;
if ( errorMessage )
{
*errorMessage += '\n' + QObject::tr( "Import was canceled at %1 of %2" ).arg( progress->value() ).arg( progress->maximum() );
}
break;
}
if ( writer->errorCount() > 1000 )
{
if ( errorMessage )
{
*errorMessage += '\n' + QObject::tr( "Stopping after %1 errors" ).arg( writer->errorCount() );
}
break;
}
if ( onlySelected && !ids.contains( fet.id() ) )
continue;
if ( shallTransform )
{
try
{
if ( fet.constGeometry() )
{
fet.geometry()->transform( *ct );
}
}
catch ( QgsCsException &e )
{
delete ct;
delete writer;
QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
.arg( fet.id() ).arg( e.what() );
QgsMessageLog::logMessage( msg, QObject::tr( "Vector import" ) );
if ( errorMessage )
*errorMessage += '\n' + msg;
return ErrProjection;
}
}
if ( skipAttributeCreation )
{
fet.initAttributes( 0 );
}
if ( !writer->addFeature( fet ) )
{
if ( writer->hasError() && errorMessage )
{
*errorMessage += '\n' + writer->errorMessage();
}
}
n++;
if ( progress )
{
progress->setValue( n );
}
}
// flush the buffer to be sure that all features are written
if ( !writer->flushBuffer() )
{
if ( writer->hasError() && errorMessage )
{
*errorMessage += '\n' + writer->errorMessage();
}
}
int errors = writer->errorCount();
if ( !writer->createSpatialIndex() )
{
if ( writer->hasError() && errorMessage )
{
*errorMessage += '\n' + writer->errorMessage();
}
}
delete writer;
if ( shallTransform )
{
delete ct;
}
if ( errorMessage )
{
if ( errors > 0 )
{
*errorMessage += '\n' + QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n );
}
else
{
errorMessage->clear();
}
}
if ( cancelled )
return ErrUserCancelled;
else if ( errors > 0 )
return ErrFeatureWriteFailed;
return NoError;
}