Branch data Line data Source code
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 : : 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 : : 39 : 2109 : QgsFeature::QgsFeature( QgsFeatureId id ) 40 : 2109 : { 41 : 2109 : d = new QgsFeaturePrivate( id ); 42 : 2109 : } 43 : : 44 : 1 : QgsFeature::QgsFeature( const QgsFields &fields, QgsFeatureId id ) 45 : 1 : { 46 : 1 : d = new QgsFeaturePrivate( id ); 47 : 1 : d->fields = fields; 48 : 1 : initAttributes( d->fields.count() ); 49 : 1 : } 50 : : 51 : 4061 : QgsFeature::QgsFeature( const QgsFeature &rhs ) //NOLINT 52 : 4061 : : d( rhs.d ) 53 : 4061 : { 54 : 4061 : } 55 : : 56 : 1527 : QgsFeature &QgsFeature::operator=( const QgsFeature &rhs ) //NOLINT 57 : : { 58 : 1527 : d = rhs.d; 59 : 1527 : return *this; 60 : : } 61 : : 62 : 0 : bool QgsFeature::operator ==( const QgsFeature &other ) const 63 : : { 64 : 0 : if ( d == other.d ) 65 : 0 : return true; 66 : : 67 : 0 : if ( d->fid == other.d->fid 68 : 0 : && d->valid == other.d->valid 69 : 0 : && d->fields == other.d->fields 70 : 0 : && d->attributes == other.d->attributes 71 : 0 : && d->geometry.equals( other.d->geometry ) 72 : 0 : && d->symbol == other.d->symbol ) 73 : 0 : return true; 74 : : 75 : 0 : return false; 76 : 0 : } 77 : : 78 : 0 : bool QgsFeature::operator!=( const QgsFeature &other ) const 79 : : { 80 : 0 : return !( *this == other ); 81 : : } 82 : : 83 : 7052 : QgsFeature::~QgsFeature() //NOLINT 84 : 7052 : { 85 : 7052 : } 86 : : 87 : : /*************************************************************************** 88 : : * This class is considered CRITICAL and any change MUST be accompanied with 89 : : * full unit tests in testqgsfeature.cpp. 90 : : * See details in QEP #17 91 : : ****************************************************************************/ 92 : : 93 : 6662 : QgsFeatureId QgsFeature::id() const 94 : : { 95 : 6662 : return d->fid; 96 : : } 97 : : 98 : 0 : void QgsFeature::deleteAttribute( int field ) 99 : : { 100 : 0 : d.detach(); 101 : 0 : d->attributes.remove( field ); 102 : 0 : } 103 : : 104 : 3675 : QgsGeometry QgsFeature::geometry() const 105 : : { 106 : 3675 : return d->geometry; 107 : : } 108 : : 109 : : /*************************************************************************** 110 : : * This class is considered CRITICAL and any change MUST be accompanied with 111 : : * full unit tests in testqgsfeature.cpp. 112 : : * See details in QEP #17 113 : : ****************************************************************************/ 114 : : 115 : 1160 : void QgsFeature::setId( QgsFeatureId id ) 116 : : { 117 : 1160 : if ( id == d->fid ) 118 : 4 : return; 119 : : 120 : 1156 : d.detach(); 121 : 1156 : d->fid = id; 122 : 1156 : d->valid = true; 123 : 1160 : } 124 : : 125 : 81 : QgsAttributes QgsFeature::attributes() const 126 : : { 127 : 81 : return d->attributes; 128 : : } 129 : : 130 : 0 : int QgsFeature::attributeCount() const 131 : : { 132 : 0 : return d->attributes.size(); 133 : : } 134 : : 135 : 2 : void QgsFeature::setAttributes( const QgsAttributes &attrs ) 136 : : { 137 : 2 : if ( attrs == d->attributes ) 138 : 0 : return; 139 : : 140 : 2 : d.detach(); 141 : 2 : d->attributes = attrs; 142 : 2 : d->valid = true; 143 : 2 : } 144 : : 145 : 1199 : void QgsFeature::setGeometry( const QgsGeometry &geometry ) 146 : : { 147 : 1199 : d.detach(); 148 : 1199 : d->geometry = geometry; 149 : 1199 : d->valid = true; 150 : 1199 : } 151 : : 152 : 0 : void QgsFeature::setGeometry( std::unique_ptr<QgsAbstractGeometry> geometry ) 153 : : { 154 : 0 : d.detach(); 155 : 0 : d->geometry = QgsGeometry( std::move( geometry ) ); 156 : 0 : d->valid = true; 157 : 0 : } 158 : : 159 : 0 : void QgsFeature::clearGeometry() 160 : : { 161 : 0 : setGeometry( QgsGeometry() ); 162 : 0 : } 163 : : 164 : : /*************************************************************************** 165 : : * This class is considered CRITICAL and any change MUST be accompanied with 166 : : * full unit tests in testqgsfeature.cpp. 167 : : * See details in QEP #17 168 : : ****************************************************************************/ 169 : : 170 : 2223 : void QgsFeature::setFields( const QgsFields &fields, bool init ) 171 : : { 172 : 2223 : d.detach(); 173 : 2223 : d->fields = fields; 174 : 2223 : if ( init ) 175 : : { 176 : 0 : initAttributes( d->fields.count() ); 177 : 0 : } 178 : 2223 : } 179 : : 180 : 3 : QgsFields QgsFeature::fields() const 181 : : { 182 : 3 : return d->fields; 183 : : } 184 : : 185 : : /*************************************************************************** 186 : : * This class is considered CRITICAL and any change MUST be accompanied with 187 : : * full unit tests in testqgsfeature.cpp. 188 : : * See details in QEP #17 189 : : ****************************************************************************/ 190 : : 191 : 1 : bool QgsFeature::isValid() const 192 : : { 193 : 1 : return d->valid; 194 : : } 195 : : 196 : 3679 : void QgsFeature::setValid( bool validity ) 197 : : { 198 : 3679 : if ( d->valid == validity ) 199 : 2664 : return; 200 : : 201 : 1015 : d.detach(); 202 : 1015 : d->valid = validity; 203 : 3679 : } 204 : : 205 : 3257 : bool QgsFeature::hasGeometry() const 206 : : { 207 : 3257 : return !d->geometry.isNull(); 208 : : } 209 : : 210 : 1103 : void QgsFeature::initAttributes( int fieldCount ) 211 : : { 212 : 1103 : d.detach(); 213 : 1103 : d->attributes.resize( 0 ); // clears existing elements, while still preserving the currently allocated capacity of the list (unlike clear) 214 : : // ensures ALL attributes, including previously existing ones are default constructed. 215 : : // doing it this way also avoids clearing individual QVariants -- which can trigger a detachment. Cheaper just to make a new one. 216 : 1103 : d->attributes.resize( fieldCount ); 217 : 1103 : } 218 : : 219 : 0 : void QgsFeature::resizeAttributes( int fieldCount ) 220 : : { 221 : 0 : if ( fieldCount == d->attributes.size() ) 222 : 0 : return; 223 : : 224 : 0 : d.detach(); 225 : 0 : d->attributes.resize( fieldCount ); 226 : 0 : } 227 : : 228 : 0 : void QgsFeature::padAttributes( int count ) 229 : : { 230 : 0 : if ( count == 0 ) 231 : 0 : return; 232 : : 233 : 0 : d.detach(); 234 : 0 : d->attributes.resize( d->attributes.size() + count ); 235 : 0 : } 236 : : 237 : 1570 : bool QgsFeature::setAttribute( int idx, const QVariant &value ) 238 : : { 239 : 1570 : if ( idx < 0 || idx >= d->attributes.size() ) 240 : : { 241 : 0 : QgsMessageLog::logMessage( QObject::tr( "Attribute index %1 out of bounds [0;%2]" ).arg( idx ).arg( d->attributes.size() ), QString(), Qgis::Warning ); 242 : 0 : return false; 243 : : } 244 : : 245 : 1570 : d.detach(); 246 : 1570 : d->attributes[idx] = value; 247 : 1570 : d->valid = true; 248 : 1570 : return true; 249 : 1570 : } 250 : : 251 : : /*************************************************************************** 252 : : * This class is considered CRITICAL and any change MUST be accompanied with 253 : : * full unit tests in testqgsfeature.cpp. 254 : : * See details in QEP #17 255 : : ****************************************************************************/ 256 : : 257 : 0 : bool QgsFeature::setAttribute( const QString &name, const QVariant &value ) 258 : : { 259 : 0 : int fieldIdx = fieldNameIndex( name ); 260 : 0 : if ( fieldIdx == -1 ) 261 : 0 : return false; 262 : : 263 : 0 : d.detach(); 264 : 0 : d->attributes[fieldIdx] = value; 265 : 0 : d->valid = true; 266 : 0 : return true; 267 : 0 : } 268 : : 269 : 0 : bool QgsFeature::deleteAttribute( const QString &name ) 270 : : { 271 : 0 : int fieldIdx = fieldNameIndex( name ); 272 : 0 : if ( fieldIdx == -1 ) 273 : 0 : return false; 274 : : 275 : 0 : d.detach(); 276 : 0 : d->attributes[fieldIdx].clear(); 277 : 0 : return true; 278 : 0 : } 279 : : 280 : 6 : QVariant QgsFeature::attribute( int fieldIdx ) const 281 : : { 282 : 6 : if ( fieldIdx < 0 || fieldIdx >= d->attributes.count() ) 283 : 0 : return QVariant(); 284 : : 285 : 6 : return d->attributes.at( fieldIdx ); 286 : 6 : } 287 : : 288 : 0 : const QgsSymbol *QgsFeature::embeddedSymbol() const 289 : : { 290 : 0 : return d->symbol.get(); 291 : : } 292 : : 293 : 0 : void QgsFeature::setEmbeddedSymbol( QgsSymbol *symbol ) 294 : : { 295 : 0 : if ( symbol == d->symbol.get() ) 296 : 0 : return; 297 : : 298 : 0 : d.detach(); 299 : 0 : d->symbol.reset( symbol ); 300 : 0 : } 301 : : 302 : 0 : QVariant QgsFeature::attribute( const QString &name ) const 303 : : { 304 : 0 : int fieldIdx = fieldNameIndex( name ); 305 : 0 : if ( fieldIdx == -1 ) 306 : 0 : return QVariant(); 307 : : 308 : 0 : return d->attributes.at( fieldIdx ); 309 : 0 : } 310 : : 311 : : /*************************************************************************** 312 : : * This class is considered CRITICAL and any change MUST be accompanied with 313 : : * full unit tests in testqgsfeature.cpp. 314 : : * See details in QEP #17 315 : : ****************************************************************************/ 316 : : 317 : 0 : int QgsFeature::fieldNameIndex( const QString &fieldName ) const 318 : : { 319 : 0 : return d->fields.lookupField( fieldName ); 320 : : } 321 : : 322 : 0 : static size_t qgsQStringApproximateMemoryUsage( const QString &str ) 323 : : { 324 : 0 : return sizeof( QString ) + str.size() * sizeof( QChar ); 325 : : } 326 : : 327 : 0 : static size_t qgsQVariantApproximateMemoryUsage( const QVariant &v ) 328 : : { 329 : : // A QVariant has a private structure that is a union of things whose larger 330 : : // size if a long long, and a int 331 : 0 : size_t s = sizeof( QVariant ) + sizeof( long long ) + sizeof( int ); 332 : 0 : if ( v.type() == QVariant::String ) 333 : : { 334 : 0 : s += qgsQStringApproximateMemoryUsage( v.toString() ); 335 : 0 : } 336 : 0 : else if ( v.type() == QVariant::StringList ) 337 : : { 338 : 0 : for ( const QString &str : v.toStringList() ) 339 : 0 : s += qgsQStringApproximateMemoryUsage( str ); 340 : 0 : } 341 : 0 : else if ( v.type() == QVariant::List ) 342 : : { 343 : 0 : for ( const QVariant &subV : v.toList() ) 344 : 0 : s += qgsQVariantApproximateMemoryUsage( subV ); 345 : 0 : } 346 : 0 : return s; 347 : 0 : } 348 : : 349 : 0 : int QgsFeature::approximateMemoryUsage() const 350 : : { 351 : 0 : size_t s = sizeof( *this ) + sizeof( *d ); 352 : : 353 : : // Attributes 354 : 0 : for ( const QVariant &attr : std::as_const( d->attributes ) ) 355 : : { 356 : 0 : s += qgsQVariantApproximateMemoryUsage( attr ); 357 : : } 358 : : 359 : : // Geometry 360 : 0 : s += sizeof( QAtomicInt ) + sizeof( void * ); // ~ sizeof(QgsGeometryPrivate) 361 : : // For simplicity we consider that the RAM usage is the one of the WKB 362 : : // representation 363 : 0 : s += d->geometry.wkbSize(); 364 : : 365 : : // Fields 366 : 0 : s += sizeof( QgsFieldsPrivate ); 367 : : // TODO potentially: take into account the length of the name, comment, default value, etc... 368 : 0 : s += d->fields.size() * ( sizeof( QgsField ) + sizeof( QgsFieldPrivate ) ); 369 : : 370 : 0 : return static_cast<int>( s ); 371 : : } 372 : : 373 : : 374 : : /*************************************************************************** 375 : : * This class is considered CRITICAL and any change MUST be accompanied with 376 : : * full unit tests in testqgsfeature.cpp. 377 : : * See details in QEP #17 378 : : ****************************************************************************/ 379 : : 380 : 0 : QDataStream &operator<<( QDataStream &out, const QgsFeature &feature ) 381 : : { 382 : 0 : out << feature.id(); 383 : 0 : out << feature.attributes(); 384 : 0 : if ( feature.hasGeometry() ) 385 : : { 386 : 0 : out << ( feature.geometry() ); 387 : 0 : } 388 : : else 389 : : { 390 : 0 : QgsGeometry geometry; 391 : 0 : out << geometry; 392 : 0 : } 393 : 0 : out << feature.isValid(); 394 : 0 : return out; 395 : 0 : } 396 : : 397 : 0 : QDataStream &operator>>( QDataStream &in, QgsFeature &feature ) 398 : : { 399 : : QgsFeatureId id; 400 : 0 : QgsGeometry geometry; 401 : : bool valid; 402 : 0 : QgsAttributes attr; 403 : 0 : in >> id >> attr >> geometry >> valid; 404 : 0 : feature.setId( id ); 405 : 0 : feature.setGeometry( geometry ); 406 : 0 : feature.setAttributes( attr ); 407 : 0 : feature.setValid( valid ); 408 : 0 : return in; 409 : 0 : } 410 : : 411 : 0 : uint qHash( const QgsFeature &key, uint seed ) 412 : : { 413 : 0 : uint hash = seed; 414 : 0 : const auto constAttributes = key.attributes(); 415 : 0 : for ( const QVariant &attr : constAttributes ) 416 : : { 417 : 0 : hash ^= qHash( attr.toString() ); 418 : : } 419 : : 420 : 0 : hash ^= qHash( key.geometry().asWkt() ); 421 : 0 : hash ^= qHash( key.id() ); 422 : : 423 : 0 : return hash; 424 : 0 : } 425 : :