QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 "qgsfield.h"
20 #include "qgsfeature.h"
21 #include "qgsgeometry.h"
22 #include "qgslogger.h"
23 #include "qgsmessagelog.h"
25 #include "qgsvectorlayerimport.h"
26 #include "qgsproviderregistry.h"
27 #include "qgsdatasourceuri.h"
28 
29 #include <QProgressDialog>
30 
31 #define FEATURE_BUFFER_SIZE 200
32 
34  const QString &uri,
35  const QgsFields &fields,
36  QGis::WkbType geometryType,
37  const QgsCoordinateReferenceSystem *destCRS,
38  bool overwrite,
39  QMap<int, int> *oldToNewAttrIdx,
40  QString *errorMessage,
41  const QMap<QString, QVariant> *options
42 );
43 
44 
46  const QString &providerKey,
47  const QgsFields& fields,
48  QGis::WkbType geometryType,
50  bool overwrite,
51  const QMap<QString, QVariant> *options,
52  QProgressDialog *progress )
53  : mErrorCount( 0 )
54  , mAttributeCount( -1 )
55  , mProgress( progress )
56 
57 {
58  mProvider = nullptr;
59 
61 
62  QLibrary *myLib = pReg->providerLibrary( providerKey );
63  if ( !myLib )
64  {
66  mErrorMessage = QObject::tr( "Unable to load %1 provider" ).arg( providerKey );
67  return;
68  }
69 
70  createEmptyLayer_t * pCreateEmpty = reinterpret_cast< createEmptyLayer_t * >( cast_to_fptr( myLib->resolve( "createEmptyLayer" ) ) );
71  if ( !pCreateEmpty )
72  {
73  delete myLib;
75  mErrorMessage = QObject::tr( "Provider %1 has no %2 method" ).arg( providerKey, "createEmptyLayer" );
76  return;
77  }
78 
79  delete myLib;
80 
81  // create an empty layer
82  QString errMsg;
83  mError = pCreateEmpty( uri, fields, geometryType, crs, overwrite, &mOldToNewAttrIdx, &errMsg, options );
84  if ( hasError() )
85  {
86  mErrorMessage = errMsg;
87  return;
88  }
89 
90  Q_FOREACH ( int idx, mOldToNewAttrIdx )
91  {
92  if ( idx > mAttributeCount )
93  mAttributeCount = idx;
94  }
95 
97 
98  QgsDebugMsg( "Created empty layer" );
99 
100  QString uriUpdated( uri );
101  // HACK sorry...
102  if ( providerKey == "ogr" )
103  {
104  QString layerName;
105  if ( options->contains( "layerName" ) )
106  layerName = options->value( "layerName" ).toString();
107  if ( !layerName.isEmpty() )
108  {
109  uriUpdated += "|layername=";
110  uriUpdated += layerName;
111  }
112  }
113  QgsVectorDataProvider *vectorProvider = dynamic_cast< QgsVectorDataProvider * >( pReg->provider( providerKey, uriUpdated ) );
114  if ( !vectorProvider || !vectorProvider->isValid() || ( vectorProvider->capabilities() & QgsVectorDataProvider::AddFeatures ) == 0 )
115  {
117  mErrorMessage = QObject::tr( "Loading of layer failed" );
118 
119  if ( vectorProvider )
120  delete vectorProvider;
121 
122  return;
123  }
124 
125  mProvider = vectorProvider;
126  mError = NoError;
127 }
128 
130 {
131  flushBuffer();
132 
133  if ( mProvider )
134  delete mProvider;
135 }
136 
138 {
139  return mError;
140 }
141 
143 {
144  return mErrorMessage;
145 }
146 
148 {
149  QgsAttributes attrs = feat.attributes();
150 
151  QgsFeature newFeat;
152  if ( feat.constGeometry() )
153  newFeat.setGeometry( *feat.constGeometry() );
154 
155  newFeat.initAttributes( mAttributeCount );
156 
157  for ( int i = 0; i < attrs.count(); ++i )
158  {
159  // add only mapped attributes (un-mapped ones will not be present in the
160  // destination layer)
161  int dstIdx = mOldToNewAttrIdx.value( i, -1 );
162  if ( dstIdx < 0 )
163  continue;
164 
165  QgsDebugMsgLevel( QString( "moving field from pos %1 to %2" ).arg( i ).arg( dstIdx ), 3 );
166  newFeat.setAttribute( dstIdx, attrs.at( i ) );
167  }
168 
169  mFeatureBuffer.append( newFeat );
170 
172  {
173  return flushBuffer();
174  }
175 
176  return true;
177 }
178 
180 {
181  if ( mFeatureBuffer.count() <= 0 )
182  return true;
183 
185  {
186  QStringList errors = mProvider->errors();
188 
189  mErrorMessage = QObject::tr( "Creation error for features from #%1 to #%2. Provider errors was: \n%3" )
190  .arg( mFeatureBuffer.first().id() )
191  .arg( mFeatureBuffer.last().id() )
192  .arg( errors.join( "\n" ) );
193 
196 
199  return false;
200  }
201 
203  return true;
204 }
205 
207 {
209  {
210  return mProvider->createSpatialIndex();
211  }
212  else
213  {
214  return true;
215  }
216 }
217 
220  const QString& uri,
221  const QString& providerKey,
222  const QgsCoordinateReferenceSystem *destCRS,
223  bool onlySelected,
225  bool skipAttributeCreation,
226  QMap<QString, QVariant> *options,
227  QProgressDialog *progress )
228 {
229  const QgsCoordinateReferenceSystem* outputCRS;
230  QgsCoordinateTransform* ct = nullptr;
231  bool shallTransform = false;
232 
233  if ( !layer )
234  return ErrInvalidLayer;
235 
236  if ( destCRS && destCRS->isValid() )
237  {
238  // This means we should transform
239  outputCRS = destCRS;
240  shallTransform = true;
241  }
242  else
243  {
244  // This means we shouldn't transform, use source CRS as output (if defined)
245  outputCRS = &layer->crs();
246  }
247 
248 
249  bool overwrite = false;
250  bool forceSinglePartGeom = false;
251  if ( options )
252  {
253  overwrite = options->take( "overwrite" ).toBool();
254  forceSinglePartGeom = options->take( "forceSinglePartGeometryType" ).toBool();
255  }
256 
257  QgsFields fields = skipAttributeCreation ? QgsFields() : layer->fields();
258  QGis::WkbType wkbType = layer->wkbType();
259 
260  // Special handling for Shapefiles
261  if ( layer->providerType() == "ogr" && layer->storageType() == "ESRI Shapefile" )
262  {
263  // convert field names to lowercase
264  for ( int fldIdx = 0; fldIdx < fields.count(); ++fldIdx )
265  {
266  fields[fldIdx].setName( fields.at( fldIdx ).name().toLower() );
267  }
268 
269  if ( !forceSinglePartGeom )
270  {
271  // convert wkbtype to multipart (see #5547)
272  switch ( wkbType )
273  {
274  case QGis::WKBPoint:
275  wkbType = QGis::WKBMultiPoint;
276  break;
277  case QGis::WKBLineString:
278  wkbType = QGis::WKBMultiLineString;
279  break;
280  case QGis::WKBPolygon:
281  wkbType = QGis::WKBMultiPolygon;
282  break;
283  case QGis::WKBPoint25D:
284  wkbType = QGis::WKBMultiPoint25D;
285  break;
287  wkbType = QGis::WKBMultiLineString25D;
288  break;
289  case QGis::WKBPolygon25D:
290  wkbType = QGis::WKBMultiPolygon25D;
291  break;
292  default:
293  break;
294  }
295  }
296  }
297 
298  QgsVectorLayerImport * writer =
299  new QgsVectorLayerImport( uri, providerKey, fields, wkbType, outputCRS, overwrite, options, progress );
300 
301  // check whether file creation was successful
302  ImportError err = writer->hasError();
303  if ( err != NoError )
304  {
305  if ( errorMessage )
306  *errorMessage = writer->errorMessage();
307  delete writer;
308  return err;
309  }
310 
311  if ( errorMessage )
312  {
313  errorMessage->clear();
314  }
315 
316  QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->attributeList();
317  QgsFeature fet;
318 
319  QgsFeatureRequest req;
320  if ( wkbType == QGis::WKBNoGeometry )
322  if ( skipAttributeCreation )
324 
325  QgsFeatureIterator fit = layer->getFeatures( req );
326 
327  const QgsFeatureIds& ids = layer->selectedFeaturesIds();
328 
329  // Create our transform
330  if ( destCRS )
331  ct = new QgsCoordinateTransform( layer->crs(), *destCRS );
332 
333  // Check for failure
334  if ( !ct )
335  shallTransform = false;
336 
337  int n = 0;
338 
339  if ( errorMessage )
340  {
341  *errorMessage = QObject::tr( "Feature write errors:" );
342  }
343 
344  if ( progress )
345  {
346  progress->setRange( 0, layer->featureCount() );
347  }
348 
349  bool cancelled = false;
350 
351  // write all features
352  while ( fit.nextFeature( fet ) )
353  {
354  if ( progress && progress->wasCanceled() )
355  {
356  cancelled = true;
357  if ( errorMessage )
358  {
359  *errorMessage += '\n' + QObject::tr( "Import was canceled at %1 of %2" ).arg( progress->value() ).arg( progress->maximum() );
360  }
361  break;
362  }
363 
364  if ( writer->errorCount() > 1000 )
365  {
366  if ( errorMessage )
367  {
368  *errorMessage += '\n' + QObject::tr( "Stopping after %1 errors" ).arg( writer->errorCount() );
369  }
370  break;
371  }
372 
373  if ( onlySelected && !ids.contains( fet.id() ) )
374  continue;
375 
376  if ( shallTransform )
377  {
378  try
379  {
380  if ( fet.constGeometry() )
381  {
382  fet.geometry()->transform( *ct );
383  }
384  }
385  catch ( QgsCsException &e )
386  {
387  delete ct;
388  delete writer;
389 
390  QString msg = QObject::tr( "Failed to transform a point while drawing a feature with ID '%1'. Writing stopped. (Exception: %2)" )
391  .arg( fet.id() ).arg( e.what() );
392  QgsMessageLog::logMessage( msg, QObject::tr( "Vector import" ) );
393  if ( errorMessage )
394  *errorMessage += '\n' + msg;
395 
396  return ErrProjection;
397  }
398  }
399  if ( skipAttributeCreation )
400  {
401  fet.initAttributes( 0 );
402  }
403  if ( !writer->addFeature( fet ) )
404  {
405  if ( writer->hasError() && errorMessage )
406  {
407  *errorMessage += '\n' + writer->errorMessage();
408  }
409  }
410  n++;
411 
412  if ( progress )
413  {
414  progress->setValue( n );
415  }
416  }
417 
418  // flush the buffer to be sure that all features are written
419  if ( !writer->flushBuffer() )
420  {
421  if ( writer->hasError() && errorMessage )
422  {
423  *errorMessage += '\n' + writer->errorMessage();
424  }
425  }
426  int errors = writer->errorCount();
427 
428  if ( !writer->createSpatialIndex() )
429  {
430  if ( writer->hasError() && errorMessage )
431  {
432  *errorMessage += '\n' + writer->errorMessage();
433  }
434  }
435 
436  delete writer;
437 
438  if ( shallTransform )
439  {
440  delete ct;
441  }
442 
443  if ( errorMessage )
444  {
445  if ( errors > 0 )
446  {
447  *errorMessage += '\n' + QObject::tr( "Only %1 of %2 features written." ).arg( n - errors ).arg( n );
448  }
449  else
450  {
451  errorMessage->clear();
452  }
453  }
454 
455  if ( cancelled )
456  return ErrUserCancelled;
457  else if ( errors > 0 )
458  return ErrFeatureWriteFailed;
459 
460  return NoError;
461 }
void clear()
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.
QGis::WkbType wkbType() const
Returns the WKBType or WKBUnknown in case of error.
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
bool contains(const Key &key) const
QString name
Definition: qgsfield.h:52
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
#define FEATURE_BUFFER_SIZE
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
QLibrary * providerLibrary(const QString &providerKey) const
QgsVectorLayerImport(const QString &uri, const QString &provider, const QgsFields &fields, QGis::WkbType geometryType, const QgsCoordinateReferenceSystem *crs, bool overwrite=false, const QMap< QString, QVariant > *options=nullptr, QProgressDialog *progress=nullptr)
Create a empty layer and add fields to it.
const QgsCoordinateReferenceSystem & crs() const
Returns layer&#39;s spatial reference system.
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: qgsfield.h:252
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:222
QStringList errors()
Get recorded errors.
WkbType
Used for symbology operations.
Definition: qgis.h:61
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)
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
QString join(const QString &separator) const
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
virtual bool addFeatures(QgsFeatureList &flist)
Adds a list of features.
int count() const
Return number of items.
Definition: qgsfield.cpp:402
QString tr(const char *sourceText, const char *disambiguation, int n)
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:422
QString what() const
Definition: qgsexception.h:36
QgsFields fields() const
Returns the list of fields of this layer.
void clear()
long featureCount(QgsSymbolV2 *symbol)
Number of features rendered with specified symbol.
Allows creation of spatial index.
virtual bool createSpatialIndex()
Creates a spatial index on the datasource (if supported by the provider type).
void setGeometry(const QgsGeometry &geom)
Set this feature&#39;s geometry from another QgsGeometry object.
Definition: qgsfeature.cpp:124
int count(const T &value) const
void append(const T &value)
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
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:213
bool isEmpty() const
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)
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< int > QgsAttributeList
~QgsVectorLayerImport()
Close the new created layer.
void clearErrors()
Clear recorded errors.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
QgsFeatureRequest & setFlags(const QgsFeatureRequest::Flags &flags)
Set flags that affect how features will be fetched.
T & first()
virtual int capabilities() const
Returns a bitmask containing the supported capabilities Note, some capabilities may change depending ...
void setRange(int minimum, int maximum)
ImportError mError
Contains error value.
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)
Write contents of vector layer to a different datasource.
QString toLower() const
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
bool createSpatialIndex()
Create index.
A registry / canonical manager of data providers.
QgsGeometry * geometry()
Get the geometry object associated with this feature.
Definition: qgsfeature.cpp:76
bool contains(const T &value) const
bool addFeature(QgsFeature &feature)
Add feature to the new created layer.
const QgsFeatureIds & selectedFeaturesIds() const
Return reference to identifiers of selected features.
const T & at(int i) const
void * resolve(const char *symbol)
ImportError hasError()
Checks whether there were any errors.
virtual bool isValid()=0
Returns true if this is a valid layer.
T & last()
Class for storing a coordinate reference system (CRS)
int count(const T &value) const
Class for doing transforms between two map coordinate systems.
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTransform ct.
void(*)() cast_to_fptr(void *p)
Definition: qgis.h:272
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:115
Represents a vector layer which manages a vector based data sets.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
T take(const Key &key)
const T value(const Key &key) const
bool isValid() const
Returns whether this CRS is correctly initialized and usable.