QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
qgsalgorithmexplodehstore.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmexplodehstore.h
3 ---------------------
4 begin : September 2018
5 copyright : (C) 2018 by Etienne Trimaille
6 email : etienne dot trimaille at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgis.h"
20#include "qgshstoreutils.h"
21#include "qgsprocessingutils.h"
22
24
25QString QgsExplodeHstoreAlgorithm::name() const
26{
27 return QStringLiteral( "explodehstorefield" );
28}
29
30QString QgsExplodeHstoreAlgorithm::displayName() const
31{
32 return QObject::tr( "Explode HStore Field" );
33}
34
35QStringList QgsExplodeHstoreAlgorithm::tags() const
36{
37 return QObject::tr( "field,explode,hstore,osm,openstreetmap" ).split( ',' );
38}
39
40QString QgsExplodeHstoreAlgorithm::group() const
41{
42 return QObject::tr( "Vector table" );
43}
44
45QString QgsExplodeHstoreAlgorithm::groupId() const
46{
47 return QStringLiteral( "vectortable" );
48}
49
50QString QgsExplodeHstoreAlgorithm::shortHelpString() const
51{
52 return QObject::tr( "This algorithm creates a copy of the input layer and adds a new field for every unique key in the HStore field.\n"
53 "The expected field list is an optional comma separated list. By default, all unique keys are added. If this list is specified, only these fields are added and the HStore field is updated." );
54}
55
56QString QgsExplodeHstoreAlgorithm::shortDescription() const
57{
58 return QObject::tr( "Creates a copy of the input layer and adds a new field for every unique key in the HStore field." );
59}
60
61QgsProcessingAlgorithm *QgsExplodeHstoreAlgorithm::createInstance() const
62{
63 return new QgsExplodeHstoreAlgorithm();
64}
65
66void QgsExplodeHstoreAlgorithm::initAlgorithm( const QVariantMap & )
67{
68 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
69 addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "HStore field" ), QVariant(), QStringLiteral( "INPUT" ) ) );
70 addParameter( new QgsProcessingParameterString( QStringLiteral( "EXPECTED_FIELDS" ), QObject::tr( "Expected list of fields separated by a comma" ), QVariant(), false, true ) );
71 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Exploded" ) ) );
72}
73
74QVariantMap QgsExplodeHstoreAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
75{
76 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
77 if ( !source )
78 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
79 int attrSourceCount = source->fields().count();
80
81 QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
82 int fieldIndex = source->fields().lookupField( fieldName );
83 if ( fieldIndex < 0 )
84 throw QgsProcessingException( QObject::tr( "Invalid HStore field" ) );
85
86 QStringList expectedFields;
87 QString fieldList = parameterAsString( parameters, QStringLiteral( "EXPECTED_FIELDS" ), context );
88 if ( !fieldList.trimmed().isEmpty() )
89 {
90 expectedFields = fieldList.split( ',' );
91 }
92
93 QList<QString> fieldsToAdd;
94 QHash<QgsFeatureId, QVariantMap> hstoreFeatures;
95 QList<QgsFeature> features;
96
97 double step = source->featureCount() > 0 ? 50.0 / source->featureCount() : 1;
98 int i = 0;
99 QgsFeatureIterator featIterator = source->getFeatures();
100 QgsFeature feat;
101 while ( featIterator.nextFeature( feat ) )
102 {
103 i++;
104 if ( feedback->isCanceled() )
105 break;
106
107 double progress = i * step;
108 if ( progress >= 50 )
109 feedback->setProgress( 50.0 );
110 else
111 feedback->setProgress( progress );
112
113 QVariantMap currentHStore = QgsHstoreUtils::parse( feat.attribute( fieldName ).toString() );
114 for ( auto key = currentHStore.keyBegin(); key != currentHStore.keyEnd(); key++ )
115 {
116 if ( expectedFields.isEmpty() && !fieldsToAdd.contains( *key ) )
117 fieldsToAdd.insert( 0, *key );
118 }
119 hstoreFeatures.insert( feat.id(), currentHStore );
120 features.append( feat );
121 }
122
123 if ( !expectedFields.isEmpty() )
124 {
125 fieldsToAdd = expectedFields;
126 }
127
128 QgsFields hstoreFields;
129 for ( const QString &fieldName : fieldsToAdd )
130 {
131 hstoreFields.append( QgsField( fieldName, QMetaType::Type::QString ) );
132 }
133
134 QgsFields outFields = QgsProcessingUtils::combineFields( source->fields(), hstoreFields );
135
136 QString sinkId;
137 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, sinkId, outFields, source->wkbType(), source->sourceCrs() ) );
138 if ( !sink )
139 throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
140
141 QList<int> fieldIndicesInput = QgsProcessingUtils::fieldNamesToIndices( QStringList(), source->fields() );
142 int attrCount = attrSourceCount + fieldsToAdd.count();
143 QgsFeature outFeature;
144 step = !features.empty() ? 50.0 / features.count() : 1;
145 i = 0;
146 for ( const QgsFeature &feat : std::as_const( features ) )
147 {
148 i++;
149 if ( feedback->isCanceled() )
150 break;
151
152 feedback->setProgress( i * step + 50.0 );
153
154 QgsAttributes outAttributes( attrCount );
155
156 const QgsAttributes attrs( feat.attributes() );
157 for ( int i = 0; i < fieldIndicesInput.count(); ++i )
158 outAttributes[i] = attrs[fieldIndicesInput[i]];
159
160 QVariantMap currentHStore = hstoreFeatures.take( feat.id() );
161
162 QString current;
163 for ( int i = 0; i < fieldsToAdd.count(); ++i )
164 {
165 current = fieldsToAdd.at( i );
166 if ( currentHStore.contains( current ) )
167 {
168 outAttributes[attrSourceCount + i] = currentHStore.take( current );
169 }
170 }
171
172 if ( !expectedFields.isEmpty() )
173 {
174 outAttributes[fieldIndex] = QgsHstoreUtils::build( currentHStore );
175 }
176
177 outFeature.setGeometry( QgsGeometry( feat.geometry() ) );
178 outFeature.setAttributes( outAttributes );
179 if ( !sink->addFeature( outFeature, QgsFeatureSink::FastInsert ) )
180 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
181 }
182
183 sink->finalize();
184
185 QVariantMap outputs;
186 outputs.insert( QStringLiteral( "OUTPUT" ), sinkId );
187 return outputs;
188}
189
A vector of attributes.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFeatureId id
Definition qgsfeature.h:66
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:69
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:70
A geometry is the spatial representation of a feature.
static QString build(const QVariantMap &map)
Build a hstore-formatted string from a QVariantMap.
static QVariantMap parse(const QString &string)
Returns a QVariantMap object containing the key and values from a hstore-formatted string.
Abstract base class for processing algorithms.
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
A feature sink output for processing algorithms.
An input feature source (such as vector layers) parameter for processing algorithms.
A vector layer or feature source field parameter for processing algorithms.
A string parameter for processing algorithms.
static QList< int > fieldNamesToIndices(const QStringList &fieldNames, const QgsFields &fields)
Returns a list of field indices parsed from the given list of field names.
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix=QString())
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).