QGIS API Documentation 4.3.0-Master (ffcfc20b9b4)
Loading...
Searching...
No Matches
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
19
20#include "qgis.h"
21#include "qgshstoreutils.h"
22#include "qgsprocessingutils.h"
23
24#include <QString>
25
26using namespace Qt::StringLiterals;
27
29
30QString QgsExplodeHstoreAlgorithm::name() const
31{
32 return u"explodehstorefield"_s;
33}
34
35QString QgsExplodeHstoreAlgorithm::displayName() const
36{
37 return QObject::tr( "Explode HStore Field" );
38}
39
40QStringList QgsExplodeHstoreAlgorithm::tags() const
41{
42 return QObject::tr( "field,explode,hstore,osm,openstreetmap" ).split( ',' );
43}
44
45QString QgsExplodeHstoreAlgorithm::group() const
46{
47 return QObject::tr( "Vector table" );
48}
49
50QString QgsExplodeHstoreAlgorithm::groupId() const
51{
52 return u"vectortable"_s;
53}
54
55QString QgsExplodeHstoreAlgorithm::shortHelpString() const
56{
57 return QObject::tr(
58 "This algorithm creates a copy of the input layer and adds a new field for every unique key in the HStore field.\n"
59 "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."
60 );
61}
62
63QString QgsExplodeHstoreAlgorithm::shortDescription() const
64{
65 return QObject::tr( "Creates a copy of the input layer and adds a new field for every unique key in the HStore field." );
66}
67
68QgsProcessingAlgorithm *QgsExplodeHstoreAlgorithm::createInstance() const
69{
70 return new QgsExplodeHstoreAlgorithm();
71}
72
73void QgsExplodeHstoreAlgorithm::initAlgorithm( const QVariantMap & )
74{
75 addParameter( new QgsProcessingParameterFeatureSource( u"INPUT"_s, QObject::tr( "Input layer" ) ) );
76 addParameter( new QgsProcessingParameterField( u"FIELD"_s, QObject::tr( "HStore field" ), QVariant(), u"INPUT"_s ) );
77 addParameter( new QgsProcessingParameterString( u"EXPECTED_FIELDS"_s, QObject::tr( "Expected list of fields separated by a comma" ), QVariant(), false, true ) );
78 addParameter( new QgsProcessingParameterFeatureSink( u"OUTPUT"_s, QObject::tr( "Exploded" ) ) );
79}
80
81QVariantMap QgsExplodeHstoreAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
82{
83 QGS_MARK_ALGORITHM_SOURCE
84
85 std::unique_ptr<QgsProcessingFeatureSource> source( parameterAsSource( parameters, u"INPUT"_s, context ) );
86 if ( !source )
87 throw QgsProcessingException( invalidSourceError( parameters, u"INPUT"_s ) );
88 int attrSourceCount = source->fields().count();
89
90 QString fieldName = parameterAsString( parameters, u"FIELD"_s, context );
91 int fieldIndex = source->fields().lookupField( fieldName );
92 if ( fieldIndex < 0 )
93 throw QgsProcessingException( QObject::tr( "Invalid HStore field" ) );
94
95 QStringList expectedFields;
96 QString fieldList = parameterAsString( parameters, u"EXPECTED_FIELDS"_s, context );
97 if ( !fieldList.trimmed().isEmpty() )
98 {
99 expectedFields = fieldList.split( ',' );
100 }
101
102 QList<QString> fieldsToAdd;
103 QHash<QgsFeatureId, QVariantMap> hstoreFeatures;
104 QList<QgsFeature> features;
105
106 double step = source->featureCount() > 0 ? 50.0 / source->featureCount() : 1;
107 int i = 0;
108 QgsFeatureIterator featIterator = source->getFeatures();
109 QgsFeature feat;
110 while ( featIterator.nextFeature( feat ) )
111 {
112 i++;
113 if ( feedback->isCanceled() )
114 break;
115
116 double progress = i * step;
117 if ( progress >= 50 )
118 feedback->setProgress( 50.0 );
119 else
120 feedback->setProgress( progress );
121
122 QVariantMap currentHStore = QgsHstoreUtils::parse( feat.attribute( fieldName ).toString() );
123 for ( auto key = currentHStore.keyBegin(); key != currentHStore.keyEnd(); key++ )
124 {
125 if ( expectedFields.isEmpty() && !fieldsToAdd.contains( *key ) )
126 fieldsToAdd.insert( 0, *key );
127 }
128 hstoreFeatures.insert( feat.id(), currentHStore );
129 features.append( feat );
130 }
131
132 if ( !expectedFields.isEmpty() )
133 {
134 fieldsToAdd = expectedFields;
135 }
136
137 QgsFields hstoreFields;
138 for ( const QString &fieldName : fieldsToAdd )
139 {
140 hstoreFields.append( QgsField( fieldName, QMetaType::Type::QString ) );
141 }
142
143 QgsFields outFields = QgsProcessingUtils::combineFields( source->fields(), hstoreFields );
144
145 QString sinkId;
146 std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, u"OUTPUT"_s, context, sinkId, outFields, source->wkbType(), source->sourceCrs() ) );
147 if ( !sink )
148 throw QgsProcessingException( invalidSinkError( parameters, u"OUTPUT"_s ) );
149
150 QList<int> fieldIndicesInput = QgsProcessingUtils::fieldNamesToIndices( QStringList(), source->fields() );
151 int attrCount = attrSourceCount + fieldsToAdd.count();
152 QgsFeature outFeature;
153 step = !features.empty() ? 50.0 / features.count() : 1;
154 i = 0;
155 for ( const QgsFeature &feat : std::as_const( features ) )
156 {
157 i++;
158 if ( feedback->isCanceled() )
159 break;
160
161 feedback->setProgress( i * step + 50.0 );
162
163 QgsAttributes outAttributes( attrCount );
164
165 const QgsAttributes attrs( feat.attributes() );
166 for ( int i = 0; i < fieldIndicesInput.count(); ++i )
167 outAttributes[i] = attrs[fieldIndicesInput[i]];
168
169 QVariantMap currentHStore = hstoreFeatures.take( feat.id() );
170
171 QString current;
172 for ( int i = 0; i < fieldsToAdd.count(); ++i )
173 {
174 current = fieldsToAdd.at( i );
175 if ( currentHStore.contains( current ) )
176 {
177 outAttributes[attrSourceCount + i] = currentHStore.take( current );
178 }
179 }
180
181 if ( !expectedFields.isEmpty() )
182 {
183 outAttributes[fieldIndex] = QgsHstoreUtils::build( currentHStore );
184 }
185
186 outFeature.setGeometry( QgsGeometry( feat.geometry() ) );
187 outFeature.setAttributes( outAttributes );
188 if ( !sink->addFeature( outFeature, QgsFeatureSink::FastInsert ) )
189 throw QgsProcessingException( writeFeatureError( sink.get(), parameters, u"OUTPUT"_s ) );
190 else
191 feedback->featureAddedToSink( u"OUTPUT"_s );
192 }
193
194 sink->finalize();
195 feedback->featureSinkFinalized( u"OUTPUT"_s );
196
197 QVariantMap outputs;
198 outputs.insert( u"OUTPUT"_s, sinkId );
199 return outputs;
200}
201
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:60
QgsAttributes attributes
Definition qgsfeature.h:64
QgsFeatureId id
Definition qgsfeature.h:63
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:66
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:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
Container of fields for a vector layer.
Definition qgsfields.h:45
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
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.
void featureAddedToSink(const QString &output)
Reports that a feature was added to the the sink associated with the specified algorithm output.
void featureSinkFinalized(const QString &output)
Reports that a feature sink has been finalized.
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).