diff --git a/python/plugins/processing/algs/qgis/EquivalentNumField.py b/python/plugins/processing/algs/qgis/EquivalentNumField.py
deleted file mode 100644
index 42129863073..00000000000
--- a/python/plugins/processing/algs/qgis/EquivalentNumField.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-***************************************************************************
- EquivalentNumField.py
- ---------------------
- Date : August 2012
- Copyright : (C) 2012 by Victor Olaya
- Email : volayaf at gmail dot com
-***************************************************************************
-* *
-* This program is free software; you can redistribute it and/or modify *
-* it under the terms of the GNU General Public License as published by *
-* the Free Software Foundation; either version 2 of the License, or *
-* (at your option) any later version. *
-* *
-***************************************************************************
-"""
-
-__author__ = 'Victor Olaya'
-__date__ = 'August 2012'
-__copyright__ = '(C) 2012, Victor Olaya'
-
-# This will get replaced with a git SHA1 when you do a git archive
-
-__revision__ = '$Format:%H$'
-
-from qgis.PyQt.QtCore import QVariant
-from qgis.core import (QgsField,
- QgsFeatureSink,
- QgsProcessingParameterFeatureSource,
- QgsProcessingParameterField,
- QgsProcessingParameterFeatureSink)
-from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
-
-
-class EquivalentNumField(QgisAlgorithm):
-
- INPUT = 'INPUT'
- OUTPUT = 'OUTPUT'
- FIELD = 'FIELD'
-
- def group(self):
- return self.tr('Vector table')
-
- def groupId(self):
- return 'vectortable'
-
- def __init__(self):
- super().__init__()
-
- def initAlgorithm(self, config=None):
- self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
- self.tr('Input layer')))
- self.addParameter(QgsProcessingParameterField(self.FIELD,
- self.tr('Class field'),
- None, self.INPUT, QgsProcessingParameterField.Any))
-
- self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Layer with index field')))
-
- def name(self):
- return 'adduniquevalueindexfield'
-
- def displayName(self):
- return self.tr('Add unique value index field')
-
- def processAlgorithm(self, parameters, context, feedback):
- source = self.parameterAsSource(parameters, self.INPUT, context)
- fields = source.fields()
- fields.append(QgsField('NUM_FIELD', QVariant.Int))
-
- (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
- fields, source.wkbType(), source.sourceCrs())
-
- field_name = self.parameterAsString(parameters, self.FIELD, context)
- field_index = source.fields().lookupField(field_name)
-
- classes = {}
-
- features = source.getFeatures()
- total = 100.0 / source.featureCount() if source.featureCount() else 0
- for current, feature in enumerate(features):
- if feedback.isCanceled():
- break
-
- feedback.setProgress(int(current * total))
-
- attributes = feature.attributes()
- clazz = attributes[field_index]
-
- if clazz not in classes:
- classes[clazz] = len(list(classes.keys()))
-
- attributes.append(classes[clazz])
- feature.setAttributes(attributes)
- sink.addFeature(feature, QgsFeatureSink.FastInsert)
-
- return {self.OUTPUT: dest_id}
diff --git a/python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py
index c461fc249e8..6b3c0652ae5 100644
--- a/python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py
+++ b/python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py
@@ -58,7 +58,6 @@ from .DensifyGeometries import DensifyGeometries
from .DensifyGeometriesInterval import DensifyGeometriesInterval
from .Difference import Difference
from .EliminateSelection import EliminateSelection
-from .EquivalentNumField import EquivalentNumField
from .ExecuteSQL import ExecuteSQL
from .Explode import Explode
from .ExportGeometryInfo import ExportGeometryInfo
@@ -180,7 +179,6 @@ class QgisAlgorithmProvider(QgsProcessingProvider):
DensifyGeometriesInterval(),
Difference(),
EliminateSelection(),
- EquivalentNumField(),
ExecuteSQL(),
Explode(),
ExportGeometryInfo(),
diff --git a/python/plugins/processing/algs/qgis/VectorLayerHistogram.py b/python/plugins/processing/algs/qgis/VectorLayerHistogram.py
index cf5da168ecd..b87fec33329 100644
--- a/python/plugins/processing/algs/qgis/VectorLayerHistogram.py
+++ b/python/plugins/processing/algs/qgis/VectorLayerHistogram.py
@@ -2,7 +2,7 @@
"""
***************************************************************************
- EquivalentNumField.py
+ VectorLayerHistogram.py
---------------------
Date : January 2013
Copyright : (C) 2013 by Victor Olaya
diff --git a/python/plugins/processing/tests/testdata/expected/add_unique_field_summary.gml b/python/plugins/processing/tests/testdata/expected/add_unique_field_summary.gml
new file mode 100644
index 00000000000..f778b595610
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/add_unique_field_summary.gml
@@ -0,0 +1,27 @@
+
+
+ missing
+
+
+
+ 0
+ 2
+
+
+
+
+ 1
+ 1
+
+
+
+
+ 2
+ 0
+
+
+
diff --git a/python/plugins/processing/tests/testdata/expected/add_unique_field_summary.xsd b/python/plugins/processing/tests/testdata/expected/add_unique_field_summary.xsd
new file mode 100644
index 00000000000..0e7d63820fb
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/add_unique_field_summary.xsd
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
index 9196e78b115..a0db954238e 100755
--- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
+++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
@@ -2534,7 +2534,7 @@ tests:
fid: skip
- - algorithm: qgis:adduniquevalueindexfield
+ - algorithm: native:adduniquevalueindexfield
name: add unique field based on another field
params:
FIELD: id2
@@ -2546,6 +2546,24 @@ tests:
name: expected/add_unique_field.gml
type: vector
+ - algorithm: native:adduniquevalueindexfield
+ name: Add unique field summary
+ params:
+ FIELD: id2
+ FIELD_NAME: classes
+ INPUT:
+ name: points.gml
+ type: vector
+ results:
+ SUMMARY_OUTPUT:
+ name: expected/add_unique_field_summary.gml
+ type: vector
+ compare:
+ fields:
+ fid: skip
+ pk:
+ - id2
+
- algorithm: qgis:linestopolygons
name: convert lines to polygon
params:
diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt
index ac15ac8bf5b..19b4337de48 100755
--- a/src/analysis/CMakeLists.txt
+++ b/src/analysis/CMakeLists.txt
@@ -65,6 +65,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmtransect.cpp
processing/qgsalgorithmtransform.cpp
processing/qgsalgorithmtranslate.cpp
+ processing/qgsalgorithmuniquevalueindex.cpp
processing/qgsnativealgorithms.cpp
diff --git a/src/analysis/processing/qgsalgorithmuniquevalueindex.cpp b/src/analysis/processing/qgsalgorithmuniquevalueindex.cpp
new file mode 100644
index 00000000000..cbe49646dc9
--- /dev/null
+++ b/src/analysis/processing/qgsalgorithmuniquevalueindex.cpp
@@ -0,0 +1,172 @@
+/***************************************************************************
+ qgsalgorithmuniquevalueindex.cpp
+ ---------------------
+ begin : January 2018
+ copyright : (C) 2018 by Nyall Dawson
+ email : nyall dot dawson at gmail dot com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "qgsalgorithmuniquevalueindex.h"
+
+///@cond PRIVATE
+
+
+QgsProcessingAlgorithm::Flags QgsAddUniqueValueIndexAlgorithm::flags() const
+{
+ return QgsProcessingAlgorithm::flags() | QgsProcessingAlgorithm::FlagCanRunInBackground;
+}
+
+QString QgsAddUniqueValueIndexAlgorithm::name() const
+{
+ return QStringLiteral( "adduniquevalueindexfield" );
+}
+
+QString QgsAddUniqueValueIndexAlgorithm::displayName() const
+{
+ return QObject::tr( "Add unique value index field" );
+}
+
+QStringList QgsAddUniqueValueIndexAlgorithm::tags() const
+{
+ return QObject::tr( "categorize,categories,category,reclassify,classes,create" ).split( ',' );
+}
+
+QString QgsAddUniqueValueIndexAlgorithm::group() const
+{
+ return QObject::tr( "Vector table" );
+}
+
+QString QgsAddUniqueValueIndexAlgorithm::groupId() const
+{
+ return QStringLiteral( "vectortable" );
+}
+
+void QgsAddUniqueValueIndexAlgorithm::initAlgorithm( const QVariantMap & )
+{
+ addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
+ addParameter( new QgsProcessingParameterField( QStringLiteral( "FIELD" ), QObject::tr( "Class field" ), QVariant(),
+ QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any ) );
+ addParameter( new QgsProcessingParameterString( QStringLiteral( "FIELD_NAME" ),
+ QObject::tr( "Output field name" ), QStringLiteral( "NUM_FIELD" ) ) );
+
+ std::unique_ptr< QgsProcessingParameterFeatureSink > classedOutput = qgis::make_unique< QgsProcessingParameterFeatureSink >( QStringLiteral( "OUTPUT" ), QObject::tr( "Layer with index field" ), QgsProcessing::TypeVectorAnyGeometry, QVariant(), true );
+ classedOutput->setCreateByDefault( true );
+ addParameter( classedOutput.release() );
+
+ addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Layer with index field" ), QgsProcessing::TypeVector, QVariant(), true ) );
+
+ std::unique_ptr< QgsProcessingParameterFeatureSink > summaryOutput = qgis::make_unique< QgsProcessingParameterFeatureSink >( QStringLiteral( "SUMMARY_OUTPUT" ), QObject::tr( "Class summary" ),
+ QgsProcessing::TypeVector, QVariant(), true );
+ summaryOutput->setCreateByDefault( false );
+ addParameter( summaryOutput.release() );
+}
+
+QString QgsAddUniqueValueIndexAlgorithm::shortHelpString() const
+{
+ 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 "
+ "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"
+ "The new attribute is not added to the input layer but a new layer is generated instead.\n\n"
+ "Optionally, a separate table can be output which contains a summary of the class field values mapped to the new unique numeric value." );
+}
+
+QgsAddUniqueValueIndexAlgorithm *QgsAddUniqueValueIndexAlgorithm::createInstance() const
+{
+ return new QgsAddUniqueValueIndexAlgorithm();
+}
+
+QVariantMap QgsAddUniqueValueIndexAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
+{
+ std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
+ if ( !source )
+ return QVariantMap();
+
+ QString newFieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context );
+ QgsFields fields = source->fields();
+ QgsField newField = QgsField( newFieldName, QVariant::Int );
+ fields.append( newField );
+
+ QString dest;
+ std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, source->wkbType(), source->sourceCrs() ) );
+
+ QString sourceFieldName = parameterAsString( parameters, QStringLiteral( "FIELD" ), context );
+ int fieldIndex = source->fields().lookupField( sourceFieldName );
+ if ( fieldIndex < 0 )
+ throw QgsProcessingException( QObject::tr( "Invalid field name %1" ).arg( sourceFieldName ) );
+
+ QString summaryDest;
+ QgsFields summaryFields;
+ summaryFields.append( newField );
+ summaryFields.append( source->fields().at( fieldIndex ) );
+ std::unique_ptr< QgsFeatureSink > summarySink( parameterAsSink( parameters, QStringLiteral( "SUMMARY_OUTPUT" ), context, summaryDest, summaryFields, QgsWkbTypes::NoGeometry ) );
+
+ QHash< QVariant, int > classes;
+
+ QgsFeatureIterator it = source->getFeatures();
+
+ long count = source->featureCount();
+ double step = count > 0 ? 100.0 / count : 1;
+ int current = 0;
+ QgsFeature feature;
+ while ( it.nextFeature( feature ) )
+ {
+ if ( feedback->isCanceled() )
+ {
+ break;
+ }
+
+ QgsAttributes attributes = feature.attributes();
+ QVariant clazz = attributes.at( fieldIndex );
+
+ int thisValue = classes.value( clazz, -1 );
+ if ( thisValue == -1 )
+ {
+ thisValue = classes.count();
+ classes.insert( clazz, thisValue );
+ }
+
+ if ( sink )
+ {
+ attributes.append( thisValue );
+ feature.setAttributes( attributes );
+ sink->addFeature( feature, QgsFeatureSink::FastInsert );
+ }
+
+ feedback->setProgress( current * step );
+ current++;
+ }
+
+ if ( summarySink )
+ {
+ //generate summary table - first we make a sorted version of the classes
+ QMap< int, QVariant > sorted;
+ for ( auto classIt = classes.constBegin(); classIt != classes.constEnd(); ++classIt )
+ {
+ sorted.insert( classIt.value(), classIt.key() );
+ }
+ // now save them
+ for ( auto sortedIt = sorted.constBegin(); sortedIt != sorted.constEnd(); ++sortedIt )
+ {
+ QgsFeature f;
+ f.setAttributes( QgsAttributes() << sortedIt.key() << sortedIt.value() );
+ summarySink->addFeature( f, QgsFeatureSink::FastInsert );
+ }
+ }
+
+ QVariantMap results;
+ if ( sink )
+ results.insert( QStringLiteral( "OUTPUT" ), dest );
+ if ( summarySink )
+ results.insert( QStringLiteral( "SUMMARY_OUTPUT" ), summaryDest );
+ return results;
+}
+
+///@endcond
diff --git a/src/analysis/processing/qgsalgorithmuniquevalueindex.h b/src/analysis/processing/qgsalgorithmuniquevalueindex.h
new file mode 100644
index 00000000000..e5f2ec30670
--- /dev/null
+++ b/src/analysis/processing/qgsalgorithmuniquevalueindex.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ qgsalgorithmuniquevalueindex.h
+ ------------------------------
+ begin : January 2018
+ copyright : (C) 2018 by Nyall Dawson
+ email : nyall dot dawson at gmail dot com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef QGSALGORITHMUNIQUEVALUEINDEX_H
+#define QGSALGORITHMUNIQUEVALUEINDEX_H
+
+#define SIP_NO_FILE
+
+#include "qgis.h"
+#include "qgsprocessingalgorithm.h"
+
+///@cond PRIVATE
+
+/**
+ * Native add unique value index field algorithm.
+ */
+class QgsAddUniqueValueIndexAlgorithm : public QgsProcessingAlgorithm
+{
+
+ public:
+
+ QgsAddUniqueValueIndexAlgorithm() = default;
+ QgsProcessingAlgorithm::Flags flags() const override;
+ void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
+ QString name() const override;
+ QString displayName() const override;
+ QStringList tags() const override;
+ QString group() const override;
+ QString groupId() const override;
+ QString shortHelpString() const override;
+ QgsAddUniqueValueIndexAlgorithm *createInstance() const override SIP_FACTORY;
+
+ protected:
+
+ QVariantMap processAlgorithm( const QVariantMap ¶meters,
+ QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
+
+};
+
+///@endcond PRIVATE
+
+#endif // QGSALGORITHMUNIQUEVALUEINDEX_H
+
+
diff --git a/src/analysis/processing/qgsnativealgorithms.cpp b/src/analysis/processing/qgsnativealgorithms.cpp
index a0445807a27..ed268f4f847 100644
--- a/src/analysis/processing/qgsnativealgorithms.cpp
+++ b/src/analysis/processing/qgsnativealgorithms.cpp
@@ -62,6 +62,7 @@
#include "qgsalgorithmtransect.h"
#include "qgsalgorithmtransform.h"
#include "qgsalgorithmtranslate.h"
+#include "qgsalgorithmuniquevalueindex.h"
///@cond PRIVATE
@@ -98,6 +99,7 @@ bool QgsNativeAlgorithms::supportsNonFileBasedOutput() const
void QgsNativeAlgorithms::loadAlgorithms()
{
addAlgorithm( new QgsAddIncrementalFieldAlgorithm() );
+ addAlgorithm( new QgsAddUniqueValueIndexAlgorithm() );
addAlgorithm( new QgsAssignProjectionAlgorithm() );
addAlgorithm( new QgsBoundaryAlgorithm() );
addAlgorithm( new QgsBoundingBoxAlgorithm() );