Skip to content

Commit 7853aa1

Browse files
committed
[FEATURE] Make processing dissolve algorithm accept multiple fields
This allows you to dissolve based on more than one field value (cherry-picked from bb54b4f)
1 parent 644cbd9 commit 7853aa1

File tree

3 files changed

+90
-21
lines changed

3 files changed

+90
-21
lines changed

python/plugins/processing/algs/qgis/Dissolve.py

+19-21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
__revision__ = '$Format:%H$'
2727

2828
import os
29+
from collections import defaultdict
2930

3031
from qgis.PyQt.QtGui import QIcon
3132

@@ -36,7 +37,7 @@
3637
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
3738
from processing.core.parameters import ParameterVector
3839
from processing.core.parameters import ParameterBoolean
39-
from processing.core.parameters import ParameterTableField
40+
from processing.core.parameters import ParameterTableMultipleField
4041
from processing.core.outputs import OutputVector
4142
from processing.tools import vector, dataobjects
4243

@@ -60,14 +61,14 @@ def defineCharacteristics(self):
6061
self.tr('Input layer'),
6162
[ParameterVector.VECTOR_TYPE_POLYGON, ParameterVector.VECTOR_TYPE_LINE]))
6263
self.addParameter(ParameterBoolean(Dissolve.DISSOLVE_ALL,
63-
self.tr('Dissolve all (do not use field)'), True))
64-
self.addParameter(ParameterTableField(Dissolve.FIELD,
65-
self.tr('Unique ID field'), Dissolve.INPUT, optional=True))
64+
self.tr('Dissolve all (do not use fields)'), True))
65+
self.addParameter(ParameterTableMultipleField(Dissolve.FIELD,
66+
self.tr('Unique ID fields'), Dissolve.INPUT, optional=True))
6667
self.addOutput(OutputVector(Dissolve.OUTPUT, self.tr('Dissolved')))
6768

6869
def processAlgorithm(self, progress):
6970
useField = not self.getParameterValue(Dissolve.DISSOLVE_ALL)
70-
fieldname = self.getParameterValue(Dissolve.FIELD)
71+
field_names = self.getParameterValue(Dissolve.FIELD)
7172
vlayerA = dataobjects.getObjectFromUri(
7273
self.getParameterValue(Dissolve.INPUT))
7374
fields = vlayerA.fields()
@@ -124,20 +125,16 @@ def processAlgorithm(self, progress):
124125
outFeat.setAttributes(attrs)
125126
writer.addFeature(outFeat)
126127
else:
127-
fieldIdx = vlayerA.fieldNameIndex(fieldname)
128-
unique = vector.getUniqueValues(vlayerA, int(fieldIdx))
129-
nFeat = len(unique)
130-
myDict = {}
131-
attrDict = {}
132-
for item in unique:
133-
myDict[unicode(item).strip()] = []
134-
attrDict[unicode(item).strip()] = None
128+
field_indexes = [vlayerA.fieldNameIndex(f) for f in field_names.split(';')]
135129

136-
unique = None
130+
attribute_dict = {}
131+
geometry_dict = defaultdict(lambda: [])
137132

138133
for inFeat in features:
139134
attrs = inFeat.attributes()
140-
tempItem = attrs[fieldIdx]
135+
136+
index_attrs = tuple([attrs[i] for i in field_indexes])
137+
141138
tmpInGeom = QgsGeometry(inFeat.geometry())
142139
if tmpInGeom.isGeosEmpty():
143140
continue
@@ -151,16 +148,17 @@ def processAlgorithm(self, progress):
151148
'geometry: ')
152149
+ error.what())
153150

154-
if attrDict[unicode(tempItem).strip()] is None:
151+
if not index_attrs in attribute_dict:
155152
# keep attributes of first feature
156-
attrDict[unicode(tempItem).strip()] = attrs
153+
attribute_dict[index_attrs] = attrs
157154

158-
myDict[unicode(tempItem).strip()].append(tmpInGeom)
155+
geometry_dict[index_attrs].append(tmpInGeom)
159156

160-
features = None
157+
nFeat = len(attribute_dict)
161158

162159
nElement = 0
163-
for key, value in myDict.items():
160+
for key, value in geometry_dict.items():
161+
outFeat = QgsFeature()
164162
nElement += 1
165163
progress.setPercentage(int(nElement * 100 / nFeat))
166164
try:
@@ -169,7 +167,7 @@ def processAlgorithm(self, progress):
169167
raise GeoAlgorithmExecutionException(
170168
self.tr('Geometry exception while dissolving'))
171169
outFeat.setGeometry(tmpOutGeom)
172-
outFeat.setAttributes(attrDict[key])
170+
outFeat.setAttributes(attribute_dict[key])
173171
writer.addFeature(outFeat)
174172

175173
del writer
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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/ dissolve_two_fields.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy>
8+
<gml:Box>
9+
<gml:coord><gml:X>-1</gml:X><gml:Y>-3</gml:Y></gml:coord>
10+
<gml:coord><gml:X>9.162955854126682</gml:X><gml:Y>6.088675623800385</gml:Y></gml:coord>
11+
</gml:Box>
12+
</gml:boundedBy>
13+
14+
<gml:featureMember>
15+
<ogr:dissolve_two_fields fid="polys.0">
16+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,2 6,1 6,-3 2,-1 -1,-1 -1,3 3,3 3,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
17+
<ogr:name>aa</ogr:name>
18+
<ogr:intval>1</ogr:intval>
19+
<ogr:floatval>44.123456</ogr:floatval>
20+
</ogr:dissolve_two_fields>
21+
</gml:featureMember>
22+
<gml:featureMember>
23+
<ogr:dissolve_two_fields fid="polys.1">
24+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6.241458733205375,-0.054510556621882 7.241458733205375,-1.054510556621882 5.241458733205375,-1.054510556621882 6.241458733205375,-0.054510556621882</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
25+
<ogr:name>dd</ogr:name>
26+
<ogr:floatval>0</ogr:floatval>
27+
</ogr:dissolve_two_fields>
28+
</gml:featureMember>
29+
<gml:featureMember>
30+
<ogr:dissolve_two_fields fid="polys.2">
31+
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>4.172552783109405,4.822648752399233 4.172552783109405,5.822648752399233 5.172552783109405,5.822648752399233 5.172552783109405,4.822648752399233 4.172552783109405,4.822648752399233</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2.443378119001919,4.423608445297505 2.443378119001919,5.0 2,5 2,6 3,6 3.0,5.423608445297505 3.443378119001919,5.423608445297505 3.443378119001919,4.423608445297505 2.443378119001919,4.423608445297505</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
32+
<ogr:name>bb</ogr:name>
33+
<ogr:intval>1</ogr:intval>
34+
<ogr:floatval>0.123</ogr:floatval>
35+
</ogr:dissolve_two_fields>
36+
</gml:featureMember>
37+
<gml:featureMember>
38+
<ogr:dissolve_two_fields fid="polys.3">
39+
<ogr:intval>120</ogr:intval>
40+
<ogr:floatval>-100291.43213</ogr:floatval>
41+
</ogr:dissolve_two_fields>
42+
</gml:featureMember>
43+
<gml:featureMember>
44+
<ogr:dissolve_two_fields fid="polys.8">
45+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2.620729366602688,5.088675623800385 2.620729366602688,6.088675623800385 3.620729366602688,6.088675623800385 3.620729366602688,5.088675623800385 2.620729366602688,5.088675623800385</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
46+
<ogr:name>bb</ogr:name>
47+
<ogr:intval>2</ogr:intval>
48+
<ogr:floatval>0.123</ogr:floatval>
49+
</ogr:dissolve_two_fields>
50+
</gml:featureMember>
51+
<gml:featureMember>
52+
<ogr:dissolve_two_fields fid="polys.7">
53+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>8.162955854126682,2.738771593090211 8.162955854126682,3.738771593090211 9.162955854126682,3.738771593090211 9.162955854126682,2.738771593090211 8.162955854126682,2.738771593090211</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
54+
<ogr:name>cc</ogr:name>
55+
<ogr:floatval>0.123</ogr:floatval>
56+
</ogr:dissolve_two_fields>
57+
</gml:featureMember>
58+
</ogr:FeatureCollection>

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

+13
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,19 @@ tests:
218218
name: expected/dissolve_field.gml
219219
type: vector
220220

221+
- algorithm: qgis:dissolve
222+
name: Dissolve using two fields
223+
params:
224+
DISSOLVE_ALL: false
225+
FIELD: intval;name
226+
INPUT:
227+
name: dissolve_polys.gml
228+
type: vector
229+
results:
230+
OUTPUT:
231+
name: expected/dissolve_two_fields.gml
232+
type: vector
233+
221234
- name: Dissolve with geometries reported as valid but as invalid with isGeosValid
222235
algorithm: qgis:dissolve
223236
params:

0 commit comments

Comments
 (0)