QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
qgsfeature.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfeature.cpp - Spatial Feature Implementation
3  --------------------------------------
4 Date : 09-Sep-2003
5 Copyright : (C) 2003 by Gary E.Sherman
6 email : sherman at mrcc.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 "qgsfeature.h"
17 #include "qgsfeature_p.h"
18 #include "qgsfields.h"
19 #include "qgsgeometry.h"
20 #include "qgsrectangle.h"
21 #include "qgsfield_p.h" // for approximateMemoryUsage()
22 #include "qgsfields_p.h" // for approximateMemoryUsage()
23 
24 #include "qgsmessagelog.h"
25 #include "qgslogger.h"
26 #include <QDataStream>
27 
28 /***************************************************************************
29  * This class is considered CRITICAL and any change MUST be accompanied with
30  * full unit tests in testqgsfeature.cpp.
31  * See details in QEP #17
32  ****************************************************************************/
33 
34 
35 //
36 // QgsFeature
37 //
38 
40 {
41  d = new QgsFeaturePrivate( id );
42 }
43 
45 {
46  d = new QgsFeaturePrivate( id );
47  d->fields = fields;
48  initAttributes( d->fields.count() );
49 }
50 
51 QgsFeature::QgsFeature( const QgsFeature &rhs ) //NOLINT
52  : d( rhs.d )
53 {
54 }
55 
57 {
58  d = rhs.d;
59  return *this;
60 }
61 
62 bool QgsFeature::operator ==( const QgsFeature &other ) const
63 {
64  if ( d == other.d )
65  return true;
66 
67  if ( !( d->fid == other.d->fid
68  && d->valid == other.d->valid
69  && d->fields == other.d->fields
70  && d->attributes == other.d->attributes
71  && d->symbol == other.d->symbol ) )
72  return false;
73 
74  // compare geometry
75  if ( d->geometry.isNull() && other.d->geometry.isNull() )
76  return true;
77  else if ( d->geometry.isNull() || other.d->geometry.isNull() )
78  return false;
79  else if ( !d->geometry.equals( other.d->geometry ) )
80  return false;
81 
82  return true;
83 }
84 
85 bool QgsFeature::operator!=( const QgsFeature &other ) const
86 {
87  return !( *this == other );
88 }
89 
91 {
92 }
93 
94 /***************************************************************************
95  * This class is considered CRITICAL and any change MUST be accompanied with
96  * full unit tests in testqgsfeature.cpp.
97  * See details in QEP #17
98  ****************************************************************************/
99 
101 {
102  return d->fid;
103 }
104 
106 {
107  d.detach();
108  d->attributes.remove( field );
109 }
110 
112 {
113  return d->geometry;
114 }
115 
116 /***************************************************************************
117  * This class is considered CRITICAL and any change MUST be accompanied with
118  * full unit tests in testqgsfeature.cpp.
119  * See details in QEP #17
120  ****************************************************************************/
121 
123 {
124  if ( id == d->fid )
125  return;
126 
127  d.detach();
128  d->fid = id;
129  d->valid = true;
130 }
131 
133 {
134  return d->attributes;
135 }
136 
137 QVariantMap QgsFeature::attributeMap() const
138 {
139  QVariantMap res;
140  const int fieldSize = d->fields.size();
141  const int attributeSize = d->attributes.size();
142  if ( fieldSize != attributeSize )
143  {
144  QgsDebugError( QStringLiteral( "Attribute size (%1) does not match number of fields (%2)" ).arg( attributeSize ).arg( fieldSize ) );
145  return QVariantMap();
146  }
147 
148  for ( int i = 0; i < attributeSize; ++i )
149  {
150  res[d->fields.at( i ).name()] = d->attributes.at( i );
151  }
152  return res;
153 }
154 
156 {
157  return d->attributes.size();
158 }
159 
161 {
162  d.detach();
163  d->attributes = attrs;
164  d->valid = true;
165 }
166 
167 void QgsFeature::setGeometry( const QgsGeometry &geometry )
168 {
169  d.detach();
170  d->geometry = geometry;
171  d->valid = true;
172 }
173 
174 void QgsFeature::setGeometry( std::unique_ptr<QgsAbstractGeometry> geometry )
175 {
176  d.detach();
177  d->geometry = QgsGeometry( std::move( geometry ) );
178  d->valid = true;
179 }
180 
182 {
183  if ( d->geometry.isNull() && d->valid )
184  return;
185 
187 }
188 
189 /***************************************************************************
190  * This class is considered CRITICAL and any change MUST be accompanied with
191  * full unit tests in testqgsfeature.cpp.
192  * See details in QEP #17
193  ****************************************************************************/
194 
195 void QgsFeature::setFields( const QgsFields &fields, bool init )
196 {
197  d.detach();
198  d->fields = fields;
199  if ( init )
200  {
201  initAttributes( d->fields.count() );
202  }
203 }
204 
206 {
207  return d->fields;
208 }
209 
210 /***************************************************************************
211  * This class is considered CRITICAL and any change MUST be accompanied with
212  * full unit tests in testqgsfeature.cpp.
213  * See details in QEP #17
214  ****************************************************************************/
215 
217 {
218  return d->valid;
219 }
220 
221 void QgsFeature::setValid( bool validity )
222 {
223  if ( d->valid == validity )
224  return;
225 
226  d.detach();
227  d->valid = validity;
228 }
229 
231 {
232  return !d->geometry.isNull();
233 }
234 
235 void QgsFeature::initAttributes( int fieldCount )
236 {
237  d.detach();
238  d->attributes.resize( 0 ); // clears existing elements, while still preserving the currently allocated capacity of the list (unlike clear)
239  // ensures ALL attributes, including previously existing ones are default constructed.
240  // doing it this way also avoids clearing individual QVariants -- which can trigger a detachment. Cheaper just to make a new one.
241  d->attributes.resize( fieldCount );
242 }
243 
244 void QgsFeature::resizeAttributes( int fieldCount )
245 {
246  if ( fieldCount == d->attributes.size() )
247  return;
248 
249  d.detach();
250  d->attributes.resize( fieldCount );
251 }
252 
253 void QgsFeature::padAttributes( int count )
254 {
255  if ( count == 0 )
256  return;
257 
258  d.detach();
259  d->attributes.resize( d->attributes.size() + count );
260 }
261 
262 bool QgsFeature::setAttribute( int idx, const QVariant &value )
263 {
264  if ( idx < 0 || idx >= d->attributes.size() )
265  {
266  QgsMessageLog::logMessage( QObject::tr( "Attribute index %1 out of bounds [0;%2]" ).arg( idx ).arg( d->attributes.size() ), QString(), Qgis::MessageLevel::Warning );
267  return false;
268  }
269 
270  d.detach();
271  d->attributes[idx] = value;
272  d->valid = true;
273  return true;
274 }
275 
276 /***************************************************************************
277  * This class is considered CRITICAL and any change MUST be accompanied with
278  * full unit tests in testqgsfeature.cpp.
279  * See details in QEP #17
280  ****************************************************************************/
281 
282 bool QgsFeature::setAttribute( const QString &name, const QVariant &value )
283 {
284  int fieldIdx = fieldNameIndex( name );
285  if ( fieldIdx == -1 )
286  return false;
287 
288  d.detach();
289  d->attributes[fieldIdx] = value;
290  d->valid = true;
291  return true;
292 }
293 
294 bool QgsFeature::deleteAttribute( const QString &name )
295 {
296  int fieldIdx = fieldNameIndex( name );
297  if ( fieldIdx == -1 )
298  return false;
299 
300  d.detach();
301  d->attributes[fieldIdx].clear();
302  return true;
303 }
304 
305 QVariant QgsFeature::attribute( int fieldIdx ) const
306 {
307  if ( fieldIdx < 0 || fieldIdx >= d->attributes.count() )
308  return QVariant();
309 
310  return d->attributes.at( fieldIdx );
311 }
312 
313 bool QgsFeature::isUnsetValue( int fieldIdx ) const
314 {
315  if ( fieldIdx < 0 || fieldIdx >= d->attributes.count() )
316  return false;
317 
318  return d->attributes.at( fieldIdx ).userType() == QMetaType::type( "QgsUnsetAttributeValue" );
319 }
320 
322 {
323  return d->symbol.get();
324 }
325 
327 {
328  if ( symbol == d->symbol.get() )
329  return;
330 
331  d.detach();
332  d->symbol.reset( symbol );
333 }
334 
335 QVariant QgsFeature::attribute( const QString &name ) const
336 {
337  int fieldIdx = fieldNameIndex( name );
338  if ( fieldIdx == -1 )
339  return QVariant();
340 
341  return d->attributes.at( fieldIdx );
342 }
343 
344 /***************************************************************************
345  * This class is considered CRITICAL and any change MUST be accompanied with
346  * full unit tests in testqgsfeature.cpp.
347  * See details in QEP #17
348  ****************************************************************************/
349 
350 int QgsFeature::fieldNameIndex( const QString &fieldName ) const
351 {
352  return d->fields.lookupField( fieldName );
353 }
354 
355 static size_t qgsQStringApproximateMemoryUsage( const QString &str )
356 {
357  return sizeof( QString ) + str.size() * sizeof( QChar );
358 }
359 
360 static size_t qgsQVariantApproximateMemoryUsage( const QVariant &v )
361 {
362  // A QVariant has a private structure that is a union of things whose larger
363  // size if a long long, and a int
364  size_t s = sizeof( QVariant ) + sizeof( long long ) + sizeof( int );
365  if ( v.type() == QVariant::String )
366  {
367  s += qgsQStringApproximateMemoryUsage( v.toString() );
368  }
369  else if ( v.type() == QVariant::StringList )
370  {
371  for ( const QString &str : v.toStringList() )
372  s += qgsQStringApproximateMemoryUsage( str );
373  }
374  else if ( v.type() == QVariant::List )
375  {
376  for ( const QVariant &subV : v.toList() )
377  s += qgsQVariantApproximateMemoryUsage( subV );
378  }
379  return s;
380 }
381 
383 {
384  size_t s = sizeof( *this ) + sizeof( *d );
385 
386  // Attributes
387  for ( const QVariant &attr : std::as_const( d->attributes ) )
388  {
389  s += qgsQVariantApproximateMemoryUsage( attr );
390  }
391 
392  // Geometry
393  s += sizeof( QAtomicInt ) + sizeof( void * ); // ~ sizeof(QgsGeometryPrivate)
394  // For simplicity we consider that the RAM usage is the one of the WKB
395  // representation
396  s += d->geometry.wkbSize();
397 
398  // Fields
399  s += sizeof( QgsFieldsPrivate );
400  // TODO potentially: take into account the length of the name, comment, default value, etc...
401  s += d->fields.size() * ( sizeof( QgsField ) + sizeof( QgsFieldPrivate ) );
402 
403  return static_cast<int>( s );
404 }
405 
406 
407 /***************************************************************************
408  * This class is considered CRITICAL and any change MUST be accompanied with
409  * full unit tests in testqgsfeature.cpp.
410  * See details in QEP #17
411  ****************************************************************************/
412 
413 QDataStream &operator<<( QDataStream &out, const QgsFeature &feature )
414 {
415  out << feature.id();
416  out << feature.attributes();
417  if ( feature.hasGeometry() )
418  {
419  out << ( feature.geometry() );
420  }
421  else
422  {
423  QgsGeometry geometry;
424  out << geometry;
425  }
426  out << feature.isValid();
427  return out;
428 }
429 
430 QDataStream &operator>>( QDataStream &in, QgsFeature &feature )
431 {
432  QgsFeatureId id;
433  QgsGeometry geometry;
434  bool valid;
435  QgsAttributes attr;
436  in >> id >> attr >> geometry >> valid;
437  feature.setId( id );
438  feature.setGeometry( geometry );
439  feature.setAttributes( attr );
440  feature.setValid( valid );
441  return in;
442 }
443 
444 uint qHash( const QgsFeature &key, uint seed )
445 {
446  uint hash = seed;
447  const auto constAttributes = key.attributes();
448  for ( const QVariant &attr : constAttributes )
449  {
450  hash ^= qHash( attr.toString() );
451  }
452 
453  hash ^= qHash( key.geometry().asWkt() );
454  hash ^= qHash( key.id() );
455 
456  return hash;
457 }
458 
A vector of attributes.
Definition: qgsattributes.h:59
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
Definition: qgsfeature.cpp:262
void resizeAttributes(int fieldCount)
Resizes the attributes attached to this feature to the given number of fields.
Definition: qgsfeature.cpp:244
QgsAttributes attributes
Definition: qgsfeature.h:65
bool operator!=(const QgsFeature &other) const
Compares two features.
Definition: qgsfeature.cpp:85
virtual ~QgsFeature()
Definition: qgsfeature.cpp:90
QgsFields fields
Definition: qgsfeature.h:66
int fieldNameIndex(const QString &fieldName) const
Utility method to get attribute index from name.
Definition: qgsfeature.cpp:350
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:235
void deleteAttribute(int field)
Clear's an attribute's value by its index.
Definition: qgsfeature.cpp:105
QgsFeature & operator=(const QgsFeature &rhs)
Assignment operator.
Definition: qgsfeature.cpp:56
int approximateMemoryUsage() const
Returns the approximate RAM usage of the feature, in bytes.
Definition: qgsfeature.cpp:382
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
Definition: qgsfeature.cpp:160
bool operator==(const QgsFeature &other) const
Compares two features.
Definition: qgsfeature.cpp:62
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:195
QgsFeature(QgsFeatureId id=FID_NULL)
Constructor for QgsFeature.
Definition: qgsfeature.cpp:39
int attributeCount() const
Returns the number of attributes attached to the feature.
Definition: qgsfeature.cpp:155
void padAttributes(int count)
Resizes the attributes attached to this feature by appending the specified count of NULL values to th...
Definition: qgsfeature.cpp:253
const QgsSymbol * embeddedSymbol() const
Returns the feature's embedded symbology, or nullptr if the feature has no embedded symbol.
Definition: qgsfeature.cpp:321
void setId(QgsFeatureId id)
Sets the feature id for this feature.
Definition: qgsfeature.cpp:122
QgsGeometry geometry
Definition: qgsfeature.h:67
void setEmbeddedSymbol(QgsSymbol *symbol)
Sets the feature's embedded symbol.
Definition: qgsfeature.cpp:326
void clearGeometry()
Removes any geometry associated with the feature.
Definition: qgsfeature.cpp:181
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:221
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:230
bool isUnsetValue(int fieldIdx) const
Returns true if the attribute at the specified index is an unset value.
Definition: qgsfeature.cpp:313
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:216
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:335
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:167
QVariantMap attributeMap() const
Returns the feature's attributes as a map of field name to value.
Definition: qgsfeature.cpp:137
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:53
Container of fields for a vector layer.
Definition: qgsfields.h:45
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:162
QString asWkt(int precision=17) const
Exports the geometry to WKT.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:94
#define str(x)
Definition: qgis.cpp:38
uint qHash(const QgsFeature &key, uint seed)
Definition: qgsfeature.cpp:444
QDataStream & operator<<(QDataStream &out, const QgsFeature &feature)
Writes the feature to stream out. QGIS version compatibility is not guaranteed.
Definition: qgsfeature.cpp:413
QDataStream & operator>>(QDataStream &in, QgsFeature &feature)
Reads a feature from stream in into feature. QGIS version compatibility is not guaranteed.
Definition: qgsfeature.cpp:430
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28
#define QgsDebugError(str)
Definition: qgslogger.h:38