QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgsmemoryprovider.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  memoryprovider.cpp - provider with storage in memory
3  ------------------
4  begin : June 2008
5  copyright : (C) 2008 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmemoryprovider.h"
18 
19 #include "qgsfeature.h"
20 #include "qgsfields.h"
21 #include "qgsgeometry.h"
22 #include "qgslogger.h"
23 #include "qgsspatialindex.h"
25 
26 #include <QUrl>
27 #include <QRegExp>
28 
30 
31 static const QString TEXT_PROVIDER_KEY = QStringLiteral( "memory" );
32 static const QString TEXT_PROVIDER_DESCRIPTION = QStringLiteral( "Memory provider" );
33 
34 QgsMemoryProvider::QgsMemoryProvider( const QString &uri, const ProviderOptions &options )
35  : QgsVectorDataProvider( uri, options )
36 {
37  // Initialize the geometry with the uri to support old style uri's
38  // (ie, just 'point', 'line', 'polygon')
39  QUrl url = QUrl::fromEncoded( uri.toUtf8() );
40  QString geometry;
41  if ( url.hasQueryItem( QStringLiteral( "geometry" ) ) )
42  {
43  geometry = url.queryItemValue( QStringLiteral( "geometry" ) );
44  }
45  else
46  {
47  geometry = url.path();
48  }
49 
50  if ( geometry.compare( QLatin1String( "none" ), Qt::CaseInsensitive ) == 0 )
51  {
52  mWkbType = QgsWkbTypes::NoGeometry;
53  }
54  else
55  {
56  mWkbType = QgsWkbTypes::parseType( geometry );
57  }
58 
59  if ( url.hasQueryItem( QStringLiteral( "crs" ) ) )
60  {
61  QString crsDef = url.queryItemValue( QStringLiteral( "crs" ) );
62  mCrs.createFromString( crsDef );
63  }
64 
65  mNextFeatureId = 1;
66 
67  setNativeTypes( QList< NativeType >()
68  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), QStringLiteral( "integer" ), QVariant::Int, 0, 10 )
69  // Decimal number from OGR/Shapefile/dbf may come with length up to 32 and
70  // precision up to length-2 = 30 (default, if width is not specified in dbf is length = 24 precision = 15)
71  // We know that double (QVariant::Double) has only 15-16 significant numbers,
72  // but setting that correct limits would disable the use of memory provider with
73  // data from Shapefiles. In any case, the data are handled as doubles.
74  // So the limits set here are not correct but enable use of data from Shapefiles.
75  << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "double" ), QVariant::Double, 0, 32, 0, 30 )
76  << QgsVectorDataProvider::NativeType( tr( "Text (string)" ), QStringLiteral( "string" ), QVariant::String, 0, 255 )
77 
78  // date type
79  << QgsVectorDataProvider::NativeType( tr( "Date" ), QStringLiteral( "date" ), QVariant::Date, -1, -1, -1, -1 )
80  << QgsVectorDataProvider::NativeType( tr( "Time" ), QStringLiteral( "time" ), QVariant::Time, -1, -1, -1, -1 )
81  << QgsVectorDataProvider::NativeType( tr( "Date & Time" ), QStringLiteral( "datetime" ), QVariant::DateTime, -1, -1, -1, -1 )
82 
83  // integer types
84  << QgsVectorDataProvider::NativeType( tr( "Whole number (smallint - 16bit)" ), QStringLiteral( "int2" ), QVariant::Int, -1, -1, 0, 0 )
85  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer - 32bit)" ), QStringLiteral( "int4" ), QVariant::Int, -1, -1, 0, 0 )
86  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer - 64bit)" ), QStringLiteral( "int8" ), QVariant::LongLong, -1, -1, 0, 0 )
87  << QgsVectorDataProvider::NativeType( tr( "Decimal number (numeric)" ), QStringLiteral( "numeric" ), QVariant::Double, 1, 20, 0, 20 )
88  << QgsVectorDataProvider::NativeType( tr( "Decimal number (decimal)" ), QStringLiteral( "decimal" ), QVariant::Double, 1, 20, 0, 20 )
89 
90  // floating point
91  << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "real" ), QVariant::Double, -1, -1, -1, -1 )
92  << QgsVectorDataProvider::NativeType( tr( "Decimal number (double)" ), QStringLiteral( "double precision" ), QVariant::Double, -1, -1, -1, -1 )
93 
94  // string types
95  << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), QStringLiteral( "text" ), QVariant::String, -1, -1, -1, -1 )
96  );
97 
98  if ( url.hasQueryItem( QStringLiteral( "field" ) ) )
99  {
100  QList<QgsField> attributes;
101  QRegExp reFieldDef( "\\:"
102  "(int|integer|long|int8|real|double|string|date|time|datetime)" // type
103  "(?:\\((\\-?\\d+)" // length
104  "(?:\\,(\\-?\\d+))?" // precision
105  "\\))?(\\[\\])?" // array
106  "$", Qt::CaseInsensitive );
107  QStringList fields = url.allQueryItemValues( QStringLiteral( "field" ) );
108  for ( int i = 0; i < fields.size(); i++ )
109  {
110  QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
111  QVariant::Type type = QVariant::String;
112  QVariant::Type subType = QVariant::Invalid;
113  QString typeName( QStringLiteral( "string" ) );
114  int length = 255;
115  int precision = 0;
116 
117  int pos = reFieldDef.indexIn( name );
118  if ( pos >= 0 )
119  {
120  name = name.mid( 0, pos );
121  typeName = reFieldDef.cap( 1 ).toLower();
122  if ( typeName == QLatin1String( "int" ) || typeName == QLatin1String( "integer" ) )
123  {
124  type = QVariant::Int;
125  typeName = QStringLiteral( "integer" );
126  length = -1;
127  }
128  else if ( typeName == QLatin1String( "int8" ) || typeName == QLatin1String( "long" ) )
129  {
130  type = QVariant::LongLong;
131  typeName = QStringLiteral( "int8" );
132  length = -1;
133  }
134  else if ( typeName == QLatin1String( "real" ) || typeName == QLatin1String( "double" ) )
135  {
136  type = QVariant::Double;
137  typeName = QStringLiteral( "double" );
138  length = 20;
139  precision = 5;
140  }
141  else if ( typeName == QLatin1String( "date" ) )
142  {
143  type = QVariant::Date;
144  typeName = QStringLiteral( "date" );
145  length = -1;
146  }
147  else if ( typeName == QLatin1String( "time" ) )
148  {
149  type = QVariant::Time;
150  typeName = QStringLiteral( "time" );
151  length = -1;
152  }
153  else if ( typeName == QLatin1String( "datetime" ) )
154  {
155  type = QVariant::DateTime;
156  typeName = QStringLiteral( "datetime" );
157  length = -1;
158  }
159 
160  if ( !reFieldDef.cap( 2 ).isEmpty() )
161  {
162  length = reFieldDef.cap( 2 ).toInt();
163  }
164  if ( !reFieldDef.cap( 3 ).isEmpty() )
165  {
166  precision = reFieldDef.cap( 3 ).toInt();
167  }
168  if ( !reFieldDef.cap( 4 ).isEmpty() )
169  {
170  //array
171  subType = type;
172  type = ( subType == QVariant::String ? QVariant::StringList : QVariant::List );
173  }
174  }
175  if ( !name.isEmpty() )
176  attributes.append( QgsField( name, type, typeName, length, precision, QString(), subType ) );
177  }
178  addAttributes( attributes );
179  }
180 
181  if ( url.hasQueryItem( QStringLiteral( "index" ) ) && url.queryItemValue( QStringLiteral( "index" ) ) == QLatin1String( "yes" ) )
182  {
183  createSpatialIndex();
184  }
185 
186 }
187 
188 QgsMemoryProvider::~QgsMemoryProvider()
189 {
190  delete mSpatialIndex;
191 }
192 
193 QString QgsMemoryProvider::providerKey()
194 {
195  return TEXT_PROVIDER_KEY;
196 }
197 
198 QString QgsMemoryProvider::providerDescription()
199 {
200  return TEXT_PROVIDER_DESCRIPTION;
201 }
202 
203 QgsMemoryProvider *QgsMemoryProvider::createProvider( const QString &uri, const ProviderOptions &options )
204 {
205  return new QgsMemoryProvider( uri, options );
206 }
207 
208 QgsAbstractFeatureSource *QgsMemoryProvider::featureSource() const
209 {
210  return new QgsMemoryFeatureSource( this );
211 }
212 
213 QString QgsMemoryProvider::dataSourceUri( bool expandAuthConfig ) const
214 {
215  Q_UNUSED( expandAuthConfig )
216 
217  QUrl uri( QStringLiteral( "memory" ) );
218  QString geometry = QgsWkbTypes::displayString( mWkbType );
219  uri.addQueryItem( QStringLiteral( "geometry" ), geometry );
220 
221  if ( mCrs.isValid() )
222  {
223  QString crsDef;
224  QString authid = mCrs.authid();
225  if ( authid.startsWith( QLatin1String( "EPSG:" ) ) )
226  {
227  crsDef = authid;
228  }
229  else
230  {
231  int srid = mCrs.postgisSrid();
232  if ( srid )
233  {
234  crsDef = QStringLiteral( "postgis:%1" ).arg( srid );
235  }
236  else
237  {
238  crsDef = QStringLiteral( "wkt:%1" ).arg( mCrs.toWkt() );
239  }
240  }
241  uri.addQueryItem( QStringLiteral( "crs" ), crsDef );
242  }
243  if ( mSpatialIndex )
244  {
245  uri.addQueryItem( QStringLiteral( "index" ), QStringLiteral( "yes" ) );
246  }
247 
248  QgsAttributeList attrs = const_cast<QgsMemoryProvider *>( this )->attributeIndexes();
249  for ( int i = 0; i < attrs.size(); i++ )
250  {
251  QgsField field = mFields.at( attrs[i] );
252  QString fieldDef = field.name();
253  fieldDef.append( QStringLiteral( ":%2(%3,%4)" ).arg( field.typeName() ).arg( field.length() ).arg( field.precision() ) );
254  uri.addQueryItem( QStringLiteral( "field" ), fieldDef );
255  }
256 
257  return QString( uri.toEncoded() );
258 
259 }
260 
261 QString QgsMemoryProvider::storageType() const
262 {
263  return QStringLiteral( "Memory storage" );
264 }
265 
266 QgsFeatureIterator QgsMemoryProvider::getFeatures( const QgsFeatureRequest &request ) const
267 {
268  return QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, request ) );
269 }
270 
271 
272 QgsRectangle QgsMemoryProvider::extent() const
273 {
274  if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
275  {
276  mExtent.setMinimal();
277  if ( mSubsetString.isEmpty() )
278  {
279  // fast way - iterate through all features
280  Q_FOREACH ( const QgsFeature &feat, mFeatures )
281  {
282  if ( feat.hasGeometry() )
283  mExtent.combineExtentWith( feat.geometry().boundingBox() );
284  }
285  }
286  else
287  {
288  QgsFeature f;
289  QgsFeatureIterator fi = getFeatures( QgsFeatureRequest().setNoAttributes() );
290  while ( fi.nextFeature( f ) )
291  {
292  if ( f.hasGeometry() )
293  mExtent.combineExtentWith( f.geometry().boundingBox() );
294  }
295  }
296  }
297  else if ( mFeatures.isEmpty() )
298  {
299  mExtent.setMinimal();
300  }
301 
302  return mExtent;
303 }
304 
305 QgsWkbTypes::Type QgsMemoryProvider::wkbType() const
306 {
307  return mWkbType;
308 }
309 
310 long QgsMemoryProvider::featureCount() const
311 {
312  if ( mSubsetString.isEmpty() )
313  return mFeatures.count();
314 
315  // subset string set, no alternative but testing each feature
316  QgsFeatureIterator fit = QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, QgsFeatureRequest().setNoAttributes() ) );
317  int count = 0;
318  QgsFeature feature;
319  while ( fit.nextFeature( feature ) )
320  {
321  count++;
322  }
323  return count;
324 }
325 
326 QgsFields QgsMemoryProvider::fields() const
327 {
328  return mFields;
329 }
330 
331 bool QgsMemoryProvider::isValid() const
332 {
333  return ( mWkbType != QgsWkbTypes::Unknown );
334 }
335 
337 {
338  // TODO: make provider projection-aware
339  return mCrs; // return default CRS
340 }
341 
342 
343 bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist, Flags )
344 {
345  bool result = true;
346  // whether or not to update the layer extent on the fly as we add features
347  bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
348 
349  int fieldCount = mFields.count();
350 
351  // TODO: sanity checks of fields
352  for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end(); ++it )
353  {
354  it->setId( mNextFeatureId );
355  it->setValid( true );
356  if ( it->attributes().count() < fieldCount )
357  {
358  // ensure features have the correct number of attributes by padding
359  // them with null attributes for missing values
360  QgsAttributes attributes = it->attributes();
361  for ( int i = it->attributes().count(); i < mFields.count(); ++i )
362  {
363  attributes.append( QVariant( mFields.at( i ).type() ) );
364  }
365  it->setAttributes( attributes );
366  }
367  else if ( it->attributes().count() > fieldCount )
368  {
369  // too many attributes
370  pushError( tr( "Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( it->attributes().count() ) );
371  QgsAttributes attributes = it->attributes();
372  attributes.resize( mFields.count() );
373  it->setAttributes( attributes );
374  }
375 
376  if ( it->hasGeometry() && mWkbType == QgsWkbTypes::NoGeometry )
377  {
378  it->clearGeometry();
379  }
380  else if ( it->hasGeometry() && QgsWkbTypes::geometryType( it->geometry().wkbType() ) !=
381  QgsWkbTypes::geometryType( mWkbType ) )
382  {
383  pushError( tr( "Could not add feature with geometry type %1 to layer of type %2" ).arg( QgsWkbTypes::displayString( it->geometry().wkbType() ),
384  QgsWkbTypes::displayString( mWkbType ) ) );
385  result = false;
386  continue;
387  }
388 
389  mFeatures.insert( mNextFeatureId, *it );
390 
391  if ( it->hasGeometry() )
392  {
393  if ( updateExtent )
394  mExtent.combineExtentWith( it->geometry().boundingBox() );
395 
396  // update spatial index
397  if ( mSpatialIndex )
398  mSpatialIndex->addFeature( *it );
399  }
400 
401  mNextFeatureId++;
402  }
403 
404  clearMinMaxCache();
405  return result;
406 }
407 
408 bool QgsMemoryProvider::deleteFeatures( const QgsFeatureIds &id )
409 {
410  for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
411  {
412  QgsFeatureMap::iterator fit = mFeatures.find( *it );
413 
414  // check whether such feature exists
415  if ( fit == mFeatures.end() )
416  continue;
417 
418  // update spatial index
419  if ( mSpatialIndex )
420  mSpatialIndex->deleteFeature( *fit );
421 
422  mFeatures.erase( fit );
423  }
424 
425  updateExtents();
426  clearMinMaxCache();
427 
428  return true;
429 }
430 
431 bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
432 {
433  for ( QList<QgsField>::const_iterator it = attributes.begin(); it != attributes.end(); ++it )
434  {
435  switch ( it->type() )
436  {
437  case QVariant::Int:
438  case QVariant::Double:
439  case QVariant::String:
440  case QVariant::Date:
441  case QVariant::Time:
442  case QVariant::DateTime:
443  case QVariant::LongLong:
444  case QVariant::StringList:
445  case QVariant::List:
446  break;
447  default:
448  QgsDebugMsg( "Field type not supported: " + it->typeName() );
449  continue;
450  }
451  // add new field as a last one
452  mFields.append( *it );
453 
454  for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
455  {
456  QgsFeature &f = fit.value();
457  QgsAttributes attr = f.attributes();
458  attr.append( QVariant() );
459  f.setAttributes( attr );
460  }
461  }
462  return true;
463 }
464 
465 bool QgsMemoryProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes )
466 {
467  QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
468  bool result = true;
469  for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
470  {
471  int fieldIndex = renameIt.key();
472  if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
473  {
474  result = false;
475  continue;
476  }
477  if ( mFields.indexFromName( renameIt.value() ) >= 0 )
478  {
479  //field name already in use
480  result = false;
481  continue;
482  }
483 
484  mFields[ fieldIndex ].setName( renameIt.value() );
485  }
486  return result;
487 }
488 
489 bool QgsMemoryProvider::deleteAttributes( const QgsAttributeIds &attributes )
490 {
491  QList<int> attrIdx = attributes.toList();
492  std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
493 
494  // delete attributes one-by-one with decreasing index
495  for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
496  {
497  int idx = *it;
498  mFields.remove( idx );
499 
500  for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
501  {
502  QgsFeature &f = fit.value();
503  QgsAttributes attr = f.attributes();
504  attr.remove( idx );
505  f.setAttributes( attr );
506  }
507  }
508  clearMinMaxCache();
509  return true;
510 }
511 
512 bool QgsMemoryProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
513 {
514  for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
515  {
516  QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
517  if ( fit == mFeatures.end() )
518  continue;
519 
520  const QgsAttributeMap &attrs = it.value();
521  for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
522  fit->setAttribute( it2.key(), it2.value() );
523  }
524  clearMinMaxCache();
525  return true;
526 }
527 
528 bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
529 {
530  for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
531  {
532  QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
533  if ( fit == mFeatures.end() )
534  continue;
535 
536  // update spatial index
537  if ( mSpatialIndex )
538  mSpatialIndex->deleteFeature( *fit );
539 
540  fit->setGeometry( it.value() );
541 
542  // update spatial index
543  if ( mSpatialIndex )
544  mSpatialIndex->addFeature( *fit );
545  }
546 
547  updateExtents();
548 
549  return true;
550 }
551 
552 QString QgsMemoryProvider::subsetString() const
553 {
554  return mSubsetString;
555 }
556 
557 bool QgsMemoryProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
558 {
559  Q_UNUSED( updateFeatureCount );
560 
561  if ( !theSQL.isEmpty() )
562  {
563  QgsExpression tempExpression( theSQL );
564  if ( tempExpression.hasParserError() )
565  return false;
566  }
567 
568  if ( theSQL == mSubsetString )
569  return true;
570 
571  mSubsetString = theSQL;
572  clearMinMaxCache();
573  mExtent.setMinimal();
574 
575  emit dataChanged();
576  return true;
577 }
578 
579 bool QgsMemoryProvider::createSpatialIndex()
580 {
581  if ( !mSpatialIndex )
582  {
583  mSpatialIndex = new QgsSpatialIndex();
584 
585  // add existing features to index
586  for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
587  {
588  mSpatialIndex->addFeature( *it );
589  }
590  }
591  return true;
592 }
593 
594 QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities() const
595 {
596  return AddFeatures | DeleteFeatures | ChangeGeometries |
597  ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
598  SelectAtId | CircularGeometries;
599 }
600 
601 
602 void QgsMemoryProvider::updateExtents()
603 {
604  mExtent.setMinimal();
605 }
606 
607 QString QgsMemoryProvider::name() const
608 {
609  return TEXT_PROVIDER_KEY;
610 }
611 
612 QString QgsMemoryProvider::description() const
613 {
614  return TEXT_PROVIDER_DESCRIPTION;
615 }
616 
617 
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
QMap< QgsFeatureId, QgsGeometry > QgsGeometryMap
Definition: qgsfeature.h:566
int precision
A rectangle specified with double values.
Definition: qgsrectangle.h:40
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:150
QString name
Definition: qgsfield.h:57
int precision
Definition: qgsfield.h:54
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:571
Handles storage of information regarding WKB types and their properties.
Definition: qgswkbtypes.h:40
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Container of fields for a vector layer.
Definition: qgsfields.h:42
void setAttributes(const QgsAttributes &attrs)
Sets the feature&#39;s attributes.
Definition: qgsfeature.cpp:127
static Type parseType(const QString &wktStr)
Attempts to extract the WKB type from a WKT string.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsCoordinateReferenceSystem & crs
const QgsAttributeList & attributeIndexes
int length
Definition: qgsfield.h:53
QSet< int > QgsAttributeIds
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QString typeName() const
Gets the field type.
Definition: qgsfield.cpp:105
const QString & typeName
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:801
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
Base class that can be used for any class that is capable of returning features.
QMap< int, QString > QgsFieldNameMap
Definition: qgsattributes.h:44
A spatial index for QgsFeature objects.
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:557
This class represents a coordinate reference system (CRS).
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
static QString displayString(Type type)
Returns a display string type for a WKB type, e.g., the geometry name used in WKT geometry representa...
QgsGeometry geometry
Definition: qgsfeature.h:67
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
This is the base class for vector data providers.
A vector of attributes.
Definition: qgsattributes.h:57
QgsAttributes attributes
Definition: qgsfeature.h:65