Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsalgorithmuniquevalueindex.cpp
3 : : ---------------------
4 : : begin : January 2018
5 : : copyright : (C) 2018 by Nyall Dawson
6 : : email : nyall dot dawson 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 "qgsalgorithmuniquevalueindex.h"
19 : :
20 : : ///@cond PRIVATE
21 : :
22 : 0 : QString QgsAddUniqueValueIndexAlgorithm::name() const
23 : : {
24 : 0 : return QStringLiteral( "adduniquevalueindexfield" );
25 : : }
26 : :
27 : 0 : QString QgsAddUniqueValueIndexAlgorithm::displayName() const
28 : : {
29 : 0 : return QObject::tr( "Add unique value index field" );
30 : : }
31 : :
32 : 0 : QStringList QgsAddUniqueValueIndexAlgorithm::tags() const
33 : : {
34 : 0 : return QObject::tr( "categorize,categories,category,reclassify,classes,create" ).split( ',' );
35 : 0 : }
36 : :
37 : 0 : QString QgsAddUniqueValueIndexAlgorithm::group() const
38 : : {
39 : 0 : return QObject::tr( "Vector table" );
40 : : }
41 : :
42 : 0 : QString QgsAddUniqueValueIndexAlgorithm::groupId() const
43 : : {
44 : 0 : return QStringLiteral( "vectortable" );
45 : : }
46 : :
47 : 0 : void QgsAddUniqueValueIndexAlgorithm::initAlgorithm( const QVariantMap & )
48 : : {
49 : 0 : addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ),
50 : 0 : QList< int >() << QgsProcessing::TypeVector ) );
51 : 0 : addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Class field" ), QVariant(),
52 : 0 : QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any ) );
53 : 0 : addParameter( new QgsProcessingParameterString( QStringLiteral( "FIELD_NAME" ),
54 : 0 : QObject::tr( "Output field name" ), QStringLiteral( "NUM_FIELD" ) ) );
55 : :
56 : 0 : std::unique_ptr< QgsProcessingParameterFeatureSink > classedOutput = std::make_unique< QgsProcessingParameterFeatureSink >( QStringLiteral( "OUTPUT" ), QObject::tr( "Layer with index field" ), QgsProcessing::TypeVectorAnyGeometry, QVariant(), true );
57 : 0 : classedOutput->setCreateByDefault( true );
58 : 0 : addParameter( classedOutput.release() );
59 : :
60 : 0 : std::unique_ptr< QgsProcessingParameterFeatureSink > summaryOutput = std::make_unique< QgsProcessingParameterFeatureSink >( QStringLiteral( "SUMMARY_OUTPUT" ), QObject::tr( "Class summary" ),
61 : 0 : QgsProcessing::TypeVector, QVariant(), true );
62 : 0 : summaryOutput->setCreateByDefault( false );
63 : 0 : addParameter( summaryOutput.release() );
64 : 0 : }
65 : :
66 : 0 : QString QgsAddUniqueValueIndexAlgorithm::shortHelpString() const
67 : : {
68 : 0 : return QObject::tr( "This algorithm takes a vector layer and an attribute and adds a new numeric field. Values in this field correspond to values in the specified attribute, so features with the same "
69 : : "value for the attribute will have the same value in the new numeric field. This creates a numeric equivalent of the specified attribute, which defines the same classes.\n\n"
70 : : "The new attribute is not added to the input layer but a new layer is generated instead.\n\n"
71 : : "Optionally, a separate table can be output which contains a summary of the class field values mapped to the new unique numeric value." );
72 : : }
73 : :
74 : 0 : QgsAddUniqueValueIndexAlgorithm *QgsAddUniqueValueIndexAlgorithm::createInstance() const
75 : : {
76 : 0 : return new QgsAddUniqueValueIndexAlgorithm();
77 : : }
78 : :
79 : 0 : QVariantMap QgsAddUniqueValueIndexAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
80 : : {
81 : 0 : std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
82 : 0 : if ( !source )
83 : 0 : throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
84 : :
85 : 0 : QString newFieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context );
86 : 0 : QgsFields fields = source->fields();
87 : 0 : QgsField newField = QgsField( newFieldName, QVariant::Int );
88 : 0 : fields.append( newField );
89 : :
90 : 0 : QString dest;
91 : 0 : std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, source->wkbType(), source->sourceCrs() ) );
92 : :
93 : 0 : QString sourceFieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
94 : 0 : int fieldIndex = source->fields().lookupField( sourceFieldName );
95 : 0 : if ( fieldIndex < 0 )
96 : 0 : throw QgsProcessingException( QObject::tr( "Invalid field name %1" ).arg( sourceFieldName ) );
97 : :
98 : 0 : QString summaryDest;
99 : 0 : QgsFields summaryFields;
100 : 0 : summaryFields.append( newField );
101 : 0 : summaryFields.append( source->fields().at( fieldIndex ) );
102 : 0 : std::unique_ptr< QgsFeatureSink > summarySink( parameterAsSink( parameters, QStringLiteral( "SUMMARY_OUTPUT" ), context, summaryDest, summaryFields, QgsWkbTypes::NoGeometry ) );
103 : :
104 : 0 : QHash< QVariant, int > classes;
105 : :
106 : 0 : QgsFeatureIterator it = source->getFeatures( QgsFeatureRequest(), QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks );
107 : :
108 : 0 : long count = source->featureCount();
109 : 0 : double step = count > 0 ? 100.0 / count : 1;
110 : 0 : int current = 0;
111 : 0 : QgsFeature feature;
112 : 0 : while ( it.nextFeature( feature ) )
113 : : {
114 : 0 : if ( feedback->isCanceled() )
115 : : {
116 : 0 : break;
117 : : }
118 : :
119 : 0 : QgsAttributes attributes = feature.attributes();
120 : 0 : QVariant clazz = attributes.at( fieldIndex );
121 : :
122 : 0 : int thisValue = classes.value( clazz, -1 );
123 : 0 : if ( thisValue == -1 )
124 : : {
125 : 0 : thisValue = classes.count();
126 : 0 : classes.insert( clazz, thisValue );
127 : 0 : }
128 : :
129 : 0 : if ( sink )
130 : : {
131 : 0 : attributes.append( thisValue );
132 : 0 : feature.setAttributes( attributes );
133 : 0 : sink->addFeature( feature, QgsFeatureSink::FastInsert );
134 : 0 : }
135 : :
136 : 0 : feedback->setProgress( current * step );
137 : 0 : current++;
138 : 0 : }
139 : :
140 : 0 : if ( summarySink )
141 : : {
142 : : //generate summary table - first we make a sorted version of the classes
143 : 0 : QMap< int, QVariant > sorted;
144 : 0 : for ( auto classIt = classes.constBegin(); classIt != classes.constEnd(); ++classIt )
145 : : {
146 : 0 : sorted.insert( classIt.value(), classIt.key() );
147 : 0 : }
148 : : // now save them
149 : 0 : for ( auto sortedIt = sorted.constBegin(); sortedIt != sorted.constEnd(); ++sortedIt )
150 : : {
151 : 0 : QgsFeature f;
152 : 0 : f.setAttributes( QgsAttributes() << sortedIt.key() << sortedIt.value() );
153 : 0 : summarySink->addFeature( f, QgsFeatureSink::FastInsert );
154 : 0 : }
155 : 0 : }
156 : :
157 : 0 : QVariantMap results;
158 : 0 : if ( sink )
159 : 0 : results.insert( QStringLiteral( "OUTPUT" ), dest );
160 : 0 : if ( summarySink )
161 : 0 : results.insert( QStringLiteral( "SUMMARY_OUTPUT" ), summaryDest );
162 : 0 : return results;
163 : 0 : }
164 : :
165 : : ///@endcond
|