QGIS API Documentation  2.99.0-Master (e077efd)
qgsvectorlayerimport.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectorlayerimport.cpp
3  vector layer importer
4  -------------------
5  begin : Thu Aug 25 2011
6  copyright : (C) 2011 by Giuseppe Sucameli
7  email : brush.tyler at gmail.com
8  ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
19 #include "qgsfields.h"
20 #include "qgsfeature.h"
21 #include "qgsfeatureiterator.h"
22 #include "qgsgeometry.h"
23 #include "qgslogger.h"
24 #include "qgsmessagelog.h"
26 #include "qgsvectorlayerimport.h"
27 #include "qgsproviderregistry.h"
28 #include "qgsdatasourceuri.h"
29 #include "qgscsexception.h"
30 #include "qgsvectordataprovider.h"
31 #include "qgsvectorlayer.h"
32 
33 #include <QProgressDialog>
34 
35 #define FEATURE_BUFFER_SIZE 200
36 
38  const QString &uri,
39  const QgsFields &fields,
40  QgsWkbTypes::Type geometryType,
41  const QgsCoordinateReferenceSystem &destCRS,
42  bool overwrite,
43  QMap<int, int> *oldToNewAttrIdx,
44  QString *errorMessage,
45  const QMap<QString, QVariant> *options
46 );
47 
48 
50  const QString &providerKey,
51  const QgsFields& fields,
52  QgsWkbTypes::Type geometryType,
54  bool overwrite,
55  const QMap<QString, QVariant> *options,
56  QProgressDialog *progress )
57  : mErrorCount( 0 )
58  , mAttributeCount( -1 )
59  , mProgress( progress )
60 
61 {
62  mProvider = nullptr;
63 
65 
66  QLibrary *myLib = pReg->providerLibrary( providerKey );
67  if ( !myLib )
68  {
70  mErrorMessage = QObject::tr( "Unable to load %1 provider" ).arg( providerKey );
71  return;
72  }
73 
74  createEmptyLayer_t * pCreateEmpty = reinterpret_cast< createEmptyLayer_t * >( cast_to_fptr( myLib->resolve( "createEmptyLayer" ) ) );
75  if ( !pCreateEmpty )
76  {
77  delete myLib;
79  mErrorMessage = QObject::tr( "Provider %1 has no %2 method" ).arg( providerKey, QStringLiteral( "createEmptyLayer" ) );
80  return;
81  }
82 
83  delete myLib;
84 
85  // create an empty layer
86  QString errMsg;
87  mError = pCreateEmpty( uri, fields, geometryType, crs, overwrite, &mOldToNewAttrIdx, &errMsg, options );
88  if ( hasError() )
89  {
90  mErrorMessage = errMsg;
91  return;
92  }
93 
94  Q_FOREACH ( int idx, mOldToNewAttrIdx )
95  {
96  if ( idx > mAttributeCount )
97  mAttributeCount = idx;
98  }
99 
100  mAttributeCount++;
101 
102  QgsDebugMsg( "Created empty layer" );
103 
104  QgsVectorDataProvider *vectorProvider = dynamic_cast< QgsVectorDataProvider* >( pReg->provider( providerKey, uri ) );
105  if ( !vectorProvider || !vectorProvider->isValid() || ( vectorProvider->capabilities() & QgsVectorDataProvider::AddFeatures ) == 0 )
106  {
108  mErrorMessage = QObject::tr( "Loading of layer failed" );
109 
110  if ( vectorProvider )
111  delete vectorProvider;
112 
113  return;
114  }
115 
116  mProvider = vectorProvider;
117  mError = NoError;
118 }
119 
121 {
122  flushBuffer();
123 
124  if ( mProvider )
125  delete mProvider;
126 }
127 
129 {
130  return mError;
131 }
132 
134 {
135  return mErrorMessage;
136 }
137 
139 {
140  QgsAttributes attrs = feat.attributes();
141 
142  QgsFeature newFeat;
143  if ( feat.hasGeometry() )
144  newFeat.setGeometry( feat.geometry() );
145 
146  newFeat.initAttributes( mAttributeCount );
147 
148  for ( int i = 0; i < attrs.count(); ++i )
149  {
150  // add only mapped attributes (un-mapped ones will not be present in the
151  // destination layer)
152  int dstIdx = mOldToNewAttrIdx.value( i, -1 );
153  if ( dstIdx < 0 )
154  continue;
155 
156  QgsDebugMsgLevel( QString( "moving field from pos %1 to %2" ).arg( i ).arg( dstIdx ), 3 );
157  newFeat.setAttribute( dstIdx, attrs.at( i ) );
158  }
159 
160  mFeatureBuffer.append( newFeat );
161 
162  if ( mFeatureBuffer.count() >= FEATURE_BUFFER_SIZE )
163  {
164  return flushBuffer();
165  }
166 
167  return true;
168 }
169 
171 {
172  if ( mFeatureBuffer.count() <= 0 )
173  return true;
174 
176  {
177  QStringList errors = mProvider->errors();
179 
180  mErrorMessage = QObject::tr( "Creation error for features from #%1 to #%2. Provider errors was: \n%3" )
181  .arg( mFeatureBuffer.first().id() )
182  .arg( mFeatureBuffer.last().id() )
183  .arg( errors.join( QStringLiteral( "\n" ) ) );
184 
186  mErrorCount += mFeatureBuffer.count();
187 
188  mFeatureBuffer.clear();
190  return false;
191  }
192 
193  mFeatureBuffer.clear();
194  return true;
195 }
196 
198 {
200  {
201  return mProvider->createSpatialIndex();
202  }
203  else
204  {
205  return true;
206  }
207 }
208 
211  const QString& uri,
212  const QString& providerKey,
213  const QgsCoordinateReferenceSystem& destCRS,
214  bool onlySelected,
215  QString *errorMessage,
216  bool skipAttributeCreation,
217  QMap<QString, QVariant> *options,
218  QProgressDialog *progress )
219 {
222  bool shallTransform = false;
223 
224  if ( !layer )
225  return ErrInvalidLayer;
226 
227  if ( destCRS.isValid() )
228  {
229  // This means we should transform
230  outputCRS = destCRS;
231  shallTransform = true;
232  }
233  else
234  {
235  // This means we shouldn't transform, use source CRS as output (if defined)
236  outputCRS = layer->crs();
237  }
238 
239 
240  bool overwrite = false;
241  bool forceSinglePartGeom = false;
242  if ( options )
243  {
244  overwrite = options->take( QStringLiteral( "overwrite" ) ).toBool();
245  forceSinglePartGeom = options->take( QStringLiteral( "forceSinglePartGeometryType" ) ).toBool();
246  }
247 
248  QgsFields fields = skipAttributeCreation ? QgsFields() : layer->fields();
249  QgsWkbTypes::Type wkbType = layer->wkbType();
250 
251  // Special handling for Shapefiles
252  if ( layer->providerType() == QLatin1String( "ogr" ) && layer->storageType() == QLatin1String( "ESRI Shapefile" ) )
253  {
254  // convert field names to lowercase
255  for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
256  {
257  fields[fldIdx].setName( fields.at( fldIdx ).name().toLower() );
258  }
259 
260  if ( !forceSinglePartGeom )
261  {
262  // convert wkbtype to multipart (see #5547)
263  switch ( wkbType )
264  {
265  case QgsWkbTypes::Point:
266  wkbType = QgsWkbTypes::MultiPoint;
267  break;
270  break;
272  wkbType = QgsWkbTypes::MultiPolygon;
273  break;
275  wkbType = QgsWkbTypes::MultiPoint25D;
276  break;
279  break;
282  break;
283  default:
284  break;
285  }
286  }
287  }
288 
289  QgsVectorLayerImport * writer =
290  new QgsVectorLayerImport( uri, providerKey, fields, wkbType, outputCRS, overwrite, options, progress );
291 
292  // check whether file creation was successful
293  ImportError err = writer->hasError();
294  if ( err != NoError )
295  {
296  if ( errorMessage )
297  *errorMessage = writer->errorMessage();
298  delete writer;
299  return err;
300  }
301 
302  if ( errorMessage )
303  {
304  errorMessage->clear();
305  }
306 
307  QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->attributeList();
308  QgsFeature fet;
309 
310  QgsFeatureRequest req;
311  if ( wkbType == QgsWkbTypes::NoGeometry )
313  if ( skipAttributeCreation )
315 
316  QgsFeatureIterator fit = layer->getFeatures( req );
317 
318  const QgsFeatureIds& ids = layer->selectedFeatureIds();
319 
320  // Create our transform
321  if ( destCRS.isValid() )
322  ct = QgsCoordinateTransform( layer->crs(), destCRS );
323 
324  // Check for failure
325  if ( !ct.isValid() )
326  shallTransform = false;
327 
328  int n = 0;
329 
330  if ( errorMessage )
331  {
332  *errorMessage = QObject::tr( "Feature write errors:" );
333  }
334 
335  if ( progress )
336  {
337  progress->setRange( 0, layer->featureCount() );
338  }
339 
340  bool cancelled = false;
341 
342  // write all features
343  while ( fit.nextFeature( fet ) )
344  {
345  if ( progress && progress->wasCanceled() )
346  {
347  cancelled = true;
348  if ( errorMessage )
349  {
350  *errorMessage += '\n' + QObject::tr( "Import was canceled at %1 of %2" ).arg( progress->value() ).arg( progress->maximum() );
351  }
352  break;
353  }
354 
355  if ( writer->errorCount() > 1000 )
356  {
357  if ( errorMessage )
358  {
359  *errorMessage += '\n' + QObject::tr( "Stopping after %1 errors" ).arg( writer->errorCount() );
360  }
361  break;
362  }
363 
364  if ( onlySelected && !ids.contains( fet.id() ) )
365  continue;
366 
367  if ( shallTransform )
368  {
369  try
370  {
371  if ( fet.hasGeometry() )
372  {
373  QgsGeometry g = fet.geometry();
374  g.transform( ct );
375  fet.setGeometry( g );
376  }
377  }
378  catch ( QgsCsException &e )
379  {
380  delete writer;
381 
382  QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
383  .arg( fet.id() ).arg( e.what() );
384  QgsMessageLog::logMessage( msg, QObject::tr( "Vector import" ) );
385  if ( errorMessage )
386  *errorMessage += '\n' + msg;
387 
388  return ErrProjection;
389  }
390  }
391  if ( skipAttributeCreation )
392  {
393  fet.initAttributes( 0 );
394  }
395  if ( !writer->addFeature( fet ) )
396  {
397  if ( writer->hasError() && errorMessage )
398  {
399  *errorMessage += '\n' + writer->errorMessage();
400  }
401  }
402  n++;
403 
404  if ( progress )
405  {
406  progress->setValue( n );
407  }
408  }
409 
410  // flush the buffer to be sure that all features are written
411  if ( !writer->flushBuffer() )
412  {
413  if ( writer->hasError() && errorMessage )
414  {
415  *errorMessage += '\n' + writer->errorMessage();
416  }
417  }
418  int errors = writer->errorCount();
419 
420  if ( !writer->createSpatialIndex() )
421  {
422  if ( writer->hasError() && errorMessage )
423  {
424  *errorMessage += '\n' + writer->errorMessage();
425  }
426  }
427 
428  delete writer;
429 
430  if ( errorMessage )
431  {
432  if ( errors > 0 )
433  {
434  *errorMessage += '\n' + QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n );
435  }
436  else
437  {
438  errorMessage->clear();
439  }
440  }
441 
442  if ( cancelled )
443  return ErrUserCancelled;
444  else if ( errors > 0 )
445  return ErrFeatureWriteFailed;
446 
447  return NoError;
448 }
QgsFeatureId id
Definition: qgsfeature.h:139
Wrapper for iterator of features from vector data provider or vector layer.
static QgsProviderRegistry * instance(const QString &pluginPath=QString::null)
Means of accessing canonical single instance.
virtual Capabilities capabilities() const
Returns flags containing the supported capabilities.
QString name
Definition: qgsfield.h:55
QMap< int, int > mOldToNewAttrIdx
Map attribute indexes to new field indexes.
QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:355
#define FEATURE_BUFFER_SIZE
QLibrary * providerLibrary(const QString &providerKey) const
static ImportError importLayer(QgsVectorLayer *layer, const QString &uri, const QString &providerKey, const QgsCoordinateReferenceSystem &destCRS, bool onlySelected=false, QString *errorMessage=nullptr, bool skipAttributeCreation=false, QMap< QString, QVariant > *options=nullptr, QProgressDialog *progress=nullptr)
Writes the contents of vector layer to a different datasource.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
bool flushBuffer()
Flush the buffer writing the features to the new layer.
QgsVectorDataProvider * mProvider
Container of fields for a vector layer.
Definition: qgsfields.h:36
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:78
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:228
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:135
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:214
virtual bool addFeatures(QgsFeatureList &flist)
Adds a list of features.
int count() const
Return number of items.
Definition: qgsfields.cpp:117
QString what() const
Definition: qgsexception.h:36
QgsFields fields() const
Returns the list of fields of this layer.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:137
Allows creation of spatial index.
virtual bool createSpatialIndex()
Creates a spatial index on the datasource (if supported by the provider type).
QgsWkbTypes::Type wkbType() const
Returns the WKBType or WKBUnknown in case of error.
const QgsFeatureIds & selectedFeatureIds() const
Return reference to identifiers of selected features.
A convenience class for writing vector files to disk.
QgsDataProvider * provider(const QString &providerKey, const QString &dataSource)
Create an instance of the provider.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
virtual bool isValid() const =0
Returns true if this is a valid layer.
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:219
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
QString errorMessage()
Retrieves error message.
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
QStringList errors() const
Get recorded errors.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< int > QgsAttributeList
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
~QgsVectorLayerImport()
Close the new created layer.
void clearErrors()
Clear recorded errors.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:113
#define cast_to_fptr(f)
Definition: qgis.h:126
ImportError mError
Contains error value.
bool createSpatialIndex()
Create index.
A registry / canonical manager of data providers.
bool addFeature(QgsFeature &feature)
Add feature to the new created layer.
QgsVectorLayerImport::ImportError 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)
QgsVectorLayerImport(const QString &uri, const QString &provider, const QgsFields &fields, QgsWkbTypes::Type geometryType, const QgsCoordinateReferenceSystem &crs, bool overwrite=false, const QMap< QString, QVariant > *options=nullptr, QProgressDialog *progress=nullptr)
Constructor for QgsVectorLayerImport.
ImportError hasError()
Checks whether there were any errors.
This class represents a coordinate reference system (CRS).
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:162
Class for doing transforms between two map coordinate systems.
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTransform ct.
Custom exception class for Coordinate Reference System related exceptions.
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: qgsfeature.h:55
Represents a vector layer which manages a vector based data sets.
QgsAttributes attributes
Definition: qgsfeature.h:140
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.