QGIS API Documentation  3.0.2-Girona (307d082)
qgsvectorlayerexporter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerexporter.cpp
3  -------------------
4  begin : Thu Aug 25 2011
5  copyright : (C) 2011 by Giuseppe Sucameli
6  email : brush.tyler at gmail.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsfields.h"
19 #include "qgsfeature.h"
20 #include "qgsfeatureiterator.h"
21 #include "qgsgeometry.h"
22 #include "qgslogger.h"
23 #include "qgsmessagelog.h"
25 #include "qgsvectorlayerexporter.h"
26 #include "qgsproviderregistry.h"
27 #include "qgsdatasourceuri.h"
28 #include "qgsexception.h"
29 #include "qgsvectordataprovider.h"
30 #include "qgsvectorlayer.h"
31 
32 #include <QProgressDialog>
33 
34 #define FEATURE_BUFFER_SIZE 200
35 
37  const QString &uri,
38  const QgsFields &fields,
39  QgsWkbTypes::Type geometryType,
40  const QgsCoordinateReferenceSystem &destCRS,
41  bool overwrite,
42  QMap<int, int> *oldToNewAttrIdx,
43  QString *errorMessage,
44  const QMap<QString, QVariant> *options
45 );
46 
47 
49  const QString &providerKey,
50  const QgsFields &fields,
51  QgsWkbTypes::Type geometryType,
53  bool overwrite,
54  const QMap<QString, QVariant> &options )
55  : mErrorCount( 0 )
56  , mAttributeCount( -1 )
57 
58 {
59  mProvider = nullptr;
60 
62 
63  std::unique_ptr< QLibrary > myLib( pReg->createProviderLibrary( providerKey ) );
64  if ( !myLib )
65  {
66  mError = ErrInvalidProvider;
67  mErrorMessage = QObject::tr( "Unable to load %1 provider" ).arg( providerKey );
68  return;
69  }
70 
71  createEmptyLayer_t *pCreateEmpty = reinterpret_cast< createEmptyLayer_t * >( cast_to_fptr( myLib->resolve( "createEmptyLayer" ) ) );
72  if ( !pCreateEmpty )
73  {
75  mErrorMessage = QObject::tr( "Provider %1 has no %2 method" ).arg( providerKey, QStringLiteral( "createEmptyLayer" ) );
76  return;
77  }
78 
79  // create an empty layer
80  QString errMsg;
81  mError = pCreateEmpty( uri, fields, geometryType, crs, overwrite, &mOldToNewAttrIdx, &errMsg, !options.isEmpty() ? &options : nullptr );
82  if ( errorCode() )
83  {
84  mErrorMessage = errMsg;
85  return;
86  }
87 
88  Q_FOREACH ( int idx, mOldToNewAttrIdx )
89  {
90  if ( idx > mAttributeCount )
91  mAttributeCount = idx;
92  }
93 
94  mAttributeCount++;
95 
96  QgsDebugMsg( "Created empty layer" );
97 
98  QString uriUpdated( uri );
99  // HACK sorry...
100  if ( providerKey == QLatin1String( "ogr" ) )
101  {
102  QString layerName;
103  if ( options.contains( QStringLiteral( "layerName" ) ) )
104  layerName = options.value( QStringLiteral( "layerName" ) ).toString();
105  if ( !layerName.isEmpty() )
106  {
107  uriUpdated += QLatin1String( "|layername=" );
108  uriUpdated += layerName;
109  }
110  }
111  QgsVectorDataProvider *vectorProvider = dynamic_cast< QgsVectorDataProvider * >( pReg->createProvider( providerKey, uriUpdated ) );
112  if ( !vectorProvider || !vectorProvider->isValid() || ( vectorProvider->capabilities() & QgsVectorDataProvider::AddFeatures ) == 0 )
113  {
114  mError = ErrInvalidLayer;
115  mErrorMessage = QObject::tr( "Loading of layer failed" );
116 
117  delete vectorProvider;
118  return;
119  }
120 
121  mProvider = vectorProvider;
122  mError = NoError;
123 }
124 
126 {
127  flushBuffer();
128 
129  if ( mProvider )
130  delete mProvider;
131 }
132 
134 {
135  return mError;
136 }
137 
139 {
140  return mErrorMessage;
141 }
142 
144 {
145  QgsFeatureList::iterator fIt = features.begin();
146  bool result = true;
147  for ( ; fIt != features.end(); ++fIt )
148  {
149  result = result && addFeature( *fIt, flags );
150  }
151  return result;
152 }
153 
155 {
156  QgsAttributes attrs = feat.attributes();
157 
158  QgsFeature newFeat;
159  if ( feat.hasGeometry() )
160  newFeat.setGeometry( feat.geometry() );
161 
162  newFeat.initAttributes( mAttributeCount );
163 
164  for ( int i = 0; i < attrs.count(); ++i )
165  {
166  // add only mapped attributes (un-mapped ones will not be present in the
167  // destination layer)
168  int dstIdx = mOldToNewAttrIdx.value( i, -1 );
169  if ( dstIdx < 0 )
170  continue;
171 
172  QgsDebugMsgLevel( QString( "moving field from pos %1 to %2" ).arg( i ).arg( dstIdx ), 3 );
173  newFeat.setAttribute( dstIdx, attrs.at( i ) );
174  }
175 
176  mFeatureBuffer.append( newFeat );
177 
178  if ( mFeatureBuffer.count() >= FEATURE_BUFFER_SIZE )
179  {
180  return flushBuffer();
181  }
182 
183  return true;
184 }
185 
187 {
188  if ( mFeatureBuffer.count() <= 0 )
189  return true;
190 
191  if ( !mProvider->addFeatures( mFeatureBuffer, QgsFeatureSink::FastInsert ) )
192  {
193  QStringList errors = mProvider->errors();
194  mProvider->clearErrors();
195 
196  mErrorMessage = QObject::tr( "Creation error for features from #%1 to #%2. Provider errors was: \n%3" )
197  .arg( mFeatureBuffer.first().id() )
198  .arg( mFeatureBuffer.last().id() )
199  .arg( errors.join( QStringLiteral( "\n" ) ) );
200 
201  mError = ErrFeatureWriteFailed;
202  mErrorCount += mFeatureBuffer.count();
203 
204  mFeatureBuffer.clear();
205  QgsDebugMsg( mErrorMessage );
206  return false;
207  }
208 
209  mFeatureBuffer.clear();
210  return true;
211 }
212 
213 bool QgsVectorLayerExporter::createSpatialIndex()
214 {
215  if ( mProvider && ( mProvider->capabilities() & QgsVectorDataProvider::CreateSpatialIndex ) != 0 )
216  {
217  return mProvider->createSpatialIndex();
218  }
219  else
220  {
221  return true;
222  }
223 }
224 
227  const QString &uri,
228  const QString &providerKey,
229  const QgsCoordinateReferenceSystem &destCRS,
230  bool onlySelected,
231  QString *errorMessage,
232  const QMap<QString, QVariant> &options,
233  QgsFeedback *feedback )
234 {
237  bool shallTransform = false;
238 
239  if ( !layer )
240  return ErrInvalidLayer;
241 
242  if ( destCRS.isValid() )
243  {
244  // This means we should transform
245  outputCRS = destCRS;
246  shallTransform = true;
247  }
248  else
249  {
250  // This means we shouldn't transform, use source CRS as output (if defined)
251  outputCRS = layer->crs();
252  }
253 
254 
255  bool overwrite = false;
256  bool forceSinglePartGeom = false;
257  QMap<QString, QVariant> providerOptions = options;
258  if ( !options.isEmpty() )
259  {
260  overwrite = providerOptions.take( QStringLiteral( "overwrite" ) ).toBool();
261  forceSinglePartGeom = providerOptions.take( QStringLiteral( "forceSinglePartGeometryType" ) ).toBool();
262  }
263 
264  QgsFields fields = layer->fields();
265  QgsWkbTypes::Type wkbType = layer->wkbType();
266 
267  // Special handling for Shapefiles
268  if ( layer->providerType() == QLatin1String( "ogr" ) && layer->storageType() == QLatin1String( "ESRI Shapefile" ) )
269  {
270  // convert field names to lowercase
271  for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
272  {
273  fields[fldIdx].setName( fields.at( fldIdx ).name().toLower() );
274  }
275 
276  if ( !forceSinglePartGeom )
277  {
278  // convert wkbtype to multipart (see #5547)
279  switch ( wkbType )
280  {
281  case QgsWkbTypes::Point:
282  wkbType = QgsWkbTypes::MultiPoint;
283  break;
286  break;
288  wkbType = QgsWkbTypes::MultiPolygon;
289  break;
291  wkbType = QgsWkbTypes::MultiPoint25D;
292  break;
295  break;
298  break;
299  default:
300  break;
301  }
302  }
303  }
304 
305  QgsVectorLayerExporter *writer =
306  new QgsVectorLayerExporter( uri, providerKey, fields, wkbType, outputCRS, overwrite, providerOptions );
307 
308  // check whether file creation was successful
309  ExportError err = writer->errorCode();
310  if ( err != NoError )
311  {
312  if ( errorMessage )
313  *errorMessage = writer->errorMessage();
314  delete writer;
315  return err;
316  }
317 
318  if ( errorMessage )
319  {
320  errorMessage->clear();
321  }
322 
323  QgsFeature fet;
324 
325  QgsFeatureRequest req;
326  if ( wkbType == QgsWkbTypes::NoGeometry )
328  if ( onlySelected )
329  req.setFilterFids( layer->selectedFeatureIds() );
330 
331  QgsFeatureIterator fit = layer->getFeatures( req );
332 
333  // Create our transform
334  if ( destCRS.isValid() )
335  {
337  ct = QgsCoordinateTransform( layer->crs(), destCRS );
339  }
340 
341  // Check for failure
342  if ( !ct.isValid() )
343  shallTransform = false;
344 
345  long n = 0;
346  long approxTotal = onlySelected ? layer->selectedFeatureCount() : layer->featureCount();
347 
348  if ( errorMessage )
349  {
350  *errorMessage = QObject::tr( "Feature write errors:" );
351  }
352 
353  bool canceled = false;
354 
355  // write all features
356  while ( fit.nextFeature( fet ) )
357  {
358  if ( feedback && feedback->isCanceled() )
359  {
360  canceled = true;
361  if ( errorMessage )
362  {
363  *errorMessage += '\n' + QObject::tr( "Import was canceled at %1 of %2" ).arg( n ).arg( approxTotal );
364  }
365  break;
366  }
367 
368  if ( writer->errorCount() > 1000 )
369  {
370  if ( errorMessage )
371  {
372  *errorMessage += '\n' + QObject::tr( "Stopping after %1 errors" ).arg( writer->errorCount() );
373  }
374  break;
375  }
376 
377  if ( shallTransform )
378  {
379  try
380  {
381  if ( fet.hasGeometry() )
382  {
383  QgsGeometry g = fet.geometry();
384  g.transform( ct );
385  fet.setGeometry( g );
386  }
387  }
388  catch ( QgsCsException &e )
389  {
390  delete writer;
391 
392  QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
393  .arg( fet.id() ).arg( e.what() );
394  QgsMessageLog::logMessage( msg, QObject::tr( "Vector import" ) );
395  if ( errorMessage )
396  *errorMessage += '\n' + msg;
397 
398  return ErrProjection;
399  }
400  }
401  if ( !writer->addFeature( fet ) )
402  {
403  if ( writer->errorCode() && errorMessage )
404  {
405  *errorMessage += '\n' + writer->errorMessage();
406  }
407  }
408  n++;
409 
410  if ( feedback )
411  {
412  feedback->setProgress( 100.0 * static_cast< double >( n ) / approxTotal );
413  }
414 
415  }
416 
417  // flush the buffer to be sure that all features are written
418  if ( !writer->flushBuffer() )
419  {
420  if ( writer->errorCode() && errorMessage )
421  {
422  *errorMessage += '\n' + writer->errorMessage();
423  }
424  }
425  int errors = writer->errorCount();
426 
427  if ( !writer->createSpatialIndex() )
428  {
429  if ( writer->errorCode() && errorMessage )
430  {
431  *errorMessage += '\n' + writer->errorMessage();
432  }
433  }
434 
435  delete writer;
436 
437  if ( errorMessage )
438  {
439  if ( errors > 0 )
440  {
441  *errorMessage += '\n' + QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n );
442  }
443  else
444  {
445  errorMessage->clear();
446  }
447  }
448 
449  if ( canceled )
450  return ErrUserCanceled;
451  else if ( errors > 0 )
452  return ErrFeatureWriteFailed;
453 
454  return NoError;
455 }
456 
457 
458 //
459 // QgsVectorLayerExporterTask
460 //
461 
462 QgsVectorLayerExporterTask::QgsVectorLayerExporterTask( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, const QMap<QString, QVariant> &options, bool ownsLayer )
463  : QgsTask( tr( "Exporting %1" ).arg( layer->name() ), QgsTask::CanCancel )
464  , mLayer( layer )
465  , mOwnsLayer( ownsLayer )
466  , mDestUri( uri )
467  , mDestProviderKey( providerKey )
468  , mDestCrs( destinationCrs )
469  , mOptions( options )
470  , mOwnedFeedback( new QgsFeedback() )
471 {
472  if ( mLayer )
473  setDependentLayers( QList< QgsMapLayer * >() << mLayer );
474 }
475 
476 QgsVectorLayerExporterTask *QgsVectorLayerExporterTask::withLayerOwnership( QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, const QMap<QString, QVariant> &options )
477 {
478  std::unique_ptr< QgsVectorLayerExporterTask > newTask( new QgsVectorLayerExporterTask( layer, uri, providerKey, destinationCrs, options ) );
479  newTask->mOwnsLayer = true;
480  return newTask.release();
481 }
482 
484 {
485  mOwnedFeedback->cancel();
486  QgsTask::cancel();
487 }
488 
490 {
491  if ( !mLayer )
492  return false;
493 
494  connect( mOwnedFeedback.get(), &QgsFeedback::progressChanged, this, &QgsVectorLayerExporterTask::setProgress );
495 
496 
498  mLayer.data(), mDestUri, mDestProviderKey, mDestCrs, false, &mErrorMessage,
499  mOptions, mOwnedFeedback.get() );
500 
501  return mError == QgsVectorLayerExporter::NoError;
502 }
503 
505 {
506  // QgsMapLayer has QTimer member, which must not be destroyed from another thread
507  if ( mOwnsLayer )
508  delete mLayer;
509 
510  if ( result )
511  emit exportComplete();
512  else
513  emit errorOccurred( mError, mErrorMessage );
514 }
QgsFeatureId id
Definition: qgsfeature.h:71
Wrapper for iterator of features from vector data provider or vector layer.
Could not access newly created destination layer.
bool flushBuffer() override
Flushes any internal buffer which may exist in the sink, causing any buffered features to be added to...
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
void setProgress(double progress)
Sets the task&#39;s current progress.
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
QString name
Definition: qgsfield.h:57
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool addFeatures(QgsFeatureList &flist, QgsFeatureSink::Flags flags=nullptr) override
Adds a list of features to the sink.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:549
#define FEATURE_BUFFER_SIZE
static ExportError exportLayer(QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destCRS, bool onlySelected=false, QString *errorMessage=nullptr, const QMap< QString, QVariant > &options=QMap< QString, QVariant >(), QgsFeedback *feedback=nullptr)
Writes the contents of vector layer to a different datasource.
int selectedFeatureCount() const
The number of features that are selected in this layer.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
QString errorMessage() const
Returns any error message encountered during the export.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:516
QgsDataProvider * createProvider(const QString &providerKey, const QString &dataSource)
Creates a new instance of a provider.
static QgsVectorLayerExporterTask * withLayerOwnership(QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, const QMap< QString, QVariant > &options=QMap< QString, QVariant >())
Creates a new QgsVectorLayerExporterTask which has ownership over a source layer. ...
void setDependentLayers(const QList< QgsMapLayer *> &dependentLayers)
Sets a list of layers on which the task depends.
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:204
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
Could not find a matching provider key.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
int count() const
Return number of items.
Definition: qgsfields.cpp:115
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning)
add a message to the instance (and create it if necessary)
ExportError errorCode() const
Returns any encountered error code, or false if no error was encountered.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QString what() const
Definition: qgsexception.h:48
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:145
Base class for feedback objects to be used for cancelation of something running in a worker thread...
Definition: qgsfeedback.h:44
Allows creation of spatial index.
virtual bool createSpatialIndex()
Creates a spatial index on the datasource (if supported by the provider type).
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:67
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
const QgsFeatureIds & selectedFeatureIds() const
Return reference to identifiers of selected features.
QgsFields fields() const override
Returns the list of fields of this layer.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QLibrary * createProviderLibrary(const QString &providerKey) const
Returns a new QLibrary for the specified providerKey.
virtual bool isValid() const =0
Returns true if this is a valid layer.
QgsVectorLayerExporterTask(QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destinationCrs, const QMap< QString, QVariant > &options=QMap< QString, QVariant >(), bool ownsLayer=false)
Constructor for QgsVectorLayerExporterTask.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:195
QgsWkbTypes::Type wkbType() const override
Returns the WKBType or WKBUnknown in case of error.
Abstract base class for long running background tasks.
QStringList errors() const
Get recorded errors.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
An error occurred while writing a feature to the destination.
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
void clearErrors()
Clear recorded errors.
QgsTask task which performs a QgsVectorLayerExporter layer export operation as a background task...
An error occurred while reprojecting features to destination CRS.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
#define cast_to_fptr(f)
Definition: qgis.h:170
void errorOccurred(int error, const QString &errorMessage)
Emitted when an error occurs which prevented the layer being exported (or if the task is canceled)...
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=nullptr) override
Adds a list of features to the sink.
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
A convenience class for exporting vector layers to a destination data provider.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) override
Adds a single feature to the sink.
A registry / canonical manager of data providers.
virtual void cancel()
Notifies the task that it should terminate.
QgsVectorLayerExporter(const QString &uri, const QString &provider, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, bool overwrite=false, const QMap< QString, QVariant > &options=QMap< QString, QVariant >())
Constructor for QgsVectorLayerExporter.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:517
~QgsVectorLayerExporter() override
Finalizes the export and closes the new created layer.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Set feature IDs that should be fetched.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
int errorCount() const
Returns the number of error messages encountered during the export.
This class represents a coordinate reference system (CRS).
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
Class for doing transforms between two map coordinate systems.
void cancel() override
Notifies the task that it should terminate.
No errors were encountered.
bool run() override
Performs the task&#39;s operation.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QString providerType() const
Return the provider type for this layer.
bool nextFeature(QgsFeature &f)
This is the base class for vector data providers.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
A vector of attributes.
Definition: qgsattributes.h:58
Represents a vector layer which manages a vector based data sets.
void finished(bool result) override
If the task is managed by a QgsTaskManager, this will be called after the task has finished (whether ...
QgsAttributes attributes
Definition: qgsfeature.h:72
Provider does not support creation of empty layers.
QgsVectorLayerExporter::ExportError createEmptyLayer_t(const QString &uri, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &destCRS, bool overwrite, QMap< int, int > *oldToNewAttrIdx, QString *errorMessage, const QMap< QString, QVariant > *options)
void exportComplete()
Emitted when exporting the layer is successfully completed.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Set flags that affect how features will be fetched.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.