1# -*- coding: utf-8 -*- 2 3""" 4*************************************************************************** 5 Dissolve.py 6 --------------------- 7 Date : Janaury 2015 8 Copyright : (C) 2015 by Giovanni Manghi 9 Email : giovanni dot manghi at naturalgis dot pt 10*************************************************************************** 11* * 12* This program is free software; you can redistribute it and/or modify * 13* it under the terms of the GNU General Public License as published by * 14* the Free Software Foundation; either version 2 of the License, or * 15* (at your option) any later version. * 16* * 17*************************************************************************** 18""" 19 20__author__ = 'Giovanni Manghi' 21__date__ = 'January 2015' 22__copyright__ = '(C) 2015, Giovanni Manghi' 23 24from qgis.core import (QgsProcessingException, 25 QgsProcessingParameterDefinition, 26 QgsProcessingParameterFeatureSource, 27 QgsProcessingParameterField, 28 QgsProcessingParameterString, 29 QgsProcessingParameterBoolean, 30 QgsProcessingParameterVectorDestination) 31from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm 32from processing.algs.gdal.GdalUtils import GdalUtils 33 34 35class Dissolve(GdalAlgorithm): 36 INPUT = 'INPUT' 37 FIELD = 'FIELD' 38 GEOMETRY = 'GEOMETRY' 39 EXPLODE_COLLECTIONS = 'EXPLODE_COLLECTIONS' 40 KEEP_ATTRIBUTES = 'KEEP_ATTRIBUTES' 41 COUNT_FEATURES = 'COUNT_FEATURES' 42 COMPUTE_AREA = 'COMPUTE_AREA' 43 COMPUTE_STATISTICS = 'COMPUTE_STATISTICS' 44 STATISTICS_ATTRIBUTE = 'STATISTICS_ATTRIBUTE' 45 OPTIONS = 'OPTIONS' 46 OUTPUT = 'OUTPUT' 47 48 def __init__(self): 49 super().__init__() 50 51 def initAlgorithm(self, config=None): 52 self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, 53 self.tr('Input layer'))) 54 self.addParameter(QgsProcessingParameterField(self.FIELD, 55 self.tr('Dissolve field'), 56 None, 57 self.INPUT, 58 QgsProcessingParameterField.Any, optional=True)) 59 self.addParameter(QgsProcessingParameterString(self.GEOMETRY, 60 self.tr('Geometry column name'), 61 defaultValue='geometry')) 62 params = [] 63 params.append(QgsProcessingParameterBoolean(self.EXPLODE_COLLECTIONS, 64 self.tr('Produce one feature for each geometry in any kind of geometry collection in the source file'), 65 defaultValue=False)) 66 params.append(QgsProcessingParameterBoolean(self.KEEP_ATTRIBUTES, 67 self.tr('Keep input attributes'), 68 defaultValue=False)) 69 params.append(QgsProcessingParameterBoolean(self.COUNT_FEATURES, 70 self.tr('Count dissolved features'), 71 defaultValue=False)) 72 params.append(QgsProcessingParameterBoolean(self.COMPUTE_AREA, 73 self.tr('Compute area and perimeter of dissolved features'), 74 defaultValue=False)) 75 params.append(QgsProcessingParameterBoolean(self.COMPUTE_STATISTICS, 76 self.tr('Compute min/max/sum/mean for attribute'), 77 defaultValue=False)) 78 params.append(QgsProcessingParameterField(self.STATISTICS_ATTRIBUTE, 79 self.tr('Numeric attribute to calculate statistics on'), 80 None, 81 self.INPUT, 82 QgsProcessingParameterField.Numeric, 83 optional=True)) 84 params.append(QgsProcessingParameterString(self.OPTIONS, 85 self.tr('Additional creation options'), 86 defaultValue='', 87 optional=True)) 88 for param in params: 89 param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced) 90 self.addParameter(param) 91 92 self.addParameter(QgsProcessingParameterVectorDestination(self.OUTPUT, 93 self.tr('Dissolved'))) 94 95 def name(self): 96 return 'dissolve' 97 98 def displayName(self): 99 return self.tr('Dissolve') 100 101 def group(self): 102 return self.tr('Vector geoprocessing') 103 104 def groupId(self): 105 return 'vectorgeoprocessing' 106 107 def commandName(self): 108 return 'ogr2ogr' 109 110 def getConsoleCommands(self, parameters, context, feedback, executing=True): 111 source = self.parameterAsSource(parameters, self.INPUT, context) 112 if source is None: 113 raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) 114 115 fields = source.fields() 116 ogrLayer, layerName = self.getOgrCompatibleSource(self.INPUT, parameters, context, feedback, executing) 117 geometry = self.parameterAsString(parameters, self.GEOMETRY, context) 118 fieldName = self.parameterAsString(parameters, self.FIELD, context) 119 120 options = self.parameterAsString(parameters, self.OPTIONS, context) 121 outFile = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) 122 self.setOutputValue(self.OUTPUT, outFile) 123 124 output, outputFormat = GdalUtils.ogrConnectionStringAndFormat(outFile, context) 125 126 other_fields = [] 127 for f in fields: 128 if f.name() == geometry: 129 continue 130 131 other_fields.append('"{}"'.format(f.name())) 132 133 if other_fields: 134 other_fields = ',*' 135 else: 136 other_fields = '' 137 138 arguments = [] 139 arguments.append(output) 140 arguments.append(ogrLayer) 141 arguments.append('-nlt PROMOTE_TO_MULTI') 142 arguments.append('-dialect') 143 arguments.append('sqlite') 144 arguments.append('-sql') 145 146 tokens = [] 147 if self.parameterAsBoolean(parameters, self.COUNT_FEATURES, context): 148 tokens.append('COUNT({}) AS count'.format(geometry)) 149 150 if self.parameterAsBoolean(parameters, self.COMPUTE_AREA, context): 151 tokens.append('SUM(ST_Area({0})) AS area, ST_Perimeter(ST_Union({0})) AS perimeter'.format(geometry)) 152 153 statsField = self.parameterAsString(parameters, self.STATISTICS_ATTRIBUTE, context) 154 if statsField and self.parameterAsBoolean(parameters, self.COMPUTE_STATISTICS, context): 155 tokens.append('SUM("{0}") AS sum, MIN("{0}") AS min, MAX("{0}") AS max, AVG("{0}") AS avg'.format(statsField)) 156 157 params = ','.join(tokens) 158 if params: 159 params = ', ' + params 160 161 group_by = '' 162 if fieldName: 163 group_by = ' GROUP BY "{}"'.format(fieldName) 164 165 if self.parameterAsBoolean(parameters, self.KEEP_ATTRIBUTES, context): 166 sql = 'SELECT ST_Union({}) AS {}{}{} FROM "{}"{}'.format(geometry, geometry, other_fields, params, layerName, group_by) 167 else: 168 sql = 'SELECT ST_Union({}) AS {}{}{} FROM "{}"{}'.format(geometry, geometry, ', "{}"'.format(fieldName) if fieldName else '', 169 params, layerName, group_by) 170 171 arguments.append(sql) 172 173 if self.parameterAsBoolean(parameters, self.EXPLODE_COLLECTIONS, context): 174 arguments.append('-explodecollections') 175 176 if options: 177 arguments.append(options) 178 179 if outputFormat: 180 arguments.append('-f {}'.format(outputFormat)) 181 182 return [self.commandName(), GdalUtils.escapeAndJoin(arguments)] 183