Skip to content

Commit

Permalink
Add some useful methods to resize QgsFeature attributes without clear…
Browse files Browse the repository at this point in the history
…ing existing attributes
  • Loading branch information
nyalldawson committed Dec 14, 2020
1 parent 55e6903 commit 53831c6
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 15 deletions.
35 changes: 35 additions & 0 deletions python/core/auto_generated/qgsfeature.sip.in
Expand Up @@ -192,6 +192,13 @@ Returns the feature's attributes.
Alternatively in Python: iterate feature, eg. @code [attr for attr in feature] @endcode

.. versionadded:: 2.9
%End

int attributeCount() const;
%Docstring
Returns the number of attributes attached to the feature.

.. versionadded:: 3.18
%End

void setAttributes( const QgsAttributes &attrs );
Expand Down Expand Up @@ -252,6 +259,34 @@ The feature will be valid if it was successful.
Initialize this feature with the given number of fields. Discard any previously set attribute data.

:param fieldCount: Number of fields to initialize

.. seealso:: :py:func:`resizedAttributes`
%End

void resizeAttributes( int fieldCount );
%Docstring
Resizes the attributes attached to this feature to the given number of fields.

If the new ``fieldCount`` is greater than the original number of fields then the additional attributes will
be filled with NULL values. All existing attributes will remain unchanged.

If the new ``fieldCount`` is less than the original number of fields then the unwanted values will be discarded from the
end of the existing attributes.

.. seealso:: :py:func:`initAttributes`

.. seealso:: :py:func:`padAttributes`

.. versionadded:: 3.18
%End

void padAttributes( int count );
%Docstring
Resizes the attributes attached to this feature by appending the specified ``count`` of NULL values to the end of the existing attributes.

.. seealso:: :py:func:`resizeAttributes`

.. versionadded:: 3.18
%End

void deleteAttribute( int field );
Expand Down
19 changes: 4 additions & 15 deletions src/analysis/processing/qgsalgorithmbatchgeocode.cpp
Expand Up @@ -121,15 +121,10 @@ QgsFeatureList QgsBatchGeocodeAlgorithm::processFeature( const QgsFeature &featu
{
QgsFeature f = feature;

QgsAttributes attr = f.attributes();

const QString address = f.attribute( mAddressField ).toString();
if ( address.isEmpty() )
{
// append null attributes
for ( int i = 0; i < mAdditionalFields.count(); ++ i )
attr.append( QVariant() );
f.setAttributes( attr );
f.padAttributes( mAdditionalFields.count() );
feedback->pushWarning( QObject::tr( "Empty address field for feature %1" ).arg( feature.id() ) );
return QgsFeatureList() << f;
}
Expand All @@ -138,25 +133,19 @@ QgsFeatureList QgsBatchGeocodeAlgorithm::processFeature( const QgsFeature &featu
const QList< QgsGeocoderResult > results = mGeocoder->geocodeString( address, geocodeContext, feedback );
if ( results.empty() )
{
// append null attributes
for ( int i = 0; i < mAdditionalFields.count(); ++ i )
attr.append( QVariant() );
f.setAttributes( attr );
f.padAttributes( mAdditionalFields.count() );
feedback->pushWarning( QObject::tr( "No result for %1" ).arg( address ) );
return QgsFeatureList() << f;
}

if ( !results.at( 0 ).isValid() )
{
// append null attributes
for ( int i = 0; i < mAdditionalFields.count(); ++ i )
attr.append( QVariant() );
f.setAttributes( attr );

f.padAttributes( mAdditionalFields.count() );
feedback->reportError( QObject::tr( "Error geocoding %1: %2" ).arg( address, results.at( 0 ).error() ) );
return QgsFeatureList() << f;
}

QgsAttributes attr = f.attributes();
const QVariantMap additionalAttributes = results.at( 0 ).additionalAttributes();
if ( !mIsInPlace )
{
Expand Down
23 changes: 23 additions & 0 deletions src/core/qgsfeature.cpp
Expand Up @@ -126,6 +126,11 @@ QgsAttributes QgsFeature::attributes() const
return d->attributes;
}

int QgsFeature::attributeCount() const
{
return d->attributes.size();
}

void QgsFeature::setAttributes( const QgsAttributes &attrs )
{
if ( attrs == d->attributes )
Expand Down Expand Up @@ -210,6 +215,24 @@ void QgsFeature::initAttributes( int fieldCount )
d->attributes.resize( fieldCount );
}

void QgsFeature::resizeAttributes( int fieldCount )
{
if ( fieldCount == d->attributes.size() )
return;

d.detach();
d->attributes.resize( fieldCount );
}

void QgsFeature::padAttributes( int count )
{
if ( count == 0 )
return;

d.detach();
d->attributes.resize( d->attributes.size() + count );
}

bool QgsFeature::setAttribute( int idx, const QVariant &value )
{
if ( idx < 0 || idx >= d->attributes.size() )
Expand Down
31 changes: 31 additions & 0 deletions src/core/qgsfeature.h
Expand Up @@ -237,6 +237,12 @@ class CORE_EXPORT QgsFeature
*/
QgsAttributes attributes() const;

/**
* Returns the number of attributes attached to the feature.
* \since QGIS 3.18
*/
int attributeCount() const;

/**
* Sets the feature's attributes.
* The feature will be valid after.
Expand Down Expand Up @@ -285,9 +291,34 @@ class CORE_EXPORT QgsFeature
/**
* Initialize this feature with the given number of fields. Discard any previously set attribute data.
* \param fieldCount Number of fields to initialize
*
* \see resizedAttributes()
*/
void initAttributes( int fieldCount );

/**
* Resizes the attributes attached to this feature to the given number of fields.
*
* If the new \a fieldCount is greater than the original number of fields then the additional attributes will
* be filled with NULL values. All existing attributes will remain unchanged.
*
* If the new \a fieldCount is less than the original number of fields then the unwanted values will be discarded from the
* end of the existing attributes.
*
* \see initAttributes()
* \see padAttributes()
* \since QGIS 3.18
*/
void resizeAttributes( int fieldCount );

/**
* Resizes the attributes attached to this feature by appending the specified \a count of NULL values to the end of the existing attributes.
*
* \see resizeAttributes()
* \since QGIS 3.18
*/
void padAttributes( int count );

/**
* Deletes an attribute and its value.
* \param field the index of the field
Expand Down
30 changes: 30 additions & 0 deletions tests/src/python/test_qgsfeature.py
Expand Up @@ -165,6 +165,36 @@ def test_SetGeometry(self):
feat.setGeometry(QgsPoint(12, 34))
self.assertEqual(feat.geometry().asWkt(), 'Point (12 34)')

def testAttributeCount(self):
f = QgsFeature()
self.assertEqual(f.attributeCount(), 0)
f.setAttributes([1, 2, 3])
self.assertEqual(f.attributeCount(), 3)

def testResizeAttributes(self):
f = QgsFeature()
f.resizeAttributes(3)
self.assertEqual(f.attributes(), [NULL, NULL, NULL])
f.setAttributes([1, 2, 3])
f.resizeAttributes(3)
self.assertEqual(f.attributes(), [1, 2, 3])
f.resizeAttributes(5)
self.assertEqual(f.attributes(), [1, 2, 3, NULL, NULL])
f.resizeAttributes(2)
self.assertEqual(f.attributes(), [1, 2])

def testPadAttributes(self):
f = QgsFeature()
f.padAttributes(3)
self.assertEqual(f.attributes(), [NULL, NULL, NULL])
f.setAttributes([1, 2, 3])
f.padAttributes(0)
self.assertEqual(f.attributes(), [1, 2, 3])
f.padAttributes(2)
self.assertEqual(f.attributes(), [1, 2, 3, NULL, NULL])
f.padAttributes(3)
self.assertEqual(f.attributes(), [1, 2, 3, NULL, NULL, NULL, NULL, NULL])


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

0 comments on commit 53831c6

Please sign in to comment.