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