QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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  else
65  {
66  // TODO - remove in QGIS 4.0. Layers without an explicit CRS set SHOULD have an invalid CRS. But in order to maintain
67  // 3.x api, we have to be tolerant/shortsighted(?) here and fallback to EPSG:4326
68  mCrs = QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) );
69  }
70 
71  mNextFeatureId = 1;
72 
73  setNativeTypes( QList< NativeType >()
74  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer)" ), QStringLiteral( "integer" ), QVariant::Int, 0, 10 )
75  // Decimal number from OGR/Shapefile/dbf may come with length up to 32 and
76  // precision up to length-2 = 30 (default, if width is not specified in dbf is length = 24 precision = 15)
77  // We know that double (QVariant::Double) has only 15-16 significant numbers,
78  // but setting that correct limits would disable the use of memory provider with
79  // data from Shapefiles. In any case, the data are handled as doubles.
80  // So the limits set here are not correct but enable use of data from Shapefiles.
81  << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "double" ), QVariant::Double, 0, 32, 0, 30 )
82  << QgsVectorDataProvider::NativeType( tr( "Text (string)" ), QStringLiteral( "string" ), QVariant::String, 0, 255 )
83 
84  // date type
85  << QgsVectorDataProvider::NativeType( tr( "Date" ), QStringLiteral( "date" ), QVariant::Date, -1, -1, -1, -1 )
86  << QgsVectorDataProvider::NativeType( tr( "Time" ), QStringLiteral( "time" ), QVariant::Time, -1, -1, -1, -1 )
87  << QgsVectorDataProvider::NativeType( tr( "Date & Time" ), QStringLiteral( "datetime" ), QVariant::DateTime, -1, -1, -1, -1 )
88 
89  // integer types
90  << QgsVectorDataProvider::NativeType( tr( "Whole number (smallint - 16bit)" ), QStringLiteral( "int2" ), QVariant::Int, -1, -1, 0, 0 )
91  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer - 32bit)" ), QStringLiteral( "int4" ), QVariant::Int, -1, -1, 0, 0 )
92  << QgsVectorDataProvider::NativeType( tr( "Whole number (integer - 64bit)" ), QStringLiteral( "int8" ), QVariant::LongLong, -1, -1, 0, 0 )
93  << QgsVectorDataProvider::NativeType( tr( "Decimal number (numeric)" ), QStringLiteral( "numeric" ), QVariant::Double, 1, 20, 0, 20 )
94  << QgsVectorDataProvider::NativeType( tr( "Decimal number (decimal)" ), QStringLiteral( "decimal" ), QVariant::Double, 1, 20, 0, 20 )
95 
96  // floating point
97  << QgsVectorDataProvider::NativeType( tr( "Decimal number (real)" ), QStringLiteral( "real" ), QVariant::Double, -1, -1, -1, -1 )
98  << QgsVectorDataProvider::NativeType( tr( "Decimal number (double)" ), QStringLiteral( "double precision" ), QVariant::Double, -1, -1, -1, -1 )
99 
100  // string types
101  << QgsVectorDataProvider::NativeType( tr( "Text, unlimited length (text)" ), QStringLiteral( "text" ), QVariant::String, -1, -1, -1, -1 )
102 
103  // boolean
104  << QgsVectorDataProvider::NativeType( tr( "Boolean" ), QStringLiteral( "bool" ), QVariant::Bool )
105 
106  // blob
107  << QgsVectorDataProvider::NativeType( tr( "Binary object (BLOB)" ), QStringLiteral( "binary" ), QVariant::ByteArray )
108 
109  );
110 
111  if ( url.hasQueryItem( QStringLiteral( "field" ) ) )
112  {
113  QList<QgsField> attributes;
114  QRegExp reFieldDef( "\\:"
115  "(int|integer|long|int8|real|double|string|date|time|datetime|binary|bool|boolean)" // type
116  "(?:\\((\\-?\\d+)" // length
117  "(?:\\,(\\-?\\d+))?" // precision
118  "\\))?(\\[\\])?" // array
119  "$", Qt::CaseInsensitive );
120  QStringList fields = url.allQueryItemValues( QStringLiteral( "field" ) );
121  for ( int i = 0; i < fields.size(); i++ )
122  {
123  QString name = QUrl::fromPercentEncoding( fields.at( i ).toUtf8() );
124  QVariant::Type type = QVariant::String;
125  QVariant::Type subType = QVariant::Invalid;
126  QString typeName( QStringLiteral( "string" ) );
127  int length = 255;
128  int precision = 0;
129 
130  int pos = reFieldDef.indexIn( name );
131  if ( pos >= 0 )
132  {
133  name = name.mid( 0, pos );
134  typeName = reFieldDef.cap( 1 ).toLower();
135  if ( typeName == QLatin1String( "int" ) || typeName == QLatin1String( "integer" ) )
136  {
137  type = QVariant::Int;
138  typeName = QStringLiteral( "integer" );
139  length = -1;
140  }
141  else if ( typeName == QLatin1String( "int8" ) || typeName == QLatin1String( "long" ) )
142  {
143  type = QVariant::LongLong;
144  typeName = QStringLiteral( "int8" );
145  length = -1;
146  }
147  else if ( typeName == QLatin1String( "real" ) || typeName == QLatin1String( "double" ) )
148  {
149  type = QVariant::Double;
150  typeName = QStringLiteral( "double" );
151  length = 20;
152  precision = 5;
153  }
154  else if ( typeName == QLatin1String( "date" ) )
155  {
156  type = QVariant::Date;
157  typeName = QStringLiteral( "date" );
158  length = -1;
159  }
160  else if ( typeName == QLatin1String( "time" ) )
161  {
162  type = QVariant::Time;
163  typeName = QStringLiteral( "time" );
164  length = -1;
165  }
166  else if ( typeName == QLatin1String( "datetime" ) )
167  {
168  type = QVariant::DateTime;
169  typeName = QStringLiteral( "datetime" );
170  length = -1;
171  }
172  else if ( typeName == QLatin1String( "bool" ) || typeName == QLatin1String( "boolean" ) )
173  {
174  type = QVariant::Bool;
175  typeName = QStringLiteral( "boolean" );
176  length = -1;
177  }
178  else if ( typeName == QLatin1String( "binary" ) )
179  {
180  type = QVariant::ByteArray;
181  typeName = QStringLiteral( "binary" );
182  length = -1;
183  }
184 
185  if ( !reFieldDef.cap( 2 ).isEmpty() )
186  {
187  length = reFieldDef.cap( 2 ).toInt();
188  }
189  if ( !reFieldDef.cap( 3 ).isEmpty() )
190  {
191  precision = reFieldDef.cap( 3 ).toInt();
192  }
193  if ( !reFieldDef.cap( 4 ).isEmpty() )
194  {
195  //array
196  subType = type;
197  type = ( subType == QVariant::String ? QVariant::StringList : QVariant::List );
198  }
199  }
200  if ( !name.isEmpty() )
201  attributes.append( QgsField( name, type, typeName, length, precision, QString(), subType ) );
202  }
203  addAttributes( attributes );
204  }
205 
206  if ( url.hasQueryItem( QStringLiteral( "index" ) ) && url.queryItemValue( QStringLiteral( "index" ) ) == QLatin1String( "yes" ) )
207  {
208  createSpatialIndex();
209  }
210 
211 }
212 
213 QgsMemoryProvider::~QgsMemoryProvider()
214 {
215  delete mSpatialIndex;
216 }
217 
218 QString QgsMemoryProvider::providerKey()
219 {
220  return TEXT_PROVIDER_KEY;
221 }
222 
223 QString QgsMemoryProvider::providerDescription()
224 {
225  return TEXT_PROVIDER_DESCRIPTION;
226 }
227 
228 QgsMemoryProvider *QgsMemoryProvider::createProvider( const QString &uri, const ProviderOptions &options )
229 {
230  return new QgsMemoryProvider( uri, options );
231 }
232 
233 QgsAbstractFeatureSource *QgsMemoryProvider::featureSource() const
234 {
235  return new QgsMemoryFeatureSource( this );
236 }
237 
238 QString QgsMemoryProvider::dataSourceUri( bool expandAuthConfig ) const
239 {
240  Q_UNUSED( expandAuthConfig )
241 
242  QUrl uri( QStringLiteral( "memory" ) );
243  QString geometry = QgsWkbTypes::displayString( mWkbType );
244  uri.addQueryItem( QStringLiteral( "geometry" ), geometry );
245 
246  if ( mCrs.isValid() )
247  {
248  QString crsDef;
249  QString authid = mCrs.authid();
250  if ( authid.startsWith( QLatin1String( "EPSG:" ) ) )
251  {
252  crsDef = authid;
253  }
254  else
255  {
256  int srid = mCrs.postgisSrid();
257  if ( srid )
258  {
259  crsDef = QStringLiteral( "postgis:%1" ).arg( srid );
260  }
261  else
262  {
263  crsDef = QStringLiteral( "wkt:%1" ).arg( mCrs.toWkt() );
264  }
265  }
266  uri.addQueryItem( QStringLiteral( "crs" ), crsDef );
267  }
268  if ( mSpatialIndex )
269  {
270  uri.addQueryItem( QStringLiteral( "index" ), QStringLiteral( "yes" ) );
271  }
272 
273  QgsAttributeList attrs = const_cast<QgsMemoryProvider *>( this )->attributeIndexes();
274  for ( int i = 0; i < attrs.size(); i++ )
275  {
276  QgsField field = mFields.at( attrs[i] );
277  QString fieldDef = field.name();
278  fieldDef.append( QStringLiteral( ":%2(%3,%4)" ).arg( field.typeName() ).arg( field.length() ).arg( field.precision() ) );
279  uri.addQueryItem( QStringLiteral( "field" ), fieldDef );
280  }
281 
282  return QString( uri.toEncoded() );
283 
284 }
285 
286 QString QgsMemoryProvider::storageType() const
287 {
288  return QStringLiteral( "Memory storage" );
289 }
290 
291 QgsFeatureIterator QgsMemoryProvider::getFeatures( const QgsFeatureRequest &request ) const
292 {
293  return QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, request ) );
294 }
295 
296 
297 QgsRectangle QgsMemoryProvider::extent() const
298 {
299  if ( mExtent.isEmpty() && !mFeatures.isEmpty() )
300  {
301  mExtent.setMinimal();
302  if ( mSubsetString.isEmpty() )
303  {
304  // fast way - iterate through all features
305  const auto constMFeatures = mFeatures;
306  for ( const QgsFeature &feat : constMFeatures )
307  {
308  if ( feat.hasGeometry() )
309  mExtent.combineExtentWith( feat.geometry().boundingBox() );
310  }
311  }
312  else
313  {
314  QgsFeature f;
315  QgsFeatureIterator fi = getFeatures( QgsFeatureRequest().setNoAttributes() );
316  while ( fi.nextFeature( f ) )
317  {
318  if ( f.hasGeometry() )
319  mExtent.combineExtentWith( f.geometry().boundingBox() );
320  }
321  }
322  }
323  else if ( mFeatures.isEmpty() )
324  {
325  mExtent.setMinimal();
326  }
327 
328  return mExtent;
329 }
330 
331 QgsWkbTypes::Type QgsMemoryProvider::wkbType() const
332 {
333  return mWkbType;
334 }
335 
336 long QgsMemoryProvider::featureCount() const
337 {
338  if ( mSubsetString.isEmpty() )
339  return mFeatures.count();
340 
341  // subset string set, no alternative but testing each feature
342  QgsFeatureIterator fit = QgsFeatureIterator( new QgsMemoryFeatureIterator( new QgsMemoryFeatureSource( this ), true, QgsFeatureRequest().setNoAttributes() ) );
343  int count = 0;
344  QgsFeature feature;
345  while ( fit.nextFeature( feature ) )
346  {
347  count++;
348  }
349  return count;
350 }
351 
352 QgsFields QgsMemoryProvider::fields() const
353 {
354  return mFields;
355 }
356 
357 bool QgsMemoryProvider::isValid() const
358 {
359  return ( mWkbType != QgsWkbTypes::Unknown );
360 }
361 
363 {
364  // TODO: make provider projection-aware
365  return mCrs; // return default CRS
366 }
367 
368 void QgsMemoryProvider::handlePostCloneOperations( QgsVectorDataProvider *source )
369 {
370  if ( QgsMemoryProvider *other = qobject_cast< QgsMemoryProvider * >( source ) )
371  {
372  // these properties aren't copied when cloning a memory provider by uri, so we need to do it manually
373  mFeatures = other->mFeatures;
374  mNextFeatureId = other->mNextFeatureId;
375  mExtent = other->mExtent;
376  }
377 }
378 
379 
380 bool QgsMemoryProvider::addFeatures( QgsFeatureList &flist, Flags )
381 {
382  bool result = true;
383  // whether or not to update the layer extent on the fly as we add features
384  bool updateExtent = mFeatures.isEmpty() || !mExtent.isEmpty();
385 
386  int fieldCount = mFields.count();
387 
388  // TODO: sanity checks of fields
389  for ( QgsFeatureList::iterator it = flist.begin(); it != flist.end(); ++it )
390  {
391  it->setId( mNextFeatureId );
392  it->setValid( true );
393  if ( it->attributes().count() < fieldCount )
394  {
395  // ensure features have the correct number of attributes by padding
396  // them with null attributes for missing values
397  QgsAttributes attributes = it->attributes();
398  for ( int i = it->attributes().count(); i < mFields.count(); ++i )
399  {
400  attributes.append( QVariant( mFields.at( i ).type() ) );
401  }
402  it->setAttributes( attributes );
403  }
404  else if ( it->attributes().count() > fieldCount )
405  {
406  // too many attributes
407  pushError( tr( "Feature has too many attributes (expecting %1, received %2)" ).arg( fieldCount ).arg( it->attributes().count() ) );
408  QgsAttributes attributes = it->attributes();
409  attributes.resize( mFields.count() );
410  it->setAttributes( attributes );
411  }
412 
413  if ( it->hasGeometry() && mWkbType == QgsWkbTypes::NoGeometry )
414  {
415  it->clearGeometry();
416  }
417  else if ( it->hasGeometry() && QgsWkbTypes::geometryType( it->geometry().wkbType() ) !=
418  QgsWkbTypes::geometryType( mWkbType ) )
419  {
420  pushError( tr( "Could not add feature with geometry type %1 to layer of type %2" ).arg( QgsWkbTypes::displayString( it->geometry().wkbType() ),
421  QgsWkbTypes::displayString( mWkbType ) ) );
422  result = false;
423  continue;
424  }
425 
426  mFeatures.insert( mNextFeatureId, *it );
427 
428  if ( it->hasGeometry() )
429  {
430  if ( updateExtent )
431  mExtent.combineExtentWith( it->geometry().boundingBox() );
432 
433  // update spatial index
434  if ( mSpatialIndex )
435  mSpatialIndex->addFeature( *it );
436  }
437 
438  mNextFeatureId++;
439  }
440 
441  clearMinMaxCache();
442  return result;
443 }
444 
445 bool QgsMemoryProvider::deleteFeatures( const QgsFeatureIds &id )
446 {
447  for ( QgsFeatureIds::const_iterator it = id.begin(); it != id.end(); ++it )
448  {
449  QgsFeatureMap::iterator fit = mFeatures.find( *it );
450 
451  // check whether such feature exists
452  if ( fit == mFeatures.end() )
453  continue;
454 
455  // update spatial index
456  if ( mSpatialIndex )
457  mSpatialIndex->deleteFeature( *fit );
458 
459  mFeatures.erase( fit );
460  }
461 
462  updateExtents();
463  clearMinMaxCache();
464 
465  return true;
466 }
467 
468 bool QgsMemoryProvider::addAttributes( const QList<QgsField> &attributes )
469 {
470  for ( QList<QgsField>::const_iterator it = attributes.begin(); it != attributes.end(); ++it )
471  {
472  switch ( it->type() )
473  {
474  case QVariant::Int:
475  case QVariant::Double:
476  case QVariant::String:
477  case QVariant::Date:
478  case QVariant::Time:
479  case QVariant::DateTime:
480  case QVariant::LongLong:
481  case QVariant::StringList:
482  case QVariant::List:
483  case QVariant::Bool:
484  case QVariant::ByteArray:
485  break;
486  default:
487  QgsDebugMsg( "Field type not supported: " + it->typeName() );
488  continue;
489  }
490  // add new field as a last one
491  mFields.append( *it );
492 
493  for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
494  {
495  QgsFeature &f = fit.value();
496  QgsAttributes attr = f.attributes();
497  attr.append( QVariant() );
498  f.setAttributes( attr );
499  }
500  }
501  return true;
502 }
503 
504 bool QgsMemoryProvider::renameAttributes( const QgsFieldNameMap &renamedAttributes )
505 {
506  QgsFieldNameMap::const_iterator renameIt = renamedAttributes.constBegin();
507  bool result = true;
508  for ( ; renameIt != renamedAttributes.constEnd(); ++renameIt )
509  {
510  int fieldIndex = renameIt.key();
511  if ( fieldIndex < 0 || fieldIndex >= mFields.count() )
512  {
513  result = false;
514  continue;
515  }
516  if ( mFields.indexFromName( renameIt.value() ) >= 0 )
517  {
518  //field name already in use
519  result = false;
520  continue;
521  }
522 
523  mFields[ fieldIndex ].setName( renameIt.value() );
524  }
525  return result;
526 }
527 
528 bool QgsMemoryProvider::deleteAttributes( const QgsAttributeIds &attributes )
529 {
530  QList<int> attrIdx = attributes.toList();
531  std::sort( attrIdx.begin(), attrIdx.end(), std::greater<int>() );
532 
533  // delete attributes one-by-one with decreasing index
534  for ( QList<int>::const_iterator it = attrIdx.constBegin(); it != attrIdx.constEnd(); ++it )
535  {
536  int idx = *it;
537  mFields.remove( idx );
538 
539  for ( QgsFeatureMap::iterator fit = mFeatures.begin(); fit != mFeatures.end(); ++fit )
540  {
541  QgsFeature &f = fit.value();
542  QgsAttributes attr = f.attributes();
543  attr.remove( idx );
544  f.setAttributes( attr );
545  }
546  }
547  clearMinMaxCache();
548  return true;
549 }
550 
551 bool QgsMemoryProvider::changeAttributeValues( const QgsChangedAttributesMap &attr_map )
552 {
553  for ( QgsChangedAttributesMap::const_iterator it = attr_map.begin(); it != attr_map.end(); ++it )
554  {
555  QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
556  if ( fit == mFeatures.end() )
557  continue;
558 
559  const QgsAttributeMap &attrs = it.value();
560  for ( QgsAttributeMap::const_iterator it2 = attrs.constBegin(); it2 != attrs.constEnd(); ++it2 )
561  fit->setAttribute( it2.key(), it2.value() );
562  }
563  clearMinMaxCache();
564  return true;
565 }
566 
567 bool QgsMemoryProvider::changeGeometryValues( const QgsGeometryMap &geometry_map )
568 {
569  for ( QgsGeometryMap::const_iterator it = geometry_map.begin(); it != geometry_map.end(); ++it )
570  {
571  QgsFeatureMap::iterator fit = mFeatures.find( it.key() );
572  if ( fit == mFeatures.end() )
573  continue;
574 
575  // update spatial index
576  if ( mSpatialIndex )
577  mSpatialIndex->deleteFeature( *fit );
578 
579  fit->setGeometry( it.value() );
580 
581  // update spatial index
582  if ( mSpatialIndex )
583  mSpatialIndex->addFeature( *fit );
584  }
585 
586  updateExtents();
587 
588  return true;
589 }
590 
591 QString QgsMemoryProvider::subsetString() const
592 {
593  return mSubsetString;
594 }
595 
596 bool QgsMemoryProvider::setSubsetString( const QString &theSQL, bool updateFeatureCount )
597 {
598  Q_UNUSED( updateFeatureCount )
599 
600  if ( !theSQL.isEmpty() )
601  {
602  QgsExpression tempExpression( theSQL );
603  if ( tempExpression.hasParserError() )
604  return false;
605  }
606 
607  if ( theSQL == mSubsetString )
608  return true;
609 
610  mSubsetString = theSQL;
611  clearMinMaxCache();
612  mExtent.setMinimal();
613 
614  emit dataChanged();
615  return true;
616 }
617 
618 bool QgsMemoryProvider::createSpatialIndex()
619 {
620  if ( !mSpatialIndex )
621  {
622  mSpatialIndex = new QgsSpatialIndex();
623 
624  // add existing features to index
625  for ( QgsFeatureMap::iterator it = mFeatures.begin(); it != mFeatures.end(); ++it )
626  {
627  mSpatialIndex->addFeature( *it );
628  }
629  }
630  return true;
631 }
632 
633 QgsVectorDataProvider::Capabilities QgsMemoryProvider::capabilities() const
634 {
635  return AddFeatures | DeleteFeatures | ChangeGeometries |
636  ChangeAttributeValues | AddAttributes | DeleteAttributes | RenameAttributes | CreateSpatialIndex |
637  SelectAtId | CircularGeometries | FastTruncate;
638 }
639 
640 bool QgsMemoryProvider::truncate()
641 {
642  mFeatures.clear();
643  clearMinMaxCache();
644  mExtent.setMinimal();
645  return true;
646 }
647 
648 void QgsMemoryProvider::updateExtents()
649 {
650  mExtent.setMinimal();
651 }
652 
653 QString QgsMemoryProvider::name() const
654 {
655  return TEXT_PROVIDER_KEY;
656 }
657 
658 QString QgsMemoryProvider::description() const
659 {
660  return TEXT_PROVIDER_DESCRIPTION;
661 }
662 
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:41
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:151
QString name
Definition: qgsfield.h:58
int precision
Definition: qgsfield.h:55
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:571
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
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
int length
Definition: qgsfield.h:54
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:812
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
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
This class represents a coordinate reference system (CRS).
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