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