Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Geocoder algorithms can be run in in-place mode too
  • Loading branch information
nyalldawson committed Dec 14, 2020
1 parent 6ae7a17 commit 578813a
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 11 deletions.
Expand Up @@ -77,6 +77,8 @@ exists for the lifetime of this algorithm.

virtual QList<int> inputLayerTypes() const;

virtual bool supportInPlaceEdit( const QgsMapLayer *layer ) const;


protected:
virtual QString outputName() const;
Expand Down
66 changes: 56 additions & 10 deletions src/analysis/processing/qgsalgorithmbatchgeocode.cpp
Expand Up @@ -19,6 +19,7 @@
#include "qgsgeocoder.h"
#include "qgsgeocoderresult.h"
#include "qgsgeocodercontext.h"
#include "qgsvectorlayer.h"

QgsBatchGeocodeAlgorithm::QgsBatchGeocodeAlgorithm( QgsGeocoderInterface *geocoder )
: QgsProcessingFeatureBasedAlgorithm()
Expand All @@ -42,16 +43,34 @@ QString QgsBatchGeocodeAlgorithm::groupId() const
return QStringLiteral( "vectorgeneral" );
}

void QgsBatchGeocodeAlgorithm::initParameters( const QVariantMap & )
void QgsBatchGeocodeAlgorithm::initParameters( const QVariantMap &configuration )
{
mIsInPlace = configuration.value( QStringLiteral( "IN_PLACE" ) ).toBool();

addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Address field" ), QVariant(), QStringLiteral( "INPUT" ), QgsProcessingParameterField::String ) );

if ( mIsInPlace )
{
const QgsFields newFields = mGeocoder->appendedFields();
for ( const QgsField &newField : newFields )
addParameter( new QgsProcessingParameterField( newField.name(), QObject::tr( "%1 field" ).arg( newField.name() ), newField.name(), QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, false, true ) );
}
}

QList<int> QgsBatchGeocodeAlgorithm::inputLayerTypes() const
{
return QList<int>() << QgsProcessing::TypeVector;
}

bool QgsBatchGeocodeAlgorithm::supportInPlaceEdit( const QgsMapLayer *layer ) const
{
if ( const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer ) )
{
return vl->geometryType() == QgsWkbTypes::PointGeometry;
}
return false;
}

QString QgsBatchGeocodeAlgorithm::outputName() const
{
return QObject::tr( "Geocoded" );
Expand All @@ -60,6 +79,14 @@ QString QgsBatchGeocodeAlgorithm::outputName() const
bool QgsBatchGeocodeAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
mAddressField = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );

if ( mIsInPlace )
{
const QgsFields newFields = mGeocoder->appendedFields();
for ( const QgsField &newField : newFields )
mInPlaceFieldMap.insert( newField.name(), parameterAsString( parameters, newField.name(), context ) );
}

return true;
}

Expand All @@ -76,12 +103,18 @@ QgsCoordinateReferenceSystem QgsBatchGeocodeAlgorithm::outputCrs( const QgsCoord

QgsFields QgsBatchGeocodeAlgorithm::outputFields( const QgsFields &inputFields ) const
{
// append any additional fields created by the geocoder
QgsFields res = inputFields;
const QgsFields newFields = mGeocoder->appendedFields();
mAdditionalFields = newFields.names();
res.extend( newFields );
return res;
if ( !mIsInPlace )
{
// append any additional fields created by the geocoder
const QgsFields newFields = mGeocoder->appendedFields();
mAdditionalFields = newFields.names();

return QgsProcessingUtils::combineFields( inputFields, newFields );
}
else
{
return inputFields;
}
}

QgsFeatureList QgsBatchGeocodeAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
Expand Down Expand Up @@ -125,11 +158,24 @@ QgsFeatureList QgsBatchGeocodeAlgorithm::processFeature( const QgsFeature &featu
}

const QVariantMap additionalAttributes = results.at( 0 ).additionalAttributes();
for ( const QString &additionalField : mAdditionalFields )
if ( !mIsInPlace )
{
for ( const QString &additionalField : mAdditionalFields )
{
attr.append( additionalAttributes.value( additionalField ) );
}
f.setAttributes( attr );
}
else
{
attr.append( additionalAttributes.value( additionalField ) );
for ( auto it = mInPlaceFieldMap.constBegin(); it != mInPlaceFieldMap.constEnd(); ++it )
{
if ( !it.value().isEmpty() )
{
f.setAttribute( it.value(), additionalAttributes.value( it.key() ) );
}
}
}
f.setAttributes( attr );

QgsCoordinateTransform transform = QgsCoordinateTransform( results.at( 0 ).crs(), mOutputCrs, context.transformContext() );
QgsGeometry g = results.at( 0 ).geometry();
Expand Down
4 changes: 3 additions & 1 deletion src/analysis/processing/qgsalgorithmbatchgeocode.h
Expand Up @@ -84,6 +84,7 @@ class ANALYSIS_EXPORT QgsBatchGeocodeAlgorithm : public QgsProcessingFeatureBase
QString group() const override;
QString groupId() const override;
QList<int> inputLayerTypes() const override;
bool supportInPlaceEdit( const QgsMapLayer *layer ) const override;

protected:
QString outputName() const override;
Expand All @@ -94,9 +95,10 @@ class ANALYSIS_EXPORT QgsBatchGeocodeAlgorithm : public QgsProcessingFeatureBase
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override;

private:

bool mIsInPlace = false;
QString mAddressField;
QgsGeocoderInterface *mGeocoder = nullptr;
QgsStringMap mInPlaceFieldMap;
mutable QgsCoordinateReferenceSystem mOutputCrs;
mutable QStringList mAdditionalFields;

Expand Down
62 changes: 62 additions & 0 deletions tests/src/python/test_qgsgeocoderalgorithm.py
Expand Up @@ -216,6 +216,68 @@ def testAppendedFields(self):
self.assertEqual(res[0].geometry().asWkt(), 'Point (11 12)')
self.assertEqual(res[0].attributes(), [17, 'b', 'xyz2', 456])

def testInPlace(self):
geocoder = TestGeocoderExtraFields()

alg = TestGeocoderAlgorithm(geocoder)
alg.initParameters({'IN_PLACE': True})

fields = QgsFields()
fields.append(QgsField('some_pk', QVariant.Int))
fields.append(QgsField('address', QVariant.String))
fields.append(QgsField('parsedx', QVariant.String))
fields.append(QgsField('accuracyx', QVariant.Int))

output_fields = alg.outputFields(fields)
self.assertEqual([f.name() for f in output_fields], ['some_pk', 'address', 'parsedx', 'accuracyx'])
self.assertEqual([f.type() for f in output_fields],
[QVariant.Int, QVariant.String, QVariant.String, QVariant.Int])

f = QgsFeature(fields)
f.initAttributes(4)
f['some_pk'] = 17

# not storing additional attributes
params = {'FIELD': 'address'}

context = QgsProcessingContext()
feedback = QgsProcessingFeedback()
self.assertTrue(alg.prepareAlgorithm(params, context, feedback))

# empty field
res = alg.processFeature(f, context, feedback)
self.assertEqual(len(res), 1)
self.assertTrue(res[0].geometry().isNull())
self.assertEqual(res[0].attributes(), [17, NULL, NULL, NULL])

f['address'] = 'a'
res = alg.processFeature(f, context, feedback)
self.assertEqual(len(res), 1)
self.assertEqual(res[0].geometry().asWkt(), 'Point (1 2)')
self.assertEqual(res[0].attributes(), [17, 'a', None, None])

f.clearGeometry()
f['address'] = NULL

# storing additional attributes
params = {'FIELD': 'address', 'parsed': 'parsedx', 'accuracy': 'accuracyx'}

context = QgsProcessingContext()
feedback = QgsProcessingFeedback()
self.assertTrue(alg.prepareAlgorithm(params, context, feedback))

# empty field
res = alg.processFeature(f, context, feedback)
self.assertEqual(len(res), 1)
self.assertTrue(res[0].geometry().isNull())
self.assertEqual(res[0].attributes(), [17, NULL, NULL, NULL])

f['address'] = 'b'
res = alg.processFeature(f, context, feedback)
self.assertEqual(len(res), 1)
self.assertEqual(res[0].geometry().asWkt(), 'Point (11 12)')
self.assertEqual(res[0].attributes(), [17, 'b', 'xyz2', 456])


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

0 comments on commit 578813a

Please sign in to comment.