Skip to content

Commit 5b1da98

Browse files
committedDec 15, 2017
[processing] List unique values improvements
- allow running on non-spatial tables - allow choice of more than one field
1 parent ebd98e3 commit 5b1da98

File tree

7 files changed

+150
-20
lines changed

7 files changed

+150
-20
lines changed
 

‎python/plugins/processing/algs/help/qgis.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ qgis:linestopolygons: >
267267
The attribute table of the output layer is the same as the one from of the input line layer.
268268

269269
qgis:listuniquevalues: >
270-
This algorithm generates a report with information about the categories found in a given attribute of a vector layer.
270+
This algorithm generates a report with information about the unique values found in a given attribute (or attributes) of a vector layer.
271271

272272
qgis:meanandstandarddeviationplot:
273273

‎python/plugins/processing/algs/qgis/UniqueValues.py

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
QgsWkbTypes,
3535
QgsFeature,
3636
QgsFeatureSink,
37+
QgsFeatureRequest,
3738
QgsFields,
39+
QgsProcessing,
3840
QgsProcessingParameterField,
3941
QgsProcessingParameterFeatureSource,
4042
QgsProcessingParameterFeatureSink,
@@ -51,7 +53,7 @@
5153
class UniqueValues(QgisAlgorithm):
5254

5355
INPUT = 'INPUT'
54-
FIELD_NAME = 'FIELD_NAME'
56+
FIELDS = 'FIELDS'
5557
TOTAL_VALUES = 'TOTAL_VALUES'
5658
UNIQUE_VALUES = 'UNIQUE_VALUES'
5759
OUTPUT = 'OUTPUT'
@@ -71,10 +73,10 @@ def __init__(self):
7173

7274
def initAlgorithm(self, config=None):
7375
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
74-
self.tr('Input layer')))
75-
self.addParameter(QgsProcessingParameterField(self.FIELD_NAME,
76-
self.tr('Target field'),
77-
parentLayerParameterName=self.INPUT, type=QgsProcessingParameterField.Any))
76+
self.tr('Input layer'), types=[QgsProcessing.TypeVector]))
77+
self.addParameter(QgsProcessingParameterField(self.FIELDS,
78+
self.tr('Target field(s)'),
79+
parentLayerParameterName=self.INPUT, type=QgsProcessingParameterField.Any, allowMultiple=True))
7880

7981
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Unique values'), optional=True, defaultValue=''))
8082

@@ -91,23 +93,48 @@ def displayName(self):
9193

9294
def processAlgorithm(self, parameters, context, feedback):
9395
source = self.parameterAsSource(parameters, self.INPUT, context)
94-
field_name = self.parameterAsString(parameters, self.FIELD_NAME, context)
95-
values = source.uniqueValues(source.fields().lookupField(field_name))
96+
field_names = self.parameterAsFields(parameters, self.FIELDS, context)
9697

9798
fields = QgsFields()
98-
field = source.fields()[source.fields().lookupField(field_name)]
99-
field.setName('VALUES')
100-
fields.append(field)
99+
field_indices = []
100+
for field_name in field_names:
101+
field_index = source.fields().lookupField(field_name)
102+
if field_index < 0:
103+
feedback.reportError(self.tr('Invalid field name {}').format(field_name))
104+
continue
105+
field = source.fields()[field_index]
106+
fields.append(field)
107+
field_indices.append(field_index)
101108
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
102109
fields, QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem())
110+
103111
results = {}
112+
values = set()
113+
if len(field_indices) == 1:
114+
# one field, can use provider optimised method
115+
values = tuple([v] for v in source.uniqueValues(field_indices[0]))
116+
else:
117+
# have to scan whole table
118+
# TODO - add this support to QgsVectorDataProvider so we can run it on
119+
# the backend
120+
request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry)
121+
request.setSubsetOfAttributes(field_indices)
122+
total = 100.0 / source.featureCount() if source.featureCount() else 0
123+
for current, f in enumerate(source.getFeatures(request)):
124+
if feedback.isCanceled():
125+
break
126+
127+
value = tuple(f.attribute(i) for i in field_indices)
128+
values.add(value)
129+
feedback.setProgress(int(current * total))
130+
104131
if sink:
105132
for value in values:
106133
if feedback.isCanceled():
107134
break
108135

109136
f = QgsFeature()
110-
f.setAttributes([value])
137+
f.setAttributes([attr for attr in value])
111138
sink.addFeature(f, QgsFeatureSink.FastInsert)
112139
results[self.OUTPUT] = dest_id
113140

@@ -117,7 +144,7 @@ def processAlgorithm(self, parameters, context, feedback):
117144
results[self.OUTPUT_HTML_FILE] = output_file
118145

119146
results[self.TOTAL_VALUES] = len(values)
120-
results[self.UNIQUE_VALUES] = ';'.join([str(v) for v in
147+
results[self.UNIQUE_VALUES] = ';'.join([','.join([str(attr) for attr in v]) for v in
121148
values])
122149
return results
123150

@@ -130,5 +157,5 @@ def createHTML(self, outputFile, algData):
130157
f.write(self.tr('<p>Unique values:</p>'))
131158
f.write('<ul>')
132159
for s in algData:
133-
f.write('<li>' + str(s) + '</li>')
160+
f.write('<li>' + ','.join([str(attr) for attr in s]) + '</li>')
134161
f.write('</ul></body></html>')

‎python/plugins/processing/tests/testdata/expected/unique_values.gfs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
<FeatureCount>3</FeatureCount>
88
</DatasetSpecificInfo>
99
<PropertyDefn>
10-
<Name>VALUES</Name>
11-
<ElementPath>VALUES</ElementPath>
10+
<Name>id2</Name>
11+
<ElementPath>id2</ElementPath>
1212
<Type>Integer</Type>
1313
</PropertyDefn>
1414
</GMLFeatureClass>

‎python/plugins/processing/tests/testdata/expected/unique_values.gml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@
88

99
<gml:featureMember>
1010
<ogr:unique_values fid="unique_values.0">
11-
<ogr:VALUES>0</ogr:VALUES>
11+
<ogr:id2>0</ogr:id2>
1212
</ogr:unique_values>
1313
</gml:featureMember>
1414
<gml:featureMember>
1515
<ogr:unique_values fid="unique_values.1">
16-
<ogr:VALUES>1</ogr:VALUES>
16+
<ogr:id2>1</ogr:id2>
1717
</ogr:unique_values>
1818
</gml:featureMember>
1919
<gml:featureMember>
2020
<ogr:unique_values fid="unique_values.2">
21-
<ogr:VALUES>2</ogr:VALUES>
21+
<ogr:id2>2</ogr:id2>
2222
</ogr:unique_values>
2323
</gml:featureMember>
2424
</ogr:FeatureCollection>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ unique_values_multiple.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:unique_values_multiple fid="unique_values_multiple.0">
11+
<ogr:name>aa</ogr:name>
12+
<ogr:intval>1</ogr:intval>
13+
</ogr:unique_values_multiple>
14+
</gml:featureMember>
15+
<gml:featureMember>
16+
<ogr:unique_values_multiple fid="unique_values_multiple.1">
17+
<ogr:name>bb</ogr:name>
18+
<ogr:intval>2</ogr:intval>
19+
</ogr:unique_values_multiple>
20+
</gml:featureMember>
21+
<gml:featureMember>
22+
<ogr:unique_values_multiple fid="unique_values_multiple.2">
23+
<ogr:name>cc</ogr:name>
24+
<ogr:intval xsi:nil="true"/>
25+
</ogr:unique_values_multiple>
26+
</gml:featureMember>
27+
<gml:featureMember>
28+
<ogr:unique_values_multiple fid="unique_values_multiple.3">
29+
<ogr:name xsi:nil="true"/>
30+
<ogr:intval>120</ogr:intval>
31+
</ogr:unique_values_multiple>
32+
</gml:featureMember>
33+
<gml:featureMember>
34+
<ogr:unique_values_multiple fid="unique_values_multiple.4">
35+
<ogr:name>bb</ogr:name>
36+
<ogr:intval>1</ogr:intval>
37+
</ogr:unique_values_multiple>
38+
</gml:featureMember>
39+
<gml:featureMember>
40+
<ogr:unique_values_multiple fid="unique_values_multiple.5">
41+
<ogr:name>dd</ogr:name>
42+
<ogr:intval xsi:nil="true"/>
43+
</ogr:unique_values_multiple>
44+
</gml:featureMember>
45+
</ogr:FeatureCollection>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="unique_values_multiple" type="ogr:unique_values_multiple_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="unique_values_multiple_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="name" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:string">
21+
<xs:maxLength value="2"/>
22+
</xs:restriction>
23+
</xs:simpleType>
24+
</xs:element>
25+
<xs:element name="intval" nillable="true" minOccurs="0" maxOccurs="1">
26+
<xs:simpleType>
27+
<xs:restriction base="xs:integer">
28+
<xs:totalDigits value="10"/>
29+
</xs:restriction>
30+
</xs:simpleType>
31+
</xs:element>
32+
</xs:sequence>
33+
</xs:extension>
34+
</xs:complexContent>
35+
</xs:complexType>
36+
</xs:schema>

‎python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,11 +408,33 @@ tests:
408408
INPUT:
409409
name: points.gml
410410
type: vector
411-
FIELD_NAME: id2
411+
FIELDS: id2
412412
results:
413413
OUTPUT:
414414
name: expected/unique_values.gml
415415
type: vector
416+
pk:
417+
- id2
418+
419+
- algorithm: qgis:listuniquevalues
420+
name: Unique values (multiple fields)
421+
params:
422+
FIELDS:
423+
- name
424+
- intval
425+
INPUT:
426+
name: dissolve_polys.gml
427+
type: vector
428+
results:
429+
OUTPUT:
430+
name: expected/unique_values_multiple.gml
431+
type: vector
432+
compare:
433+
fields:
434+
fid: skip
435+
pk:
436+
- name
437+
- intval
416438

417439
- algorithm: native:addautoincrementalfield
418440
name: Add autoincremental field

0 commit comments

Comments
 (0)
Please sign in to comment.