Skip to content

Commit

Permalink
[FEATURE] Make processing dissolve algorithm accept multiple fields
Browse files Browse the repository at this point in the history
This allows you to dissolve based on more than one field value
  • Loading branch information
nyalldawson committed Aug 2, 2016
1 parent f449bf2 commit bb54b4f
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 21 deletions.
40 changes: 19 additions & 21 deletions python/plugins/processing/algs/qgis/Dissolve.py
Expand Up @@ -26,6 +26,7 @@
__revision__ = '$Format:%H$'

import os
from collections import defaultdict

from qgis.PyQt.QtGui import QIcon

Expand All @@ -36,7 +37,7 @@
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterBoolean
from processing.core.parameters import ParameterTableField
from processing.core.parameters import ParameterTableMultipleField
from processing.core.outputs import OutputVector
from processing.tools import vector, dataobjects

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

def processAlgorithm(self, progress):
useField = not self.getParameterValue(Dissolve.DISSOLVE_ALL)
fieldname = self.getParameterValue(Dissolve.FIELD)
field_names = self.getParameterValue(Dissolve.FIELD)
vlayerA = dataobjects.getObjectFromUri(
self.getParameterValue(Dissolve.INPUT))
vproviderA = vlayerA.dataProvider()
Expand Down Expand Up @@ -127,20 +128,16 @@ def processAlgorithm(self, progress):
outFeat.setAttributes(attrs)
writer.addFeature(outFeat)
else:
fieldIdx = vlayerA.fieldNameIndex(fieldname)
unique = vector.getUniqueValues(vlayerA, int(fieldIdx))
nFeat = len(unique)
myDict = {}
attrDict = {}
for item in unique:
myDict[unicode(item).strip()] = []
attrDict[unicode(item).strip()] = None
field_indexes = [vlayerA.fieldNameIndex(f) for f in field_names.split(';')]

unique = None
attribute_dict = {}
geometry_dict = defaultdict(lambda: [])

for inFeat in features:
attrs = inFeat.attributes()
tempItem = attrs[fieldIdx]

index_attrs = tuple([attrs[i] for i in field_indexes])

tmpInGeom = QgsGeometry(inFeat.geometry())
if tmpInGeom.isGeosEmpty():
continue
Expand All @@ -154,16 +151,17 @@ def processAlgorithm(self, progress):
'geometry: ')
+ error.what())

if attrDict[unicode(tempItem).strip()] is None:
if not index_attrs in attribute_dict:
# keep attributes of first feature
attrDict[unicode(tempItem).strip()] = attrs
attribute_dict[index_attrs] = attrs

myDict[unicode(tempItem).strip()].append(tmpInGeom)
geometry_dict[index_attrs].append(tmpInGeom)

features = None
nFeat = len(attribute_dict)

nElement = 0
for key, value in myDict.items():
for key, value in geometry_dict.items():
outFeat = QgsFeature()
nElement += 1
progress.setPercentage(int(nElement * 100 / nFeat))
try:
Expand All @@ -172,7 +170,7 @@ def processAlgorithm(self, progress):
raise GeoAlgorithmExecutionException(
self.tr('Geometry exception while dissolving'))
outFeat.setGeometry(tmpOutGeom)
outFeat.setAttributes(attrDict[key])
outFeat.setAttributes(attribute_dict[key])
writer.addFeature(outFeat)

del writer
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ dissolve_two_fields.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-1</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>9.162955854126682</gml:X><gml:Y>6.088675623800385</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:dissolve_two_fields fid="polys.0">
<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>
<ogr:name>aa</ogr:name>
<ogr:intval>1</ogr:intval>
<ogr:floatval>44.123456</ogr:floatval>
</ogr:dissolve_two_fields>
</gml:featureMember>
<gml:featureMember>
<ogr:dissolve_two_fields fid="polys.1">
<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>
<ogr:name>dd</ogr:name>
<ogr:floatval>0</ogr:floatval>
</ogr:dissolve_two_fields>
</gml:featureMember>
<gml:featureMember>
<ogr:dissolve_two_fields fid="polys.2">
<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>
<ogr:name>bb</ogr:name>
<ogr:intval>1</ogr:intval>
<ogr:floatval>0.123</ogr:floatval>
</ogr:dissolve_two_fields>
</gml:featureMember>
<gml:featureMember>
<ogr:dissolve_two_fields fid="polys.3">
<ogr:intval>120</ogr:intval>
<ogr:floatval>-100291.43213</ogr:floatval>
</ogr:dissolve_two_fields>
</gml:featureMember>
<gml:featureMember>
<ogr:dissolve_two_fields fid="polys.8">
<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>
<ogr:name>bb</ogr:name>
<ogr:intval>2</ogr:intval>
<ogr:floatval>0.123</ogr:floatval>
</ogr:dissolve_two_fields>
</gml:featureMember>
<gml:featureMember>
<ogr:dissolve_two_fields fid="polys.7">
<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>
<ogr:name>cc</ogr:name>
<ogr:floatval>0.123</ogr:floatval>
</ogr:dissolve_two_fields>
</gml:featureMember>
</ogr:FeatureCollection>
13 changes: 13 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -304,6 +304,19 @@ tests:
name: expected/dissolve_field.gml
type: vector

- algorithm: qgis:dissolve
name: Dissolve using two fields
params:
DISSOLVE_ALL: false
FIELD: intval;name
INPUT:
name: dissolve_polys.gml
type: vector
results:
OUTPUT:
name: expected/dissolve_two_fields.gml
type: vector

- name: Dissolve with geometries reported as valid but as invalid with isGeosValid
algorithm: qgis:dissolve
params:
Expand Down

0 comments on commit bb54b4f

Please sign in to comment.