From b863ca195ea4cbf200ad53fff0bbcfea13d558b8 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Sun, 13 May 2012 00:53:50 +0200 Subject: [PATCH] Allow access to feature attributes by name --- python/core/qgsfeature.sip | 73 +++++++++++++++++++ src/core/qgsfeature.cpp | 54 ++++++++++++++ src/core/qgsfeature.h | 44 +++++++++++ .../qgsdelimitedtextprovider.cpp | 2 +- src/providers/gpx/qgsgpxprovider.cpp | 3 + src/providers/grass/qgsgrassprovider.cpp | 1 + src/providers/memory/qgsmemoryprovider.cpp | 2 + src/providers/mssql/qgsmssqlprovider.cpp | 1 + src/providers/ogr/qgsogrprovider.cpp | 2 + src/providers/osm/osmprovider.cpp | 2 + .../postgres/qgspostgresprovider.cpp | 2 + .../spatialite/qgsspatialiteprovider.cpp | 2 + .../sqlanywhere/qgssqlanywhereprovider.cpp | 1 + src/providers/wfs/qgswfsprovider.cpp | 1 + 14 files changed, 189 insertions(+), 1 deletion(-) diff --git a/python/core/qgsfeature.sip b/python/core/qgsfeature.sip index 76ec465d3eb..d8b69497981 100644 --- a/python/core/qgsfeature.sip +++ b/python/core/qgsfeature.sip @@ -39,11 +39,34 @@ class QgsFeature } %End + SIP_PYOBJECT __getitem__(const QString& name); +%MethodCode + int fieldIdx = sipCpp->fieldNameIndex(*a0); + if (fieldIdx == -1) + PyErr_SetString(PyExc_KeyError, a0->toAscii()); + else + { + QVariant* v = new QVariant( sipCpp->attributeMap().value(fieldIdx) ); + sipRes = sipConvertFromInstance(v, sipClass_QVariant, Py_None); + } +%End + void __setitem__(int key, QVariant value); %MethodCode sipCpp->addAttribute(a0, *a1); %End + void __setitem__(const QString& key, QVariant value); +%MethodCode + int fieldIdx = sipCpp->fieldNameIndex(*a0); + if (fieldIdx == -1) + PyErr_SetString(PyExc_KeyError, a0->toAscii()); + else + { + sipCpp->addAttribute(fieldIdx, *a1); + } +%End + void __delitem__(int key); %MethodCode if (sipCpp->attributeMap().contains(a0)) @@ -52,6 +75,15 @@ class QgsFeature PyErr_SetString(PyExc_KeyError, QByteArray::number(a0)); %End + void __delitem__(const QString& name); +%MethodCode + int fieldIdx = sipCpp->fieldNameIndex(*a0); + if (fieldIdx == -1) + PyErr_SetString(PyExc_KeyError, a0->toAscii()); + else + sipCpp->deleteAttribute(fieldIdx); +%End + //! Constructor QgsFeature( qint64 id = 0, QString typeName = "" ); @@ -162,5 +194,46 @@ class QgsFeature */ void setGeometryAndOwnership( unsigned char * geom /Transfer/, size_t length ); + /** Assign a field map with the feature to allow attribute access by attribute name + * @note added in 2.0 + */ + void setFieldMap( const QgsFieldMap* fields ); + + /** Get associated field map. may be NULL + * @note added in 2.0 + */ + const QgsFieldMap* fieldMap() const; + + /** Insert a value into attribute. Returns false if attribute name could not be converted to index. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + bool addAttribute( const QString& name, QVariant value ); + + /** Change a value of an attribute. Returns false if attribute name could not be converted to index. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + bool changeAttribute( const QString& name, QVariant value ); + + /** Remove an attribute value. Returns false if attribute name could not be converted to index. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + bool deleteAttribute( const QString& name ); + + /** Lookup attribute value from attribute name. Returns invalid variant if attribute name could not be converted to index. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + QVariant attribute( const QString& name ) const; + + /** Utility method to get attribute index from name. Returns -1 if field does not exist or field map is not associated. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + int fieldNameIndex( const QString& fieldName ) const; + + }; // class QgsFeature diff --git a/src/core/qgsfeature.cpp b/src/core/qgsfeature.cpp index 0eda78bf862..25d31edf01c 100644 --- a/src/core/qgsfeature.cpp +++ b/src/core/qgsfeature.cpp @@ -14,6 +14,7 @@ email : sherman at mrcc.com ***************************************************************************/ #include "qgsfeature.h" +#include "qgsfield.h" #include "qgsgeometry.h" #include "qgsrectangle.h" @@ -28,6 +29,7 @@ QgsFeature::QgsFeature( QgsFeatureId id, QString typeName ) , mValid( false ) , mDirty( 0 ) , mTypeName( typeName ) + , mFields( 0 ) { // NOOP } @@ -40,6 +42,7 @@ QgsFeature::QgsFeature( QgsFeature const & rhs ) , mValid( rhs.mValid ) , mDirty( rhs.mDirty ) , mTypeName( rhs.mTypeName ) + , mFields( rhs.mFields ) { // copy embedded geometry @@ -60,6 +63,7 @@ QgsFeature & QgsFeature::operator=( QgsFeature const & rhs ) mAttributes = rhs.mAttributes; mValid = rhs.mValid; mTypeName = rhs.mTypeName; + mFields = rhs.mFields; // make sure to delete the old geometry (if exists) if ( mGeometry && mOwnsGeometry ) @@ -218,3 +222,53 @@ void QgsFeature::clean() { mDirty = false; } + + +bool QgsFeature::addAttribute( const QString& name, QVariant value ) +{ + int fieldIdx = fieldNameIndex( name ); + if ( fieldIdx == -1 ) + return false; + + mAttributes.insert( fieldIdx, value ); + return true; +} + +bool QgsFeature::changeAttribute( const QString& name, QVariant value ) +{ + return addAttribute( name, value ); +} + +bool QgsFeature::deleteAttribute( const QString& name ) +{ + int fieldIdx = fieldNameIndex( name ); + if ( fieldIdx == -1 ) + return false; + + mAttributes.remove( fieldIdx ); + return true; +} + +QVariant QgsFeature::attribute( const QString& name ) const +{ + int fieldIdx = fieldNameIndex( name ); + if ( fieldIdx == -1 ) + return QVariant(); + + return mAttributes.value( fieldIdx ); +} + +int QgsFeature::fieldNameIndex( const QString& fieldName ) const +{ + if ( !mFields ) + return -1; + + for ( QgsFieldMap::const_iterator it = mFields->constBegin(); it != mFields->constEnd(); ++it ) + { + if ( QString::compare( it->name(), fieldName, Qt::CaseInsensitive ) == 0 ) + { + return it.key(); + } + } + return -1; +} diff --git a/src/core/qgsfeature.h b/src/core/qgsfeature.h index c0d3877006c..b21d2637765 100644 --- a/src/core/qgsfeature.h +++ b/src/core/qgsfeature.h @@ -94,6 +94,8 @@ typedef int QgsFeatureId; // key = field index, value = field value typedef QMap QgsAttributeMap; +class QgsField; +typedef QMap QgsFieldMap; /** \ingroup core * The feature class encapsulates a single feature including its id, @@ -216,6 +218,46 @@ class CORE_EXPORT QgsFeature */ void setGeometryAndOwnership( unsigned char * geom, size_t length ); + /** Assign a field map with the feature to allow attribute access by attribute name + * @note added in 2.0 + */ + void setFieldMap( const QgsFieldMap* fields ) { mFields = fields; } + + /** Get associated field map. may be NULL + * @note added in 2.0 + */ + const QgsFieldMap* fieldMap() const { return mFields; } + + /** Insert a value into attribute. Returns false if attribute name could not be converted to index. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + bool addAttribute( const QString& name, QVariant value ); + + /** Change a value of an attribute. Returns false if attribute name could not be converted to index. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + bool changeAttribute( const QString& name, QVariant value ); + + /** Remove an attribute value. Returns false if attribute name could not be converted to index. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + bool deleteAttribute( const QString& name ); + + /** Lookup attribute value from attribute name. Returns invalid variant if attribute name could not be converted to index. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + QVariant attribute( const QString& name ) const; + + /** Utility method to get attribute index from name. Returns -1 if field does not exist or field map is not associated. + * Field map must be associated to make this work. + * @note added in 2.0 + */ + int fieldNameIndex( const QString& fieldName ) const; + private: //! feature id @@ -246,6 +288,8 @@ class CORE_EXPORT QgsFeature /// feature type name QString mTypeName; + //! Optional field map for name-based attribute lookups + const QgsFieldMap* mFields; }; // class QgsFeature diff --git a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp index 866b8347b30..ae828b895f2 100644 --- a/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp +++ b/src/providers/delimitedtext/qgsdelimitedtextprovider.cpp @@ -545,7 +545,7 @@ bool QgsDelimitedTextProvider::nextFeature( QgsFeature& feature ) // At this point the current feature values are valid feature.setValid( true ); - + feature.setFieldMap( &attributeFields ); // allow name-based attribute lookups feature.setFeatureId( mFid ); if ( geom ) diff --git a/src/providers/gpx/qgsgpxprovider.cpp b/src/providers/gpx/qgsgpxprovider.cpp index 1ddb5a5bd54..783eec9c10c 100644 --- a/src/providers/gpx/qgsgpxprovider.cpp +++ b/src/providers/gpx/qgsgpxprovider.cpp @@ -157,6 +157,7 @@ bool QgsGPXProvider::nextFeature( QgsFeature& feature ) feature.setGeometryAndOwnership(( unsigned char * )geo, sizeof( wkbPoint ) ); } feature.setValid( true ); + feature.setFieldMap( &attributeFields ); // allow name-based attribute lookups // add attributes if they are wanted for ( iter = mAttributesToFetch.begin(); iter != mAttributesToFetch.end(); ++iter ) @@ -248,6 +249,7 @@ bool QgsGPXProvider::nextFeature( QgsFeature& feature ) feature.setFeatureId( rte->id ); result = true; feature.setValid( true ); + feature.setFieldMap( &attributeFields ); // allow name-based attribute lookups // add attributes if they are wanted for ( iter = mAttributesToFetch.begin(); iter != mAttributesToFetch.end(); ++iter ) @@ -364,6 +366,7 @@ bool QgsGPXProvider::nextFeature( QgsFeature& feature ) result = true; feature.setValid( true ); + feature.setFieldMap( &attributeFields ); // allow name-based attribute lookups // add attributes if they are wanted for ( iter = mAttributesToFetch.begin(); iter != mAttributesToFetch.end(); ++iter ) diff --git a/src/providers/grass/qgsgrassprovider.cpp b/src/providers/grass/qgsgrassprovider.cpp index 4fd4a51be08..781742d6322 100644 --- a/src/providers/grass/qgsgrassprovider.cpp +++ b/src/providers/grass/qgsgrassprovider.cpp @@ -343,6 +343,7 @@ bool QgsGrassProvider::nextFeature( QgsFeature& feature ) feature.setFeatureId( id ); feature.clearAttributeMap(); + feature.setFieldMap( &fields() ); // allow name-based attribute lookups // TODO int may be 64 bits (memcpy) if ( type & ( GV_POINTS | GV_LINES | GV_FACE ) ) /* points or lines */ diff --git a/src/providers/memory/qgsmemoryprovider.cpp b/src/providers/memory/qgsmemoryprovider.cpp index d75e2375424..132610aa310 100644 --- a/src/providers/memory/qgsmemoryprovider.cpp +++ b/src/providers/memory/qgsmemoryprovider.cpp @@ -287,6 +287,7 @@ bool QgsMemoryProvider::nextFeature( QgsFeature& feature ) feature = mSelectIterator.value(); mSelectIterator++; feature.setValid( true ); + feature.setFieldMap( &mFields ); // allow name-based attribute lookups } return hasFeature; @@ -308,6 +309,7 @@ bool QgsMemoryProvider::featureAtId( QgsFeatureId featureId, feature = *it; feature.setValid( true ); + feature.setFieldMap( &mFields ); // allow name-based attribute lookups return true; } diff --git a/src/providers/mssql/qgsmssqlprovider.cpp b/src/providers/mssql/qgsmssqlprovider.cpp index f11fb44dcc9..3cf5e9a47f3 100644 --- a/src/providers/mssql/qgsmssqlprovider.cpp +++ b/src/providers/mssql/qgsmssqlprovider.cpp @@ -537,6 +537,7 @@ bool QgsMssqlProvider::nextFeature( QgsFeature& feature ) } feature.setValid( true ); + feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups return true; } return false; diff --git a/src/providers/ogr/qgsogrprovider.cpp b/src/providers/ogr/qgsogrprovider.cpp index 0893d88b66c..dcd464f4dbe 100644 --- a/src/providers/ogr/qgsogrprovider.cpp +++ b/src/providers/ogr/qgsogrprovider.cpp @@ -639,6 +639,7 @@ bool QgsOgrProvider::featureAtId( QgsFeatureId featureId, if ( !fet ) return false; + feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups feature.setFeatureId( OGR_F_GetFID( fet ) ); feature.clearAttributeMap(); // skip features without geometry @@ -714,6 +715,7 @@ bool QgsOgrProvider::nextFeature( QgsFeature& feature ) feature.setFeatureId( OGR_F_GetFID( fet ) ); feature.clearAttributeMap(); feature.setTypeName( featureTypeName ); + feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups /* fetch geometry */ if ( mFetchGeom || mUseIntersect ) diff --git a/src/providers/osm/osmprovider.cpp b/src/providers/osm/osmprovider.cpp index f39be1c50a6..da1e92f5974 100644 --- a/src/providers/osm/osmprovider.cpp +++ b/src/providers/osm/osmprovider.cpp @@ -651,6 +651,7 @@ bool QgsOSMDataProvider::fetchNode( QgsFeature& feature, sqlite3_stmt* stmt, boo feature.setFeatureId( selId ); feature.setValid( true ); + feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups return true; } @@ -762,6 +763,7 @@ bool QgsOSMDataProvider::fetchWay( QgsFeature& feature, sqlite3_stmt* stmt, bool } feature.setFeatureId( selId ); feature.setValid( true ); + feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups return true; } diff --git a/src/providers/postgres/qgspostgresprovider.cpp b/src/providers/postgres/qgspostgresprovider.cpp index b40ae6e5223..596b88e4bf5 100644 --- a/src/providers/postgres/qgspostgresprovider.cpp +++ b/src/providers/postgres/qgspostgresprovider.cpp @@ -672,6 +672,7 @@ bool QgsPostgresProvider::nextFeature( QgsFeature& feature ) mFetched++; feature.setValid( true ); + feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups return true; } @@ -877,6 +878,7 @@ bool QgsPostgresProvider::featureAtId( QgsFeatureId featureId, QgsFeature& featu mConnectionRO->closeCursor( cursorName ); feature.setValid( gotit ); + feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups #if 0 if ( gotit ) diff --git a/src/providers/spatialite/qgsspatialiteprovider.cpp b/src/providers/spatialite/qgsspatialiteprovider.cpp index ba2036501e9..3990c507afa 100644 --- a/src/providers/spatialite/qgsspatialiteprovider.cpp +++ b/src/providers/spatialite/qgsspatialiteprovider.cpp @@ -705,6 +705,7 @@ bool QgsSpatiaLiteProvider::featureAtId( QgsFeatureId featureId, QgsFeature & fe sqlite3_finalize( stmt ); + feature.setFieldMap( &attributeFields ); // allow name-based attribute lookups feature.setValid( true ); return true; } @@ -731,6 +732,7 @@ bool QgsSpatiaLiteProvider::nextFeature( QgsFeature & feature ) return false; } + feature.setFieldMap( &attributeFields ); // allow name-based attribute lookups feature.setValid( true ); return true; } diff --git a/src/providers/sqlanywhere/qgssqlanywhereprovider.cpp b/src/providers/sqlanywhere/qgssqlanywhereprovider.cpp index 45e618e1235..bcb253d93a5 100644 --- a/src/providers/sqlanywhere/qgssqlanywhereprovider.cpp +++ b/src/providers/sqlanywhere/qgssqlanywhereprovider.cpp @@ -315,6 +315,7 @@ QgsSqlAnywhereProvider::nextFeature( QgsFeature & feature, SqlAnyStatement *stmt if ( ok ) { + feature.setFieldMap( &mAttributeFields ); // allow name-based attribute lookups // iterate mAttributesToFetch feature.clearAttributeMap(); for ( QgsAttributeList::const_iterator it = mAttributesToFetch.constBegin() diff --git a/src/providers/wfs/qgswfsprovider.cpp b/src/providers/wfs/qgswfsprovider.cpp index 67216073e06..b4abbb4f13d 100644 --- a/src/providers/wfs/qgswfsprovider.cpp +++ b/src/providers/wfs/qgswfsprovider.cpp @@ -172,6 +172,7 @@ void QgsWFSProvider::copyFeature( QgsFeature* f, QgsFeature& feature, bool fetch //id and valid feature.setValid( true ); feature.setFeatureId( f->id() ); + feature.setFieldMap( &mFields ); // allow name-based attribute lookups } bool QgsWFSProvider::featureAtId( QgsFeatureId featureId,