Upgrade ported python algs to be thread ready

This commit is contained in:
Nyall Dawson 2017-06-28 19:55:08 +10:00
parent c2621b1275
commit cd7776ca1c
36 changed files with 912 additions and 576 deletions

View File

@ -74,28 +74,38 @@ class AddTableField(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Added'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Added')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_LAYER, self.tr('Added'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_LAYER, self.tr('Added')))
self.source = None
self.fieldType = None
self.fieldLength = None
self.fieldName = None
self.fieldPrecision = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'addfieldtoattributestable' return 'addfieldtoattributestable'
def displayName(self): def displayName(self):
return self.tr('Add field to attributes table') return self.tr('Add field to attributes table')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
fieldType = self.parameterAsEnum(parameters, self.FIELD_TYPE, context) self.fieldType = self.parameterAsEnum(parameters, self.FIELD_TYPE, context)
fieldName = self.parameterAsString(parameters, self.FIELD_NAME, context) self.fieldName = self.parameterAsString(parameters, self.FIELD_NAME, context)
fieldLength = self.parameterAsInt(parameters, self.FIELD_LENGTH, context) self.fieldLength = self.parameterAsInt(parameters, self.FIELD_LENGTH, context)
fieldPrecision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context) self.fieldPrecision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context)
fields = source.fields() fields = self.source.fields()
fields.append(QgsField(fieldName, self.TYPES[fieldType], '', fields.append(QgsField(self.fieldName, self.TYPES[self.fieldType], '',
fieldLength, fieldPrecision)) self.fieldLength, self.fieldPrecision))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
fields, source.wkbType(), source.sourceCrs()) fields, self.source.wkbType(), self.source.sourceCrs())
return True
features = source.getFeatures() def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, input_feature in enumerate(features): for current, input_feature in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
@ -106,7 +116,10 @@ class AddTableField(QgisAlgorithm):
attributes.append(None) attributes.append(None)
output_feature.setAttributes(attributes) output_feature.setAttributes(attributes)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert) self.sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return {self.OUTPUT_LAYER: dest_id} return True
def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT_LAYER: self.dest_id}

View File

@ -47,7 +47,7 @@ pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class Aspect(QgisAlgorithm): class Aspect(QgisAlgorithm):
INPUT_LAYER = 'INPUT_LAYER' INPUT = 'INPUT'
Z_FACTOR = 'Z_FACTOR' Z_FACTOR = 'Z_FACTOR'
OUTPUT_LAYER = 'OUTPUT_LAYER' OUTPUT_LAYER = 'OUTPUT_LAYER'
@ -60,13 +60,18 @@ class Aspect(QgisAlgorithm):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT_LAYER, self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT,
self.tr('Elevation layer'))) self.tr('Elevation layer')))
self.addParameter(QgsProcessingParameterNumber(self.Z_FACTOR, self.addParameter(QgsProcessingParameterNumber(self.Z_FACTOR,
self.tr('Z factor'), QgsProcessingParameterNumber.Double, self.tr('Z factor'), QgsProcessingParameterNumber.Double,
1, False, 1, 999999.99)) 1, False, 1, 999999.99))
self.addParameter(QgsProcessingParameterRasterOutput(self.OUTPUT_LAYER, self.tr('Aspect'))) self.addParameter(QgsProcessingParameterRasterOutput(self.OUTPUT, self.tr('Aspect')))
self.addOutput(QgsProcessingOutputRasterLayer(self.OUTPUT_LAYER, self.tr('Aspect'))) self.addOutput(QgsProcessingOutputRasterLayer(self.OUTPUT, self.tr('Aspect')))
self.inputFile = None
self.outputFile = None
self.outputFormat = None
self.zFactor = None
def name(self): def name(self):
return 'aspect' return 'aspect'
@ -74,16 +79,19 @@ class Aspect(QgisAlgorithm):
def displayName(self): def displayName(self):
return self.tr('Aspect') return self.tr('Aspect')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
inputFile = exportRasterLayer(self.parameterAsRasterLayer(parameters, self.INPUT_LAYER, context)) self.inputFile = exportRasterLayer(self.parameterAsRasterLayer(parameters, self.INPUT, context))
zFactor = self.parameterAsDouble(parameters, self.Z_FACTOR, context) self.zFactor = self.parameterAsDouble(parameters, self.Z_FACTOR, context)
outputFile = self.parameterAsRasterOutputLayer(parameters, self.OUTPUT_LAYER, context) self.outputFile = self.parameterAsRasterOutputLayer(parameters, self.OUTPUT, context)
outputFormat = raster.formatShortNameFromFileName(outputFile) self.outputFormat = raster.formatShortNameFromFileName(self.outputFile)
return True
aspect = QgsAspectFilter(inputFile, outputFile, outputFormat) def processAlgorithm(self, context, feedback):
aspect.setZFactor(zFactor) aspect = QgsAspectFilter(self.inputFile, self.outputFile, self.outputFormat)
aspect.processRaster(feedback) aspect.setZFactor(self.zFactor)
return aspect.processRaster(feedback) == 0
return {self.OUTPUT_LAYER: outputFile} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.outputFile}

View File

@ -51,6 +51,10 @@ class AutoincrementalField(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Incremented'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Incremented')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Incremented'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Incremented')))
self.source = None
self.sink = None
self.dest_id = None
def group(self): def group(self):
return self.tr('Vector table tools') return self.tr('Vector table tools')
@ -60,16 +64,18 @@ class AutoincrementalField(QgisAlgorithm):
def displayName(self): def displayName(self):
return self.tr('Add autoincremental field') return self.tr('Add autoincremental field')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
fields = source.fields() fields = self.source.fields()
fields.append(QgsField('AUTO', QVariant.Int)) fields.append(QgsField('AUTO', QVariant.Int))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, source.wkbType(), source.sourceCrs()) fields, self.source.wkbType(), self.source.sourceCrs())
return True
features = source.getFeatures() def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, input_feature in enumerate(features): for current, input_feature in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
@ -79,7 +85,9 @@ class AutoincrementalField(QgisAlgorithm):
attributes.append(current) attributes.append(current)
output_feature.setAttributes(attributes) output_feature.setAttributes(attributes)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert) self.sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -119,42 +119,52 @@ class BasicStatisticsForField(QgisAlgorithm):
self.addOutput(QgsProcessingOutputNumber(self.THIRDQUARTILE, self.tr('Third quartile'))) self.addOutput(QgsProcessingOutputNumber(self.THIRDQUARTILE, self.tr('Third quartile')))
self.addOutput(QgsProcessingOutputNumber(self.IQR, self.tr('Interquartile Range (IQR)'))) self.addOutput(QgsProcessingOutputNumber(self.IQR, self.tr('Interquartile Range (IQR)')))
self.source = None
self.field = None
self.field_name = None
self.output_file = None
self.results = {}
def name(self): def name(self):
return 'basicstatisticsforfields' return 'basicstatisticsforfields'
def displayName(self): def displayName(self):
return self.tr('Basic statistics for fields') return self.tr('Basic statistics for fields')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
field_name = self.parameterAsString(parameters, self.FIELD_NAME, context) self.field_name = self.parameterAsString(parameters, self.FIELD_NAME, context)
field = source.fields().at(source.fields().lookupField(field_name)) self.field = self.source.fields().at(self.source.fields().lookupField(self.field_name))
output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context) self.output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context)
return True
request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([field_name], source.fields()) def processAlgorithm(self, context, feedback):
features = source.getFeatures(request) request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([self.field_name], self.source.fields())
count = source.featureCount() features = self.source.getFeatures(request)
count = self.source.featureCount()
data = [] data = []
data.append(self.tr('Analyzed field: {}').format(field_name)) data.append(self.tr('Analyzed field: {}').format(self.field_name))
results = {}
if field.isNumeric(): if self.field.isNumeric():
d, results = self.calcNumericStats(features, feedback, field, count) d, self.results = self.calcNumericStats(features, feedback, self.field, count)
data.extend(d) data.extend(d)
elif field.type() in (QVariant.Date, QVariant.Time, QVariant.DateTime): elif self.field.type() in (QVariant.Date, QVariant.Time, QVariant.DateTime):
d, results = self.calcDateTimeStats(features, feedback, field, count) d, self.results = self.calcDateTimeStats(features, feedback, self.field, count)
data.extend(d) data.extend(d)
else: else:
d, results = self.calcStringStats(features, feedback, field, count) d, self.results = self.calcStringStats(features, feedback, self.field, count)
data.extend(d) data.extend(d)
if output_file: if self.output_file:
self.createHTML(output_file, data) self.createHTML(self.output_file, data)
results[self.OUTPUT_HTML_FILE] = output_file self.results[self.OUTPUT_HTML_FILE] = self.output_file
return results return True
def postProcessAlgorithm(self, context, feedback):
return self.results
def calcNumericStats(self, features, feedback, field, count): def calcNumericStats(self, features, feedback, field, count):
total = 100.0 / count if count else 0 total = 100.0 / count if count else 0

View File

@ -58,6 +58,10 @@ class Boundary(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Boundary'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Boundary')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_LAYER, self.tr("Boundaries"))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_LAYER, self.tr("Boundaries")))
self.source = None
self.sink = None
self.dest_id = None
def icon(self): def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'convex_hull.png')) return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'convex_hull.png'))
@ -70,10 +74,11 @@ class Boundary(QgisAlgorithm):
def displayName(self): def displayName(self):
return self.tr('Boundary') return self.tr('Boundary')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
input_wkb = source.wkbType() input_wkb = self.source.wkbType()
output_wkb = None
if QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.LineGeometry: if QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.LineGeometry:
output_wkb = QgsWkbTypes.MultiPoint output_wkb = QgsWkbTypes.MultiPoint
elif QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.PolygonGeometry: elif QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.PolygonGeometry:
@ -83,11 +88,13 @@ class Boundary(QgisAlgorithm):
if QgsWkbTypes.hasM(input_wkb): if QgsWkbTypes.hasM(input_wkb):
output_wkb = QgsWkbTypes.addM(output_wkb) output_wkb = QgsWkbTypes.addM(output_wkb)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
source.fields(), output_wkb, source.sourceCrs()) self.source.fields(), output_wkb, self.source.sourceCrs())
return True
features = source.getFeatures() def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, input_feature in enumerate(features): for current, input_feature in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
@ -102,7 +109,9 @@ class Boundary(QgisAlgorithm):
output_feature.setGeometry(output_geometry) output_feature.setGeometry(output_geometry)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert) self.sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT_LAYER: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT_LAYER: self.dest_id}

View File

@ -66,20 +66,26 @@ class BoundingBox(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Bounds'), QgsProcessingParameterDefinition.TypeVectorPolygon)) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Bounds'), QgsProcessingParameterDefinition.TypeVectorPolygon))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_LAYER, self.tr("Bounds"))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_LAYER, self.tr("Bounds")))
self.source = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'boundingboxes' return 'boundingboxes'
def displayName(self): def displayName(self):
return self.tr('Bounding boxes') return self.tr('Bounding boxes')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
source.fields(), QgsWkbTypes.Polygon, source.sourceCrs()) self.source.fields(), QgsWkbTypes.Polygon, self.source.sourceCrs())
return True
features = source.getFeatures() def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, input_feature in enumerate(features): for current, input_feature in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
@ -94,7 +100,9 @@ class BoundingBox(QgisAlgorithm):
output_feature.setGeometry(output_geometry) output_feature.setGeometry(output_geometry)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert) self.sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT_LAYER: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT_LAYER: self.dest_id}

View File

@ -93,46 +93,60 @@ class CheckValidity(QgisAlgorithm):
self.addOutput(QgsProcessingOutputVectorLayer(self.ERROR_OUTPUT, self.tr('Error output'))) self.addOutput(QgsProcessingOutputVectorLayer(self.ERROR_OUTPUT, self.tr('Error output')))
self.addOutput(QgsProcessingOutputNumber(self.ERROR_COUNT, self.tr('Count of errors'))) self.addOutput(QgsProcessingOutputNumber(self.ERROR_COUNT, self.tr('Count of errors')))
self.method = None
self.source = None
self.valid_output_sink = None
self.valid_output_dest_id = None
self.valid_count = 0
self.invalid_output_sink = None
self.invalid_output_dest_id = None
self.invalid_count = 0
self.error_output_sink = None
self.error_output_dest_id = None
self.error_count = 0
def name(self): def name(self):
return 'checkvalidity' return 'checkvalidity'
def displayName(self): def displayName(self):
return self.tr('Check validity') return self.tr('Check validity')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
method_param = self.parameterAsEnum(parameters, self.METHOD, context) method_param = self.parameterAsEnum(parameters, self.METHOD, context)
if method_param == 0: if method_param == 0:
settings = QgsSettings() settings = QgsSettings()
method = int(settings.value(settings_method_key, 0)) - 1 self.method = int(settings.value(settings_method_key, 0)) - 1
if method < 0: if self.method < 0:
method = 0 self.method = 0
else: else:
method = method_param - 1 self.method = method_param - 1
results = self.doCheck(method, parameters, context, feedback) self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
return results
def doCheck(self, method, parameters, context, feedback): (self.valid_output_sink, self.valid_output_dest_id) = self.parameterAsSink(parameters, self.VALID_OUTPUT,
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) context,
self.source.fields(),
self.source.wkbType(),
self.source.sourceCrs())
(valid_output_sink, valid_output_dest_id) = self.parameterAsSink(parameters, self.VALID_OUTPUT, context, invalid_fields = self.source.fields()
source.fields(), source.wkbType(), source.sourceCrs())
valid_count = 0
invalid_fields = source.fields()
invalid_fields.append(QgsField('_errors', QVariant.String, 'string', 255)) invalid_fields.append(QgsField('_errors', QVariant.String, 'string', 255))
(invalid_output_sink, invalid_output_dest_id) = self.parameterAsSink(parameters, self.INVALID_OUTPUT, context, (self.invalid_output_sink, self.invalid_output_dest_id) = self.parameterAsSink(parameters, self.INVALID_OUTPUT,
invalid_fields, source.wkbType(), source.sourceCrs()) context,
invalid_count = 0 invalid_fields,
self.source.wkbType(),
self.source.sourceCrs())
error_fields = QgsFields() error_fields = QgsFields()
error_fields.append(QgsField('message', QVariant.String, 'string', 255)) error_fields.append(QgsField('message', QVariant.String, 'string', 255))
(error_output_sink, error_output_dest_id) = self.parameterAsSink(parameters, self.ERROR_OUTPUT, context, (self.error_output_sink, self.error_output_dest_id) = self.parameterAsSink(parameters, self.ERROR_OUTPUT, context,
error_fields, QgsWkbTypes.Point, source.sourceCrs()) error_fields, QgsWkbTypes.Point,
error_count = 0 self.source.sourceCrs())
return True
features = source.getFeatures(QgsFeatureRequest(), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures(QgsFeatureRequest(), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks)
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, inFeat in enumerate(features): for current, inFeat in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
@ -141,10 +155,10 @@ class CheckValidity(QgisAlgorithm):
valid = True valid = True
if not geom.isNull() and not geom.isEmpty(): if not geom.isNull() and not geom.isEmpty():
errors = list(geom.validateGeometry(method)) errors = list(geom.validateGeometry(self.method))
if errors: if errors:
# QGIS method return a summary at the end # QGIS method return a summary at the end
if method == 1: if self.method == 1:
errors.pop() errors.pop()
valid = False valid = False
reasons = [] reasons = []
@ -153,9 +167,9 @@ class CheckValidity(QgisAlgorithm):
error_geom = QgsGeometry.fromPoint(error.where()) error_geom = QgsGeometry.fromPoint(error.where())
errFeat.setGeometry(error_geom) errFeat.setGeometry(error_geom)
errFeat.setAttributes([error.what()]) errFeat.setAttributes([error.what()])
if error_output_sink: if self.error_output_sink:
error_output_sink.addFeature(errFeat, QgsFeatureSink.FastInsert) self.error_output_sink.addFeature(errFeat, QgsFeatureSink.FastInsert)
error_count += 1 self.error_count += 1
reasons.append(error.what()) reasons.append(error.what())
@ -169,26 +183,28 @@ class CheckValidity(QgisAlgorithm):
outFeat.setAttributes(attrs) outFeat.setAttributes(attrs)
if valid: if valid:
if valid_output_sink: if self.valid_output_sink:
valid_output_sink.addFeature(outFeat, QgsFeatureSink.FastInsert) self.valid_output_sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
valid_count += 1 self.valid_count += 1
else: else:
if invalid_output_sink: if self.invalid_output_sink:
invalid_output_sink.addFeature(outFeat, QgsFeatureSink.FastInsert) self.invalid_output_sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
invalid_count += 1 self.invalid_count += 1
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
def postProcessAlgorithm(self, context, feedback):
results = { results = {
self.VALID_COUNT: valid_count, self.VALID_COUNT: self.valid_count,
self.INVALID_COUNT: invalid_count, self.INVALID_COUNT: self.invalid_count,
self.ERROR_COUNT: error_count self.ERROR_COUNT: self.error_count
} }
if valid_output_sink: if self.valid_output_sink:
results[self.VALID_OUTPUT] = valid_output_dest_id results[self.VALID_OUTPUT] = self.valid_output_dest_id
if invalid_output_sink: if self.invalid_output_sink:
results[self.INVALID_OUTPUT] = invalid_output_dest_id results[self.INVALID_OUTPUT] = self.invalid_output_dest_id
if error_output_sink: if self.error_output_sink:
results[self.ERROR_OUTPUT] = error_output_dest_id results[self.ERROR_OUTPUT] = self.error_output_dest_id
return results return results

View File

@ -53,27 +53,35 @@ class CreateAttributeIndex(QgisAlgorithm):
self.tr('Attribute to index'), None, self.INPUT)) self.tr('Attribute to index'), None, self.INPUT))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Indexed layer'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Indexed layer')))
self.layer = None
self.field = None
def name(self): def name(self):
return 'createattributeindex' return 'createattributeindex'
def displayName(self): def displayName(self):
return self.tr('Create attribute index') return self.tr('Create attribute index')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) self.layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
field = self.parameterAsString(parameters, self.FIELD, context) self.field = self.parameterAsString(parameters, self.FIELD, context)
provider = layer.dataProvider() return True
field_index = layer.fields().lookupField(field) def processAlgorithm(self, context, feedback):
if field_index < 0 or layer.fields().fieldOrigin(field_index) != QgsFields.OriginProvider: provider = self.layer.dataProvider()
feedback.pushInfo(self.tr('Can not create attribute index on "{}"').format(field))
field_index = self.layer.fields().lookupField(self.field)
if field_index < 0 or self.layer.fields().fieldOrigin(field_index) != QgsFields.OriginProvider:
feedback.pushInfo(self.tr('Can not create attribute index on "{}"').format(self.field))
else: else:
provider_index = layer.fields().fieldOriginIndex(field_index) provider_index = self.layer.fields().fieldOriginIndex(field_index)
if provider.capabilities() & QgsVectorDataProvider.CreateAttributeIndex: if provider.capabilities() & QgsVectorDataProvider.CreateAttributeIndex:
if not provider.createAttributeIndex(provider_index): if not provider.createAttributeIndex(provider_index):
feedback.pushInfo(self.tr('Could not create attribute index')) feedback.pushInfo(self.tr('Could not create attribute index'))
else: else:
feedback.pushInfo(self.tr("Layer's data provider does not support " feedback.pushInfo(self.tr("Layer's data provider does not support "
"creating attribute indexes")) "creating attribute indexes"))
return True
return {self.OUTPUT: layer.id()} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.layer.id()}

View File

@ -58,46 +58,56 @@ class DeleteColumn(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Output layer'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Output layer')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Output layer"))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Output layer")))
self.source = None
self.fields_to_delete = None
self.sink = None
self.dest_id = None
self.field_indices = []
def name(self): def name(self):
return 'deletecolumn' return 'deletecolumn'
def displayName(self): def displayName(self):
return self.tr('Drop field(s)') return self.tr('Drop field(s)')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
fields_to_delete = self.parameterAsFields(parameters, self.COLUMNS, context) self.fields_to_delete = self.parameterAsFields(parameters, self.COLUMNS, context)
fields = source.fields() fields = self.source.fields()
field_indices = []
# loop through twice - first we need to build up a list of original attribute indices # loop through twice - first we need to build up a list of original attribute indices
for f in fields_to_delete: for f in self.fields_to_delete:
index = fields.lookupField(f) index = fields.lookupField(f)
field_indices.append(index) self.field_indices.append(index)
# important - make sure we remove from the end so we aren't changing used indices as we go # important - make sure we remove from the end so we aren't changing used indices as we go
field_indices.sort(reverse=True) self.field_indices.sort(reverse=True)
# this second time we make a cleaned version of the fields # this second time we make a cleaned version of the fields
for index in field_indices: for index in self.field_indices:
fields.remove(index) fields.remove(index)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, source.wkbType(), source.sourceCrs()) fields, self.source.wkbType(), self.source.sourceCrs())
return True
features = source.getFeatures() def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, f in enumerate(features): for current, f in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
attributes = f.attributes() attributes = f.attributes()
for index in field_indices: for index in self.field_indices:
del attributes[index] del attributes[index]
f.setAttributes(attributes) f.setAttributes(attributes)
sink.addFeature(f, QgsFeatureSink.FastInsert) self.sink.addFeature(f, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return {self.OUTPUT: dest_id} return True
def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -58,31 +58,40 @@ class DeleteHoles(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Cleaned'), QgsProcessingParameterDefinition.TypeVectorPolygon)) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Cleaned'), QgsProcessingParameterDefinition.TypeVectorPolygon))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Cleaned'), QgsProcessingParameterDefinition.TypeVectorPolygon)) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Cleaned'), QgsProcessingParameterDefinition.TypeVectorPolygon))
self.source = None
self.min_area = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'deleteholes' return 'deleteholes'
def displayName(self): def displayName(self):
return self.tr('Delete holes') return self.tr('Delete holes')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
min_area = self.parameterAsDouble(parameters, self.MIN_AREA, context) self.min_area = self.parameterAsDouble(parameters, self.MIN_AREA, context)
if min_area == 0.0: if self.min_area == 0.0:
min_area = -1.0 self.min_area = -1.0
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), source.wkbType(), source.sourceCrs()) self.source.fields(), self.source.wkbType(), self.source.sourceCrs())
return True
features = source.getFeatures() def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, f in enumerate(features): for current, f in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
if f.hasGeometry(): if f.hasGeometry():
f.setGeometry(f.geometry().removeInteriorRings(min_area)) f.setGeometry(f.geometry().removeInteriorRings(self.min_area))
sink.addFeature(f, QgsFeatureSink.FastInsert) self.sink.addFeature(f, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -63,21 +63,28 @@ class DensifyGeometries(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Densified'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Densified')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Densified'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Densified')))
self.source = None
self.sink = None
self.vertices = None
self.dest_id = None
def name(self): def name(self):
return 'densifygeometries' return 'densifygeometries'
def displayName(self): def displayName(self):
return self.tr('Densify geometries') return self.tr('Densify geometries')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
vertices = self.parameterAsInt(parameters, self.VERTICES, context) self.vertices = self.parameterAsInt(parameters, self.VERTICES, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), source.wkbType(), source.sourceCrs()) self.source.fields(), self.source.wkbType(), self.source.sourceCrs())
return True
features = source.getFeatures() def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, f in enumerate(features): for current, f in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
@ -85,9 +92,11 @@ class DensifyGeometries(QgisAlgorithm):
feature = f feature = f
if feature.hasGeometry(): if feature.hasGeometry():
new_geometry = feature.geometry().densifyByCount(vertices) new_geometry = feature.geometry().densifyByCount(self.vertices)
feature.setGeometry(new_geometry) feature.setGeometry(new_geometry)
sink.addFeature(feature, QgsFeatureSink.FastInsert) self.sink.addFeature(feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -61,31 +61,40 @@ class DensifyGeometriesInterval(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Densified'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Densified')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Densified'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Densified')))
self.source = None
self.interval = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'densifygeometriesgivenaninterval' return 'densifygeometriesgivenaninterval'
def displayName(self): def displayName(self):
return self.tr('Densify geometries given an interval') return self.tr('Densify geometries given an interval')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
interval = self.parameterAsDouble(parameters, self.INTERVAL, context) self.interval = self.parameterAsDouble(parameters, self.INTERVAL, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), source.wkbType(), source.sourceCrs()) self.source.fields(), self.source.wkbType(), self.source.sourceCrs())
return True
features = source.getFeatures() def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, f in enumerate(features): for current, f in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
feature = f feature = f
if feature.hasGeometry(): if feature.hasGeometry():
new_geometry = feature.geometry().densifyByDistance(float(interval)) new_geometry = feature.geometry().densifyByDistance(float(self.interval))
feature.setGeometry(new_geometry) feature.setGeometry(new_geometry)
sink.addFeature(feature, QgsFeatureSink.FastInsert) self.sink.addFeature(feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -39,8 +39,8 @@ from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
class DropGeometry(QgisAlgorithm): class DropGeometry(QgisAlgorithm):
INPUT_LAYER = 'INPUT_LAYER' INPUT = 'INPUT'
OUTPUT_TABLE = 'OUTPUT_TABLE' OUTPUT = 'OUTPUT'
def tags(self): def tags(self):
return self.tr('remove,drop,delete,geometry,objects').split(',') return self.tr('remove,drop,delete,geometry,objects').split(',')
@ -51,9 +51,13 @@ class DropGeometry(QgisAlgorithm):
def __init__(self): def __init__(self):
super().__init__() super().__init__()
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_LAYER, self.tr('Input layer'), [QgsProcessingParameterDefinition.TypeVectorPoint, QgsProcessingParameterDefinition.TypeVectorLine, QgsProcessingParameterDefinition.TypeVectorPolygon])) self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer'), [QgsProcessingParameterDefinition.TypeVectorPoint, QgsProcessingParameterDefinition.TypeVectorLine, QgsProcessingParameterDefinition.TypeVectorPolygon]))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_TABLE, self.tr('Dropped geometry'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Dropped geometry')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_TABLE, self.tr("Dropped geometry"))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Dropped geometry")))
self.source = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'dropgeometries' return 'dropgeometries'
@ -61,21 +65,26 @@ class DropGeometry(QgisAlgorithm):
def displayName(self): def displayName(self):
return self.tr('Drop geometries') return self.tr('Drop geometries')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_TABLE, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) self.source.fields(), QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem())
return True
def processAlgorithm(self, context, feedback):
request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry) request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry)
features = source.getFeatures(request) features = self.source.getFeatures(request)
total = 100.0 / source.featureCount() if source.featureCount() else 0 total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, input_feature in enumerate(features): for current, input_feature in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
input_feature.clearGeometry() input_feature.clearGeometry()
sink.addFeature(input_feature, QgsFeatureSink.FastInsert) self.sink.addFeature(input_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return {self.OUTPUT_TABLE: dest_id} return True
def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -79,15 +79,20 @@ class ExtentFromLayer(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extent'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extent')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Extent"), QgsProcessingParameterDefinition.TypeVectorPolygon)) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Extent"), QgsProcessingParameterDefinition.TypeVectorPolygon))
self.source = None
self.byFeature = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'polygonfromlayerextent' return 'polygonfromlayerextent'
def displayName(self): def displayName(self):
return self.tr('Polygon from layer extent') return self.tr('Polygon from layer extent')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) self.source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
byFeature = self.parameterAsBool(parameters, self.BY_FEATURE, context) self.byFeature = self.parameterAsBool(parameters, self.BY_FEATURE, context)
fields = QgsFields() fields = QgsFields()
fields.append(QgsField('MINX', QVariant.Double)) fields.append(QgsField('MINX', QVariant.Double))
@ -101,15 +106,19 @@ class ExtentFromLayer(QgisAlgorithm):
fields.append(QgsField('HEIGHT', QVariant.Double)) fields.append(QgsField('HEIGHT', QVariant.Double))
fields.append(QgsField('WIDTH', QVariant.Double)) fields.append(QgsField('WIDTH', QVariant.Double))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.Polygon, source.sourceCrs()) fields, QgsWkbTypes.Polygon, self.source.sourceCrs())
return True
if byFeature: def processAlgorithm(self, context, feedback):
self.featureExtent(source, context, sink, feedback) if self.byFeature:
self.featureExtent(self.source, context, self.sink, feedback)
else: else:
self.layerExtent(source, sink, feedback) self.layerExtent(self.source, self.sink, feedback)
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}
def layerExtent(self, source, sink, feedback): def layerExtent(self, source, sink, feedback):
rect = source.sourceExtent() rect = source.sourceExtent()

View File

@ -55,20 +55,26 @@ class FixGeometry(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Fixed geometries'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Fixed geometries')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Fixed geometries"))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Fixed geometries")))
self.source = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'fixgeometries' return 'fixgeometries'
def displayName(self): def displayName(self):
return self.tr('Fix geometries') return self.tr('Fix geometries')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), QgsWkbTypes.multiType(source.wkbType()), source.sourceCrs()) self.source.fields(), QgsWkbTypes.multiType(self.source.wkbType()), self.source.sourceCrs())
return True
features = source.getFeatures(QgsFeatureRequest(), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks) def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures(QgsFeatureRequest(), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks)
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, inputFeature in enumerate(features): for current, inputFeature in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
@ -86,7 +92,7 @@ class FixGeometry(QgisAlgorithm):
try: try:
g.convertToMultiType() g.convertToMultiType()
outputFeature.setGeometry(QgsGeometry(g)) outputFeature.setGeometry(QgsGeometry(g))
sink.addFeature(outputFeature, QgsFeatureSink.FastInsert) self.sink.addFeature(outputFeature, QgsFeatureSink.FastInsert)
except: except:
pass pass
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
@ -95,7 +101,9 @@ class FixGeometry(QgisAlgorithm):
outputGeometry.convertToMultiType() outputGeometry.convertToMultiType()
outputFeature.setGeometry(outputGeometry) outputFeature.setGeometry(outputGeometry)
sink.addFeature(outputFeature, QgsFeatureSink.FastInsert) self.sink.addFeature(outputFeature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -102,41 +102,52 @@ class GridPolygon(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Grid'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Grid')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Grid'), QgsProcessingParameterDefinition.TypeVectorPolygon)) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Grid'), QgsProcessingParameterDefinition.TypeVectorPolygon))
self.idx = None
self.hSpacing = None
self.vSpacing = None
self.hOverlay = None
self.vOverlay = None
self.width = None
self.height = None
self.originX = None
self.originY = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'creategridpolygon' return 'creategridpolygon'
def displayName(self): def displayName(self):
return self.tr('Create grid (polygon)') return self.tr('Create grid (polygon)')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
idx = self.parameterAsEnum(parameters, self.TYPE, context) self.idx = self.parameterAsEnum(parameters, self.TYPE, context)
hSpacing = self.parameterAsDouble(parameters, self.HSPACING, context) self.hSpacing = self.parameterAsDouble(parameters, self.HSPACING, context)
vSpacing = self.parameterAsDouble(parameters, self.VSPACING, context) self.vSpacing = self.parameterAsDouble(parameters, self.VSPACING, context)
hOverlay = self.parameterAsDouble(parameters, self.HOVERLAY, context) self.hOverlay = self.parameterAsDouble(parameters, self.HOVERLAY, context)
vOverlay = self.parameterAsDouble(parameters, self.VOVERLAY, context) self.vOverlay = self.parameterAsDouble(parameters, self.VOVERLAY, context)
bbox = self.parameterAsExtent(parameters, self.EXTENT, context) bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
crs = self.parameterAsCrs(parameters, self.CRS, context) crs = self.parameterAsCrs(parameters, self.CRS, context)
self.width = bbox.width()
self.height = bbox.height()
self.originX = bbox.xMinimum()
self.originY = bbox.yMaximum()
width = bbox.width() if self.hSpacing <= 0 or self.vSpacing <= 0:
height = bbox.height()
originX = bbox.xMinimum()
originY = bbox.yMaximum()
if hSpacing <= 0 or vSpacing <= 0:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Invalid grid spacing: {0}/{1}').format(hSpacing, vSpacing)) self.tr('Invalid grid spacing: {0}/{1}').format(self.hSpacing, self.vSpacing))
if width < hSpacing: if self.width < self.hSpacing:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Horizontal spacing is too small for the covered area')) self.tr('Horizontal spacing is too small for the covered area'))
if hSpacing <= hOverlay or vSpacing <= vOverlay: if self.hSpacing <= self.hOverlay or self.vSpacing <= self.vOverlay:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Invalid overlay: {0}/{1}').format(hOverlay, vOverlay)) self.tr('Invalid overlay: {0}/{1}').format(self.hOverlay, self.vOverlay))
if height < vSpacing: if self.height < self.vSpacing:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Vertical spacing is too small for the covered area')) self.tr('Vertical spacing is too small for the covered area'))
@ -147,20 +158,24 @@ class GridPolygon(QgisAlgorithm):
fields.append(QgsField('bottom', QVariant.Double, '', 24, 16)) fields.append(QgsField('bottom', QVariant.Double, '', 24, 16))
fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('id', QVariant.Int, '', 10, 0))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.Polygon, crs) fields, QgsWkbTypes.Polygon, crs)
return True
if idx == 0: def processAlgorithm(self, context, feedback):
if self.idx == 0:
self._rectangleGrid( self._rectangleGrid(
sink, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback) self.sink, self.width, self.height, self.originX, self.originY, self.hSpacing, self.vSpacing, self.hOverlay, self.vOverlay, feedback)
elif idx == 1: elif self.idx == 1:
self._diamondGrid( self._diamondGrid(
sink, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback) self.sink, self.width, self.height, self.originX, self.originY, self.hSpacing, self.vSpacing, self.hOverlay, self.vOverlay, feedback)
elif idx == 2: elif self.idx == 2:
self._hexagonGrid( self._hexagonGrid(
sink, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback) self.sink, self.width, self.height, self.originX, self.originY, self.hSpacing, self.vSpacing, self.hOverlay, self.vOverlay, feedback)
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}
def _rectangleGrid(self, sink, width, height, originX, originY, def _rectangleGrid(self, sink, width, height, originX, originY,
hSpacing, vSpacing, hOverlay, vOverlay, feedback): hSpacing, vSpacing, hOverlay, vOverlay, feedback):

View File

@ -27,9 +27,7 @@ __revision__ = '$Format:%H$'
from qgis.core import (QgsVectorLayerExporter, from qgis.core import (QgsVectorLayerExporter,
QgsSettings, QgsSettings,
QgsApplication,
QgsFeatureSink, QgsFeatureSink,
QgsProcessingUtils,
QgsProcessingParameterFeatureSource, QgsProcessingParameterFeatureSource,
QgsProcessingParameterString, QgsProcessingParameterString,
QgsProcessingParameterField, QgsProcessingParameterField,
@ -38,10 +36,6 @@ from qgis.core import (QgsVectorLayerExporter,
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterBoolean
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterString
from processing.core.parameters import ParameterTableField
from processing.tools import postgis from processing.tools import postgis
@ -112,70 +106,86 @@ class ImportIntoPostGIS(QgisAlgorithm):
self.addParameter(QgsProcessingParameterBoolean(self.FORCE_SINGLEPART, self.addParameter(QgsProcessingParameterBoolean(self.FORCE_SINGLEPART,
self.tr('Create single-part geometries instead of multi-part'), False)) self.tr('Create single-part geometries instead of multi-part'), False))
self.db = None
self.schema = None
self.overwrite = None
self.createIndex = None
self.convertLowerCase = None
self.dropStringLength = None
self.forceSinglePart = None
self.primaryKeyField = None
self.encoding = None
self.source = None
self.table = None
self.providerName = None
self.geomColumn = None
def name(self): def name(self):
return 'importintopostgis' return 'importintopostgis'
def displayName(self): def displayName(self):
return self.tr('Import into PostGIS') return self.tr('Import into PostGIS')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
connection = self.parameterAsString(parameters, self.DATABASE, context) connection = self.parameterAsString(parameters, self.DATABASE, context)
db = postgis.GeoDB.from_name(connection) self.db = postgis.GeoDB.from_name(connection)
schema = self.parameterAsString(parameters, self.SCHEMA, context) self.schema = self.parameterAsString(parameters, self.SCHEMA, context)
overwrite = self.parameterAsBool(parameters, self.OVERWRITE, context) self.overwrite = self.parameterAsBool(parameters, self.OVERWRITE, context)
createIndex = self.parameterAsBool(parameters, self.CREATEINDEX, context) self.createIndex = self.parameterAsBool(parameters, self.CREATEINDEX, context)
convertLowerCase = self.parameterAsBool(parameters, self.LOWERCASE_NAMES, context) self.convertLowerCase = self.parameterAsBool(parameters, self.LOWERCASE_NAMES, context)
dropStringLength = self.parameterAsBool(parameters, self.DROP_STRING_LENGTH, context) self.dropStringLength = self.parameterAsBool(parameters, self.DROP_STRING_LENGTH, context)
forceSinglePart = self.parameterAsBool(parameters, self.FORCE_SINGLEPART, context) self.forceSinglePart = self.parameterAsBool(parameters, self.FORCE_SINGLEPART, context)
primaryKeyField = self.parameterAsString(parameters, self.PRIMARY_KEY, context) or 'id' self.primaryKeyField = self.parameterAsString(parameters, self.PRIMARY_KEY, context) or 'id'
encoding = self.parameterAsString(parameters, self.ENCODING, context) self.encoding = self.parameterAsString(parameters, self.ENCODING, context)
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
table = self.parameterAsString(parameters, self.TABLENAME, context) self.table = self.parameterAsString(parameters, self.TABLENAME, context)
if table: if self.table:
table.strip() self.table.strip()
if not table or table == '': if not self.table or self.table == '':
table = source.sourceName() self.table = self.source.sourceName()
table = table.replace('.', '_') self.table = self.table.replace('.', '_')
table = table.replace(' ', '').lower()[0:62] self.table = self.table.replace(' ', '').lower()[0:62]
providerName = 'postgres' self.providerName = 'postgres'
geomColumn = self.parameterAsString(parameters, self.GEOMETRY_COLUMN, context) self.geomColumn = self.parameterAsString(parameters, self.GEOMETRY_COLUMN, context)
if not geomColumn: if not self.geomColumn:
geomColumn = 'geom' self.geomColumn = 'geom'
return True
def processAlgorithm(self, context, feedback):
options = {} options = {}
if overwrite: if self.overwrite:
options['overwrite'] = True options['overwrite'] = True
if convertLowerCase: if self.convertLowerCase:
options['lowercaseFieldNames'] = True options['lowercaseFieldNames'] = True
geomColumn = geomColumn.lower() self.geomColumn = self.geomColumn.lower()
if dropStringLength: if self.dropStringLength:
options['dropStringConstraints'] = True options['dropStringConstraints'] = True
if forceSinglePart: if self.forceSinglePart:
options['forceSinglePartGeometryType'] = True options['forceSinglePartGeometryType'] = True
# Clear geometry column for non-geometry tables # Clear geometry column for non-geometry tables
if source.wkbType() == QgsWkbTypes.NoGeometry: if self.source.wkbType() == QgsWkbTypes.NoGeometry:
geomColumn = None self.geomColumn = None
uri = db.uri uri = self.db.uri
uri.setDataSource(schema, table, geomColumn, '', primaryKeyField) uri.setDataSource(self.schema, self.table, self.geomColumn, '', self.primaryKeyField)
if encoding: if self.encoding:
options['fileEncoding'] = encoding options['fileEncoding'] = self.encoding
exporter = QgsVectorLayerExporter(uri.uri(), providerName, source.fields(), exporter = QgsVectorLayerExporter(uri.uri(), self.providerName, self.source.fields(),
source.wkbType(), source.sourceCrs(), overwrite, options) self.source.wkbType(), self.source.sourceCrs(), self.overwrite, options)
if exporter.errorCode() != QgsVectorLayerExporter.NoError: if exporter.errorCode() != QgsVectorLayerExporter.NoError:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Error importing to PostGIS\n{0}').format(exporter.errorMessage())) self.tr('Error importing to PostGIS\n{0}').format(exporter.errorMessage()))
features = source.getFeatures() features = self.source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0 total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, f in enumerate(features): for current, f in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
@ -190,11 +200,13 @@ class ImportIntoPostGIS(QgisAlgorithm):
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Error importing to PostGIS\n{0}').format(exporter.errorMessage())) self.tr('Error importing to PostGIS\n{0}').format(exporter.errorMessage()))
if geomColumn and createIndex: if self.geomColumn and self.createIndex:
db.create_spatial_index(table, schema, geomColumn) self.db.create_spatial_index(self.table, self.schema, self.geomColumn)
db.vacuum_analyze(table, schema) self.db.vacuum_analyze(self.table, self.schema)
return True
def postProcessAlgorithm(self, context, feedback):
return {} return {}
def dbConnectionNames(self): def dbConnectionNames(self):

View File

@ -28,8 +28,6 @@ __revision__ = '$Format:%H$'
from qgis.core import (QgsDataSourceUri, from qgis.core import (QgsDataSourceUri,
QgsFeatureSink, QgsFeatureSink,
QgsVectorLayerExporter, QgsVectorLayerExporter,
QgsApplication,
QgsProcessingUtils,
QgsProcessingParameterFeatureSource, QgsProcessingParameterFeatureSource,
QgsProcessingParameterVectorLayer, QgsProcessingParameterVectorLayer,
QgsProcessingParameterField, QgsProcessingParameterField,
@ -73,13 +71,26 @@ class ImportIntoSpatialite(QgisAlgorithm):
self.addParameter(QgsProcessingParameterBoolean(self.DROP_STRING_LENGTH, self.tr('Drop length constraints on character fields'), False)) self.addParameter(QgsProcessingParameterBoolean(self.DROP_STRING_LENGTH, self.tr('Drop length constraints on character fields'), False))
self.addParameter(QgsProcessingParameterBoolean(self.FORCE_SINGLEPART, self.tr('Create single-part geometries instead of multi-part'), False)) self.addParameter(QgsProcessingParameterBoolean(self.FORCE_SINGLEPART, self.tr('Create single-part geometries instead of multi-part'), False))
self.db = None
self.overwrite = None
self.createIndex = None
self.dropStringLength = None
self.convertLowerCase = None
self.forceSinglePart = None
self.primaryKeyField = None
self.encoding = None
self.source = None
self.table = None
self.providerName = None
self.geomColumn = None
def name(self): def name(self):
return 'importintospatialite' return 'importintospatialite'
def displayName(self): def displayName(self):
return self.tr('Import into Spatialite') return self.tr('Import into Spatialite')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
database = self.parameterAsVectorLayer(parameters, self.DATABASE, context) database = self.parameterAsVectorLayer(parameters, self.DATABASE, context)
databaseuri = database.dataProvider().dataSourceUri() databaseuri = database.dataProvider().dataSourceUri()
uri = QgsDataSourceUri(databaseuri) uri = QgsDataSourceUri(databaseuri)
@ -87,61 +98,64 @@ class ImportIntoSpatialite(QgisAlgorithm):
if '|layerid' in databaseuri: if '|layerid' in databaseuri:
databaseuri = databaseuri[:databaseuri.find('|layerid')] databaseuri = databaseuri[:databaseuri.find('|layerid')]
uri = QgsDataSourceUri('dbname=\'%s\'' % (databaseuri)) uri = QgsDataSourceUri('dbname=\'%s\'' % (databaseuri))
db = spatialite.GeoDB(uri) self.db = spatialite.GeoDB(uri)
overwrite = self.parameterAsBool(parameters, self.OVERWRITE, context) self.overwrite = self.parameterAsBool(parameters, self.OVERWRITE, context)
createIndex = self.parameterAsBool(parameters, self.CREATEINDEX, context) self.createIndex = self.parameterAsBool(parameters, self.CREATEINDEX, context)
convertLowerCase = self.parameterAsBool(parameters, self.LOWERCASE_NAMES, context) self.convertLowerCase = self.parameterAsBool(parameters, self.LOWERCASE_NAMES, context)
dropStringLength = self.parameterAsBool(parameters, self.DROP_STRING_LENGTH, context) self.dropStringLength = self.parameterAsBool(parameters, self.DROP_STRING_LENGTH, context)
forceSinglePart = self.parameterAsBool(parameters, self.FORCE_SINGLEPART, context) self.forceSinglePart = self.parameterAsBool(parameters, self.FORCE_SINGLEPART, context)
primaryKeyField = self.parameterAsString(parameters, self.PRIMARY_KEY, context) or 'id' self.primaryKeyField = self.parameterAsString(parameters, self.PRIMARY_KEY, context) or 'id'
encoding = self.parameterAsString(parameters, self.ENCODING, context) self.encoding = self.parameterAsString(parameters, self.ENCODING, context)
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
table = self.parameterAsString(parameters, self.TABLENAME, context) self.table = self.parameterAsString(parameters, self.TABLENAME, context)
if table: if self.table:
table.strip() self.table.strip()
if not table or table == '': if not self.table or self.table == '':
table = source.sourceName() self.table = self.source.sourceName()
table = table.replace('.', '_') self.table = self.table.replace('.', '_')
table = table.replace(' ', '').lower() self.table = self.table.replace(' ', '').lower()
providerName = 'spatialite' self.providerName = 'spatialite'
geomColumn = self.parameterAsString(parameters, self.GEOMETRY_COLUMN, context) self.geomColumn = self.parameterAsString(parameters, self.GEOMETRY_COLUMN, context)
if not geomColumn: if not self.geomColumn:
geomColumn = 'geom' self.geomColumn = 'geom'
return True
def processAlgorithm(self, context, feedback):
options = {} options = {}
if overwrite: if self.overwrite:
options['overwrite'] = True options['overwrite'] = True
if convertLowerCase: if self.convertLowerCase:
options['lowercaseFieldNames'] = True options['lowercaseFieldNames'] = True
geomColumn = geomColumn.lower() self.geomColumn = self.geomColumn.lower()
if dropStringLength: if self.dropStringLength:
options['dropStringConstraints'] = True options['dropStringConstraints'] = True
if forceSinglePart: if self.forceSinglePart:
options['forceSinglePartGeometryType'] = True options['forceSinglePartGeometryType'] = True
# Clear geometry column for non-geometry tables # Clear geometry column for non-geometry tables
if source.wkbType() == QgsWkbTypes.NoGeometry: if self.source.wkbType() == QgsWkbTypes.NoGeometry:
geomColumn = None self.geomColumn = None
uri = db.uri uri = self.db.uri
uri.setDataSource('', table, geomColumn, '', primaryKeyField) uri.setDataSource('', self.table, self.geomColumn, '', self.primaryKeyField)
if encoding: if self.encoding:
options['fileEncoding'] = encoding options['fileEncoding'] = self.encoding
exporter = QgsVectorLayerExporter(uri.uri(), providerName, source.fields(), exporter = QgsVectorLayerExporter(uri.uri(), self.providerName, self.source.fields(),
source.wkbType(), source.sourceCrs(), overwrite, options) self.source.wkbType(), self.source.sourceCrs(), self.overwrite, options)
if exporter.errorCode() != QgsVectorLayerExporter.NoError: if exporter.errorCode() != QgsVectorLayerExporter.NoError:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Error importing to Spatialite\n{0}').format(exporter.errorMessage())) self.tr('Error importing to Spatialite\n{0}').format(exporter.errorMessage()))
features = source.getFeatures() features = self.source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0 total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, f in enumerate(features): for current, f in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
@ -156,7 +170,9 @@ class ImportIntoSpatialite(QgisAlgorithm):
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Error importing to Spatialite\n{0}').format(exporter.errorMessage())) self.tr('Error importing to Spatialite\n{0}').format(exporter.errorMessage()))
if geomColumn and createIndex: if self.geomColumn and self.createIndex:
db.create_spatial_index(table, geomColumn) self.db.create_spatial_index(self.table, self.geomColumn)
return True
def postProcessAlgorithm(self, context, feedback):
return {} return {}

View File

@ -68,19 +68,26 @@ class Merge(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Merged'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Merged')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Merged'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Merged')))
self.input_layers = []
self.fields = None
self.add_layer_field = None
self.add_path_field = None
self.sink = None
self.dest_id = None
self.dest_crs = None
def name(self): def name(self):
return 'mergevectorlayers' return 'mergevectorlayers'
def displayName(self): def displayName(self):
return self.tr('Merge vector layers') return self.tr('Merge vector layers')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
input_layers = self.parameterAsLayerList(parameters, self.LAYERS, context) self.input_layers = self.parameterAsLayerList(parameters, self.LAYERS, context)
layers = [] layers = []
fields = QgsFields() self.fields = QgsFields()
totalFeatureCount = 0 totalFeatureCount = 0
for layer in input_layers: for layer in self.input_layers:
if layer.type() != QgsMapLayer.VectorLayer: if layer.type() != QgsMapLayer.VectorLayer:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('All layers must be vector layers!')) self.tr('All layers must be vector layers!'))
@ -95,7 +102,7 @@ class Merge(QgisAlgorithm):
for sindex, sfield in enumerate(layer.fields()): for sindex, sfield in enumerate(layer.fields()):
found = None found = None
for dfield in fields: for dfield in self.fields:
if (dfield.name().upper() == sfield.name().upper()): if (dfield.name().upper() == sfield.name().upper()):
found = dfield found = dfield
if (dfield.type() != sfield.type()): if (dfield.type() != sfield.type()):
@ -104,35 +111,38 @@ class Merge(QgisAlgorithm):
'data type than in other layers.'.format(sfield.name(), layerSource))) 'data type than in other layers.'.format(sfield.name(), layerSource)))
if not found: if not found:
fields.append(sfield) self.fields.append(sfield)
add_layer_field = False self.add_layer_field = False
if fields.lookupField('layer') < 0: if self.fields.lookupField('layer') < 0:
fields.append(QgsField('layer', QVariant.String, '', 100)) self.fields.append(QgsField('layer', QVariant.String, '', 100))
add_layer_field = True self.add_layer_field = True
add_path_field = False self.add_path_field = False
if fields.lookupField('path') < 0: if self.fields.lookupField('path') < 0:
fields.append(QgsField('path', QVariant.String, '', 200)) self.fields.append(QgsField('path', QVariant.String, '', 200))
add_path_field = True self.add_path_field = True
total = 100.0 / totalFeatureCount if totalFeatureCount else 1 total = 100.0 / totalFeatureCount if totalFeatureCount else 1
dest_crs = layers[0].crs() self.dest_crs = layers[0].crs()
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, layers[0].wkbType(), dest_crs) self.fields, layers[0].wkbType(), self.dest_crs)
return True
def processAlgorithm(self, context, feedback):
featureCount = 0 featureCount = 0
for layer in layers: for layer in self.layers:
for feature in layer.getFeatures(QgsFeatureRequest().setDestinationCrs(dest_crs)): for feature in layer.getFeatures(QgsFeatureRequest().setDestinationCrs(self.dest_crs)):
if feedback.isCanceled(): if feedback.isCanceled():
break break
sattributes = feature.attributes() sattributes = feature.attributes()
dattributes = [] dattributes = []
for dindex, dfield in enumerate(fields): for dindex, dfield in enumerate(self.fields):
if add_layer_field and dfield.name() == 'layer': if self.add_layer_field and dfield.name() == 'layer':
dattributes.append(layer.name()) dattributes.append(layer.name())
continue continue
if add_path_field and dfield.name() == 'path': if self.add_path_field and dfield.name() == 'path':
dattributes.append(layer.publicSource()) dattributes.append(layer.publicSource())
continue continue
@ -154,8 +164,10 @@ class Merge(QgisAlgorithm):
dattributes.append(dattribute) dattributes.append(dattribute)
feature.setAttributes(dattributes) feature.setAttributes(dattributes)
sink.addFeature(feature, QgsFeatureSink.FastInsert) self.sink.addFeature(feature, QgsFeatureSink.FastInsert)
featureCount += 1 featureCount += 1
feedback.setProgress(int(featureCount * total)) feedback.setProgress(int(featureCount * self.total))
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -53,20 +53,28 @@ class PostGISExecuteSQL(QgisAlgorithm):
self.addParameter(db_param) self.addParameter(db_param)
self.addParameter(QgsProcessingParameterString(self.SQL, self.tr('SQL query'))) self.addParameter(QgsProcessingParameterString(self.SQL, self.tr('SQL query')))
self.connection = None
self.sql = None
def name(self): def name(self):
return 'postgisexecutesql' return 'postgisexecutesql'
def displayName(self): def displayName(self):
return self.tr('PostGIS execute SQL') return self.tr('PostGIS execute SQL')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
connection = self.parameterAsString(parameters, self.DATABASE, context) self.connection = self.parameterAsString(parameters, self.DATABASE, context)
db = postgis.GeoDB.from_name(connection) self.sql = self.parameterAsString(parameters, self.SQL, context).replace('\n', ' ')
return True
sql = self.parameterAsString(parameters, self.SQL, context).replace('\n', ' ') def processAlgorithm(self, context, feedback):
db = postgis.GeoDB.from_name(self.connection)
try: try:
db._exec_sql_and_commit(str(sql)) db._exec_sql_and_commit(str(self.sql))
except postgis.DbError as e: except postgis.DbError as e:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Error executing SQL:\n{0}').format(str(e))) self.tr('Error executing SQL:\n{0}').format(str(e)))
return True
def postProcessAlgorithm(self, context, feedback):
return {} return {}

View File

@ -28,9 +28,7 @@ __revision__ = '$Format:%H$'
import random import random
from qgis.core import (QgsApplication, from qgis.core import (QgsFeatureSink,
QgsFeatureSink,
QgsProcessingUtils,
QgsProcessingParameterFeatureSource, QgsProcessingParameterFeatureSource,
QgsProcessingParameterEnum, QgsProcessingParameterEnum,
QgsProcessingParameterNumber, QgsProcessingParameterNumber,
@ -38,7 +36,6 @@ from qgis.core import (QgsApplication,
QgsProcessingOutputVectorLayer) QgsProcessingOutputVectorLayer)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.outputs import OutputVector
class RandomExtract(QgisAlgorithm): class RandomExtract(QgisAlgorithm):
@ -69,42 +66,54 @@ class RandomExtract(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extracted (random)'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extracted (random)')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Extracted (random)'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Extracted (random)')))
self.source = None
self.method = None
self.value = None
self.featureCount = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'randomextract' return 'randomextract'
def displayName(self): def displayName(self):
return self.tr('Random extract') return self.tr('Random extract')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
method = self.parameterAsEnum(parameters, self.METHOD, context) self.method = self.parameterAsEnum(parameters, self.METHOD, context)
self.value = self.parameterAsInt(parameters, self.NUMBER, context)
self.featureCount = self.source.featureCount()
features = source.getFeatures() if self.method == 0:
featureCount = source.featureCount() if self.value > self.featureCount:
value = self.parameterAsInt(parameters, self.NUMBER, context)
if method == 0:
if value > featureCount:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Selected number is greater than feature count. ' self.tr('Selected number is greater than feature count. '
'Choose a lower value and try again.')) 'Choose a lower value and try again.'))
else: else:
if value > 100: if self.value > 100:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr("Percentage can't be greater than 100. Set a " self.tr("Percentage can't be greater than 100. Set a "
"different value and try again.")) "different value and try again."))
value = int(round(value / 100.0000, 4) * featureCount) self.value = int(round(self.value / 100.0000, 4) * self.featureCount)
selran = random.sample(list(range(featureCount)), value) (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
self.source.fields(), self.source.wkbType(), self.source.sourceCrs())
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, return True
source.fields(), source.wkbType(), source.sourceCrs())
total = 100.0 / featureCount if featureCount else 1 def processAlgorithm(self, context, feedback):
selran = random.sample(list(range(self.featureCount)), self.value)
features = self.source.getFeatures()
total = 100.0 / self.featureCount if self.featureCount else 1
for i, feat in enumerate(features): for i, feat in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
if i in selran: if i in selran:
sink.addFeature(feat, QgsFeatureSink.FastInsert) self.sink.addFeature(feat, QgsFeatureSink.FastInsert)
feedback.setProgress(int(i * total)) feedback.setProgress(int(i * total))
return {self.OUTPUT: dest_id} return True
def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -74,38 +74,46 @@ class RandomExtractWithinSubsets(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extracted (random stratified)'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extracted (random stratified)')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Extracted (random stratified)'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Extracted (random stratified)')))
self.source = None
self.method = None
self.field = None
self.value = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'randomextractwithinsubsets' return 'randomextractwithinsubsets'
def displayName(self): def displayName(self):
return self.tr('Random extract within subsets') return self.tr('Random extract within subsets')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
method = self.parameterAsEnum(parameters, self.METHOD, context) self.method = self.parameterAsEnum(parameters, self.METHOD, context)
field = self.parameterAsString(parameters, self.FIELD, context) self.field = self.parameterAsString(parameters, self.FIELD, context)
self.value = self.parameterAsInt(parameters, self.NUMBER, context)
(self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
self.source.fields(), self.source.wkbType(), self.source.sourceCrs())
return True
index = source.fields().lookupField(field) def processAlgorithm(self, context, feedback):
index = self.source.fields().lookupField(self.field)
features = source.getFeatures() features = self.source.getFeatures()
featureCount = source.featureCount() featureCount = self.source.featureCount()
unique = source.uniqueValues(index) unique = self.source.uniqueValues(index)
value = self.parameterAsInt(parameters, self.NUMBER, context) if self.method == 0:
if method == 0: if self.value > featureCount:
if value > featureCount:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Selected number is greater that feature count. ' self.tr('Selected number is greater that feature count. '
'Choose lesser value and try again.')) 'Choose lesser value and try again.'))
else: else:
if value > 100: if self.value > 100:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr("Percentage can't be greater than 100. Set " self.tr("Percentage can't be greater than 100. Set "
"correct value and try again.")) "correct value and try again."))
value = value / 100.0 self.value = self.value / 100.0
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), source.wkbType(), source.sourceCrs())
selran = [] selran = []
total = 100.0 / (featureCount * len(unique)) if featureCount else 1 total = 100.0 / (featureCount * len(unique)) if featureCount else 1
@ -119,13 +127,17 @@ class RandomExtractWithinSubsets(QgisAlgorithm):
feedback.setProgress(int(i * total)) feedback.setProgress(int(i * total))
for subset in classes.values(): for subset in classes.values():
selValue = value if method != 1 else int(round(value * len(subset), 0)) selValue = self.value if self.method != 1 else int(round(self.value * len(subset), 0))
selran.extend(random.sample(subset, selValue)) selran.extend(random.sample(subset, selValue))
total = 100.0 / featureCount if featureCount else 1 total = 100.0 / featureCount if featureCount else 1
for (i, feat) in enumerate(selran): for (i, feat) in enumerate(selran):
if feedback.isCanceled(): if feedback.isCanceled():
break break
sink.addFeature(feat, QgsFeatureSink.FastInsert) self.sink.addFeature(feat, QgsFeatureSink.FastInsert)
feedback.setProgress(int(i * total)) feedback.setProgress(int(i * total))
return {self.OUTPUT: dest_id}
return True
def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -81,55 +81,66 @@ class RegularPoints(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Regular points'), QgsProcessingParameterDefinition.TypeVectorPoint)) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Regular points'), QgsProcessingParameterDefinition.TypeVectorPoint))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Regular points'), QgsProcessingParameterDefinition.TypeVectorPoint)) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Regular points'), QgsProcessingParameterDefinition.TypeVectorPoint))
self.extent = None
self.spacing = None
self.inset = None
self.randomize = None
self.isSpacing = None
self.fields = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'regularpoints' return 'regularpoints'
def displayName(self): def displayName(self):
return self.tr('Regular points') return self.tr('Regular points')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
extent = self.parameterAsExtent(parameters, self.EXTENT, context) self.extent = self.parameterAsExtent(parameters, self.EXTENT, context)
spacing = self.parameterAsDouble(parameters, self.SPACING, context) self.spacing = self.parameterAsDouble(parameters, self.SPACING, context)
inset = self.parameterAsDouble(parameters, self.INSET, context) self.inset = self.parameterAsDouble(parameters, self.INSET, context)
randomize = self.parameterAsBool(parameters, self.RANDOMIZE, context) self.randomize = self.parameterAsBool(parameters, self.RANDOMIZE, context)
isSpacing = self.parameterAsBool(parameters, self.IS_SPACING, context) self.isSpacing = self.parameterAsBool(parameters, self.IS_SPACING, context)
crs = self.parameterAsCrs(parameters, self.CRS, context) crs = self.parameterAsCrs(parameters, self.CRS, context)
fields = QgsFields() self.fields = QgsFields()
fields.append(QgsField('id', QVariant.Int, '', 10, 0)) self.fields.append(QgsField('id', QVariant.Int, '', 10, 0))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.Point, crs) self.fields, QgsWkbTypes.Point, crs)
return True
if randomize: def processAlgorithm(self, context, feedback):
if self.randomize:
seed() seed()
area = extent.width() * extent.height() area = self.extent.width() * self.extent.height()
if isSpacing: if self.isSpacing:
pSpacing = spacing pSpacing = self.spacing
else: else:
pSpacing = sqrt(area / spacing) pSpacing = sqrt(area / self.spacing)
f = QgsFeature() f = QgsFeature()
f.initAttributes(1) f.initAttributes(1)
f.setFields(fields) f.setFields(self.fields)
count = 0 count = 0
total = 100.0 / (area / pSpacing) total = 100.0 / (area / pSpacing)
y = extent.yMaximum() - inset y = self.extent.yMaximum() - self.inset
extent_geom = QgsGeometry.fromRect(extent) extent_geom = QgsGeometry.fromRect(self.extent)
extent_engine = QgsGeometry.createGeometryEngine(extent_geom.geometry()) extent_engine = QgsGeometry.createGeometryEngine(extent_geom.geometry())
extent_engine.prepareGeometry() extent_engine.prepareGeometry()
while y >= extent.yMinimum(): while y >= self.extent.yMinimum():
x = extent.xMinimum() + inset x = self.extent.xMinimum() + self.inset
while x <= extent.xMaximum(): while x <= self.extent.xMaximum():
if feedback.isCanceled(): if feedback.isCanceled():
break break
if randomize: if self.randomize:
geom = QgsGeometry().fromPoint(QgsPointXY( geom = QgsGeometry().fromPoint(QgsPointXY(
uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)), uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)),
uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0)))) uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0))))
@ -139,10 +150,13 @@ class RegularPoints(QgisAlgorithm):
if extent_engine.intersects(geom.geometry()): if extent_engine.intersects(geom.geometry()):
f.setAttribute('id', count) f.setAttribute('id', count)
f.setGeometry(geom) f.setGeometry(geom)
sink.addFeature(f, QgsFeatureSink.FastInsert) self.sink.addFeature(f, QgsFeatureSink.FastInsert)
x += pSpacing x += pSpacing
count += 1 count += 1
feedback.setProgress(int(count * total)) feedback.setProgress(int(count * total))
y = y - pSpacing y = y - pSpacing
return {self.OUTPUT: dest_id} return True
def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -26,12 +26,10 @@ __copyright__ = '(C) 2012, Victor Olaya'
__revision__ = '$Format:%H$' __revision__ = '$Format:%H$'
from qgis.core import (QgsFeatureSink, from qgis.core import (QgsFeatureSink,
QgsProcessingUtils,
QgsProcessingParameterVectorLayer, QgsProcessingParameterVectorLayer,
QgsProcessingParameterFeatureSink, QgsProcessingParameterFeatureSink,
QgsProcessingOutputVectorLayer) QgsProcessingOutputVectorLayer)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
class SaveSelectedFeatures(QgisAlgorithm): class SaveSelectedFeatures(QgisAlgorithm):
@ -49,27 +47,35 @@ class SaveSelectedFeatures(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Selection'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Selection')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Selection"))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Selection")))
self.vectorLayer = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'saveselectedfeatures' return 'saveselectedfeatures'
def displayName(self): def displayName(self):
return self.tr('Save selected features') return self.tr('Save selected features')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
vectorLayer = self.parameterAsVectorLayer(parameters, self.INPUT, context) self.vectorLayer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
vectorLayer.fields(), vectorLayer.wkbType(), vectorLayer.sourceCrs()) self.vectorLayer.fields(), self.vectorLayer.wkbType(), self.vectorLayer.sourceCrs())
return True
features = vectorLayer.getSelectedFeatures() def processAlgorithm(self, context, feedback):
count = int(vectorLayer.selectedFeatureCount()) features = self.vectorLayer.getSelectedFeatures()
count = int(self.vectorLayer.selectedFeatureCount())
total = 100.0 / count if count else 1 total = 100.0 / count if count else 1
for current, feat in enumerate(features): for current, feat in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
sink.addFeature(feat, QgsFeatureSink.FastInsert) self.sink.addFeature(feat, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -92,47 +92,60 @@ class SelectByAttribute(QgisAlgorithm):
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Selected (attribute)'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Selected (attribute)')))
self.layer = None
self.fieldName = None
self.operator = None
self.value = None
self.input = None
def name(self): def name(self):
return 'selectbyattribute' return 'selectbyattribute'
def displayName(self): def displayName(self):
return self.tr('Select by attribute') return self.tr('Select by attribute')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) self.layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
fieldName = self.parameterAsString(parameters, self.FIELD, context) self.fieldName = self.parameterAsString(parameters, self.FIELD, context)
operator = self.OPERATORS[self.parameterAsEnum(parameters, self.OPERATOR, context)] self.operator = self.OPERATORS[self.parameterAsEnum(parameters, self.OPERATOR, context)]
value = self.parameterAsString(parameters, self.VALUE, context) self.value = self.parameterAsString(parameters, self.VALUE, context)
fields = layer.fields() self.input = parameters[self.INPUT]
return True
idx = layer.fields().lookupField(fieldName) def processAlgorithm(self, context, feedback):
fields = self.layer.fields()
idx = self.layer.fields().lookupField(self.fieldName)
fieldType = fields[idx].type() fieldType = fields[idx].type()
if fieldType != QVariant.String and operator in self.STRING_OPERATORS: if fieldType != QVariant.String and self.operator in self.STRING_OPERATORS:
op = ''.join(['"%s", ' % o for o in self.STRING_OPERATORS]) op = ''.join(['"%s", ' % o for o in self.STRING_OPERATORS])
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Operators {0} can be used only with string fields.').format(op)) self.tr('Operators {0} can be used only with string fields.').format(op))
field_ref = QgsExpression.quotedColumnRef(fieldName) field_ref = QgsExpression.quotedColumnRef(self.fieldName)
quoted_val = QgsExpression.quotedValue(value) quoted_val = QgsExpression.quotedValue(self.value)
if operator == 'is null': if self.operator == 'is null':
expression_string = '{} IS NULL'.format(field_ref) expression_string = '{} IS NULL'.format(field_ref)
elif operator == 'is not null': elif self.operator == 'is not null':
expression_string = '{} IS NOT NULL'.format(field_ref) expression_string = '{} IS NOT NULL'.format(field_ref)
elif operator == 'begins with': elif self.operator == 'begins with':
expression_string = """%s LIKE '%s%%'""" % (field_ref, value) expression_string = """%s LIKE '%s%%'""" % (field_ref, self.value)
elif operator == 'contains': elif self.operator == 'contains':
expression_string = """%s LIKE '%%%s%%'""" % (field_ref, value) expression_string = """%s LIKE '%%%s%%'""" % (field_ref, self.value)
elif operator == 'does not contain': elif self.operator == 'does not contain':
expression_string = """%s NOT LIKE '%%%s%%'""" % (field_ref, value) expression_string = """%s NOT LIKE '%%%s%%'""" % (field_ref, self.value)
else: else:
expression_string = '{} {} {}'.format(field_ref, operator, quoted_val) expression_string = '{} {} {}'.format(field_ref, self.operator, quoted_val)
expression = QgsExpression(expression_string) expression = QgsExpression(expression_string)
if expression.hasParserError(): if expression.hasParserError():
raise GeoAlgorithmExecutionException(expression.parserErrorString()) raise GeoAlgorithmExecutionException(expression.parserErrorString())
layer.selectByExpression(expression_string) self.layer.selectByExpression(expression_string)
return {self.OUTPUT: parameters[self.INPUT]} return True
def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.input}

View File

@ -60,30 +60,42 @@ class SelectByExpression(QgisAlgorithm):
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Selected (attribute)'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Selected (attribute)')))
self.layer = None
self.behavior = None
self.input = None
self.expression = None
def name(self): def name(self):
return 'selectbyexpression' return 'selectbyexpression'
def displayName(self): def displayName(self):
return self.tr('Select by expression') return self.tr('Select by expression')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
layer = self.parameterAsVectorLayer(parameters, self.INPUT, context) self.layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
method = self.parameterAsEnum(parameters, self.METHOD, context) method = self.parameterAsEnum(parameters, self.METHOD, context)
if method == 0: if method == 0:
behavior = QgsVectorLayer.SetSelection self.behavior = QgsVectorLayer.SetSelection
elif method == 1: elif method == 1:
behavior = QgsVectorLayer.AddToSelection self.behavior = QgsVectorLayer.AddToSelection
elif method == 2: elif method == 2:
behavior = QgsVectorLayer.RemoveFromSelection self.behavior = QgsVectorLayer.RemoveFromSelection
elif method == 3: elif method == 3:
behavior = QgsVectorLayer.IntersectSelection self.behavior = QgsVectorLayer.IntersectSelection
expression = self.parameterAsString(parameters, self.EXPRESSION, context) self.expression = self.parameterAsString(parameters, self.EXPRESSION, context)
qExp = QgsExpression(expression) qExp = QgsExpression(self.expression)
if qExp.hasParserError(): if qExp.hasParserError():
raise GeoAlgorithmExecutionException(qExp.parserErrorString()) raise GeoAlgorithmExecutionException(qExp.parserErrorString())
layer.selectByExpression(expression, behavior) self.input = parameters[self.INPUT]
return {self.OUTPUT: parameters[self.INPUT]} return True
def processAlgorithm(self, context, feedback):
self.layer.selectByExpression(self.expression, self.behavior)
return True
def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.input}

View File

@ -76,28 +76,37 @@ class SimplifyGeometries(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Simplified'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Simplified')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Simplified'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Simplified')))
self.source = None
self.tolerance = None
self.method = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'simplifygeometries' return 'simplifygeometries'
def displayName(self): def displayName(self):
return self.tr('Simplify geometries') return self.tr('Simplify geometries')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) self.tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context)
method = self.parameterAsEnum(parameters, self.METHOD, context) self.method = self.parameterAsEnum(parameters, self.METHOD, context)
(self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
self.source.fields(), self.source.wkbType(), self.source.sourceCrs())
return True
def processAlgorithm(self, context, feedback):
pointsBefore = 0 pointsBefore = 0
pointsAfter = 0 pointsAfter = 0
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, features = self.source.getFeatures()
source.fields(), source.wkbType(), source.sourceCrs()) total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
features = source.getFeatures() simplifier = None
total = 100.0 / source.featureCount() if source.featureCount() else 0 if self.method != 0:
simplifier = QgsMapToPixelSimplifier(QgsMapToPixelSimplifier.SimplifyGeometry, self.tolerance, self.method)
if method != 0:
simplifier = QgsMapToPixelSimplifier(QgsMapToPixelSimplifier.SimplifyGeometry, tolerance, method)
for current, input_feature in enumerate(features): for current, input_feature in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
@ -107,18 +116,20 @@ class SimplifyGeometries(QgisAlgorithm):
input_geometry = input_feature.geometry() input_geometry = input_feature.geometry()
pointsBefore += input_geometry.geometry().nCoordinates() pointsBefore += input_geometry.geometry().nCoordinates()
if method == 0: # distance if self.method == 0: # distance
output_geometry = input_geometry.simplify(tolerance) output_geometry = input_geometry.simplify(self.tolerance)
else: else:
output_geometry = simplifier.simplify(input_geometry) output_geometry = simplifier.simplify(input_geometry)
pointsAfter += output_geometry.geometry().nCoordinates() pointsAfter += output_geometry.geometry().nCoordinates()
out_feature.setGeometry(output_geometry) out_feature.setGeometry(output_geometry)
sink.addFeature(out_feature, QgsFeatureSink.FastInsert) self.sink.addFeature(out_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
QgsMessageLog.logMessage(self.tr('Simplify: Input geometries have been simplified from {0} to {1} points').format(pointsBefore, pointsAfter), QgsMessageLog.logMessage(self.tr('Simplify: Input geometries have been simplified from {0} to {1} points').format(pointsBefore, pointsAfter),
self.tr('Processing'), QgsMessageLog.INFO) self.tr('Processing'), QgsMessageLog.INFO)
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -66,37 +66,48 @@ class Smooth(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Smoothed'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Smoothed')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Smoothed'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Smoothed')))
self.source = None
self.iterations = None
self.offset = None
self.max_angle = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'smoothgeometry' return 'smoothgeometry'
def displayName(self): def displayName(self):
return self.tr('Smooth geometry') return self.tr('Smooth geometry')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
iterations = self.parameterAsInt(parameters, self.ITERATIONS, context) self.iterations = self.parameterAsInt(parameters, self.ITERATIONS, context)
offset = self.parameterAsDouble(parameters, self.OFFSET, context) self.offset = self.parameterAsDouble(parameters, self.OFFSET, context)
max_angle = self.parameterAsDouble(parameters, self.MAX_ANGLE, context) self.max_angle = self.parameterAsDouble(parameters, self.MAX_ANGLE, context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), source.wkbType(), source.sourceCrs()) self.source.fields(), self.source.wkbType(), self.source.sourceCrs())
return True
features = source.getFeatures() def processAlgorithm(self, context, feedback):
total = 100.0 / source.featureCount() if source.featureCount() else 0 features = self.source.getFeatures()
total = 100.0 / self.source.featureCount() if self.source.featureCount() else 0
for current, input_feature in enumerate(features): for current, input_feature in enumerate(features):
if feedback.isCanceled(): if feedback.isCanceled():
break break
output_feature = input_feature output_feature = input_feature
if input_feature.geometry(): if input_feature.geometry():
output_geometry = input_feature.geometry().smooth(iterations, offset, -1, max_angle) output_geometry = input_feature.geometry().smooth(self.iterations, self.offset, -1, self.max_angle)
if not output_geometry: if not output_geometry:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Error smoothing geometry')) self.tr('Error smoothing geometry'))
output_feature.setGeometry(output_geometry) output_feature.setGeometry(output_geometry)
sink.addFeature(output_feature, QgsFeatureSink.FastInsert) self.sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -49,26 +49,34 @@ class SpatialiteExecuteSQL(QgisAlgorithm):
self.addParameter(QgsProcessingParameterVectorLayer(self.DATABASE, self.tr('File Database'), False, False)) self.addParameter(QgsProcessingParameterVectorLayer(self.DATABASE, self.tr('File Database'), False, False))
self.addParameter(QgsProcessingParameterString(self.SQL, self.tr('SQL query'), '', True)) self.addParameter(QgsProcessingParameterString(self.SQL, self.tr('SQL query'), '', True))
self.database = None
self.sql = None
def name(self): def name(self):
return 'spatialiteexecutesql' return 'spatialiteexecutesql'
def displayName(self): def displayName(self):
return self.tr('Spatialite execute SQL') return self.tr('Spatialite execute SQL')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
database = self.parameterAsVectorLayer(parameters, self.DATABASE, context) self.database = self.parameterAsVectorLayer(parameters, self.DATABASE, context)
databaseuri = database.dataProvider().dataSourceUri() self.sql = self.parameterAsString(parameters, self.SQL, context).replace('\n', ' ')
return True
def processAlgorithm(self, context, feedback):
databaseuri = self.database.dataProvider().dataSourceUri()
uri = QgsDataSourceUri(databaseuri) uri = QgsDataSourceUri(databaseuri)
if uri.database() is '': if uri.database() is '':
if '|layerid' in databaseuri: if '|layerid' in databaseuri:
databaseuri = databaseuri[:databaseuri.find('|layerid')] databaseuri = databaseuri[:databaseuri.find('|layerid')]
uri = QgsDataSourceUri('dbname=\'%s\'' % (databaseuri)) uri = QgsDataSourceUri('dbname=\'%s\'' % (databaseuri))
db = spatialite.GeoDB(uri) db = spatialite.GeoDB(uri)
sql = self.parameterAsString(parameters, self.SQL, context).replace('\n', ' ')
try: try:
db._exec_sql_and_commit(str(sql)) db._exec_sql_and_commit(str(self.sql))
except spatialite.DbError as e: except spatialite.DbError as e:
raise GeoAlgorithmExecutionException( raise GeoAlgorithmExecutionException(
self.tr('Error executing SQL:\n{0}').format(str(e))) self.tr('Error executing SQL:\n{0}').format(str(e)))
return True
def postProcessAlgorithm(self, context, feedback):
return {} return {}

View File

@ -70,32 +70,39 @@ class SymmetricalDifference(QgisAlgorithm):
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Symmetrical difference'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Symmetrical difference')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Symmetrical difference'))) self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Symmetrical difference')))
self.sourceA = None
self.sourceB = None
self.sink = None
self.dest_id = None
def name(self): def name(self):
return 'symmetricaldifference' return 'symmetricaldifference'
def displayName(self): def displayName(self):
return self.tr('Symmetrical difference') return self.tr('Symmetrical difference')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
sourceA = self.parameterAsSource(parameters, self.INPUT, context) self.sourceA = self.parameterAsSource(parameters, self.INPUT, context)
sourceB = self.parameterAsSource(parameters, self.OVERLAY, context) self.sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)
geomType = QgsWkbTypes.multiType(sourceA.wkbType()) geomType = QgsWkbTypes.multiType(self.sourceA.wkbType())
fields = vector.combineFields(sourceA.fields(), sourceB.fields()) fields = vector.combineFields(self.sourceA.fields(), self.sourceB.fields())
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, (self.sink, self.dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, geomType, sourceA.sourceCrs()) fields, geomType, self.sourceA.sourceCrs())
return True
def processAlgorithm(self, context, feedback):
featB = QgsFeature() featB = QgsFeature()
outFeat = QgsFeature() outFeat = QgsFeature()
indexA = QgsSpatialIndex(sourceA) indexA = QgsSpatialIndex(self.sourceA)
indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs()))) indexB = QgsSpatialIndex(self.sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(self.sourceA.sourceCrs())))
total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1 total = 100.0 / (self.sourceA.featureCount() * self.sourceB.featureCount()) if self.sourceA.featureCount() and self.sourceB.featureCount() else 1
count = 0 count = 0
for featA in sourceA.getFeatures(): for featA in self.sourceA.getFeatures():
if feedback.isCanceled(): if feedback.isCanceled():
break break
@ -104,8 +111,8 @@ class SymmetricalDifference(QgisAlgorithm):
attrs = featA.attributes() attrs = featA.attributes()
intersects = indexB.intersects(geom.boundingBox()) intersects = indexB.intersects(geom.boundingBox())
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
request.setDestinationCrs(sourceA.sourceCrs()) request.setDestinationCrs(self.sourceA.sourceCrs())
for featB in sourceB.getFeatures(request): for featB in self.sourceB.getFeatures(request):
if feedback.isCanceled(): if feedback.isCanceled():
break break
tmpGeom = featB.geometry() tmpGeom = featB.geometry()
@ -115,7 +122,7 @@ class SymmetricalDifference(QgisAlgorithm):
try: try:
outFeat.setGeometry(diffGeom) outFeat.setGeometry(diffGeom)
outFeat.setAttributes(attrs) outFeat.setAttributes(attrs)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert) self.sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except: except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.WARNING) self.tr('Processing'), QgsMessageLog.WARNING)
@ -124,9 +131,9 @@ class SymmetricalDifference(QgisAlgorithm):
count += 1 count += 1
feedback.setProgress(int(count * total)) feedback.setProgress(int(count * total))
length = len(sourceA.fields()) length = len(self.sourceA.fields())
for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs())): for featA in self.sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(self.sourceA.sourceCrs())):
if feedback.isCanceled(): if feedback.isCanceled():
break break
@ -136,7 +143,7 @@ class SymmetricalDifference(QgisAlgorithm):
attrs = [NULL] * length + attrs attrs = [NULL] * length + attrs
intersects = indexA.intersects(geom.boundingBox()) intersects = indexA.intersects(geom.boundingBox())
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([]) request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
for featB in sourceA.getFeatures(request): for featB in self.sourceA.getFeatures(request):
if feedback.isCanceled(): if feedback.isCanceled():
break break
@ -147,7 +154,7 @@ class SymmetricalDifference(QgisAlgorithm):
try: try:
outFeat.setGeometry(diffGeom) outFeat.setGeometry(diffGeom)
outFeat.setAttributes(attrs) outFeat.setAttributes(attrs)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert) self.sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except: except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'), QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.WARNING) self.tr('Processing'), QgsMessageLog.WARNING)
@ -155,5 +162,7 @@ class SymmetricalDifference(QgisAlgorithm):
count += 1 count += 1
feedback.setProgress(int(count * total)) feedback.setProgress(int(count * total))
return True
return {self.OUTPUT: dest_id} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.dest_id}

View File

@ -28,7 +28,6 @@ __revision__ = '$Format:%H$'
import os import os
from qgis.PyQt.QtGui import QIcon
from qgis.core import (QgsProcessingUtils, from qgis.core import (QgsProcessingUtils,
QgsFeatureSink, QgsFeatureSink,
QgsProcessingParameterFeatureSource, QgsProcessingParameterFeatureSource,
@ -39,10 +38,6 @@ from qgis.core import (QgsProcessingUtils,
QgsFeatureRequest) QgsFeatureRequest)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterTableField
from processing.core.outputs import OutputDirectory
from processing.tools import vector
from processing.tools.system import mkdir from processing.tools.system import mkdir
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@ -71,42 +66,51 @@ class VectorSplit(QgisAlgorithm):
self.addOutput(QgsProcessingOutputFolder(self.OUTPUT, self.tr('Output directory'))) self.addOutput(QgsProcessingOutputFolder(self.OUTPUT, self.tr('Output directory')))
self.source = None
self.fieldName = None
self.directory = None
self.uniqueValues = None
self.sinks = {}
def name(self): def name(self):
return 'splitvectorlayer' return 'splitvectorlayer'
def displayName(self): def displayName(self):
return self.tr('Split vector layer') return self.tr('Split vector layer')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context) self.source = self.parameterAsSource(parameters, self.INPUT, context)
fieldName = self.parameterAsString(parameters, self.FIELD, context) self.fieldName = self.parameterAsString(parameters, self.FIELD, context)
directory = self.parameterAsString(parameters, self.OUTPUT, context) self.directory = self.parameterAsString(parameters, self.OUTPUT, context)
mkdir(self.directory)
mkdir(directory) fieldIndex = self.source.fields().lookupField(self.fieldName)
self.uniqueValues = self.source.uniqueValues(fieldIndex)
fieldIndex = source.fields().lookupField(fieldName) baseName = os.path.join(self.directory, '{0}'.format(self.fieldName))
uniqueValues = source.uniqueValues(fieldIndex) self.sinks = {}
baseName = os.path.join(directory, '{0}'.format(fieldName)) for current, i in enumerate(self.uniqueValues):
fields = source.fields()
crs = source.sourceCrs()
geomType = source.wkbType()
total = 100.0 / len(uniqueValues) if uniqueValues else 1
for current, i in enumerate(uniqueValues):
if feedback.isCanceled(): if feedback.isCanceled():
break break
fName = u'{0}_{1}.shp'.format(baseName, str(i).strip()) fName = u'{0}_{1}.shp'.format(baseName, str(i).strip())
feedback.pushInfo(self.tr('Creating layer: {}').format(fName)) feedback.pushInfo(self.tr('Creating layer: {}').format(fName))
sink, dest = QgsProcessingUtils.createFeatureSink(fName, context, self.source.fields, self.source.wkbType(), self.source.sourceCrs())
self.sinks[i] = sink
return True
sink, dest = QgsProcessingUtils.createFeatureSink(fName, context, fields, geomType, crs) def processAlgorithm(self, context, feedback):
total = 100.0 / len(self.uniqueValues) if self.uniqueValues else 1
for current, i in enumerate(self.uniqueValues):
if feedback.isCanceled():
break
filter = '{} = {}'.format(QgsExpression.quotedColumnRef(fieldName), QgsExpression.quotedValue(i)) sink = self.sinks[i]
filter = '{} = {}'.format(QgsExpression.quotedColumnRef(self.fieldName), QgsExpression.quotedValue(i))
req = QgsFeatureRequest().setFilterExpression(filter) req = QgsFeatureRequest().setFilterExpression(filter)
count = 0 count = 0
for f in source.getFeatures(req): for f in self.source.getFeatures(req):
if feedback.isCanceled(): if feedback.isCanceled():
break break
sink.addFeature(f, QgsFeatureSink.FastInsert) sink.addFeature(f, QgsFeatureSink.FastInsert)
@ -115,5 +119,7 @@ class VectorSplit(QgisAlgorithm):
del sink del sink
feedback.setProgress(int(current * total)) feedback.setProgress(int(current * total))
return True
return {self.OUTPUT: directory} def postProcessAlgorithm(self, context, feedback):
return {self.OUTPUT: self.directory}

View File

@ -94,30 +94,40 @@ class ZonalStatistics(QgisAlgorithm):
self.tr('Zonal statistics'), self.tr('Zonal statistics'),
QgsProcessingParameterDefinition.TypeVectorPolygon)) QgsProcessingParameterDefinition.TypeVectorPolygon))
self.bandNumber = None
self.columnPrefix = None
self.selectedStats = None
self.vectorLayer = None
self.rasterLayer = None
def name(self): def name(self):
return 'zonalstatistics' return 'zonalstatistics'
def displayName(self): def displayName(self):
return self.tr('Zonal Statistics') return self.tr('Zonal Statistics')
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
bandNumber = self.parameterAsInt(parameters, self.RASTER_BAND, context) self.bandNumber = self.parameterAsInt(parameters, self.RASTER_BAND, context)
columnPrefix = self.parameterAsString(parameters, self.COLUMN_PREFIX, context) self.columnPrefix = self.parameterAsString(parameters, self.COLUMN_PREFIX, context)
st = self.parameterAsEnums(parameters, self.STATISTICS, context) st = self.parameterAsEnums(parameters, self.STATISTICS, context)
vectorLayer = self.parameterAsVectorLayer(parameters, self.INPUT_VECTOR, context)
rasterLayer = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER, context)
keys = list(self.STATS.keys()) keys = list(self.STATS.keys())
selectedStats = 0 self.selectedStats = 0
for i in st: for i in st:
selectedStats |= self.STATS[keys[i]] self.selectedStats |= self.STATS[keys[i]]
zs = QgsZonalStatistics(vectorLayer, self.vectorLayer = self.parameterAsVectorLayer(parameters, self.INPUT_VECTOR, context)
rasterLayer, self.rasterLayer = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER, context)
columnPrefix, return True
bandNumber,
selectedStats) def processAlgorithm(self, context, feedback):
zs = QgsZonalStatistics(self.vectorLayer,
self.rasterLayer,
self.columnPrefix,
self.bandNumber,
self.selectedStats)
zs.calculateStatistics(feedback) zs.calculateStatistics(feedback)
return True
return {self.INPUT_VECTOR: vectorLayer} def postProcessAlgorithm(self, context, feedback):
return {self.INPUT_VECTOR: self.vectorLayer}

View File

@ -75,6 +75,10 @@ class ScriptAlgorithm(QgsProcessingAlgorithm):
if descriptionFile is not None: if descriptionFile is not None:
self.defineCharacteristicsFromFile() self.defineCharacteristicsFromFile()
self.ns = {}
self.cleaned_script = None
self.results = {}
def clone(self): def clone(self):
return ScriptAlgorithm(self.descriptionFile) return ScriptAlgorithm(self.descriptionFile)
@ -178,11 +182,7 @@ class ScriptAlgorithm(QgsProcessingAlgorithm):
self.tr('Could not load script: {0}.\n' self.tr('Could not load script: {0}.\n'
'Problem with line "{1}"', 'ScriptAlgorithm').format(self.descriptionFile or '', line)) 'Problem with line "{1}"', 'ScriptAlgorithm').format(self.descriptionFile or '', line))
def processAlgorithm(self, parameters, context, feedback): def prepareAlgorithm(self, parameters, context, feedback):
ns = {}
ns['scriptDescriptionFile'] = self.descriptionFile
for param in self.parameterDefinitions(): for param in self.parameterDefinitions():
method = None method = None
if param.type() == "boolean": if param.type() == "boolean":
@ -224,20 +224,19 @@ class ScriptAlgorithm(QgsProcessingAlgorithm):
method = self.parameterAsSource method = self.parameterAsSource
if method: if method:
ns[param.name()] = method(parameters, param.name(), context) self.ns[param.name()] = method(parameters, param.name(), context)
self.ns['scriptDescriptionFile'] = self.descriptionFile
for out in self.outputDefinitions(): for out in self.outputDefinitions():
ns[out.name()] = None self.ns[out.name()] = None
ns['self'] = self self.ns['self'] = self
ns['parameters'] = parameters self.ns['parameters'] = parameters
ns['feedback'] = feedback
ns['context'] = context
expression_context = self.createExpressionContext(parameters, context)
variables = re.findall('@[a-zA-Z0-9_]*', self.script) variables = re.findall('@[a-zA-Z0-9_]*', self.script)
script = 'import processing\n' script = 'import processing\n'
script += self.script script += self.script
context = self.createExpressionContext(parameters, context)
for var in variables: for var in variables:
varname = var[1:] varname = var[1:]
if context.hasVariable(varname): if context.hasVariable(varname):
@ -245,12 +244,22 @@ class ScriptAlgorithm(QgsProcessingAlgorithm):
else: else:
# messy - it's probably NOT a variable, and instead an email address or some other string containing '@' # messy - it's probably NOT a variable, and instead an email address or some other string containing '@'
QgsMessageLog.logMessage(self.tr('Cannot find variable: {0}').format(varname), self.tr('Processing'), QgsMessageLog.WARNING) QgsMessageLog.logMessage(self.tr('Cannot find variable: {0}').format(varname), self.tr('Processing'), QgsMessageLog.WARNING)
self.cleaned_script = script
exec((script), ns) return True
results = {}
def processAlgorithm(self, context, feedback):
self.ns['feedback'] = feedback
self.ns['context'] = context
exec((self.cleaned_script), self.ns)
self.results = {}
for out in self.outputDefinitions(): for out in self.outputDefinitions():
results[out.name()] = ns[out.name()] self.results[out.name()] = self.ns[out.name()]
return results return True
def postProcessAlgorithm(self, context, feedback):
return self.results
def helpUrl(self): def helpUrl(self):
if self.descriptionFile is None: if self.descriptionFile is None:

View File

@ -9,7 +9,7 @@ tests:
type: vector # Param is a vector layer type: vector # Param is a vector layer
name: polys.gml # file name name: polys.gml # file name
results: # A map of results (only one here) results: # A map of results (only one here)
OUTPUT_LAYER: OUTPUT:
type: vector # Expected result is a vector layer type: vector # Expected result is a vector layer
name: expected/polys_centroid.gml # The relative filepath from the processing testdata directory name: expected/polys_centroid.gml # The relative filepath from the processing testdata directory
compare: compare:
@ -735,7 +735,7 @@ tests:
name: lines.gml name: lines.gml
type: vector type: vector
results: results:
OUTPUT_LAYER: OUTPUT:
name: expected/centroid_lines.gml name: expected/centroid_lines.gml
type: vector type: vector
compare: compare:
@ -749,7 +749,7 @@ tests:
name: multilines.gml name: multilines.gml
type: vector type: vector
results: results:
OUTPUT_LAYER: OUTPUT:
name: expected/centroid_multilines.gml name: expected/centroid_multilines.gml
type: vector type: vector
compare: compare:
@ -763,7 +763,7 @@ tests:
name: multipoints.gml name: multipoints.gml
type: vector type: vector
results: results:
OUTPUT_LAYER: OUTPUT:
name: expected/centroid_multipoint.gml name: expected/centroid_multipoint.gml
type: vector type: vector
compare: compare:
@ -777,7 +777,7 @@ tests:
name: multipolys.gml name: multipolys.gml
type: vector type: vector
results: results:
OUTPUT_LAYER: OUTPUT:
name: expected/centroid_multipolys.gml name: expected/centroid_multipolys.gml
type: vector type: vector
compare: compare:
@ -791,7 +791,7 @@ tests:
name: points.gml name: points.gml
type: vector type: vector
results: results:
OUTPUT_LAYER: OUTPUT:
name: expected/centroid_points.gml name: expected/centroid_points.gml
type: vector type: vector
compare: compare:
@ -805,7 +805,7 @@ tests:
name: polys.gml name: polys.gml
type: vector type: vector
results: results:
OUTPUT_LAYER: OUTPUT:
name: expected/centroid_polys.gml name: expected/centroid_polys.gml
type: vector type: vector
compare: compare:
@ -1037,12 +1037,12 @@ tests:
- algorithm: qgis:aspect - algorithm: qgis:aspect
name: Aspect from QGIS analysis library name: Aspect from QGIS analysis library
params: params:
INPUT_LAYER: INPUT:
name: dem.tif name: dem.tif
type: raster type: raster
Z_FACTOR: 1.0 Z_FACTOR: 1.0
results: results:
OUTPUT_LAYER: OUTPUT:
hash: 762865ee485a6736d188402aa10e6fd38a812a9e45a7dd2d4885a63a hash: 762865ee485a6736d188402aa10e6fd38a812a9e45a7dd2d4885a63a
type: rasterhash type: rasterhash
@ -1670,11 +1670,11 @@ tests:
- algorithm: qgis:dropgeometries - algorithm: qgis:dropgeometries
name: Drop geometries name: Drop geometries
params: params:
INPUT_LAYER: INPUT:
name: polys.gml name: polys.gml
type: vector type: vector
results: results:
OUTPUT_TABLE: OUTPUT:
name: expected/dropped_geometry.csv name: expected/dropped_geometry.csv
type: vector type: vector

View File

@ -142,7 +142,7 @@ bool QgsCentroidAlgorithm::processAlgorithm( QgsProcessingContext &, QgsProcessi
QVariantMap QgsCentroidAlgorithm::postProcessAlgorithm( QgsProcessingContext &, QgsProcessingFeedback * ) QVariantMap QgsCentroidAlgorithm::postProcessAlgorithm( QgsProcessingContext &, QgsProcessingFeedback * )
{ {
QVariantMap outputs; QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT_LAYER" ), mSinkId ); outputs.insert( QStringLiteral( "OUTPUT" ), mSinkId );
return outputs; return outputs;
} }

View File

@ -4610,7 +4610,7 @@ void TestQgsProcessing::modelerAlgorithm()
QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> alg7c1outputs; QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> alg7c1outputs;
QgsProcessingModelAlgorithm::ModelOutput alg7c1out1( QStringLiteral( "my_output" ) ); QgsProcessingModelAlgorithm::ModelOutput alg7c1out1( QStringLiteral( "my_output" ) );
alg7c1out1.setChildId( "cx1" ); alg7c1out1.setChildId( "cx1" );
alg7c1out1.setChildOutputName( "OUTPUT_LAYER" ); alg7c1out1.setChildOutputName( "OUTPUT" );
alg7c1out1.setDescription( QStringLiteral( "my output" ) ); alg7c1out1.setDescription( QStringLiteral( "my output" ) );
alg7c1outputs.insert( QStringLiteral( "my_output" ), alg7c1out1 ); alg7c1outputs.insert( QStringLiteral( "my_output" ), alg7c1out1 );
alg7c1.setModelOutputs( alg7c1outputs ); alg7c1.setModelOutputs( alg7c1outputs );
@ -4630,7 +4630,7 @@ void TestQgsProcessing::modelerAlgorithm()
QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> alg7c2outputs; QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> alg7c2outputs;
QgsProcessingModelAlgorithm::ModelOutput alg7c2out1( QStringLiteral( "my_output2" ) ); QgsProcessingModelAlgorithm::ModelOutput alg7c2out1( QStringLiteral( "my_output2" ) );
alg7c2out1.setChildId( "cx2" ); alg7c2out1.setChildId( "cx2" );
alg7c2out1.setChildOutputName( "OUTPUT_LAYER" ); alg7c2out1.setChildOutputName( "OUTPUT" );
alg7c2out1.setDescription( QStringLiteral( "my output2" ) ); alg7c2out1.setDescription( QStringLiteral( "my output2" ) );
alg7c2outputs.insert( QStringLiteral( "my_output2" ), alg7c2out1 ); alg7c2outputs.insert( QStringLiteral( "my_output2" ), alg7c2out1 );
alg7c2.setModelOutputs( alg7c2outputs ); alg7c2.setModelOutputs( alg7c2outputs );
@ -4700,7 +4700,7 @@ void TestQgsProcessing::modelExecution()
alg2c1.addParameterSources( "DISSOLVE", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( false ) ); alg2c1.addParameterSources( "DISSOLVE", QgsProcessingModelAlgorithm::ChildParameterSources() << QgsProcessingModelAlgorithm::ChildParameterSource::fromStaticValue( false ) );
QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> outputs1; QMap<QString, QgsProcessingModelAlgorithm::ModelOutput> outputs1;
QgsProcessingModelAlgorithm::ModelOutput out1( "MODEL_OUT_LAYER" ); QgsProcessingModelAlgorithm::ModelOutput out1( "MODEL_OUT_LAYER" );
out1.setChildOutputName( "OUTPUT_LAYER" ); out1.setChildOutputName( "OUTPUT" );
outputs1.insert( QStringLiteral( "MODEL_OUT_LAYER" ), out1 ); outputs1.insert( QStringLiteral( "MODEL_OUT_LAYER" ), out1 );
alg2c1.setModelOutputs( outputs1 ); alg2c1.setModelOutputs( outputs1 );
model2.addChildAlgorithm( alg2c1 ); model2.addChildAlgorithm( alg2c1 );
@ -4720,7 +4720,7 @@ void TestQgsProcessing::modelExecution()
QCOMPARE( params.value( "END_CAP_STYLE" ).toInt(), 1 ); QCOMPARE( params.value( "END_CAP_STYLE" ).toInt(), 1 );
QCOMPARE( params.value( "JOIN_STYLE" ).toInt(), 2 ); QCOMPARE( params.value( "JOIN_STYLE" ).toInt(), 2 );
QCOMPARE( params.value( "INPUT" ).toString(), QStringLiteral( "my_layer_id" ) ); QCOMPARE( params.value( "INPUT" ).toString(), QStringLiteral( "my_layer_id" ) );
QCOMPARE( params.value( "OUTPUT_LAYER" ).toString(), QStringLiteral( "dest.shp" ) ); QCOMPARE( params.value( "OUTPUT" ).toString(), QStringLiteral( "dest.shp" ) );
QCOMPARE( params.count(), 7 ); QCOMPARE( params.count(), 7 );
QVariantMap results; QVariantMap results;
@ -4735,7 +4735,7 @@ void TestQgsProcessing::modelExecution()
model2.addChildAlgorithm( alg2c2 ); model2.addChildAlgorithm( alg2c2 );
params = model2.parametersForChildAlgorithm( model2.childAlgorithm( "cx2" ), modelInputs, childResults ); params = model2.parametersForChildAlgorithm( model2.childAlgorithm( "cx2" ), modelInputs, childResults );
QCOMPARE( params.value( "INPUT" ).toString(), QStringLiteral( "dest.shp" ) ); QCOMPARE( params.value( "INPUT" ).toString(), QStringLiteral( "dest.shp" ) );
QCOMPARE( params.value( "OUTPUT_LAYER" ).toString(), QStringLiteral( "memory:" ) ); QCOMPARE( params.value( "OUTPUT" ).toString(), QStringLiteral( "memory:" ) );
QCOMPARE( params.count(), 2 ); QCOMPARE( params.count(), 2 );
// a child with an optional output // a child with an optional output
@ -4770,8 +4770,8 @@ void TestQgsProcessing::modelExecution()
"##my_out=output outputVector\n" "##my_out=output outputVector\n"
"results={}\n" "results={}\n"
"outputs['cx1']=processing.run('native:buffer', {'DISSOLVE':false,'DISTANCE':parameters['DIST'],'END_CAP_STYLE':1,'INPUT':parameters['SOURCE_LAYER'],'JOIN_STYLE':2,'SEGMENTS':16}, context=context, feedback=feedback)\n" "outputs['cx1']=processing.run('native:buffer', {'DISSOLVE':false,'DISTANCE':parameters['DIST'],'END_CAP_STYLE':1,'INPUT':parameters['SOURCE_LAYER'],'JOIN_STYLE':2,'SEGMENTS':16}, context=context, feedback=feedback)\n"
"results['MODEL_OUT_LAYER']=outputs['cx1']['OUTPUT_LAYER']\n" "results['MODEL_OUT_LAYER']=outputs['cx1']['OUTPUT']\n"
"outputs['cx2']=processing.run('native:centroids', {'INPUT':outputs['cx1']['OUTPUT_LAYER']}, context=context, feedback=feedback)\n" "outputs['cx2']=processing.run('native:centroids', {'INPUT':outputs['cx1']['OUTPUT']}, context=context, feedback=feedback)\n"
"outputs['cx3']=processing.run('native:extractbyexpression', {'EXPRESSION':true,'INPUT':outputs['cx1']['OUTPUT_LAYER'],'OUTPUT':parameters['MY_OUT']}, context=context, feedback=feedback)\n" "outputs['cx3']=processing.run('native:extractbyexpression', {'EXPRESSION':true,'INPUT':outputs['cx1']['OUTPUT_LAYER'],'OUTPUT':parameters['MY_OUT']}, context=context, feedback=feedback)\n"
"results['MY_OUT']=outputs['cx3']['OUTPUT']\n" "results['MY_OUT']=outputs['cx3']['OUTPUT']\n"
"return results" ).split( '\n' ); "return results" ).split( '\n' );