diff --git a/python/plugins/processing/algs/grass7/Grass7AlgorithmProvider.py b/python/plugins/processing/algs/grass7/Grass7AlgorithmProvider.py index d153a19a334..7b36d8e9cc2 100644 --- a/python/plugins/processing/algs/grass7/Grass7AlgorithmProvider.py +++ b/python/plugins/processing/algs/grass7/Grass7AlgorithmProvider.py @@ -72,6 +72,12 @@ class Grass7AlgorithmProvider(QgsProcessingProvider): Grass7Utils.GRASS_HELP_PATH, self.tr('Location of GRASS docs'), Grass7Utils.grassHelpPath())) + # Add a setting for using v.external instead of v.in.ogr + ProcessingConfig.addSetting(Setting( + self.name(), + Grass7Utils.GRASS_USE_VEXTERNAL, + self.tr('For vector layers, use v.external (faster) instead of v.in.ogr'), + True)) ProcessingConfig.readSettings() self.refreshAlgorithms() return True @@ -83,6 +89,7 @@ class Grass7AlgorithmProvider(QgsProcessingProvider): ProcessingConfig.removeSetting(Grass7Utils.GRASS_LOG_COMMANDS) ProcessingConfig.removeSetting(Grass7Utils.GRASS_LOG_CONSOLE) ProcessingConfig.removeSetting(Grass7Utils.GRASS_HELP_PATH) + ProcessingConfig.removeSetting(Grass7Utils.GRASS_USE_VEXTERNAL) def isActive(self): return ProcessingConfig.getSetting('ACTIVATE_GRASS7') diff --git a/python/plugins/processing/algs/grass7/TODO.md b/python/plugins/processing/algs/grass7/TODO.md index c33d3e8ce4d..f06fddd9882 100644 --- a/python/plugins/processing/algs/grass7/TODO.md +++ b/python/plugins/processing/algs/grass7/TODO.md @@ -10,13 +10,19 @@ QGIS3 Processing Port * Replace all parameters by QgsProcessingParameters. * Re-enable GRASS algorithm by default. * Add GRASS 7.2 new algorithms. -* Improve unit tests -* GRASS_REGION_CELLSIZE_PARAMETER is integer or double? -* GRASS_SNAP_TOLERANCE_PARAMETER is integer or double? -* Do we need to use QgsProcessingParameters::parameterFromScriptCode for description files? - We don't NEED but we have to improve getParameterFromString (or use an internal method) at least. - There is also a problem for parameterFromScriptCode: it doesn't use description very well. - We can also use parameterFromVariantMap and a custom internal Grass7Algorithm getParameterFromString. +* Improve unit tests. +* Use some raster/vector layers with spacename into their path. +* Better support for files output that are not HTML. +* Use prepareAlgorithm for algorithm preparation. +* Opens HTML files in Viewer. +* Support ParameterTable. +* Remove specific algorithms code in Grass7Algorithm.py (move them in ext). +* Convert all ext scripts. +* Support OutputFolder. +* Support multiple output raster formats. +* Support multiple output vector formats. +* Support multiple bands input rasters. +* Review all the methods of QgsProcessingAlgorithm. * Make tests under MS-Windows 7 for Utf-8 support. Unit tests diff --git a/python/plugins/processing/algs/grass7/description/r.horizon.txt b/python/plugins/processing/algs/grass7/description/r.horizon.txt index f0e828d1541..ef91ba47b89 100644 --- a/python/plugins/processing/algs/grass7/description/r.horizon.txt +++ b/python/plugins/processing/algs/grass7/description/r.horizon.txt @@ -2,8 +2,11 @@ r.horizon Horizon angle computation from a digital elevation model. Raster (r.*) QgsProcessingParameterRasterLayer|elevation|Name of input elevation raster map|None|False -QgsProcessingParameterNumber|direction|Direction in which you want to know the horizon height|QgsProcessingParameterNumber.Double|0.0|False|360|0 -QgsProcessingParameterNumber|maxdistance|The maximum distance to consider when finding the horizon height|QgsProcessingParameterNumber.Double|10000|False|None|0 -QgsProcessingParameterString|distance|Sampling distance step coefficient (0.5-1.5)|1.0 +QgsProcessingParameterNumber|direction|Direction in which you want to know the horizon height|QgsProcessingParameterNumber.Double|0.0|True|0.0|360.0 +QgsProcessingParameterNumber|step|Angle step size for multidirectional horizon|QgsProcessingParameterNumber.Double|None|True|0.0|360.0 +QgsProcessingParameterNumber|start|Start angle for multidirectional horizon|QgsProcessingParameterNumber.Double|0.0|True|0.0|360.0 +QgsProcessingParameterNumber|end|End angle for multidirectional horizon|QgsProcessingParameterNumber.Double|360.0|True|0.0|360.0 +QgsProcessingParameterNumber|maxdistance|The maximum distance to consider when finding the horizon height|QgsProcessingParameterNumber.Double|None|True|0|None +QgsProcessingParameterNumber|distance|Sampling distance step coefficient|QgsProcessingParameterNumber.Double|1.0|True|0.5|1.5 QgsProcessingParameterBoolean|-d|Write output in degrees (default is radians)|False -QgsProcessingParameterRasterDestination|output|Horizon \ No newline at end of file +QgsProcessingParameterFolderDestination|output|Folder to get horizon rasters diff --git a/python/plugins/processing/algs/grass7/ext/r_blend_combine.py b/python/plugins/processing/algs/grass7/ext/r_blend_combine.py index 5f4e951149e..50f64441f31 100644 --- a/python/plugins/processing/algs/grass7/ext/r_blend_combine.py +++ b/python/plugins/processing/algs/grass7/ext/r_blend_combine.py @@ -16,7 +16,6 @@ * * *************************************************************************** """ -from builtins import str __author__ = 'Médéric Ribreux' __date__ = 'February 2016' @@ -27,49 +26,26 @@ __copyright__ = '(C) 2016, Médéric Ribreux' __revision__ = '$Format:%H$' -def processInputs(alg): - # If there is another raster to copy categories from - # we need to import it with r.in.gdal rather than r.external - first = alg.getParameterValue(u'first') - second = alg.getParameterValue(u'second') - if first in list(alg.exportedLayers.keys()) and second in list(alg.exportedLayers.keys()): +def processInputs(alg, parameters, context): + if 'first' and 'second' in alg.exportedLayers: return - for raster in [first, second]: - alg.setSessionProjectionFromLayer(raster, alg.commands) + for name in ['first', 'second']: + raster = alg.parameterAsRasterLayer(parameters, name, context) + alg.setSessionProjectionFromLayer(raster) + + # We need to import all the bands and color tables of the input raster + alg.commands.append( + alg.loadRasterLayer(name, + parameters[name], + False, None + ) + ) + alg.inputLayers.append(raster) + + alg.postInputs(parameters, context) - destFilename = alg.getTempFilename() - alg.exportedLayers[raster] = destFilename - command = 'r.in.gdal input={} output={} --overwrite -o'.format(raster, destFilename) - alg.commands.append(command) - - alg.setSessionProjectionFromProject(alg.commands) - - region = str(alg.getParameterValue(alg.GRASS_REGION_EXTENT_PARAMETER)) - regionCoords = region.split(',') - command = 'g.region' - command += ' -a' - command += ' n=' + str(regionCoords[3]) - command += ' s=' + str(regionCoords[2]) - command += ' e=' + str(regionCoords[1]) - command += ' w=' + str(regionCoords[0]) - cellsize = alg.getParameterValue(alg.GRASS_REGION_CELLSIZE_PARAMETER) - if cellsize: - command += ' res=' + str(cellsize) - else: - command += ' res=' + str(alg.getDefaultCellsize(parameters, context)) - alignToResolution = alg.getParameterValue(alg.GRASS_REGION_ALIGN_TO_RESOLUTION) - if alignToResolution: - command += ' -a' - alg.commands.append(command) - - -def processOutputs(alg): +def processOutputs(alg, parameters, context): # Keep color table - output = alg.getOutputValue(u'output') - command = u"r.out.gdal -t createopt=\"TFW=YES,COMPRESS=LZW\" input={} output=\"{}\" --overwrite".format( - alg.exportedLayers[output], - output - ) - alg.commands.append(command) - alg.outputCommands.append(command) + self.exportRasterLayer('output', parameters, context, True) + diff --git a/python/plugins/processing/algs/grass7/ext/r_horizon.py b/python/plugins/processing/algs/grass7/ext/r_horizon.py new file mode 100644 index 00000000000..95789dfefd3 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/r_horizon.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + r_horizon.py + ------------ + Date : September 2017 + Copyright : (C) 2017 by Médéric Ribreux + Email : medspx at medspx dot fr +*************************************************************************** +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +*************************************************************************** +""" + +__author__ = 'Médéric Ribreux' +__date__ = 'September 2017' +__copyright__ = '(C) 2017, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' +import os + +def processOutputs(alg, parameters, context): + # There will be as outputs as the difference between start and end divided by steps + start = alg.parameterAsDouble(parameters, 'start', context) + end = alg.parameterAsDouble(parameters, 'end', context) + step = alg.parameterAsDouble(parameters, 'step', context) + num = start + step + directory = alg.parameterAsString(parameters, 'output', context) + while num < end: + grassName = '{}_{}'.format(alg.exportedLayers['output'], int(num)) + fileName = '{}.tif'.format(os.path.join(directory, '{0:0>3}'.format(int(num)))) + alg.exportRasterLayer(grassName, fileName) + num += step diff --git a/python/plugins/processing/algs/grass7/ext/r_rgb.py b/python/plugins/processing/algs/grass7/ext/r_rgb.py index afe5cd26cea..6b1efe485a1 100644 --- a/python/plugins/processing/algs/grass7/ext/r_rgb.py +++ b/python/plugins/processing/algs/grass7/ext/r_rgb.py @@ -27,44 +27,39 @@ __revision__ = '$Format:%H$' def processInputs(alg, parameters, context): - # We need to import all the bands and color tables of the input raster if 'input' in alg.exportedLayers: return - raster = alg.parameterAsRasterLayer(parameters, 'input', context) - alg.setSessionProjectionFromLayer(raster) - alg.prepareInputs() + + # We need to import all the bands and color tables of the input raster + alg.loadRasterLayerFromParameter('input', parameters, context, False, None) + alg.postInputs(parameters, context) def processCommand(alg, parameters, context): - # We need to introduce something clever: # if the input raster is multiband: export each component directly - raster = alg.exportedLayers[alg.getParameterValue('input')] + rasterInput = alg.exportedLayers['input'] + raster = alg.parameterAsRasterLayer(parameters, 'input', context) for color in ['red', 'green', 'blue']: - alg.exportedLayers[alg.getOutputValue(color)] = color + alg.uniqueSuffix - - commands = ["if [ $(g.list type=rast pattern='{}.*' | wc -l) -eq \"0\" ]; then".format(raster)] - commands.append(" r.rgb input={} red={} green={} blue={} --overwrite".format( - raster, - alg.exportedLayers[alg.getOutputValue('red')], - alg.exportedLayers[alg.getOutputValue('green')], - alg.exportedLayers[alg.getOutputValue('blue')] - )) - commands.append("fi") - alg.commands.extend(commands) - + alg.exportedLayers[color] = color + alg.uniqueSuffix + + # If the raster is not multiband, really do r.rgb + if raster.bandCount() == 1: + alg.commands.append(" r.rgb input={} red={} green={} blue={} --overwrite".format( + rasterInput, + alg.exportedLayers['red'], + alg.exportedLayers['green'], + alg.exportedLayers['blue'] + )) def processOutputs(alg, parameters, context): - raster = alg.exportedLayers[alg.getParameterValue('input')] - commands = ["if [ $(g.list type=rast pattern='{}.*' | wc -l) -eq \"0\" ]; then".format(raster)] - for color in ['red', 'green', 'blue']: - commands.append(" r.out.gdal -t input={} output={} createopt=\"TFW=YES,COMPRESS=LZW\" --overwrite".format( - alg.exportedLayers[alg.getOutputValue(color)], - alg.getOutputValue(color) - )) - commands.append("else") - for color in ['red', 'green', 'blue']: - commands.append(" r.out.gdal -t input={} output={} createopt=\"TFW=YES,COMPRESS=LZW\" --overwrite".format( - '{}.{}'.format(raster, color), - alg.getOutputValue(color) - )) - commands.append("fi") - alg.commands.extend(commands) + raster = alg.parameterAsRasterLayer(parameters, 'input', context) + + # if the raster was monoband, export from r.rgb + if raster.bandCount() == 1: + for color in ['red', 'green', 'blue']: + alg.exportRasterLayerFromOutput(color, parameters, context, True) + # otherwise, export directly from the multibands + else: + for color in ['red', 'green', 'blue']: + fileName = alg.parameterAsOutputLayer(parameters, color, context) + grassName = '{}{}'.format(alg.exportedLayers['input'], color) + alg.exportRasterLayer(grassName, fileName, True) diff --git a/python/plugins/processing/core/parameters.py b/python/plugins/processing/core/parameters.py index 0a8d36f4920..eb3dad771e4 100644 --- a/python/plugins/processing/core/parameters.py +++ b/python/plugins/processing/core/parameters.py @@ -60,12 +60,14 @@ from qgis.core import (QgsRasterLayer, QgsVectorLayer, QgsMapLayer, QgsCoordinat QgsProcessingParameterRasterDestination, # NOQA QgsProcessingParameterVectorDestination, # NOQA QgsProcessingParameterFileDestination, + QgsProcessingParameterFolderDestination, QgsProcessingParameterString, # NOQA QgsProcessingParameterMultipleLayers, QgsProcessingParameterFeatureSource, QgsProcessingParameterNumber) # TODELETE from qgis.core import (QgsMessageLog) + from processing.tools.vector import resolveFieldIndex from processing.tools import dataobjects from processing.core.outputs import OutputNumber, OutputRaster, OutputVector