Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add QgsFeature::attributeMap() method to return feature's attributes
as a field name -> value dictionary
  • Loading branch information
nyalldawson authored and github-actions[bot] committed Dec 3, 2021
1 parent 21573a5 commit c416d10
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 1 deletion.
40 changes: 40 additions & 0 deletions python/core/auto_generated/qgsfeature.sip.in
Expand Up @@ -198,9 +198,49 @@ its attributes:

.. seealso:: :py:func:`setAttributes`

.. seealso:: :py:func:`attributeMap`

.. versionadded:: 2.9
%End


SIP_PYOBJECT attributeMap() const /TypeHint="Dict[str, Optional[object]]"/;
%Docstring
Returns the feature's attributes as a map of field name to value.

.. note::

The fields definition must be associated with the feature using :py:func:`~QgsFeature.setFields` before this method can be used.

:raises ValueError: if the field definition is unset or the size of the fields does not match the size of the feature's attributes()


.. seealso:: :py:func:`attributes`

.. seealso:: :py:func:`setAttributes`

.. versionadded:: 3.22.2
%End
%MethodCode
const int fieldSize = sipCpp->fields().size();
const int attributeSize = sipCpp->attributes().size();
if ( fieldSize == 0 && attributeSize != 0 )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Field definition has not been set for feature" ).toUtf8().constData() );
sipIsErr = 1;
}
else if ( fieldSize != attributeSize )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Feature attribute size (%1) does not match number of fields (%2)" ).arg( attributeSize ).arg( fieldSize ).toUtf8().constData() );
sipIsErr = 1;
}
else
{
QVariantMap *v = new QVariantMap( sipCpp->attributeMap() );
sipRes = sipConvertFromNewType( v, sipType_QVariantMap, Py_None );
}
%End

int attributeCount() const;
%Docstring
Returns the number of attributes attached to the feature.
Expand Down
19 changes: 18 additions & 1 deletion src/core/qgsfeature.cpp
Expand Up @@ -22,7 +22,7 @@ email : sherman at mrcc.com
#include "qgsfields_p.h" // for approximateMemoryUsage()

#include "qgsmessagelog.h"

#include "qgslogger.h"
#include <QDataStream>

/***************************************************************************
Expand Down Expand Up @@ -127,6 +127,23 @@ QgsAttributes QgsFeature::attributes() const
return d->attributes;
}

QVariantMap QgsFeature::attributeMap() const
{
QVariantMap res;
const int fieldSize = d->fields.size();
const int attributeSize = d->attributes.size();
if ( fieldSize != attributeSize )
{
QgsDebugMsg( QStringLiteral( "Attribute size (%1) does not match number of fields (%2)" ).arg( attributeSize ).arg( fieldSize ) );
}

for ( int i = 0; i < attributeSize && i < fieldSize; ++i )
{
res[d->fields.at( i ).name()] = d->attributes.at( i );
}
return res;
}

int QgsFeature::attributeCount() const
{
return d->attributes.size();
Expand Down
48 changes: 48 additions & 0 deletions src/core/qgsfeature.h
Expand Up @@ -244,10 +244,58 @@ class CORE_EXPORT QgsFeature
* \endcode
*
* \see setAttributes()
* \see attributeMap()
* \since QGIS 2.9
*/
QgsAttributes attributes() const;

#ifndef SIP_RUN

/**
* Returns the feature's attributes as a map of field name to value.
*
* \note The fields definition must be associated with the feature using setFields() before this method can be used.
*
* \see attributes()
* \see setAttributes()
* \since QGIS 3.22.2
*/
QVariantMap attributeMap() const;
#else

/**
* Returns the feature's attributes as a map of field name to value.
*
* \note The fields definition must be associated with the feature using setFields() before this method can be used.
*
* \throws ValueError if the field definition is unset or the size of the fields does not match the size of the feature's attributes()
*
* \see attributes()
* \see setAttributes()
* \since QGIS 3.22.2
*/
SIP_PYOBJECT attributeMap() const SIP_TYPEHINT( Dict[str, Optional[object]] );
% MethodCode
const int fieldSize = sipCpp->fields().size();
const int attributeSize = sipCpp->attributes().size();
if ( fieldSize == 0 && attributeSize != 0 )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Field definition has not been set for feature" ).toUtf8().constData() );
sipIsErr = 1;
}
else if ( fieldSize != attributeSize )
{
PyErr_SetString( PyExc_ValueError, QStringLiteral( "Feature attribute size (%1) does not match number of fields (%2)" ).arg( attributeSize ).arg( fieldSize ).toUtf8().constData() );
sipIsErr = 1;
}
else
{
QVariantMap *v = new QVariantMap( sipCpp->attributeMap() );
sipRes = sipConvertFromNewType( v, sipType_QVariantMap, Py_None );
}
% End
#endif

/**
* Returns the number of attributes attached to the feature.
* \since QGIS 3.18
Expand Down
29 changes: 29 additions & 0 deletions tests/src/python/test_qgsfeature.py
Expand Up @@ -195,6 +195,35 @@ def testPadAttributes(self):
f.padAttributes(3)
self.assertEqual(f.attributes(), [1, 2, 3, NULL, NULL, NULL, NULL, NULL])

def testAttributeMap(self):
# start with a feature with no fields
f = QgsFeature()
f.setAttributes([1, 'a', NULL])
with self.assertRaises(ValueError):
_ = f.attributeMap()

# set fields
fields = QgsFields()
field1 = QgsField('my_field')
fields.append(field1)
field2 = QgsField('my_field2')
fields.append(field2)
field3 = QgsField('my_field3')
fields.append(field3)
f.setFields(fields)
f.setAttributes([1, 'a', NULL])
self.assertEqual(f.attributeMap(), {'my_field': 1, 'my_field2': 'a', 'my_field3': NULL})

# unbalanced fields/attributes -- should be handled gracefully
# less attributes than fields
f.setAttributes([1, 'a'])
with self.assertRaises(ValueError):
_ = f.attributeMap()
f.setAttributes([1, 'a', 2, 3])
# more attributes than fields
with self.assertRaises(ValueError):
_ = f.attributeMap()


if __name__ == '__main__':
unittest.main()

0 comments on commit c416d10

Please sign in to comment.