From 651302e360bbbc457a4252913694e7564af52375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 12:38:06 +0200 Subject: [PATCH 01/55] Add i.albedo and i.cluster algorithms --- .../algs/grass7/description/i.albedo.txt | 11 ++ .../algs/grass7/description/i.cluster.txt | 13 ++ .../plugins/processing/algs/grass7/ext/i.py | 185 ++++++++++++++++++ .../processing/algs/grass7/ext/i_albedo.py | 38 ++++ .../processing/algs/grass7/ext/i_cluster.py | 54 +++++ 5 files changed, 301 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.albedo.txt create mode 100644 python/plugins/processing/algs/grass7/description/i.cluster.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i.py create mode 100644 python/plugins/processing/algs/grass7/ext/i_albedo.py create mode 100644 python/plugins/processing/algs/grass7/ext/i_cluster.py diff --git a/python/plugins/processing/algs/grass7/description/i.albedo.txt b/python/plugins/processing/algs/grass7/description/i.albedo.txt new file mode 100644 index 000000000000..dbeef0c956cf --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.albedo.txt @@ -0,0 +1,11 @@ +i.albedo +Computes broad band albedo from surface reflectance. +Imagery (i.*) +ParameterMultipleInput|input|Name of input raster maps|3|False +ParameterBoolean|-m|MODIS (7 input bands:1,2,3,4,5,6,7)|False +ParameterBoolean|-n|NOAA AVHRR (2 input bands:1,2)|False +ParameterBoolean|-l|Landsat 5+7 (6 input bands:1,2,3,4,5,7)|False +ParameterBoolean|-a|ASTER (6 input bands:1,3,5,6,8,9)|False +ParameterBoolean|-c|Aggressive mode (Landsat)|False +ParameterBoolean|-d|Soft mode (MODIS)|False +OutputRaster|output|Albedo diff --git a/python/plugins/processing/algs/grass7/description/i.cluster.txt b/python/plugins/processing/algs/grass7/description/i.cluster.txt new file mode 100644 index 000000000000..cf3048ac779f --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.cluster.txt @@ -0,0 +1,13 @@ +i.cluster +Generates spectral signatures for land cover types in an image using a clustering algorithm. +Imagery (i.*) +ParameterMultipleInput|input|Input rasters|3|False +ParameterNumber|classes|Initial number of classes (1-255)|1|255|1|True +ParameterFile|seed|Name of file containing initial signatures|False|True +ParameterString|sample|Sampling intervals (by row and col)|None|False|True +ParameterNumber|iterations|Maximum number of iterations|1|None|30|True +ParameterNumber|convergence|Percent convergence|0.0|100.0|98.0|True +ParameterNumber|separation|Cluster separation|0.0|None|0.0|True +ParameterNumber|min_size|Minimum number of pixels in a class|1|None|17|True +OutputFile|signaturefile|Signature File +OutputFile|reportfile|Final Report File diff --git a/python/plugins/processing/algs/grass7/ext/i.py b/python/plugins/processing/algs/grass7/ext/i.py new file mode 100644 index 000000000000..a3898ad35799 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i.py + ---- + Date : February 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from processing.core.parameters import ParameterRaster, getParameterFromString +from processing.tools.system import isWindows + + +def multipleOutputDir(alg, field, basename=None): + """ + Handle multiple output of rasters into a + directory. + """ + # We need to know where is the output directory + outputDir = alg.getOutputValue(field) + + # We need to grab the variable basename + if basename: + commands = ["for r in $(g.list type=rast pattern='{}*'); do".format(basename)] + # Otherwise, export everything + else: + commands = ["for r in $(g.list type=rast); do".format(basename)] + commands.append(" r.out.gdal -c -t input=${{r}} output={}/${{r}}.tif createopt=\"TFW=YES,COMPRESS=LZW\"".format(outputDir)) + commands.append("done") + alg.commands.extend(commands) + alg.outputCommands.extend(commands) + + +def orderedInput(alg, inputParameter, targetParameterDef): + """Inport multiple rasters in the order""" + rasters = alg.getParameterValue(inputParameter).split(';') + # TODO: make targetParameter + inputParameter = getParameterFromString(targetParameterDef) + rootFilename = '{}_'.format(alg.getTempFilename()) + inputParameter.value = rootFilename + alg.addParameter(inputParameter) + for idx in range(len(rasters)): + layer = rasters[idx] + if layer in alg.exportedLayers.keys(): + continue + else: + destFilename = '{}{}'.format(rootFilename, idx + 1) + alg.setSessionProjectionFromLayer(layer, alg.commands) + alg.exportedLayers[layer] = destFilename + command = 'r.external input={} band=1 output={} --overwrite -o'.format(layer, destFilename) + alg.commands.append(command) + + alg.setSessionProjectionFromProject(alg.commands) + + region = \ + unicode(alg.getParameterValue(alg.GRASS_REGION_EXTENT_PARAMETER)) + regionCoords = region.split(',') + command = 'g.region' + command += ' -a' + command += ' n=' + unicode(regionCoords[3]) + command += ' s=' + unicode(regionCoords[2]) + command += ' e=' + unicode(regionCoords[1]) + command += ' w=' + unicode(regionCoords[0]) + cellsize = alg.getParameterValue(alg.GRASS_REGION_CELLSIZE_PARAMETER) + if cellsize: + command += ' res=' + unicode(cellsize) + else: + command += ' res=' + unicode(alg.getDefaultCellsize()) + alignToResolution = \ + alg.getParameterValue(alg.GRASS_REGION_ALIGN_TO_RESOLUTION) + if alignToResolution: + command += ' -a' + alg.commands.append(command) + return rootFilename + + +def regroupRasters(alg, field, groupField, subgroupField=None): + """ + Group multiple input rasters into a group + """ + # List of rasters names + rasters = alg.getParameterFromName(field) + rastersList = rasters.value.split(';') + alg.parameters.remove(rasters) + + # Insert a i.group command + group = getParameterFromString("ParameterString|{}|group of rasters|None|False|False".format(groupField)) + group.value = alg.getTempFilename() + alg.addParameter(group) + + if subgroupField: + subgroup = getParameterFromString("ParameterString|{}|subgroup of rasters|None|False|False".format(subgroupField)) + subgroup.value = alg.getTempFilename() + alg.addParameter(subgroup) + + command = 'i.group group={}{} input={}'.format( + group.value, + ' subgroup={}'.format(subgroup.value) if subgroup else '', + ','.join([alg.exportedLayers[f] for f in rastersList]) + ) + alg.commands.append(command) + + # modify parameters values + alg.processCommand() + + # Re-add input rasters + alg.addParameter(rasters) + # Delete group: + alg.parameters.remove(group) + if subgroupField: + alg.parameters.remove(subgroup) + + if subgroupField: + return group.value, subgroup.value + + return group.value + + +def exportInputRasters(alg, rasterDic): + """ + Export input rasters + Use a dict to make input/output link: + { 'inputName1': 'outputName1', 'inputName2': 'outputName2'} + """ + # Get inputs and outputs + for inputName, outputName in rasterDic.iteritems(): + inputRaster = alg.getParameterValue(inputName) + outputRaster = alg.getOutputFromName(outputName) + command = 'r.out.gdal -c -t --overwrite createopt="TFW=YES,COMPRESS=LZW" input={} output=\"{}\"'.format( + alg.exportedLayers[inputRaster], + outputRaster.value + ) + alg.commands.append(command) + alg.outputCommands.append(command) + + +def verifyRasterNum(alg, rasters, mini, maxi=None): + """Verify if we have at least n rasters in multipleInput""" + num = len(alg.getParameterValue(rasters).split(';')) + if num < mini: + return 'You need to set at least {} input rasters for this algorithm!'.format(mini) + if maxi and num > maxi: + return 'You need to set a maximum of {} input rasters for this algorithm!'.format(maxi) + return None + + +def file2Output(alg, output): + """Transform an OutputFile to a parameter""" + # Get the outputFile + outputFile = alg.getOutputFromName(output) + alg.removeOutputFromName(output) + + # Create output parameter + param = getParameterFromString("ParameterString|{}|output file|None|False|False".format(output)) + param.value = outputFile.value + alg.addParameter(param) + + return outputFile + + +def moveFile(alg, fromFile, toFile): + # move the file + if isWindows(): + command = "MOVE /Y {} {}".format(fromFile, toFile) + else: + command = "mv -f {} {}".format(fromFile, toFile) + alg.commands.append(command) diff --git a/python/plugins/processing/algs/grass7/ext/i_albedo.py b/python/plugins/processing/algs/grass7/ext/i_albedo.py new file mode 100644 index 000000000000..423eafd31696 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_albedo.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_albedo.py + ----------- + Date : February 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import verifyRasterNum + + +def checkParameterValuesBeforeExecuting(alg): + if alg.getParameterValue('-m'): + return verifyRasterNum(alg, 'input', 7) + elif alg.getParameterValue('-n'): + return verifyRasterNum(alg, 'input', 2) + elif alg.getParameterValue('-l') or alg.getParameterValue('-a'): + return verifyRasterNum(alg, 'input', 6) + return None diff --git a/python/plugins/processing/algs/grass7/ext/i_cluster.py b/python/plugins/processing/algs/grass7/ext/i_cluster.py new file mode 100644 index 000000000000..ee9463b09f18 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_cluster.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_cluster.py + ------------ + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import regroupRasters, file2Output, moveFile +from os import path +from ..Grass7Utils import Grass7Utils + + +def processCommand(alg): + # We need to extract the basename of the signature file + signatureFile = alg.getOutputFromName('signaturefile') + origSigFile = signatureFile.value + shortSigFile = path.basename(origSigFile) + alg.setOutputValue('signaturefile', shortSigFile) + + # Transform output files in string parameters + signatureFile = file2Output(alg, 'signaturefile') + reportFile = file2Output(alg, 'reportfile') + + # Regroup rasters + group, subgroup = regroupRasters(alg, 'input', 'group', 'subgroup') + + # Re-add signature files + alg.addOutput(signatureFile) + alg.addOutput(reportFile) + + # Find Grass directory + interSig = path.join(Grass7Utils.grassMapsetFolder(), 'PERMANENT', 'group', group, 'subgroup', subgroup, 'sig', shortSigFile) + moveFile(alg, interSig, origSigFile) + alg.setOutputValue('signaturefile', origSigFile) From 89ec2fbb7cdc2fbb431abbe4c17c13a7488f86e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 12:42:13 +0200 Subject: [PATCH 02/55] Add i.colors.enhance algorithm --- .../grass7/description/i.colors.enhance.txt | 15 ++++++ .../algs/grass7/ext/i_colors_enhance.py | 48 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.colors.enhance.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_colors_enhance.py diff --git a/python/plugins/processing/algs/grass7/description/i.colors.enhance.txt b/python/plugins/processing/algs/grass7/description/i.colors.enhance.txt new file mode 100644 index 000000000000..4a931c6132d5 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.colors.enhance.txt @@ -0,0 +1,15 @@ +i.colors.enhance +Performs auto-balancing of colors for RGB images. +Imagery (i.*) +ParameterRaster|red|Name of red channel|False +ParameterRaster|green|Name of green channel|False +ParameterRaster|blue|Name of blue channel|False +ParameterNumber|strength|Cropping intensity (upper brightness level)|0|100|98|True +*ParameterBoolean|-f|Extend colors to full range of data on each channel|False +*ParameterBoolean|-p|Preserve relative colors, adjust brightness only|False +*ParameterBoolean|-r|Reset to standard color range|False +*ParameterBoolean|-s|Process bands serially (default: run in parallel)|False +OutputRaster|redoutput|Enhanced Red +OutputRaster|greenoutput|Enhanced Green +OutputRaster|blueoutput|Enhanced Blue + diff --git a/python/plugins/processing/algs/grass7/ext/i_colors_enhance.py b/python/plugins/processing/algs/grass7/ext/i_colors_enhance.py new file mode 100644 index 000000000000..b5f45dbcf516 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_colors_enhance.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_colors_enhance.py + ------------------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import exportInputRasters + + +def processCommand(alg): + + # Temporary remove outputs: + outputs = [alg.getOutputFromName('{}output'.format(f)) for f in ['red', 'green', 'blue']] + for out in outputs: + alg.removeOutputFromName(out.name) + + alg.processCommand() + + # Re-add outputs + for output in outputs: + alg.addOutput(output) + + +def processOutputs(alg): + # Input rasters are output rasters + rasterDic = {'red': 'redoutput', 'green': 'greenoutput', 'blue': 'blueoutput'} + exportInputRasters(alg, rasterDic) From e3da53779e3a6442bc7c5f66a6b5201d5971a414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 12:51:09 +0200 Subject: [PATCH 03/55] Add i.eb.evapfr algorithm --- .../processing/algs/grass7/description/i.eb.evapfr.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.eb.evapfr.txt diff --git a/python/plugins/processing/algs/grass7/description/i.eb.evapfr.txt b/python/plugins/processing/algs/grass7/description/i.eb.evapfr.txt new file mode 100644 index 000000000000..c0a3c0cfd433 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.eb.evapfr.txt @@ -0,0 +1,9 @@ +i.eb.evapfr +Computes evaporative fraction (Bastiaanssen, 1995) and root zone soil moisture (Makin, Molden and Bastiaanssen, 2001). +Imagery (i.*) +ParameterRaster|netradiation|Name of Net Radiation raster map [W/m2]|False +ParameterRaster|soilheatflux|Name of soil heat flux raster map [W/m2]|False +ParameterRaster|sensibleheatflux|Name of sensible heat flux raster map [W/m2]|False +Hardcoded|-m +OutputRaster|evaporativefraction|Evaporative Fraction +OutputRaster|soilmoisture|Root Zone Soil Moisture From 8c69220b794442044d87fc223d3180336c261fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 12:51:22 +0200 Subject: [PATCH 04/55] Add i.eb.eta algorithm --- .../processing/algs/grass7/description/i.eb.eta.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.eb.eta.txt diff --git a/python/plugins/processing/algs/grass7/description/i.eb.eta.txt b/python/plugins/processing/algs/grass7/description/i.eb.eta.txt new file mode 100644 index 000000000000..0dabb99c6c5c --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.eb.eta.txt @@ -0,0 +1,7 @@ +i.eb.eta +Actual evapotranspiration for diurnal period (Bastiaanssen, 1995). +Imagery (i.*) +ParameterRaster|netradiationdiurnal|Name of the diurnal net radiation map [W/m2]|False +ParameterRaster|evaporativefraction|Name of the evaporative fraction map|False +ParameterRaster|temperature|Name of the surface skin temperature [K]|False +OutputRaster|output|Evapotranspiration From a5afb9412122115b0cc2ea167fa049bb559f43e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 13:19:11 +0200 Subject: [PATCH 05/55] Add i.eb.hsebal01 algorithm --- .../grass7/description/i.eb.hsebal01.coords.txt | 15 +++++++++++++++ .../algs/grass7/description/i.eb.hsebal01.txt | 11 +++++++++++ 2 files changed, 26 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.eb.hsebal01.coords.txt create mode 100644 python/plugins/processing/algs/grass7/description/i.eb.hsebal01.txt diff --git a/python/plugins/processing/algs/grass7/description/i.eb.hsebal01.coords.txt b/python/plugins/processing/algs/grass7/description/i.eb.hsebal01.coords.txt new file mode 100644 index 000000000000..fb7b8e37817c --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.eb.hsebal01.coords.txt @@ -0,0 +1,15 @@ +i.eb.hsebal01 +i.eb.hsebal01.coords - Computes sensible heat flux iteration SEBAL 01. Inline coordinates +Imagery (i.*) +ParameterRaster|netradiation|Name of instantaneous net radiation raster map [W/m2]|False +ParameterRaster|soilheatflux|Name of instantaneous soil heat flux raster map [W/m2]|False +ParameterRaster|aerodynresistance|Name of aerodynamic resistance to heat momentum raster map [s/m]|False +ParameterRaster|temperaturemeansealevel|Name of altitude corrected surface temperature raster map [K]|False +ParameterNumber|frictionvelocitystar|Value of the height independent friction velocity (u*) [m/s]|0.0|None|0.32407|False +ParameterNumber|vapourpressureactual|Value of the actual vapour pressure (e_act) [KPa]|0.0|None|1.511|False +ParameterString|row_wet_pixel|Row value of the wet pixel|None|False|False +ParameterString|column_wet_pixel|Column value of the wet pixel|None|False|False +ParameterString|row_dry_pixel|Row value of the dry pixel|None|False|False +ParameterString|column_dry_pixel|Column value of the dry pixel|None|False|False +*ParameterBoolean|-c|Dry/Wet pixels coordinates are in image projection, not row/col|False +OutputRaster|output|Sensible Heat Flux diff --git a/python/plugins/processing/algs/grass7/description/i.eb.hsebal01.txt b/python/plugins/processing/algs/grass7/description/i.eb.hsebal01.txt new file mode 100644 index 000000000000..3f70ca78ab2a --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.eb.hsebal01.txt @@ -0,0 +1,11 @@ +i.eb.hsebal01 +Computes sensible heat flux iteration SEBAL 01. +Imagery (i.*) +ParameterRaster|netradiation|Name of instantaneous net radiation raster map [W/m2]|False +ParameterRaster|soilheatflux|Name of instantaneous soil heat flux raster map [W/m2]|False +ParameterRaster|aerodynresistance|Name of aerodynamic resistance to heat momentum raster map [s/m]|False +ParameterRaster|temperaturemeansealevel|Name of altitude corrected surface temperature raster map [K]|False +ParameterNumber|frictionvelocitystar|Value of the height independent friction velocity (u*) [m/s]|0.0|None|0.32407|False +ParameterNumber|vapourpressureactual|Value of the actual vapour pressure (e_act) [KPa]|0.0|None|1.511|False +Hardcoded|-a +OutputRaster|output|Sensible Heat Flux From b355940395d599b606bf82e169324dd52d987e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 13:22:57 +0200 Subject: [PATCH 06/55] Add i.eb.netrad algorithm --- .../algs/grass7/description/i.eb.netrad.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.eb.netrad.txt diff --git a/python/plugins/processing/algs/grass7/description/i.eb.netrad.txt b/python/plugins/processing/algs/grass7/description/i.eb.netrad.txt new file mode 100644 index 000000000000..7fec8da8c71e --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.eb.netrad.txt @@ -0,0 +1,13 @@ +i.eb.netrad +Net radiation approximation (Bastiaanssen, 1995). +Imagery (i.*) +ParameterRaster|albedo|Name of albedo raster map [0.0;1.0]|False +ParameterRaster|ndvi|Name of NDVI raster map [-1.0;+1.0]|False +ParameterRaster|temperature|Name of surface temperature raster map [K]|False +ParameterRaster|localutctime|Name of time of satellite overpass raster map [local time in UTC]|False +ParameterRaster|temperaturedifference2m|Name of the difference map of temperature from surface skin to about 2 m height [K]|False +ParameterRaster|emissivity|Name of the emissivity map [-]|False +ParameterRaster|transmissivity_singleway|Name of the single-way atmospheric transmissivitymap [-]|False +ParameterRaster|dayofyear|Name of the Day Of Year (DOY) map [-]|False +ParameterRaster|sunzenithangle|Name of the sun zenith angle map [degrees]|False +OutputRaster|output|Net Radiation From 13402f653c365804081db449d43583a8bc7c4577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 13:25:52 +0200 Subject: [PATCH 07/55] Add i.eb.soilheatflux algorithm --- .../algs/grass7/description/i.eb.soilheatflux.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.eb.soilheatflux.txt diff --git a/python/plugins/processing/algs/grass7/description/i.eb.soilheatflux.txt b/python/plugins/processing/algs/grass7/description/i.eb.soilheatflux.txt new file mode 100644 index 000000000000..da6e0138e7d8 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.eb.soilheatflux.txt @@ -0,0 +1,10 @@ +i.eb.soilheatflux +Soil heat flux approximation (Bastiaanssen, 1995). +Imagery (i.*) +ParameterRaster|albedo|Name of albedo raster map [0.0;1.0]|False +ParameterRaster|ndvi|Name of NDVI raster map [-1.0;+1.0]|False +ParameterRaster|temperature|Name of Surface temperature raster map [K]|False +ParameterRaster|netradiation|Name of Net Radiation raster map [W/m2]|False +ParameterRaster|localutctime|Name of time of satellite overpass raster map [local time in UTC]|False +ParameterBoolean|-r|HAPEX-Sahel empirical correction (Roerink, 1995)|False +OutputRaster|output|Soil Heat Flux From 0529062f636a4edc84c33e59be83be7b195bef0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 13:27:46 +0200 Subject: [PATCH 08/55] Add i.biomass algorithm --- .../processing/algs/grass7/description/i.biomass.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.biomass.txt diff --git a/python/plugins/processing/algs/grass7/description/i.biomass.txt b/python/plugins/processing/algs/grass7/description/i.biomass.txt new file mode 100644 index 000000000000..26bb661b34a8 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.biomass.txt @@ -0,0 +1,10 @@ +i.biomass +Computes biomass growth, precursor of crop yield calculation. +Imagery (i.*) +ParameterRaster|fpar|Name of fPAR raster map|False +ParameterRaster|lightuse_efficiency|Name of light use efficiency raster map (UZB:cotton=1.9)|False +ParameterRaster|latitude|Name of degree latitude raster map [dd.ddd]|False +ParameterRaster|dayofyear|Name of Day of Year raster map [1-366]|False +ParameterRaster|transmissivity_singleway|Name of single-way transmissivity raster map [0.0-1.0]False +ParameterRaster|water_availability|Value of water availability raster map [0.0-1.0]|False +OutputRaster|output|Biomass From 109a7f4fbc1be9e144bb9232e15b0e638fd589c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 13:29:10 +0200 Subject: [PATCH 09/55] Add i.emissivity algorithm --- .../processing/algs/grass7/description/i.emissivity.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.emissivity.txt diff --git a/python/plugins/processing/algs/grass7/description/i.emissivity.txt b/python/plugins/processing/algs/grass7/description/i.emissivity.txt new file mode 100644 index 000000000000..1510b9dcec07 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.emissivity.txt @@ -0,0 +1,5 @@ +i.emissivity +Computes emissivity from NDVI, generic method for sparse land. +Imagery (i.*) +ParameterRaster|input|Name of NDVI raster map [-]|False +OutputRaster|output|Emissivity From b6396cb9e0a87a0d8d3fe111cfdfc2a0d4a72e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 13:38:40 +0200 Subject: [PATCH 10/55] Add i.evapo.mh algorithm --- .../algs/grass7/description/i.evapo.mh.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.evapo.mh.txt diff --git a/python/plugins/processing/algs/grass7/description/i.evapo.mh.txt b/python/plugins/processing/algs/grass7/description/i.evapo.mh.txt new file mode 100644 index 000000000000..a6b740d211b3 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.evapo.mh.txt @@ -0,0 +1,12 @@ +i.evapo.mh +Computes evapotranspiration calculation modified or original Hargreaves formulation, 2001. +Imagery (i.*) +ParameterRaster|netradiation_diurnal|Name of input diurnal net radiation raster map [W/m2/d]|False +ParameterRaster|average_temperature|Name of input average air temperature raster map [C]|False +ParameterRaster|minimum_temperature|Name of input minimum air temperature raster map [C]|False +ParameterRaster|maximum_temperature|Name of input maximum air temperature raster map [C]|False +ParameterRaster|precipitation|Name of precipitation raster map [mm/month]|True +*ParameterBoolean|-z|Set negative ETa to zero|False +*ParameterBoolean|-h|Use original Hargreaves (1985)|False +*ParameterBoolean|-s|Use Hargreaves-Samani (1985)|False +OutputRaster|output|Evapotranspiration From 474a258ea54ad625390d5b16e85df8a0c9cf5f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 13:43:15 +0200 Subject: [PATCH 11/55] Add i.evapo.pm algorithm --- .../algs/grass7/description/i.evapo.pm.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.evapo.pm.txt diff --git a/python/plugins/processing/algs/grass7/description/i.evapo.pm.txt b/python/plugins/processing/algs/grass7/description/i.evapo.pm.txt new file mode 100644 index 000000000000..aef1d96312f1 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.evapo.pm.txt @@ -0,0 +1,12 @@ +i.evapo.pm +Computes potential evapotranspiration calculation with hourly Penman-Monteith. +Imagery (i.*) +ParameterRaster|elevation|Name of input elevation raster map [m a.s.l.]|False +ParameterRaster|temperature|Name of input temperature raster map [C]|False +ParameterRaster|relativehumidity|Name of input relative humidity raster map [%]|False +ParameterRaster|windspeed|Name of input wind speed raster map [m/s]|False +ParameterRaster|netradiation|Name of input net solar radiation raster map [MJ/m2/h]|False +ParameterRaster|cropheight|Name of input crop height raster map [m]|False +*ParameterBoolean|-z|Set negative ETa to zero|False +*ParameterBoolean|-n|Use Night-time|False +OutputRaster|output|Evapotranspiration From 0b8784cf650e01fd7e1d07d50b3aac422d952a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 15:28:04 +0200 Subject: [PATCH 12/55] Add i.evapo.pt algorithm --- .../processing/algs/grass7/description/i.evapo.pt.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.evapo.pt.txt diff --git a/python/plugins/processing/algs/grass7/description/i.evapo.pt.txt b/python/plugins/processing/algs/grass7/description/i.evapo.pt.txt new file mode 100644 index 000000000000..0fdc44bda878 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.evapo.pt.txt @@ -0,0 +1,10 @@ +i.evapo.pt +Computes evapotranspiration calculation Priestley and Taylor formulation, 1972. +Imagery (i.*) +ParameterRaster|net_radiation|Name of input net radiation raster map [W/m2]|False +ParameterRaster|soil_heatflux|Name of input soil heat flux raster map [W/m2]|False +ParameterRaster|air_temperature|Name of input air temperature raster map [K]|False +ParameterRaster|atmospheric_pressure|Name of input atmospheric pressure raster map [millibars]|False +ParameterNumber|priestley_taylor_coeff|Priestley-Taylor coefficient|0.0|None|1.26|False +*ParameterBoolean|-z|Set negative ETa to zero|False +OutputRaster|output|Evapotranspiration From b5330003f2d8cf2c446939cbe8b3f2ba8947ee74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 15:34:10 +0200 Subject: [PATCH 13/55] Add i.evapo.time algorithm --- .../algs/grass7/description/i.evapo.time.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.evapo.time.txt diff --git a/python/plugins/processing/algs/grass7/description/i.evapo.time.txt b/python/plugins/processing/algs/grass7/description/i.evapo.time.txt new file mode 100644 index 000000000000..5991dc97aeba --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.evapo.time.txt @@ -0,0 +1,10 @@ +i.evapo.time +Computes temporal integration of satellite ET actual (ETa) following the daily ET reference (ETo) from meteorological station(s). +Imagery (i.*) +ParameterMultipleInput|eta|Names of satellite ETa raster maps [mm/d or cm/d]|3|False +ParameterMultipleInput|eta_doy|Names of satellite ETa Day of Year (DOY) raster maps [0-400] [-]|3|False +ParameterMultipleInput|eto|Names of meteorological station ETo raster maps [0-400] [mm/d or cm/d]|3|False +ParameterNumber|eto_doy_min|Value of DOY for ETo first day|0|366|1|False +ParameterNumber|start_period|Value of DOY for the first day of the period studied|0|366|1|False +ParameterNumber|end_period|Value of DOY for the last day of the period studied|0|366|1|False +OutputRaster|output|Temporal integration From a231bbd9c2f0add97b08e29f8bef07149b0c47ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 15:58:11 +0200 Subject: [PATCH 14/55] Add i.gensig algorithm --- .../algs/grass7/description/i.gensig.txt | 7 +++ .../processing/algs/grass7/ext/i_gensig.py | 51 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.gensig.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_gensig.py diff --git a/python/plugins/processing/algs/grass7/description/i.gensig.txt b/python/plugins/processing/algs/grass7/description/i.gensig.txt new file mode 100644 index 000000000000..8b95d602bf7d --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.gensig.txt @@ -0,0 +1,7 @@ +i.gensig +Generates statistics for i.maxlik from raster map. +Imagery (i.*) +ParameterRaster|trainingmap|Ground truth training map|False +ParameterMultipleInput|input|Input rasters|3|False +OutputFile|signaturefile|Signature File + diff --git a/python/plugins/processing/algs/grass7/ext/i_gensig.py b/python/plugins/processing/algs/grass7/ext/i_gensig.py new file mode 100644 index 000000000000..0179472eb64e --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_gensig.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_gensig.py + ----------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import regroupRasters, file2Output, moveFile +from os import path +from ..Grass7Utils import Grass7Utils + + +def processCommand(alg): + # Transform output files in string parameter + signatureFile = alg.getOutputFromName('signaturefile') + origSigFile = signatureFile.value + shortSigFile = path.basename(origSigFile) + alg.setOutputValue('signaturefile', shortSigFile) + + signatureFile = file2Output(alg, 'signaturefile') + + # Regroup rasters + group, subgroup = regroupRasters(alg, 'input', 'group', 'subgroup') + + # Re-add signature files + alg.addOutput(signatureFile) + + # Find Grass directory + interSig = path.join(Grass7Utils.grassMapsetFolder(), 'PERMANENT', 'group', group, 'subgroup', subgroup, 'sig', shortSigFile) + moveFile(alg, interSig, origSigFile) + alg.setOutputValue('signaturefile', origSigFile) From 83b6cfdf8b293da3c8ea75b7c99c775ce54deacf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 16:10:28 +0200 Subject: [PATCH 15/55] Add i.gensigset algorithm --- .../algs/grass7/description/i.gensigset.txt | 8 +++ .../processing/algs/grass7/ext/i_gensigset.py | 51 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.gensigset.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_gensigset.py diff --git a/python/plugins/processing/algs/grass7/description/i.gensigset.txt b/python/plugins/processing/algs/grass7/description/i.gensigset.txt new file mode 100644 index 000000000000..b7d4ec7598d4 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.gensigset.txt @@ -0,0 +1,8 @@ +i.gensigset +Generates statistics for i.smap from raster map. +Imagery (i.*) +ParameterRaster|trainingmap|Ground truth training map|False +ParameterMultipleInput|input|Input rasters|3|False +ParameterNumber|maxsig|Maximum number of sub-signatures in any class|1|None|5|True +OutputFile|signaturefile|Signature File + diff --git a/python/plugins/processing/algs/grass7/ext/i_gensigset.py b/python/plugins/processing/algs/grass7/ext/i_gensigset.py new file mode 100644 index 000000000000..27a0e7c3ad1b --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_gensigset.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_gensigset.py + -------------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import regroupRasters, file2Output, moveFile +from os import path +from ..Grass7Utils import Grass7Utils + + +def processCommand(alg): + # Transform output files in string parameter + signatureFile = alg.getOutputFromName('signaturefile') + origSigFile = signatureFile.value + shortSigFile = path.basename(origSigFile) + alg.setOutputValue('signaturefile', shortSigFile) + + signatureFile = file2Output(alg, 'signaturefile') + + # Regroup rasters + group, subgroup = regroupRasters(alg, 'input', 'group', 'subgroup') + + # Re-add signature files + alg.addOutput(signatureFile) + + # Find Grass directory + interSig = path.join(Grass7Utils.grassMapsetFolder(), 'PERMANENT', 'group', group, 'subgroup', subgroup, 'sigset', shortSigFile) + moveFile(alg, interSig, origSigFile) + alg.setOutputValue('signaturefile', origSigFile) From 689d41511b8f9ab09957d924f3b46f6e9aac6961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 16:15:53 +0200 Subject: [PATCH 16/55] Add i.group algorithm --- .../algs/grass7/description/i.group.txt | 6 ++++ .../processing/algs/grass7/ext/i_group.py | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.group.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_group.py diff --git a/python/plugins/processing/algs/grass7/description/i.group.txt b/python/plugins/processing/algs/grass7/description/i.group.txt new file mode 100644 index 000000000000..43296b0ab5d4 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.group.txt @@ -0,0 +1,6 @@ +i.group +Regroup multiple mono-band rasters into a single multiband raster. +Imagery (i.*) +ParameterMultipleInput|input|Input rasters|3|False +OutputRaster|group|Multiband raster + diff --git a/python/plugins/processing/algs/grass7/ext/i_group.py b/python/plugins/processing/algs/grass7/ext/i_group.py new file mode 100644 index 000000000000..7bd2469a04ad --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_group.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_group.py + ---------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import verifyRasterNum + + +def checkParameterValuesBeforeExecuting(alg): + return verifyRasterNum(alg, 'input', 2) From d39614f7d14ae27facad4bf6abab66a08c015e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 16:17:39 +0200 Subject: [PATCH 17/55] Add i.evapo.mh parameters detection --- .../processing/algs/grass7/ext/i_evapo_mh.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/ext/i_evapo_mh.py diff --git a/python/plugins/processing/algs/grass7/ext/i_evapo_mh.py b/python/plugins/processing/algs/grass7/ext/i_evapo_mh.py new file mode 100644 index 000000000000..b2041239a594 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_evapo_mh.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_evapo_mh.py + ------------- + Date : February 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import verifyRasterNum + + +def checkParameterValuesBeforeExecuting(alg): + if alg.getParameterValue('-h') and alg.getParameterValue('precipitation'): + return alg.tr('You can\'t use original Hargreaves flag and precipitation parameter together!') + if not alg.getParameterValue('-h') and not alg.getParameterValue('precipitation'): + return alg.tr('If you don\'t use original Hargreaves flag, you must set the precipitation raster parameter!') + return None From aabcc1e0ed8c5e71e1b9641f0e93573c27fefed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sun, 24 Apr 2016 16:23:16 +0200 Subject: [PATCH 18/55] Add i.image.mosaic (needs Redmine #5742 to be implemented) --- .../processing/algs/grass7/description/i.image.mosaic.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.image.mosaic.txt diff --git a/python/plugins/processing/algs/grass7/description/i.image.mosaic.txt b/python/plugins/processing/algs/grass7/description/i.image.mosaic.txt new file mode 100644 index 000000000000..ec620e785910 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.image.mosaic.txt @@ -0,0 +1,6 @@ +i.image.mosaic +Mosaics several images and extends colormap. +Imagery (i.*) +ParameterMultipleInput|input|Input rasters|3|False +OutputRaster|output|Mosaic Raster + From ecf6d2ad37a04efcfe576da297ed48138e2ea90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Thu, 28 Apr 2016 12:03:46 +0200 Subject: [PATCH 19/55] Add i.landsat.toar algorithm --- .../grass7/description/i.landsat.toar.txt | 19 ++++++ .../algs/grass7/ext/i_landsat_toar.py | 68 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.landsat.toar.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_landsat_toar.py diff --git a/python/plugins/processing/algs/grass7/description/i.landsat.toar.txt b/python/plugins/processing/algs/grass7/description/i.landsat.toar.txt new file mode 100644 index 000000000000..6c9ad52d40f8 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.landsat.toar.txt @@ -0,0 +1,19 @@ +i.landsat.toar +Calculates top-of-atmosphere radiance or reflectance and temperature for Landsat MSS/TM/ETM+/OLI +Imagery (i.*) +ParameterMultipleInput|rasters|Landsat input rasters|3|False +ParameterFile|metfile|Name of Landsat metadata file (.met or MTL.txt)|False|True +ParameterSelection|sensor|Spacecraft sensor|mss1;mss2;mss3;mss4;mss5;tm4;tm5;tm7;oli8|7 +ParameterSelection|method|Atmospheric correction method|uncorrected;dos1;dos2;dos2b;dos3;dos4|0 +ParameterString|date|Image acquisition date (yyyy-mm-dd)|None|False|True +ParameterString|sun_elevation|Sun elevation in degrees|None|False|True +ParameterString|product_date|Image creation date (yyyy-mm-dd)|None|False|True +ParameterString|gain|Gain (H/L) of all Landsat ETM+ bands (1-5,61,62,7,8)|None|False|True +ParameterNumber|percent|Percent of solar radiance in path radiance|0.0|100.0|0.01|True +ParameterNumber|pixel|Minimum pixels to consider digital number as dark object|0|None|1000|True +ParameterNumber|rayleigh|Rayleigh atmosphere (diffuse sky irradiance)|0.0|None|0.0|True +ParameterNumber|scale|Scale factor for output|1.0|None|1.0|True +*ParameterBoolean|-r|Output at-sensor radiance instead of reflectance for all bands|False +*ParameterBoolean|-n|Input raster maps use as extension the number of the band instead the code|False +OutputDirectory|output|Output Directory + diff --git a/python/plugins/processing/algs/grass7/ext/i_landsat_toar.py b/python/plugins/processing/algs/grass7/ext/i_landsat_toar.py new file mode 100644 index 000000000000..d084ebde5a5b --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_landsat_toar.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_landsat_toar.py + ----------------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import multipleOutputDir, verifyRasterNum, regroupRasters, orderedInput +from processing.core.parameters import getParameterFromString + + +def checkParameterValuesBeforeExecuting(alg): + return verifyRasterNum(alg, 'rasters', 5, 12) + + +def processInputs(alg): + orderedInput(alg, 'rasters', + "ParameterString|input|Base name of input raster bands|None|False|False") + + +def processCommand(alg): + # Remove rasters parameter + rasters = alg.getParameterFromName('rasters') + alg.parameters.remove(rasters) + + # Remove output + output = alg.getOutputFromName('output') + alg.removeOutputFromName('output') + + # Create output parameter + param = getParameterFromString("ParameterString|output|output basename|None|False|False") + param.value = '{}_'.format(alg.getTempFilename()) + alg.addParameter(param) + + # Regroup rasters + alg.processCommand() + + # re-add output + alg.addOutput(output) + alg.addParameter(rasters) + + +def processOutputs(alg): + param = alg.getParameterFromName('output') + multipleOutputDir(alg, 'output', param.value) + + # Delete output parameter + alg.parameters.remove(param) From 68f42a406c9141370a7ce09924f2033bd20e9f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Thu, 28 Apr 2016 12:30:44 +0200 Subject: [PATCH 20/55] Add i.vi algorithm --- .../processing/algs/grass7/description/i.vi.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.vi.txt diff --git a/python/plugins/processing/algs/grass7/description/i.vi.txt b/python/plugins/processing/algs/grass7/description/i.vi.txt new file mode 100644 index 000000000000..6359a3959dfd --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.vi.txt @@ -0,0 +1,15 @@ +i.vi +Calculates different types of vegetation indices. +Imagery (i.*) +ParameterRaster|red|Name of input red channel surface reflectance map [0.0-1.0]|True +ParameterSelection|viname|Type of vegetation index|arvi;dvi;evi;evi2;gvi;gari;gemi;ipvi;msavi;msavi2;ndvi;pvi;savi;sr;vari;wdvi|10 +ParameterRaster|nir|Name of input nir channel surface reflectance map [0.0-1.0]|True +ParameterRaster|green|Name of input green channel surface reflectance map [0.0-1.0]|True +ParameterRaster|blue|Name of input blue channel surface reflectance map [0.0-1.0]|True +ParameterRaster|band5|Name of input 5th channel surface reflectance map [0.0-1.0]|True +ParameterRaster|band7|Name of input 7th channel surface reflectance map [0.0-1.0]|True +ParameterString|soil_line_slope|Value of the slope of the soil line (MSAVI2 only)|None|False|True +ParameterString|soil_line_intercept|Value of the factor of reduction of soil noise (MSAVI2 only)|None|False|True +ParameterString|soil_noise_reduction|Value of the slope of the soil line (MSAVI2 only)|None|False|True +ParameterSelection|storage_bit|Maximum bits for digital numbers|7;8;9;10;16|1 +OutputRaster|output|Vegetation Index From 297c632566f540147982acb760ceb08082dd9ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Thu, 28 Apr 2016 12:42:53 +0200 Subject: [PATCH 21/55] Add i.tasscap algorithm --- .../algs/grass7/description/i.tasscap.txt | 6 ++ .../processing/algs/grass7/ext/i_tasscap.py | 57 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.tasscap.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_tasscap.py diff --git a/python/plugins/processing/algs/grass7/description/i.tasscap.txt b/python/plugins/processing/algs/grass7/description/i.tasscap.txt new file mode 100644 index 000000000000..1caab8aa33f7 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.tasscap.txt @@ -0,0 +1,6 @@ +i.tasscap +Performs Tasseled Cap (Kauth Thomas) transformation. +Imagery (i.*) +ParameterMultipleInput|input|Input rasters. Landsat4-7: bands 1,2,3,4,5,7; Landsat8: bands 2,3,4,5,6,7; MODIS: bands 1,2,3,4,5,6,7|3|False +ParameterSelection|sensor|Satellite sensor|landsat4_tm;landsat5_tm;landsat7_etm;landsat8_oli;modis|0 +OutputDirectory|output|Output Directory diff --git a/python/plugins/processing/algs/grass7/ext/i_tasscap.py b/python/plugins/processing/algs/grass7/ext/i_tasscap.py new file mode 100644 index 000000000000..752dddd895d0 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_tasscap.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_tasscap.py + ------------ + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import multipleOutputDir, verifyRasterNum, regroupRasters +from processing.core.parameters import getParameterFromString + + +def checkParameterValuesBeforeExecuting(alg): + return verifyRasterNum(alg, 'input', 6, 8) + + +def processCommand(alg): + # Remove output + output = alg.getOutputFromName('output') + alg.removeOutputFromName('output') + + # Create output parameter + param = getParameterFromString("ParameterString|output|output basename|None|False|False") + param.value = alg.getTempFilename() + alg.addParameter(param) + + alg.processCommand() + + # re-add output + alg.addOutput(output) + + +def processOutputs(alg): + param = alg.getParameterFromName('output') + multipleOutputDir(alg, 'output', param.value) + + # Delete output parameter + alg.parameters.remove(param) From 7bc533884bfe39e42f86c1de91bedc5b2185e61f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Mon, 2 May 2016 14:15:33 +0200 Subject: [PATCH 22/55] Add i.smap algorithm --- .../algs/grass7/description/i.smap.txt | 9 ++++ .../plugins/processing/algs/grass7/ext/i.py | 54 ++++++++++++++++--- .../processing/algs/grass7/ext/i_smap.py | 33 ++++++++++++ 3 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 python/plugins/processing/algs/grass7/description/i.smap.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_smap.py diff --git a/python/plugins/processing/algs/grass7/description/i.smap.txt b/python/plugins/processing/algs/grass7/description/i.smap.txt new file mode 100644 index 000000000000..67250d02b4b5 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.smap.txt @@ -0,0 +1,9 @@ +i.smap +Performs contextual image classification using sequential maximum a posteriori (SMAP) estimation. +Imagery (i.*) +ParameterMultipleInput|input|Input rasters|3|False +ParameterFile|signaturefile|Name of input file containing signatures|False|False +ParameterNumber|blocksize|Size of submatrix to process at one time|1|None|1024|True +*ParameterBoolean|-m|Use maximum likelihood estimation (instead of smap)|False +OutputRaster|output|Classification +OutputRaster|goodness|Goodness_of_fit diff --git a/python/plugins/processing/algs/grass7/ext/i.py b/python/plugins/processing/algs/grass7/ext/i.py index a3898ad35799..1f8e1808694b 100644 --- a/python/plugins/processing/algs/grass7/ext/i.py +++ b/python/plugins/processing/algs/grass7/ext/i.py @@ -27,6 +27,8 @@ from processing.core.parameters import ParameterRaster, getParameterFromString from processing.tools.system import isWindows +from ..Grass7Utils import Grass7Utils +from os import path def multipleOutputDir(alg, field, basename=None): @@ -92,9 +94,12 @@ def orderedInput(alg, inputParameter, targetParameterDef): return rootFilename -def regroupRasters(alg, field, groupField, subgroupField=None): +def regroupRasters(alg, field, groupField, subgroupField=None, sigsetField=None): """ Group multiple input rasters into a group + * If there is a subgroupField, a subgroup will automatically created. + * When a sigset file is provided, the file is copied into the sigset + directory of the subgroup. """ # List of rasters names rasters = alg.getParameterFromName(field) @@ -118,6 +123,19 @@ def regroupRasters(alg, field, groupField, subgroupField=None): ) alg.commands.append(command) + # If there is a sigset and a subgroupField, we copy the sigset file + if subgroupField and sigsetField: + sigsetFile = alg.getParameterValue(sigsetField) + shortSigsetFile = path.basename(sigsetFile) + if sigsetFile: + interSigsetFile = path.join(Grass7Utils.grassMapsetFolder(), + 'PERMANENT', + 'group', group.value, + 'subgroup', subgroup.value, + 'sigset', shortSigsetFile) + copyFile(alg, sigsetFile, interSigsetFile) + alg.setParameterValue(sigsetField, shortSigsetFile) + # modify parameters values alg.processCommand() @@ -127,8 +145,6 @@ def regroupRasters(alg, field, groupField, subgroupField=None): alg.parameters.remove(group) if subgroupField: alg.parameters.remove(subgroup) - - if subgroupField: return group.value, subgroup.value return group.value @@ -176,10 +192,32 @@ def file2Output(alg, output): return outputFile +def createDestDir(alg, toFile): + """ Generates an mkdir command for GRASS7 script """ + # Creates the destination directory + command = "{} {}".format( + "MD" if isWindows() else "mkdir -p", + path.dirname(toFile) + ) + alg.commands.append(command) + + def moveFile(alg, fromFile, toFile): - # move the file - if isWindows(): - command = "MOVE /Y {} {}".format(fromFile, toFile) - else: - command = "mv -f {} {}".format(fromFile, toFile) + """ Generates a move command for GRASS7 script """ + createDestDir(alg, toFile) + command = "{} {} {}".format( + "MOVE /Y" if isWindows() else "mv -f", + fromFile, + toFile + ) + alg.commands.append(command) + + +def copyFile(alg, fromFile, toFile): + """ Generates a copy command for GRASS7 script """ + createDestDir(alg, toFile) + command = "{} {} {}".format( + "COPY /Y" if isWindows() else "cp -f", + fromFile, + toFile) alg.commands.append(command) diff --git a/python/plugins/processing/algs/grass7/ext/i_smap.py b/python/plugins/processing/algs/grass7/ext/i_smap.py new file mode 100644 index 000000000000..187cb57e2ecf --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_smap.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_smap.py + --------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import regroupRasters, file2Output + + +def processCommand(alg): + # Regroup rasters + regroupRasters(alg, 'input', 'group', 'subgroup', 'signaturefile') From a73ed12dd7e01cb3c7d4cdae00680286a8b34d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Mon, 2 May 2016 14:59:36 +0200 Subject: [PATCH 23/55] Add i.segment algorithm --- .../algs/grass7/description/i.segment.txt | 16 +++++++++ .../plugins/processing/algs/grass7/ext/i.py | 2 +- .../processing/algs/grass7/ext/i_segment.py | 33 +++++++++++++++++++ .../processing/algs/grass7/ext/i_tasscap.py | 2 +- 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 python/plugins/processing/algs/grass7/description/i.segment.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_segment.py diff --git a/python/plugins/processing/algs/grass7/description/i.segment.txt b/python/plugins/processing/algs/grass7/description/i.segment.txt new file mode 100644 index 000000000000..a66fb8664218 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.segment.txt @@ -0,0 +1,16 @@ +i.segment +Identifies segments (objects) from imagery data. +Imagery (i.*) +ParameterMultipleInput|input|Input rasters|3|False +ParameterNumber|threshold|Difference threshold between 0 and 1|0.0|1.0|0.5|False +ParameterSelection|method|Segmentation method|region_growing|0 +ParameterSelection|similarity|Similarity calculation method|euclidean;manhattan|0 +ParameterNumber|minsize|Minimum number of cells in a segment|1|100000|1|True +ParameterNumber|memory|Amount of memory to use in MB|1|None|300|True +ParameterNumber|iterations|Maximum number of iterations|1|None|20|True +ParameterRaster|seeds|Name for input raster map with starting seeds|True +ParameterRaster|bounds|Name of input bounding/constraining raster map|True +*ParameterBoolean|-d|Use 8 neighbors (3x3 neighborhood) instead of the default 4 neighbors for each pixel|False +*ParameterBoolean|-w|Weighted input, do not perform the default scaling of input raster maps|False +OutputRaster|output|Segmented Raster +OutputRaster|goodness|Goodness Raster diff --git a/python/plugins/processing/algs/grass7/ext/i.py b/python/plugins/processing/algs/grass7/ext/i.py index 1f8e1808694b..564096a3e0c6 100644 --- a/python/plugins/processing/algs/grass7/ext/i.py +++ b/python/plugins/processing/algs/grass7/ext/i.py @@ -118,7 +118,7 @@ def regroupRasters(alg, field, groupField, subgroupField=None, sigsetField=None) command = 'i.group group={}{} input={}'.format( group.value, - ' subgroup={}'.format(subgroup.value) if subgroup else '', + ' subgroup={}'.format(subgroup.value) if subgroupField else '', ','.join([alg.exportedLayers[f] for f in rastersList]) ) alg.commands.append(command) diff --git a/python/plugins/processing/algs/grass7/ext/i_segment.py b/python/plugins/processing/algs/grass7/ext/i_segment.py new file mode 100644 index 000000000000..6fd217410e5b --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_segment.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_segment.py + ------------ + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import regroupRasters + + +def processCommand(alg): + # Regroup rasters + regroupRasters(alg, 'input', 'group') diff --git a/python/plugins/processing/algs/grass7/ext/i_tasscap.py b/python/plugins/processing/algs/grass7/ext/i_tasscap.py index 752dddd895d0..e0f01a1575d9 100644 --- a/python/plugins/processing/algs/grass7/ext/i_tasscap.py +++ b/python/plugins/processing/algs/grass7/ext/i_tasscap.py @@ -25,7 +25,7 @@ __revision__ = '$Format:%H$' -from i import multipleOutputDir, verifyRasterNum, regroupRasters +from i import multipleOutputDir, verifyRasterNum from processing.core.parameters import getParameterFromString From cce451c722997b8d69e7c05a54f657221defc03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Mon, 2 May 2016 15:43:55 +0200 Subject: [PATCH 24/55] Add i.pca algorithm --- .../algs/grass7/description/i.pca.txt | 10 ++++ .../plugins/processing/algs/grass7/ext/i.py | 4 +- .../processing/algs/grass7/ext/i_pca.py | 56 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 python/plugins/processing/algs/grass7/description/i.pca.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_pca.py diff --git a/python/plugins/processing/algs/grass7/description/i.pca.txt b/python/plugins/processing/algs/grass7/description/i.pca.txt new file mode 100644 index 000000000000..d4351af75047 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.pca.txt @@ -0,0 +1,10 @@ +i.pca +Principal components analysis (PCA) for image processing. +Imagery (i.*) +ParameterMultipleInput|input|Name of two or more input raster maps|3|False +ParameterString|rescale|Rescaling range for output maps. For no rescaling use 0,0|0,255|False|True +ParameterNumber|percent|Cumulative percent importance for filtering|50.0|99.0|99.0|True +*ParameterBoolean|-n|Normalize (center and scale) input maps|False +*ParameterBoolean|-f|Output will be filtered input bands|False +OutputDirectory|output|Output Directory + diff --git a/python/plugins/processing/algs/grass7/ext/i.py b/python/plugins/processing/algs/grass7/ext/i.py index 564096a3e0c6..d9ada90e09fe 100644 --- a/python/plugins/processing/algs/grass7/ext/i.py +++ b/python/plugins/processing/algs/grass7/ext/i.py @@ -45,7 +45,7 @@ def multipleOutputDir(alg, field, basename=None): # Otherwise, export everything else: commands = ["for r in $(g.list type=rast); do".format(basename)] - commands.append(" r.out.gdal -c -t input=${{r}} output={}/${{r}}.tif createopt=\"TFW=YES,COMPRESS=LZW\"".format(outputDir)) + commands.append(" r.out.gdal -c -t -f input=${{r}} output={}/${{r}}.tif createopt=\"TFW=YES,COMPRESS=LZW\"".format(outputDir)) commands.append("done") alg.commands.extend(commands) alg.outputCommands.extend(commands) @@ -160,7 +160,7 @@ def exportInputRasters(alg, rasterDic): for inputName, outputName in rasterDic.iteritems(): inputRaster = alg.getParameterValue(inputName) outputRaster = alg.getOutputFromName(outputName) - command = 'r.out.gdal -c -t --overwrite createopt="TFW=YES,COMPRESS=LZW" input={} output=\"{}\"'.format( + command = 'r.out.gdal -c -t -f --overwrite createopt="TFW=YES,COMPRESS=LZW" input={} output=\"{}\"'.format( alg.exportedLayers[inputRaster], outputRaster.value ) diff --git a/python/plugins/processing/algs/grass7/ext/i_pca.py b/python/plugins/processing/algs/grass7/ext/i_pca.py new file mode 100644 index 000000000000..180da937e705 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_pca.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_pca.py + -------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import multipleOutputDir, verifyRasterNum +from processing.core.parameters import getParameterFromString + + +def checkParameterValuesBeforeExecuting(alg): + return verifyRasterNum(alg, 'input', 2) + + +def processCommand(alg): + # Remove output + output = alg.getOutputFromName('output') + alg.removeOutputFromName('output') + + # Create output parameter + param = getParameterFromString("ParameterString|output|output basename|None|False|False") + param.value = alg.getTempFilename() + alg.addParameter(param) + + alg.processCommand() + # re-add output + alg.addOutput(output) + + +def processOutputs(alg): + param = alg.getParameterFromName('output') + multipleOutputDir(alg, 'output', param.value) + + # Delete output parameter + alg.parameters.remove(param) From 5182748e103fa12989371ed04adfb45ee775d597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Mon, 2 May 2016 15:54:11 +0200 Subject: [PATCH 25/55] Add i.topo.corr algorithm --- .../grass7/description/i.topo.corr.ill.txt | 8 +++ .../algs/grass7/description/i.topo.corr.txt | 9 ++++ .../processing/algs/grass7/ext/i_topo_corr.py | 52 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.topo.corr.ill.txt create mode 100644 python/plugins/processing/algs/grass7/description/i.topo.corr.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_topo_corr.py diff --git a/python/plugins/processing/algs/grass7/description/i.topo.corr.ill.txt b/python/plugins/processing/algs/grass7/description/i.topo.corr.ill.txt new file mode 100644 index 000000000000..9982db8bcac1 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.topo.corr.ill.txt @@ -0,0 +1,8 @@ +i.topo.corr +i.topo.coor.ill - Creates illumination model for topographic correction of reflectance. +Imagery (i.*) +ParameterRaster|basemap|Name of elevation raster map|False +ParameterNumber|zenith|Solar zenith in degrees|0.0|360.0|0.0|False +ParameterNumber|azimuth|Solar azimuth in degrees|0.0|360.0|0.0|False +Hardcoded|-i +OutputRaster|output|Illumination Model diff --git a/python/plugins/processing/algs/grass7/description/i.topo.corr.txt b/python/plugins/processing/algs/grass7/description/i.topo.corr.txt new file mode 100644 index 000000000000..fb53a95d2b54 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.topo.corr.txt @@ -0,0 +1,9 @@ +i.topo.corr +Computes topographic correction of reflectance. +Imagery (i.*) +ParameterMultipleInput|input|Name of reflectance raster maps to be corrected topographically|3|False +ParameterRaster|basemap|Name of illumination input base raster map|False +ParameterNumber|zenith|Solar zenith in degrees|0.0|360.0|0.0|False +ParameterSelection|method|Topographic correction method|cosine;minnaert;c-factor;percent|0 +*ParameterBoolean|-s|Scale output to input and copy color rules|False +OutputDirectory|output|Output Directory diff --git a/python/plugins/processing/algs/grass7/ext/i_topo_corr.py b/python/plugins/processing/algs/grass7/ext/i_topo_corr.py new file mode 100644 index 000000000000..62038f55a1e9 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_topo_corr.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_topo_corr.py + -------------- + Date : April 2016 + Copyright : (C) 2016 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__ = 'April 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import multipleOutputDir, verifyRasterNum +from processing.core.parameters import getParameterFromString + + +def processCommand(alg): + # Remove output + output = alg.getOutputFromName('output') + alg.removeOutputFromName('output') + + # Create output parameter + param = getParameterFromString("ParameterString|output|output basename|None|False|False") + param.value = alg.getTempFilename() + alg.addParameter(param) + + alg.processCommand() + # re-add output + alg.addOutput(output) + + +def processOutputs(alg): + param = alg.getParameterFromName('output') + multipleOutputDir(alg, 'output', param.value) + + # Delete output parameter + alg.parameters.remove(param) From c9f56125b90181eea2fa025cd8d612867d6e6187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Mon, 2 May 2016 16:16:32 +0200 Subject: [PATCH 26/55] Add i.pansharpen algorithm --- .../algs/grass7/description/i.pansharpen.txt | 14 +++++ .../algs/grass7/ext/i_pansharpen.py | 58 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.pansharpen.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_pansharpen.py diff --git a/python/plugins/processing/algs/grass7/description/i.pansharpen.txt b/python/plugins/processing/algs/grass7/description/i.pansharpen.txt new file mode 100644 index 000000000000..e945b24d6475 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.pansharpen.txt @@ -0,0 +1,14 @@ +i.pansharpen +Image fusion algorithms to sharpen multispectral with high-res panchromatic channels +Imagery (i.*) +ParameterRaster|red|Name of red channel|False +ParameterRaster|green|Name of green channel|False +ParameterRaster|blue|Name of blue channel|False +ParameterRaster|pan|Name of raster map to be used for high resolution panchromatic channel|False +ParameterSelection|method|Method|brovey;ihs;pca|1 +*ParameterBoolean|-l|Rebalance blue channel for LANDSAT|False +*ParameterBoolean|-s|Process bands serially (default: run in parallel)|False +OutputRaster|redoutput|Enhanced Red +OutputRaster|greenoutput|Enhanced Green +OutputRaster|blueoutput|Enhanced Blue + diff --git a/python/plugins/processing/algs/grass7/ext/i_pansharpen.py b/python/plugins/processing/algs/grass7/ext/i_pansharpen.py new file mode 100644 index 000000000000..eeea0077403b --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_pansharpen.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_pansharpen.py + --------------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import exportInputRasters +from processing.core.parameters import ParameterRaster, getParameterFromString + + +def processCommand(alg): + + # Temporary remove outputs: + outputs = [alg.getOutputFromName('{}output'.format(f)) for f in ['red', 'green', 'blue']] + for out in outputs: + alg.removeOutputFromName(out.name) + + # create a false output + base = getParameterFromString('ParameterString|output|Name for output basename raster map(s)|None|False|False') + base.value = alg.getTempFilename() + alg.addParameter(base) + alg.processCommand() + + # Re-add outputs + for output in outputs: + alg.addOutput(output) + + +def processOutputs(alg): + base = alg.getParameterValue('output') + for channel in ['red', 'green', 'blue']: + command = 'r.out.gdal -c -t -f --overwrite createopt="TFW=YES,COMPRESS=LZW" input={} output=\"{}\"'.format( + '{}_{}'.format(base, channel), + alg.getOutputValue('{}output'.format(channel)) + ) + alg.commands.append(command) + alg.outputCommands.append(command) From 02bbcbb81a3b413bff571d7b5d7d9c4de9f6f950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Mon, 2 May 2016 16:29:26 +0200 Subject: [PATCH 27/55] Add i.oif algorithm --- .../algs/grass7/description/i.oif.txt | 8 ++++ .../processing/algs/grass7/ext/i_oif.py | 42 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.oif.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_oif.py diff --git a/python/plugins/processing/algs/grass7/description/i.oif.txt b/python/plugins/processing/algs/grass7/description/i.oif.txt new file mode 100644 index 000000000000..c5e5677ec216 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.oif.txt @@ -0,0 +1,8 @@ +i.oif +Calculates Optimum-Index-Factor table for spectral bands +Imagery (i.*) +ParameterMultipleInput|input|Name of input raster map(s)|3|False +*ParameterBoolean|-g|Print in shell script style|False +*ParameterBoolean|-s|Process bands serially (default: run in parallel)|False +OutputFile|output|OIF File + diff --git a/python/plugins/processing/algs/grass7/ext/i_oif.py b/python/plugins/processing/algs/grass7/ext/i_oif.py new file mode 100644 index 000000000000..50caeefab9d2 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_oif.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_oif.py + -------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import file2Output, verifyRasterNum + + +def checkParameterValuesBeforeExecuting(alg): + return verifyRasterNum(alg, 'input', 4) + + +def processCommand(alg): + # Transform output file in string parameter + oifFile = file2Output(alg, 'output') + + alg.processCommand() + + # Re-add output file + alg.addOutput(oifFile) From 1d1534279fdaea829c39187e5d0a0640666400b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Tue, 3 May 2016 10:35:38 +0200 Subject: [PATCH 28/55] Add i.cca algorithm (segfaults from GRASS) --- .../algs/grass7/description/i.cca.txt | 6 ++ .../plugins/processing/algs/grass7/ext/i.py | 43 ++++++++------ .../processing/algs/grass7/ext/i_cca.py | 58 +++++++++++++++++++ .../processing/algs/grass7/ext/i_smap.py | 2 +- 4 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 python/plugins/processing/algs/grass7/description/i.cca.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_cca.py diff --git a/python/plugins/processing/algs/grass7/description/i.cca.txt b/python/plugins/processing/algs/grass7/description/i.cca.txt new file mode 100644 index 000000000000..7492fcb00acf --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.cca.txt @@ -0,0 +1,6 @@ +i.cca +Canonical components analysis (CCA) program for image processing. +Imagery (i.*) +ParameterMultipleInput|input|Input rasters (2 to 8)|3|False +ParameterFile|signature|File containing spectral signatures|False|False +OutputDirectory|output|Output Directory diff --git a/python/plugins/processing/algs/grass7/ext/i.py b/python/plugins/processing/algs/grass7/ext/i.py index d9ada90e09fe..3a7bb6f2be81 100644 --- a/python/plugins/processing/algs/grass7/ext/i.py +++ b/python/plugins/processing/algs/grass7/ext/i.py @@ -4,9 +4,9 @@ *************************************************************************** i.py ---- - Date : February 2016 + Date : April 2016 Copyright : (C) 2016 by Médéric Ribreux - Email : medspx at medspx dot fr + Email : mederic dot ribreux at medspx dot fr *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * @@ -18,7 +18,7 @@ """ __author__ = 'Médéric Ribreux' -__date__ = 'March 2016' +__date__ = 'April 2016' __copyright__ = '(C) 2016, Médéric Ribreux' # This will get replaced with a git SHA1 when you do a git archive @@ -94,12 +94,13 @@ def orderedInput(alg, inputParameter, targetParameterDef): return rootFilename -def regroupRasters(alg, field, groupField, subgroupField=None, sigsetField=None): +def regroupRasters(alg, field, groupField, subgroupField=None, extFile=None): """ Group multiple input rasters into a group * If there is a subgroupField, a subgroup will automatically created. - * When a sigset file is provided, the file is copied into the sigset + * When an external file is provided, the file is copied into the respective directory of the subgroup. + * extFile is a dict of the form 'parameterName':'directory name'. """ # List of rasters names rasters = alg.getParameterFromName(field) @@ -123,24 +124,32 @@ def regroupRasters(alg, field, groupField, subgroupField=None, sigsetField=None) ) alg.commands.append(command) - # If there is a sigset and a subgroupField, we copy the sigset file - if subgroupField and sigsetField: - sigsetFile = alg.getParameterValue(sigsetField) - shortSigsetFile = path.basename(sigsetFile) - if sigsetFile: - interSigsetFile = path.join(Grass7Utils.grassMapsetFolder(), - 'PERMANENT', - 'group', group.value, - 'subgroup', subgroup.value, - 'sigset', shortSigsetFile) - copyFile(alg, sigsetFile, interSigsetFile) - alg.setParameterValue(sigsetField, shortSigsetFile) + # Handle external files + origExtParams = {} + if subgroupField and extFile: + for ext in extFile.keys(): + extFileName = alg.getParameterValue(ext) + if extFileName: + shortFileName = path.basename(extFileName) + destPath = path.join(Grass7Utils.grassMapsetFolder(), + 'PERMANENT', + 'group', group.value, + 'subgroup', subgroup.value, + extFile[ext], shortFileName) + copyFile(alg, extFileName, destPath) + origExtParams[ext] = extFileName + alg.setParameterValue(ext, shortFileName) # modify parameters values alg.processCommand() # Re-add input rasters alg.addParameter(rasters) + + # replace external files value with original value + for param in origExtParams.keys(): + alg.setParameterValue(param, origExtParams[param]) + # Delete group: alg.parameters.remove(group) if subgroupField: diff --git a/python/plugins/processing/algs/grass7/ext/i_cca.py b/python/plugins/processing/algs/grass7/ext/i_cca.py new file mode 100644 index 000000000000..d2b7a2cd0d62 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_cca.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_cca.py + -------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import multipleOutputDir, verifyRasterNum, regroupRasters +from processing.core.parameters import getParameterFromString + + +def checkParameterValuesBeforeExecuting(alg): + return verifyRasterNum(alg, 'input', 2, 8) + + +def processCommand(alg): + # Remove output + output = alg.getOutputFromName('output') + alg.removeOutputFromName('output') + + # Create output parameter + param = getParameterFromString("ParameterString|output|output basename|None|False|False") + param.value = alg.getTempFilename() + alg.addParameter(param) + + # Regroup rasters + regroupRasters(alg, 'input', 'group', 'subgroup', {'signature': 'sig'}) + + # re-add output + alg.addOutput(output) + + +def processOutputs(alg): + param = alg.getParameterFromName('output') + multipleOutputDir(alg, 'output', param.value) + + # Delete output parameter + alg.parameters.remove(param) diff --git a/python/plugins/processing/algs/grass7/ext/i_smap.py b/python/plugins/processing/algs/grass7/ext/i_smap.py index 187cb57e2ecf..8ba96ec32f8d 100644 --- a/python/plugins/processing/algs/grass7/ext/i_smap.py +++ b/python/plugins/processing/algs/grass7/ext/i_smap.py @@ -30,4 +30,4 @@ def processCommand(alg): # Regroup rasters - regroupRasters(alg, 'input', 'group', 'subgroup', 'signaturefile') + regroupRasters(alg, 'input', 'group', 'subgroup', {'signaturefile': 'sigset'}) From fdce74885aca40ec9640a01ef5e17f18e4a154c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Tue, 3 May 2016 10:49:20 +0200 Subject: [PATCH 29/55] Add i.maxlik algorithm --- .../algs/grass7/description/i.maxlik.txt | 7 ++++ .../processing/algs/grass7/ext/i_maxlik.py | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.maxlik.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_maxlik.py diff --git a/python/plugins/processing/algs/grass7/description/i.maxlik.txt b/python/plugins/processing/algs/grass7/description/i.maxlik.txt new file mode 100644 index 000000000000..bc060085141c --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.maxlik.txt @@ -0,0 +1,7 @@ +i.maxlik +Classifies the cell spectral reflectances in imagery data. +Imagery (i.*) +ParameterMultipleInput|input|Input rasters|3|False +ParameterFile|signaturefile|Name of input file containing signatures|False|False +OutputRaster|output|Classification +OutputRaster|reject|Reject Threshold diff --git a/python/plugins/processing/algs/grass7/ext/i_maxlik.py b/python/plugins/processing/algs/grass7/ext/i_maxlik.py new file mode 100644 index 000000000000..629a1dc829d7 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_maxlik.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_maxlik.py + ----------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import regroupRasters, file2Output + + +def processCommand(alg): + # Regroup rasters + regroupRasters(alg, 'input', 'group', 'subgroup', {'signaturefile': 'sig'}) From 033fd1fa8bef4d9404757709a1b965dcf2351219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Wed, 4 May 2016 08:55:00 +0200 Subject: [PATCH 30/55] Add i.modis.qc algorithm --- .../processing/algs/grass7/description/i.modis.qc.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.modis.qc.txt diff --git a/python/plugins/processing/algs/grass7/description/i.modis.qc.txt b/python/plugins/processing/algs/grass7/description/i.modis.qc.txt new file mode 100644 index 000000000000..d13dc5b965cd --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.modis.qc.txt @@ -0,0 +1,9 @@ +i.modis.qc +Extracts quality control parameters from MODIS QC layers. +Imagery (i.*) +ParameterRaster|input|Name of input surface reflectance QC layer [bit array]|False +ParameterSelection|productname|Name of MODIS product type|mod09Q1;mod09A1;mod09A1s;mod09CMG;mod09CMGs;mod09CMGi;mod11A1;mod11A2;mod13A2;mcd43B2;mcd43B2q|8 +ParameterSelection|qcname|Name of QC type to extract|adjcorr;atcorr;cloud;data_quality;diff_orbit_from_500m;modland_qa;mandatory_qa_11A1;data_quality_flag_11A1;emis_error_11A1;lst_error_11A1;data_quality_flag_11A2;emis_error_11A2;mandatory_qa_11A2;lst_error_11A2;aerosol_quantity;brdf_correction_performed;cirrus_detected;cloud_shadow;cloud_state;internal_cloud_algorithm;internal_fire_algorithm;internal_snow_mask;land_water;mod35_snow_ice;pixel_adjacent_to_cloud;icm_cloudy;icm_clear;icm_high_clouds;icm_low_clouds;icm_snow;icm_fire;icm_sun_glint;icm_dust;icm_cloud_shadow;icm_pixel_is_adjacent_to_cloud;icm_cirrus;icm_pan_flag;icm_criteria_for_aerosol_retrieval;icm_aot_has_clim_val;modland_qa;vi_usefulness;aerosol_quantity;pixel_adjacent_to_cloud;brdf_correction_performed;mixed_clouds;land_water;possible_snow_ice;possible_shadow;platform;land_water;sun_z_angle_at_local_noon;brdf_correction_performed|5 +ParameterString|band|Band number of MODIS product (mod09Q1=[1,2],mod09A1=[1-7],m[o/y]d09CMG=[1-7], mcd43B2q=[1-7])|None|False|True +OutputRaster|output|QC Classification + From 90758b533cf774ed0a63a60d8bfc9c45b3a53d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Wed, 4 May 2016 10:02:32 +0200 Subject: [PATCH 31/55] Add i.in.spotvgt algorithm --- .../algs/grass7/description/i.in.spotvgt.txt | 7 ++++ .../algs/grass7/ext/i_in_spotvgt.py | 33 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.in.spotvgt.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_in_spotvgt.py diff --git a/python/plugins/processing/algs/grass7/description/i.in.spotvgt.txt b/python/plugins/processing/algs/grass7/description/i.in.spotvgt.txt new file mode 100644 index 000000000000..1f47a9758174 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.in.spotvgt.txt @@ -0,0 +1,7 @@ +i.in.spotvgt +Imports SPOT VGT NDVI data into a raster map. +Imagery (i.*) +ParameterRaster|input|Name of input SPOT VGT NDVI HDF file|False +*ParameterBoolean|-a|Also import quality map (SM status map layer) and filter NDVI map|False +OutputRaster|output|SPOT NDVI Raster + diff --git a/python/plugins/processing/algs/grass7/ext/i_in_spotvgt.py b/python/plugins/processing/algs/grass7/ext/i_in_spotvgt.py new file mode 100644 index 000000000000..e3293d242536 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_in_spotvgt.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_in_spotvgt.py + --------------- + Date : April 2016 + Copyright : (C) 2016 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__ = 'April 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + + +def processInputs(alg): + # Here, we apply directly the algorithm + # So we just need to get the projection of the layer ! + layer = alg.getParameterValue('input') + alg.setSessionProjectionFromLayer(layer, alg.commands) From 8ff07cf91acfe20fb562123f9993bbbd08363f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Wed, 4 May 2016 15:18:27 +0200 Subject: [PATCH 32/55] Add i.landsat.acc algorithm --- .../grass7/description/i.landsat.acca.txt | 14 ++++++ .../plugins/processing/algs/grass7/ext/i.py | 8 ++- .../algs/grass7/ext/i_landsat_acca.py | 49 +++++++++++++++++++ .../algs/grass7/ext/i_landsat_toar.py | 4 +- 4 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 python/plugins/processing/algs/grass7/description/i.landsat.acca.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_landsat_acca.py diff --git a/python/plugins/processing/algs/grass7/description/i.landsat.acca.txt b/python/plugins/processing/algs/grass7/description/i.landsat.acca.txt new file mode 100644 index 000000000000..4b7e8f23bdb4 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.landsat.acca.txt @@ -0,0 +1,14 @@ +i.landsat.acca +Performs Landsat TM/ETM+ Automatic Cloud Cover Assessment (ACCA). +Imagery (i.*) +ParameterMultipleInput|rasters|Landsat input rasters|3|False +ParameterNumber|b56composite|B56composite (step 6)|0|None|225|True +ParameterNumber|b45ratio|B45ratio: Desert detection (step 10)|0|None|1|True +ParameterNumber|histogram|Number of classes in the cloud temperature histogram|0|None|100|True +*ParameterBoolean|-5|Data is Landsat-5 TM|False +*ParameterBoolean|-f|Apply post-processing filter to remove small holes|False +*ParameterBoolean|-x|Always use cloud signature (step 14)|False +*ParameterBoolean|-2|Bypass second-pass processing, and merge warm (not ambiguous) and cold clouds|False +*ParameterBoolean|-s|Include a category for cloud shadows|False +OutputRaster|output|ACCA Raster + diff --git a/python/plugins/processing/algs/grass7/ext/i.py b/python/plugins/processing/algs/grass7/ext/i.py index 3a7bb6f2be81..3b58250e8f7b 100644 --- a/python/plugins/processing/algs/grass7/ext/i.py +++ b/python/plugins/processing/algs/grass7/ext/i.py @@ -51,7 +51,7 @@ def multipleOutputDir(alg, field, basename=None): alg.outputCommands.extend(commands) -def orderedInput(alg, inputParameter, targetParameterDef): +def orderedInput(alg, inputParameter, targetParameterDef, numSeq=None): """Inport multiple rasters in the order""" rasters = alg.getParameterValue(inputParameter).split(';') # TODO: make targetParameter @@ -59,12 +59,16 @@ def orderedInput(alg, inputParameter, targetParameterDef): rootFilename = '{}_'.format(alg.getTempFilename()) inputParameter.value = rootFilename alg.addParameter(inputParameter) + # Handle specific range + if numSeq is None: + numSeq = range(1, len(rasters) + 1) + for idx in range(len(rasters)): layer = rasters[idx] if layer in alg.exportedLayers.keys(): continue else: - destFilename = '{}{}'.format(rootFilename, idx + 1) + destFilename = '{}{}'.format(rootFilename, numSeq[idx]) alg.setSessionProjectionFromLayer(layer, alg.commands) alg.exportedLayers[layer] = destFilename command = 'r.external input={} band=1 output={} --overwrite -o'.format(layer, destFilename) diff --git a/python/plugins/processing/algs/grass7/ext/i_landsat_acca.py b/python/plugins/processing/algs/grass7/ext/i_landsat_acca.py new file mode 100644 index 000000000000..cef94b077a01 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_landsat_acca.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_landsat_acca.py + ----------------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import verifyRasterNum, orderedInput + + +def checkParameterValuesBeforeExecuting(alg): + return verifyRasterNum(alg, 'rasters', 5, 5) + + +def processInputs(alg): + orderedInput(alg, 'rasters', + "ParameterString|input|Base name of input raster bands|None|False|False", + [2, 3, 4, 5, 61]) + + +def processCommand(alg): + # Remove rasters parameter + rasters = alg.getParameterFromName('rasters') + alg.parameters.remove(rasters) + + alg.processCommand() + + # re-add rasters + alg.addParameter(rasters) diff --git a/python/plugins/processing/algs/grass7/ext/i_landsat_toar.py b/python/plugins/processing/algs/grass7/ext/i_landsat_toar.py index d084ebde5a5b..4e360cd7e52f 100644 --- a/python/plugins/processing/algs/grass7/ext/i_landsat_toar.py +++ b/python/plugins/processing/algs/grass7/ext/i_landsat_toar.py @@ -35,7 +35,8 @@ def checkParameterValuesBeforeExecuting(alg): def processInputs(alg): orderedInput(alg, 'rasters', - "ParameterString|input|Base name of input raster bands|None|False|False") + "ParameterString|input|Base name of input raster bands|None|False|False", + [1, 2, 3, 4, 5, 61, 62, 7, 8]) def processCommand(alg): @@ -52,7 +53,6 @@ def processCommand(alg): param.value = '{}_'.format(alg.getTempFilename()) alg.addParameter(param) - # Regroup rasters alg.processCommand() # re-add output From fe5c6f51574f8c0d79a3e354c1492f1163c0e2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Wed, 4 May 2016 16:56:23 +0200 Subject: [PATCH 33/55] Add i.rectify algorithm --- .../algs/grass7/description/i.rectify.txt | 14 +++ .../processing/algs/grass7/ext/i_rectify.py | 102 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.rectify.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_rectify.py diff --git a/python/plugins/processing/algs/grass7/description/i.rectify.txt b/python/plugins/processing/algs/grass7/description/i.rectify.txt new file mode 100644 index 000000000000..9713451c8d55 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.rectify.txt @@ -0,0 +1,14 @@ +i.rectify +Rectifies an image by computing a coordinate transformation for each pixel in the image based on the control points. +Imagery (i.*) +ParameterMultipleInput|rasters|Name of raster maps to rectify|3|False +ParameterFile|gcp|Ground Control Points file|False|False +ParameterSelection|order|Rectification polynomial order|1;2;3|0 +ParameterString|resolution|Target resolution|None|False|True +ParameterNumber|memory|Amount of memory to use in MB|1|None|300|True +ParameterSelection|method|Interpolation method to use|nearest;linear;cubic;lanczos;linear_f;cubic_f;lanczos_f|0 +ParameterCrs|crs|Destination CRS|None|False +Hardcoded|extension=rectified +*ParameterBoolean|-t|Use thin plate spline|False +OutputDirectory|output|Output Directory + diff --git a/python/plugins/processing/algs/grass7/ext/i_rectify.py b/python/plugins/processing/algs/grass7/ext/i_rectify.py new file mode 100644 index 000000000000..092d5816e412 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_rectify.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_rectify.py + ------------ + Date : April 2016 + Copyright : (C) 2016 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__ = 'April 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import regroupRasters, copyFile, multipleOutputDir +from qgis.core import QgsMessageLog +from qgis.core import QgsCoordinateReferenceSystem +from ..Grass7Utils import Grass7Utils +from processing.core.parameters import getParameterFromString +from os import path + + +def processCommand(alg): + # Creates a new location with the CRS + crsParam = alg.getParameterFromName('crs') + crsId = int(crsParam.value[5:]) + #QgsMessageLog.logMessage('crs = {}'.format(crs), 'DEBUG', QgsMessageLog.INFO) + crs = QgsCoordinateReferenceSystem() + crs.createFromId(crsId, QgsCoordinateReferenceSystem.EpsgCrsId) + command = "g.proj proj4=\"{}\" location=TARGET".format(crs.toProj4()) + alg.commands.append(command) + alg.parameters.remove(crsParam) + + # Regroup rasters + rasters = alg.getParameterFromName('rasters') + rastersList = rasters.value.split(';') + alg.parameters.remove(rasters) + + # Insert a i.group command + group = getParameterFromString("ParameterString|group|group of rasters|None|False|False") + group.value = alg.getTempFilename() + alg.addParameter(group) + + command = 'i.group group={} input={}'.format( + group.value, + ','.join([alg.exportedLayers[f] for f in rastersList]) + ) + alg.commands.append(command) + + # Handle POINT File + gcp = alg.getParameterFromName('gcp') + extFileName = gcp.value + destPath = path.join(Grass7Utils.grassMapsetFolder(), + 'PERMANENT', + 'group', group.value, + 'POINTS') + copyFile(alg, extFileName, destPath) + alg.parameters.remove(gcp) + + # Add a target destination for our group + command = "i.target group={} location=TARGET mapset=PERMANENT".format(group.value) + alg.commands.append(command) + + # remove output + output = alg.getOutputFromName('output') + alg.removeOutputFromName('output') + + # Add an extension + #extension = getParameterFromString("ParameterString|extension|Output raster map(s) suffix|None|False|False") + #extension.value = "rectified" + #alg.addParameter(extension) + + # modify parameters values + alg.processCommand() + + # Re-add input rasters + alg.addParameter(rasters) + alg.addParameter(gcp) + alg.addParameter(crs) + + # Re-add output + alg.addOutput(output) + + +def processOutputs(alg): + # We need to export from the TARGET location + command = "g.mapset location=TARGET mapset=PERMANENT" + alg.commands.append(command) + multipleOutputDir(alg, 'output') From 2aab32487a686fb1387e6d24abe05e98e56f0a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Wed, 4 May 2016 16:57:55 +0200 Subject: [PATCH 34/55] Add i.aster.toar algorithm (need to fix a GRASS7 bug about number of files) --- .../algs/grass7/description/i.aster.toar.txt | 13 +++++ .../algs/grass7/ext/i_aster_toar.py | 52 +++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 python/plugins/processing/algs/grass7/description/i.aster.toar.txt create mode 100644 python/plugins/processing/algs/grass7/ext/i_aster_toar.py diff --git a/python/plugins/processing/algs/grass7/description/i.aster.toar.txt b/python/plugins/processing/algs/grass7/description/i.aster.toar.txt new file mode 100644 index 000000000000..266686e6b6e8 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/i.aster.toar.txt @@ -0,0 +1,13 @@ +i.aster.toar +Calculates Top of Atmosphere Radiance/Reflectance/Brightness Temperature from ASTER DN. +Imagery (i.*) +ParameterMultipleInput|input|Names of ASTER DN layers (15 layers)|3|False +ParameterNumber|dayofyear|Day of Year of satellite overpass [0-366]|0|366|0|False +ParameterNumber|sun_elevation|Sun elevation angle (degrees, < 90.0)|0.0|90.0|45.0|False +ParameterBoolean|-r|Output is radiance (W/m2)|False +ParameterBoolean|-a|VNIR is High Gain|False +ParameterBoolean|-b|SWIR is High Gain|False +ParameterBoolean|-c|VNIR is Low Gain 1|False +ParameterBoolean|-d|SWIR is Low Gain 1|False +ParameterBoolean|-e|SWIR is Low Gain 2|False +OutputDirectory|output|Output Directory diff --git a/python/plugins/processing/algs/grass7/ext/i_aster_toar.py b/python/plugins/processing/algs/grass7/ext/i_aster_toar.py new file mode 100644 index 000000000000..d0aac7027042 --- /dev/null +++ b/python/plugins/processing/algs/grass7/ext/i_aster_toar.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + i_aster_toar.py + --------------- + Date : March 2016 + Copyright : (C) 2016 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__ = 'March 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +from i import multipleOutputDir +from processoing.core.parameters import getParameterFromString + + +def processCommand(alg): + # Remove output + output = alg.getOutputFromName('output') + alg.removeOutputFromName('output') + + # Create output parameter + param = getParameterFromString("ParameterString|output|output basename|None|False|False") + param.value = alg.getTempFilename() + alg.addParameter(param) + + alg.processCommand() + # re-add output + alg.addOutput(output) + + +def processOutputs(alg): + param = alg.getParameterFromName('output') + multipleOutputDir(alg, 'output', param.value) + + # Delete output parameter + alg.parameters.remove(param) From ed97ca5628a758da97899edd968bc00bb5cc7cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Fri, 6 May 2016 11:59:11 +0200 Subject: [PATCH 35/55] Fix some algorithms --- .../plugins/processing/algs/grass7/description/i.atcorr.txt | 2 +- python/plugins/processing/algs/grass7/ext/i_cluster.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/python/plugins/processing/algs/grass7/description/i.atcorr.txt b/python/plugins/processing/algs/grass7/description/i.atcorr.txt index fa8e9a992317..41868a58c645 100644 --- a/python/plugins/processing/algs/grass7/description/i.atcorr.txt +++ b/python/plugins/processing/algs/grass7/description/i.atcorr.txt @@ -6,7 +6,7 @@ ParameterBoolean|-a|Input from ETM+ image taken after July 1, 2000|False ParameterBoolean|-b|Input from ETM+ image taken before July 1, 2000|False ParameterRaster|elevation|Input altitude raster map in m (optional)|True ParameterRaster|visibility|Input visibility raster map in km (optional)|True -ParameterFile|parameters|Name of input text file|False +ParameterFile|parameters|Name of input text file|False|False ParameterRange|range|Input imagery range [0,255]|0,255 ParameterBoolean|-o|Try to increase computation speed when altitude and/or visibility map is used|True OutputRaster|output|Atmospheric correction diff --git a/python/plugins/processing/algs/grass7/ext/i_cluster.py b/python/plugins/processing/algs/grass7/ext/i_cluster.py index ee9463b09f18..dff3b933a122 100644 --- a/python/plugins/processing/algs/grass7/ext/i_cluster.py +++ b/python/plugins/processing/algs/grass7/ext/i_cluster.py @@ -25,11 +25,15 @@ __revision__ = '$Format:%H$' -from i import regroupRasters, file2Output, moveFile +from i import regroupRasters, file2Output, moveFile, verifyRasterNum from os import path from ..Grass7Utils import Grass7Utils +def checkParameterValuesBeforeExecuting(alg): + return verifyRasterNum(alg, 'input', 2) + + def processCommand(alg): # We need to extract the basename of the signature file signatureFile = alg.getOutputFromName('signaturefile') From e70f9bcf0c0d8e5ff6fdc84b5de71f860c81c4de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Fri, 6 May 2016 12:02:59 +0200 Subject: [PATCH 36/55] Add first set of unit tests --- .../processing/algs/grass7/Grass7Algorithm.py | 2 +- .../processing/tests/AlgorithmsTestBase.py | 5 +- .../plugins/processing/tests/CMakeLists.txt | 1 + .../processing/tests/Grass7AlgorithmsTest.py | 58 +++++ .../testdata/custom/grass7/raster_4class.tif | Bin 0 -> 6635 bytes .../custom/grass7/raster_4class.tif.aux.xml | 27 ++ .../testdata/grass7_algorithm_tests.yaml | 238 ++++++++++++++++++ 7 files changed, 327 insertions(+), 4 deletions(-) create mode 100644 python/plugins/processing/tests/Grass7AlgorithmsTest.py create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/raster_4class.tif create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/raster_4class.tif.aux.xml create mode 100644 python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml diff --git a/python/plugins/processing/algs/grass7/Grass7Algorithm.py b/python/plugins/processing/algs/grass7/Grass7Algorithm.py index 226b29ca9ddd..7a703c1cc834 100644 --- a/python/plugins/processing/algs/grass7/Grass7Algorithm.py +++ b/python/plugins/processing/algs/grass7/Grass7Algorithm.py @@ -558,7 +558,7 @@ def exportVectorLayer(self, orgFilename): return command def setSessionProjectionFromProject(self, commands): - if not Grass7Utils.projectionSet: + if not Grass7Utils.projectionSet and iface: proj4 = iface.mapCanvas().mapSettings().destinationCrs().toProj4() command = 'g.proj' command += ' -c' diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index c999aa057cd2..8311c3120de6 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -85,7 +85,6 @@ def check_algorithm(self, name, defs): QgsMapLayerRegistry.instance().removeAllMapLayers() params = self.load_params(defs['params']) - alg = processing.Processing.getAlgorithm(defs['algorithm']).getCopy() if isinstance(params, list): @@ -100,7 +99,7 @@ def check_algorithm(self, name, defs): expectFailure = False if 'expectedFailure' in defs: - exec('\n'.join(defs['expectedFailure'][:-1])) in globals(), locals() + exec(('\n'.join(defs['expectedFailure'][:-1])), globals(), locals()) expectFailure = eval(defs['expectedFailure'][-1]) def doCheck(): @@ -174,7 +173,7 @@ def load_layer(self, param): if param['type'] == 'vector': lyr = QgsVectorLayer(filepath, param['name'], 'ogr') elif param['type'] == 'raster': - lyr = QgsRasterLayer(filepath, param['name'], 'ogr') + lyr = QgsRasterLayer(filepath, param['name'], 'gdal') self.assertTrue(lyr.isValid(), 'Could not load layer "{}"'.format(filepath)) QgsMapLayerRegistry.instance().addMapLayer(lyr) diff --git a/python/plugins/processing/tests/CMakeLists.txt b/python/plugins/processing/tests/CMakeLists.txt index 8a76fd217492..c3c482f7c9d1 100644 --- a/python/plugins/processing/tests/CMakeLists.txt +++ b/python/plugins/processing/tests/CMakeLists.txt @@ -9,4 +9,5 @@ IF(ENABLE_TESTS) ADD_PYTHON_TEST(ProcessingParametersTest ParametersTest.py) ADD_PYTHON_TEST(ProcessingQgisAlgorithmsTest QgisAlgorithmsTest.py) ADD_PYTHON_TEST(ProcessingGdalAlgorithmsTest GdalAlgorithmsTest.py) + ADD_PYTHON_TEST(ProcessingGrass7AlgorithmsTest Grass7AlgorithmsTest.py) ENDIF(ENABLE_TESTS) diff --git a/python/plugins/processing/tests/Grass7AlgorithmsTest.py b/python/plugins/processing/tests/Grass7AlgorithmsTest.py new file mode 100644 index 000000000000..8bc7a88dc371 --- /dev/null +++ b/python/plugins/processing/tests/Grass7AlgorithmsTest.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + Grass7AlgorithmTests.py + ----------------------- + Date : May 2016 + Copyright : (C) 2016 by Médéric Ribreux + Email : mederic dot ribreux 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__ = 'May 2016' +__copyright__ = '(C) 2016, Médéric Ribreux' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = ':%H$' + +import AlgorithmsTestBase + +import nose2 +import shutil + +from qgis.testing import ( + start_app, + unittest +) + + +class TestGrass7Algorithms(unittest.TestCase, AlgorithmsTestBase.AlgorithmsTest): + + @classmethod + def setUpClass(cls): + start_app() + from processing.core.Processing import Processing + Processing.initialize() + cls.cleanup_paths = [] + + @classmethod + def tearDownClass(cls): + for path in cls.cleanup_paths: + shutil.rmtree(path) + + def test_definition_file(self): + return 'grass7_algorithm_tests.yaml' + + +if __name__ == '__main__': + nose2.main() diff --git a/python/plugins/processing/tests/testdata/custom/grass7/raster_4class.tif b/python/plugins/processing/tests/testdata/custom/grass7/raster_4class.tif new file mode 100644 index 0000000000000000000000000000000000000000..29828d8ddafebccd09e5da2d4410764320744466 GIT binary patch literal 6635 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 + + + + 0.625 + 4.375 + 4 + 0 + 0 + 300|1700|6000|7000 + + + + 5 + 1.000000e+00 1.600000e+00 255 255 0 0 255 0 + 1.600000e+00 2.200000e+00 0 255 0 0 255 255 + 2.200000e+00 2.800000e+00 0 255 255 0 0 255 + 2.800000e+00 3.400000e+00 0 0 255 255 0 255 + 3.400000e+00 4.000000e+00 255 0 255 255 0 0 + GRASS GIS 7.0.3 + 4 + 3.3133333333333 + 1 + 0.74954801195268 + + + diff --git a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml new file mode 100644 index 000000000000..76a2ac6b8761 --- /dev/null +++ b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml @@ -0,0 +1,238 @@ +# See ../README.md for a description of the file format + +tests: +# i.* modules + - algorithm: grass7:i.emissivity + name: GRASS7 i.emissivity + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + type: raster + name: custom/grass7/raster_4class.tif + results: + output: + type: rasterhash + hash: 0850127d19d5098ff7cc7bb5991b7881d792e4a64aed42b811cb031b + + - algorithm: grass7:i.biomass + name: GRASS7 i.biomass + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + dayofyear: + name: custom/grass7/raster_4class.tif + type: raster + fpar: + name: custom/grass7/raster_4class.tif + type: raster + latitude: + name: custom/grass7/raster_4class.tif + type: raster + lightuse_efficiency: + name: custom/grass7/raster_4class.tif + type: raster + transmissivity_singleway: + name: custom/grass7/raster_4class.tif + type: raster + water_availability: + name: custom/grass7/raster_4class.tif + type: raster + results: + output: + hash: 2359a7d0db659ea48272248e663ead0b9cb11e6e97b3fcb14db8bd95 + type: rasterhash + + - algorithm: grass7:i.eb.eta + name: GRASS7 i.eb.eta + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + evaporativefraction: + name: custom/grass7/raster_4class.tif + type: raster + netradiationdiurnal: + name: custom/grass7/raster_4class.tif + type: raster + temperature: + name: custom/grass7/raster_4class.tif + type: raster + results: + output: + hash: ea1b463ce6d426eb3b6d40dbd5b475a010bede837f5a3e3faf3dfb40 + type: rasterhash + + - algorithm: grass7:i.eb.netrad + name: GRASS7 i.eb.netrad + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + albedo: + name: custom/grass7/raster_4class.tif + type: raster + dayofyear: + name: custom/grass7/raster_4class.tif + type: raster + emissivity: + name: custom/grass7/raster_4class.tif + type: raster + localutctime: + name: custom/grass7/raster_4class.tif + type: raster + ndvi: + name: custom/grass7/raster_4class.tif + type: raster + sunzenithangle: + name: custom/grass7/raster_4class.tif + type: raster + temperature: + name: custom/grass7/raster_4class.tif + type: raster + temperaturedifference2m: + name: custom/grass7/raster_4class.tif + type: raster + transmissivity_singleway: + name: custom/grass7/raster_4class.tif + type: raster + results: + output: + hash: d6fc0efa1ebff447b8b04a1bd222acd9e37b1aa3a655ad5b1d69a49f + type: rasterhash + + - algorithm: grass7:i.eb.soilheatflux + name: GRASS7 i.eb.soilheatflux + params: + -r: 'False' + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + albedo: + name: custom/grass7/raster_4class.tif + type: raster + localutctime: + name: custom/grass7/raster_4class.tif + type: raster + ndvi: + name: custom/grass7/raster_4class.tif + type: raster + netradiation: + name: custom/grass7/raster_4class.tif + type: raster + temperature: + name: custom/grass7/raster_4class.tif + type: raster + results: + output: + hash: b8a09be667e73465a3d06e3c1a1d99a8663f21be1918f498e7b49eca + type: rasterhash + + - algorithm: grass7:i.evapo.mh + name: GRASS7 i.evapo.mh + params: + -h: 'True' + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + average_temperature: + name: custom/grass7/raster_4class.tif + type: raster + maximum_temperature: + name: custom/grass7/raster_4class.tif + type: raster + minimum_temperature: + name: custom/grass7/raster_4class.tif + type: raster + netradiation_diurnal: + name: custom/grass7/raster_4class.tif + type: raster + results: + output: + hash: 182223a6c8da7b51118794de826c0bc53f288f377351d62aecc63837 + type: rasterhash + + - algorithm: grass7:i.evapo.pm + name: GRASS7 i.evapo.pm + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + cropheight: + name: custom/grass7/raster_4class.tif + type: raster + elevation: + name: custom/grass7/raster_4class.tif + type: raster + netradiation: + name: custom/grass7/raster_4class.tif + type: raster + relativehumidity: + name: custom/grass7/raster_4class.tif + type: raster + temperature: + name: custom/grass7/raster_4class.tif + type: raster + windspeed: + name: custom/grass7/raster_4class.tif + type: raster + results: + output: + hash: 53ad3d55bb9593decb21a6ee10802dd79a42cb17f067ce38ac286ab2 + type: rasterhash + + - algorithm: grass7:i.evapo.pt + name: GRASS7 i.evapo.pt + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + air_temperature: + name: custom/grass7/raster_4class.tif + type: raster + atmospheric_pressure: + name: custom/grass7/raster_4class.tif + type: raster + net_radiation: + name: custom/grass7/raster_4class.tif + type: raster + priestley_taylor_coeff: 1.26 + soil_heatflux: + name: custom/grass7/raster_4class.tif + type: raster + results: + output: + hash: 182223a6c8da7b51118794de826c0bc53f288f377351d62aecc63837 + type: rasterhash + + - algorithm: grass7:i.topo.coor.ill + name: GRASS7 i.topo.coor.ill + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + azimuth: 50 + basemap: + name: custom/grass7/raster_4class.tif + type: raster + zenith: 50 + results: + output: + hash: d3da5e4b9d088760d01d70de956d2cd35433c96d175ca68394d4dee8 + type: rasterhash + + - algorithm: grass7:i.vi + name: GRASS7 i.vi + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + nir: + name: custom/grass7/raster_4class.tif + type: raster + red: + name: custom/grass7/raster_4class.tif + type: raster + storage_bit: 1 + viname: 10 + results: + output: + hash: 182223a6c8da7b51118794de826c0bc53f288f377351d62aecc63837 + type: rasterhash + + - algorithm: grass7:i.zc + name: GRASS7 i.zc + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + name: custom/grass7/raster_4class.tif + type: raster + orientations: 1 + threshold: 10 + width: 9 + results: + output: + hash: 270bbef9dd111af5df23a819cb0848e104e0cf4949e794a67fa0b3f2 + type: rasterhash From 1f21af6672cd3bbaf854cffd3efb48b96009a11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Fri, 6 May 2016 14:25:12 +0200 Subject: [PATCH 37/55] Improve tests: add external file support into AlgorithmsTest --- .../processing/tests/AlgorithmsTestBase.py | 4 +- python/plugins/processing/tests/README.md | 14 ++ .../testdata/custom/grass7/float_raster.tif | Bin 0 -> 44366 bytes .../custom/grass7/float_raster.tif.aux.xml | 20 ++ .../testdata/custom/grass7/raster_5class.tif | Bin 0 -> 6599 bytes .../custom/grass7/raster_5class.tif.aux.xml | 27 +++ .../testdata/custom/grass7/raster_6class.tif | Bin 0 -> 7239 bytes .../custom/grass7/raster_6class.tif.aux.xml | 27 +++ .../testdata/expected/grass7/i.gensig.txt | 17 ++ .../testdata/expected/grass7/i.gensigset.txt | 124 ++++++++++ .../testdata/grass7_algorithm_tests.yaml | 228 ++++++++++++++++++ 11 files changed, 460 insertions(+), 1 deletion(-) create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/float_raster.tif create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/float_raster.tif.aux.xml create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/raster_5class.tif create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/raster_5class.tif.aux.xml create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/raster_6class.tif create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/raster_6class.tif.aux.xml create mode 100644 python/plugins/processing/tests/testdata/expected/grass7/i.gensig.txt create mode 100644 python/plugins/processing/tests/testdata/expected/grass7/i.gensigset.txt diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index 8311c3120de6..16bc8fc1b869 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -136,8 +136,10 @@ def load_param(self, param): try: if param['type'] == 'vector' or param['type'] == 'raster': return self.load_layer(param) - if param['type'] == 'multi': + elif param['type'] == 'multi': return [self.load_param(p) for p in param['params']] + elif param['type'] == 'file': + return self.filepath_from_param(param) except TypeError: # No type specified, use whatever is there return param diff --git a/python/plugins/processing/tests/README.md b/python/plugins/processing/tests/README.md index e8940723f373..0bb8e91e6c99 100644 --- a/python/plugins/processing/tests/README.md +++ b/python/plugins/processing/tests/README.md @@ -98,6 +98,20 @@ params: OTHER: another param ``` +### File type parameters + +If you need an external file for the algorithm test, you need to specify the 'file' type and the (relative) path to the file in its 'name': + +```yaml +params: + PAR: 2 + STR: string + EXTFILE: + type: file + name: custom/grass7/extfile.txt + OTHER: another param +``` + ### Results Results are specified very similar. diff --git a/python/plugins/processing/tests/testdata/custom/grass7/float_raster.tif b/python/plugins/processing/tests/testdata/custom/grass7/float_raster.tif new file mode 100644 index 0000000000000000000000000000000000000000..de65085860d296dfdb98ee99eb2d1ce5eeb6fa54 GIT binary patch literal 44366 zcmdS9cQo7a`~OWTwc9Exh_+VkT`Q<6YAaf`_udq-Yqu1wJ)^2dY8AC-N{t#ZQk%r6 zO@bgK;r97{KY#r0bME{8|Gv(7p6ik4>w2H}xn4=mdFRSYQ&WKCbtMVO9TE~!G7?e} z5|X5Ak^YxGuldG*`N1`l{g-cDGsS=TKiAwLB`5ihcX2JI|HEhgFVkQ9EdQ;Uzh?IT zqek(+eB;_T`Y)4RGkpv#$$v87`UcUlv?LVQEOE`X31lSP*KBvqKN9}8=KD3*CEOqZ zT>HPS`9A^t{}9LZ+TwNH#kKBV-}uS3jIZT)txwmgxz_)N>C^vv{;&Q2lEx{RRQ|Ix z@!s@*@_(50e|GhHcgX*jueafUyc^_o35@?)zg3q&LBdEvaa}@6lEOuQ>&JD6*FpQA z*KW7S|0nZv{m1%$3PTNbImrj24|Hrj?Hv8y3d>1`{f`w!0-oQ8Mg8ZJ-Y@4y)+;2h zuDktD^mqeZNJ#(xx=KoS-NLUgXZoJ>GGG4F4#_(WGM4r0h6;Fc8N%%S=YPX>B^y*e zXPdDu@H}RG`{8BZj`lb!YvLF8`nObWPhKZ?G;P%cI>@WNBv+ew_&C~()s0E5N<{5t z$>T&Z%jie1XDEg`(Pt^}fv~cvMLgaga$MjKF*wm1Mx2^xPlyT!D>vPfeeQQ@>1OT- zjeNQj3^#OWEiI;1);HZzS~NW3*XELtI;twIBbM59N`(N?2mES9&Fg#X#Al{sar6O5 z5B@3P)-IV>kw1#(0<|v&4gW$XM?$oV)%kx$6H}e8L{ue<%mq4ap*|e&MV=E&Z+tCFiRY+MvHPll3c~ z?Ym;X0kVDXm>S+DB%N=F;?r)_*7`I0%Ed0efiOIYRalhq8aiv0I?{$l2)h0laqdQi z1si?v?m4p^^Us9-l3YA9haT~2$8e;bu5AjOhUxF%CJcKYoXx%xc;}jPme7U^=i4=k z8E3G;Kue=eY>>20McJA7F$0C!v#KZmyweR)tjMfQYv<%D9uvJI6Q50Squ*^^jNy_Qv^ZfiJ364g zYRH}LRlUaX@}A{SM4#ot)i6E+x}%)6^QIx3AB+^%h1d(6(C-@JwJ`hUTI3>(d{tp^xCcz!|=*5a2pdQkcoxuL2{ssW4<{ zezTEc`Znkwd}I>GkI3?8*dJk4vO!1Y2*4^Q1Wgm*;iUW{L`G+=A#EL9E0;kTch~z? zbRsb`WR7u0^lHs-m~ebU5%Z7M>XbIR__8Ip^7K)X?3DZ0y?DdyaCocqgQbN?(wFD zb4wOkBvVjy&wkfTh(jQt*?_CsMu{rBor3zf3E)(c=^s*uA^hZ9b9V+zO{G>u>=dr^nybh`IyV3K>q@v}^fE1LmNxR#GE7 zmCEQe@>?KwiO9UmHX%-UGsLUm6WoSMm0)p3O`+@1T97%Yxr~_UDBWEd8S;(>WubQ zeyD;DE)XuwmqV8j<0tVO`fCyTC^kk!(Y#Dlr0{(m)!zHd6S~6cff}`kUvaftqHmqa zyS2Z%H*7{LTd+1DS4Ln;%b~XmBVvQLb6+H91hao!(e@w>>Yy1Dh7I?~B%u*AU+FNd z_?Od8se690w&Bm_)+)F{rjsLB#*=ntdg4>f?)(UWjD9g6egL=v{`b%Fd#SZX*`WlusL(;#P2yJV@s)aL>lv-to!)E!>`{EsJsH8_hKb@w(jz_R z>#1PM`BlgL8zGzvOmsxLjxffh*sBj>?!M(Oe+*r+g0@be!Ve^gTfddVI#^nA+7*Ws zah5Pux(V}aSdm)%vnxs^mbU`!A;}cY|0HpeZv`6XSd?eq>lMZ*74N^os_~qha@HA7 zl&>(&v+L)%b@mv@6gleO2>t1n;vYbWX@`Ak7=s^v5p$c zMvdKZDce_evDxq3Vht~pO&cNYx9TWy6x>OPXC8b$&tAQTCJA`Ia>fYO_y-G1fwW9V z-RYD_geUuM9%irjNS>68Mf^ahi**;VHxw5C+O@e*GhJR{&r`gw+gW(YTISumuI%%6 zYHbas#{=0;<5b)!w_Rbdg2t-7Mehyop)KAF&wi5g^t!nD>hI~5!RG}&; zSATJ{<=}awEdCuSJ#Jsf)OSyFOl{*&nz>|qhBPKP4$xC`q4V<-Bw#X6}%N#2H!ks|ETcE^cRTpTdhChaT{DeZ_ocbK(V4Lqpd_#?Z^UX zP22FA#~I*?V&8nXU(~{-v=*8N(f$&N)Aps=@fSAJae@}>$+<1P2WC~M*)X+rPL7ih zl?KoKaLGl|M%Xv9e-yAj9IVB`k`V*llmpwg_1<)>_7SG#5*mR+y4A!YUkKgS*1x?Z zhLLyfQ4JSEm!!C5(funlkaXBUdm*depNo&7Yo z7|~#p__R^KEoUdL!i!F=}ao1LOkWJYDV& z-{L?5tIq3+NpkCmbM@4rav9H$8Ns>=fJI3QLo=qtju*N;?VBXiF65LQen*iLi<_u@ zcOSYvr*Trah)qIm##8=kd<;k>*o_OILDB&<`0e*_K4Yt}#;w_I00hlD3mwI>Qjhm41~Yz*a5_?IHuRCV&9B{CPIT~i-Nx&rc4SFnYQ z^YUx3owkq&G}JXz z^U_ElLnXaz{6l|cq1JM}y7+R?YBemOh0QEKgt4nYcez9(o;m1Gr*HBv@aJrc9J_;C z3sLNax8w);6k?=7)uvP_>&T75J^0{`id!;$%9MMTXbYL5HJe(}I1^l)_xMJeE^l6R zXu!Jr=g|6KHwsu{=h#2M9kz(+Zy0$;f7agMcx{(Byqk_aty&?l9hT$9jTVNl~@Zo%aH zOMRbO-~1~KNbSjoeJ%A1>W_+xjF#IlF8t3`Mbto~DE9}6LK^CPJ`l~&fv~%`dtwPZ1f%1`1u8^wt z&XB}0M73I6fnmHhk~GzHtd-wX7XffqLI}0jf5JYLW4n zbpo=2YM!%5l@g^ED__3SbKBqLlZBFA^7pynVR#f$)F|FUOw~*0b13B&k@i9=5zW-x zcc!89cLoK=)ImC(L{|^MdSw1iiJ|c*hb#prm>a}GGTw?}+en6px&(}^4#|bcMZ7QW zzgd;;Y{j3EN%&}SPJ$Dk!8d)C@wTp{Smuxgjzq-qeiE;@`D|Y%V3>scTa}95=#H=T zDW>kOp`WzZ&;LV6hffTeh7{9>(yJF+hcx=n^)fu(9Z0 z?l%sHE06EDB3HsYrq$GGOIFB?aF0v_$%TERkS?6k7m_(f?YHEFoi~9OQsMT&Mok#8 z)Z{NS!DD&LpC!{I(;dD*xyA*?7RDCEFCQwBT2l^HnzktB2t%9epLrGxlTlKv%=K_} zM0%9p*lo*L)4Az%0>tgtwRXo}%2yjrNVeT8j!@fZy!ajOHsq%`u*wC07m!&Tza)pe z*GUs;=TLXs)cr#H$8&e$X%K(=r&JHZ;TFhh4)>@fJ*wBC4A=oT>3XxA=yh|*A>GBO zTak@(3c9^z7Cfl6LZkb4c{D6xpmZn@x`rXyn2gsR{IEVop7Y@cfL!cMv|XMC79TAsNtd8xN(Or`x5(O(n;a2T=qC0`y^hx?vU zGA9oG{GkU<@p;b7J3K5mm;3n!)jm)HFfMMe+)0Knn$3q{z-kJAWl6$qq9Ux&4)>MC zo945>J*Mkc_Y#8tnYWA)+a?80lV0Sl4@>>4PXv}|bI1dBv$an&9B#@PX8VKi_0}z2 z?A$mYphQ}F%U8LrkUIDJ&ZMZuW#rJM8B!Gw*^N0XA#68>QW4VEzq8(2{l4RY`P zbb_i+H*J63Lc z=mL^wuNZE0FL`nOUpz-ywN_vvDuS#ghk}!hp))RhsJeu3I@tw#VGBwy)YsbgL zCDv4Ac(3R(-oN0PZcoeA(~aMEH(N zY_M&qa|6`1YTthU0aqojn+^l=r-n72R z$1<(SUKDc2AbzfO>A)mpkOKwu^0|aP6b%?zrHkWn;cTrcP1lJO;vVr=T@ z33;%Y?iG{z_^1iH)@L%m+rk#wmfJ(~uN+&uxu>G64^E0QAm0r>StVXSC5ZCmOW5~K z`(NhkjDp-M@US#>MW&-D_jr%29)-3AT<@jaMxjcG!093U3jQe5ne*qLjrES|N|!1Y z%VTQ>hJhh=-0r$bnY`Wu>#zJT-}dQSHWZ1x>brz|vT%*$EOZXkE229XnI*-}H&QD; zlB8fZxC%#&OZ)uVxYH@!gfMWWT9j|q4tF7pH0`OJV$rsbwIG16T`~G=D!5ubr=|^v z-iDvdoDhcMA}0H-*qZSgqx49k@Os7#NLoUw)dp^H2q!72kK*>@l??M04a>RsiE4le zfYr)w6<3-{gl2W+nGOc0tRFb^-M-RG7hlxdY2qw!SD~EKUiU&qU?PPVpG)EyY-*WM z=giBb5xGepb>FmGZy?!Fmn&ryO+GHn5MB%i@=-#46`LxWh|%aBNr{wPw@bdws-zW| z{8cV);n0>~M~ei8W;sKbuuz1LGx+yKI12gV@<>tx@x?hDti>+6_fQ5u=%9$Gewgh(nM~&aC%O@5>++Lr3Jh>Whe69*E z5YCOfp3-*p7o`u?0uR(;k?rTWWbd2%(eTj?n-lA-oYbql3i zRbO<(ZW&3cB)1qnsbDI3EFW7~T4Sb`EH)%}7G8Fi+I8XPH+B{l0fHX~E0t??wmluv z7j{i9olY%{Vi4BloUX>~0RIJM=HZ3n4l*rj&HhB%LfA23sU2`V?Zs$8 z$%KNlHJ8!Dlb**Jmy?DL3a)OYJn-EtJFYBhW$W*RMBw?d43ol);lW*1)n?6qXTqh+ z^~TG(W&{T9-_F9)m&tI^AM01u#?nQ{!6^wjy)w7)V`XXXbfX=PZW{so5^nrpbK~5# zRjqI@f%?pc$mO!1c4O~_vnU4h?-3Hw)c+E3>sr8Vxy`h z-^17>pnR7pzt$jlIv zyV2RN>5uWZE`H3V9RswDET+PWipftn+j8+%+$kl($>IE^ELs!CI!r^2SZGE?nv-6m zF_Cm;--Ev`yLcXHxRB3%rM!(qZvQ>U{;+c`n4D2|;_p@b?GFaF+;52usa|UcXa4TB zXgGY(cbp83@7;2qGki4K>6{Cgoi4^Y_XXbBT+&h$Dd$r#Z04aKoy@*X3@B95bBB(G z7_G#`KTFj-uhgM~*j60NV9}D0Nc7#fl#i93(}tsUh^?D`;d5#2e+=lvO&DXs)sGcz zDxMZ7m)xW`in{k^PKa**xpGiO9(`t-v%heT$Vko~>A>MecMCd1so;ZiuN!2fs=v?I zPdlK$7sh2UlSl8QG&!-an}3GttOO0YM@hYWNH1n}Op$Z+q6B|?f!9ct-sdW_G-lU*q(AQa5J;3T%QfaP1JvT zSitb4@;okh2b2c*+)J*w)25b3!53v{T$qh@a}ku4h%An;2t}UT6os0LKnIU`13HS{ zH1RNtp_fJx7*?NMco;*%-F3qg?}LX~zqZ%+_~Ny<@EntvLYsqIKR1JG7R&(U{pn3! zg<3k;FJ$2__9@G<0Ro*jE~hmilrWnl9uz-(3RN1P$LU#d9;&^Mf~Mv@>2PziBPk?P z6%Jq5+_P8nIV&J z0L6TDmAb)@FVke$8$bu870I^_7y5?foE=Jf@QLOqRa&O;%zNNd&o5 zZjakXpYe?MVcZUqdTd*a-&u!my?xgvOM_kGcKt*!8Y$}Gq>a;ux;Sy2D{Ikn{(@1` z-Q0S4@&k%$E@0rTTr1l-O{iswE*uuvx&N$IU9-8^JYd%=Z5QklsHWETr#O$lMqKA_ zSW4L>`hvZP54CFsO%9f*SKf^3Ru5^(nZH-jnh&77B;&A6p%QYsdd6@@64QQvQ9F6! zL)S3uCE3N0$I)Ss6Ip(8#=!mWG0rMKLG=afKyfKLE3J_{Uu+5Rl1E>yz3Z9i z_$3qsjx}fe8Kw-Jz791iT&j$AYS_-)z|`S0k&-6as~wB%@`dLk)X$)4%r2 z_67~BG0a!u&7Il0ShDev9>W8LB!b)mSw#6th^Z zRLPG(o^40kB`paVb<8tT7Uyh_CH0`5ZiX-(3>gQa14nzWk$8K4?}8)T{`JYqhyEFj zzkb@ayg_{QZe}M)1ej%rt~-elaTdJud_?W1*l1YAqfK&)p@LguJx}qOBeJ4!oZv}J zOcdZ^GCuI#u&-01zrgorxxB^0q(AxOKLEq9jL%e%1;@r6HNK2Cmp0r{Pmn>L0r!qC{S1eM5BJa@RiIQ`;NX+tcggzyt{KGX9G` z`ID42gToXK!{n?cH9XGIEkJL>2TIBS(5MFo1)2xG=#e;{@9ShhAd{+@`{Zn`da!sy zSCJlaMFTQSAeB`}=Dpaq#5rT5kgbkb_Wk?B9KtxEFBP%s@FQ^}GRk_%RI_6&(*_uu3jA&O+D^0%ji@@Rm&UcAb(jYp9mjukhAj+@@`(JpCxbV*}b z+?aTG`i(PRa7ob2{aK!@~{{ zx6ED^_1w?FVi~2ICoXS2l$7`BrHeUrQzwI3raB7R>e;?wXo`bqtNRpaqh6uWJXg(UMQUV1Zr<}@SOdH!vGBN1G22BNjEEeckA+Y-G!GUGm2}F-Wj+3fhCLw>PXxN`BOP}{qDW@AW5U_uUs7~uCwD{c<`^X2XnzDh@&RYjVYBud@z&cejv*Yw zOW;=YkY0b*tQf|uhGrbGfJ1OBZx`SVyoZIri>(2FnTL!-+|{CQSofj<=W@90NR?B< zU~ybs!`As|y!;-=w;;VpUX=SrZ685Ve*GJ>@@>vTddc$L6_1=SwrzVdrE*(F@aFPe zU&M+lb;0L;8eEJG6MOuOfjO32r>2HO=frZ;AZCB2$B~%^qTb#+qQprY?VZrXmNE#* z_!CEl!Bw~Y|LK6QxN<_{+;hm}oL(?@j!YyxYS+qvjIsajdS}%*nNE8W5G28Z@rAMH z^qLIhzOr{QrnjV9ov{PMxB~>eYI^)T1m%%;8UnmYyI{smG?7pBjrO$d`^;D6F0Fec zPfH(!bHrXfN!8MSTj};Pm4NDrn@RN5&6(eF)<-DT||2B8!c#CZ@^>tf-ct44-E zz&(ms*K<)6Ta{Ex>`w&?${Kl=Z!eCm{>$^KJChsp{3TiKS24IZwtu53CD7%b*T2JZ z*Pr5SrB7V^Z|Tgegu3-uoR~P$UHU;@OKeJD!6adExy5Q1CilNZ;4*&6gJhKc=#K7G zwD;)TDsUQ4Cz0feMW24nEM&M#)5iHEOCYvi&*Qs`U6iWewl+35u>-Gw7$MMLGo%BB z6sVMXPNWIX^$kr{k8AC(&if6x{2MuIc%f5v9dq2k`uE5o*ruD0@z)f!4J~}}RzC}Z zg;QQtv@@Z%*m3C(7W@7G_aA5&UaH13!J_^NGW%;ocyU;F;xl$97pTn@8e z#hhC)z$;c%8-|VoWxv%3`i)-l5P%=-HWP>bj))NAXo_U=pQD@O8<0LrZE}$l{a(hv z%Et}wO=+5p@V=Z@kKZR?O43D~MrMJ1XQD=|*b%&V&M!fhi5c!jze4x#9Qw2G^#fP- zAA!H5sdU1gZP{WH{%l2X!%dkCNHI>qd2%~vcun7Vyc4?HO3|rR*WgV6D3vf6VViKudp(HSZ3(fw zG9{6gc-F?V*q)Oz3ajTbgN>wDOSH;q@HBks%YV~n9x(MA$)h6cRl%VA!rz?~@FT#* zSXIDMJdpi`5P14cCv={Ao-QFCH9Dv`^7hu>J|*eES87Bsetr$6c*s0nMH{Meiw+*s z3$m#EIzuJPsFG?iWkaqAT=8ojh~$J)Pq(an{aD5*Qx*3!yJfw0(gApe(1}it=6FX)fk@w zqF@JI4sK+b??WWzMif!$(DNdp*=HVoINmMP{xMsga%R>lR~F`S`8JFX$8N=tGLfS5 zj+j=kgs-b?;L}A3r*DNXDJ^Pus!v~CDbR)u($3zNW{7M0{|GZ@!P zOCuS&glimXWKz=&E9u048D_-}o8%qas^M-&OWJl=F`gxNlBA&3h7Uh_IT^Z1w_S)S z%%8-&%|Ra_YN-j>sKd-8UEzZmYux0cR=tV*mssDAxh9m>duL(kE@2M)H~t-O`E{5> zS=~ko?tphIZ*XNOMVF&zpK^w*FyX@%{7&bH?1`_o*1|_5Ywd>>mQs)a9}OC_505@}jV{Jck#Xi9DXJ~a*4}u9An!`NzFA#a8li;aQva=kf+=x-`E$VDs9G{Q`9=w`sw3y|EoW} zIYlEekHlG^s_Jz0&Un`N13B&-8pQWPyb%Z<;k=~com4p?_oLlc(SmDpHWQN`3_V+ETUwBnw$k~y zM@xoz=r03N&`3@BI9X{GjIzx#>x1;fSjUP6rx)bJ8O1|;+^`&?9I$L0#!>{o^oGGA zBmnAkV8d+jLNO?YQ`~4wHXKK)Ys*WaPg0}-_})8kQu5n1kVNKC4dueN9F)Z&F$}yd zvKEYd+K`(q3_|K&8et|>m%fjGS|{s6cvXCysrsc5zkg*a)NlrnI%$*;H(Fy(=pX;Q zqTBc0iThAf{2~j@H`cfXz#HGGH6HDF-#sGdJ$C-@qatVRfs@GD%u( znTwg|Ckf-d5o3AJuJ(eoH6fht@&1v#T{4)ay*FHoGKtLX3@f35i0>kPWTLHV8_W!O zK3@~|)Jjr&4%uh}=Fn{8FJ;sBNT%K?l*^dK-iXUG>bm`Es=3 zeMw;0%R4EKwRYuQSveRcInZk=eIcdemkHcmqoI$0B>N&lejcsP@x(8{eK!XuUG^cl z0u!@RVYKXbOlGcq3yc2iM>V(oi;bMt16wNZU|l+4q*a1{*?>#V;1+FgbR>&_lt8 zPRW$jTeuWTAtAxzA<)g)0m8b|aXD6=s3O^bG;$9s&Q`iOU!I}rZz?|^7_T_a=ML?s zM(sMrQ{Oq&18FJ94lTQTo)=|%GlC=i55hxpE{#_zdlBXKqHg^l{?o4ul^8+mHS?YA zeQngPT>@x*X3)QIaUqdH`fCG^Q*hc+__vtg?OmsYkFMYQgp_hP{zT*siGY3+15jnb zOzaLOfWG|H1+?q9KRTAEtnoHodky;)%iiMX#WM4pDYQWY7U17*#R0fKKXr+`cMiK! z@36+#%91HIBNc4u{sxf|pHA6v9#MkdGHpiaiw*E%7KR>T+dhMGhk=Ck@nF;r*zes! zu#?*RaHtL1eeZ%!kbTFZPbjKAeG5K*TRa-N=ei}?_0X$Zqk1FctpG_DAFR}0X_J^A z5j}Ssztbm;b0S0{g-`L9^==1#-|y_})G@R(JR0EJw`ALkOq_{suRANjR+x~Ht~NMJ z5nb!9WSs2_IO+x$U;L`heH~cHaR}yO!u?+4Bt`EHsW^G(61_>-y}vzSn?Pjx{K6}F zzM;=fafGBqiNAF~-EkXTWN4r%o2a%8Tp}r=oTE~o<#ZZqJ|qzvW#3I0=E9lcBT22g znvny{)E2!WOPIQo$Zy`-k^ebN>w7(>o$fEnck(d6Q`iUGCwGi~uywc6K4$FJ4%0oA zJNHZzg_cY{k2%bLbxQj3*!+pkoLOV^*T>QIrsm{o2G?`lkKr+obo|i~-gpgVU4Jx@ zIEF6R1i}5iGA-+$ZL5~9BxXd+7}{l)<~kalI%pT0y1IOTPG;P*nklyNF0ZdPgp0NW z@$nn~G?slM5aH(PqO2{g!MdaT*LVkTpsWr6tL3paBidq05rtJj1;yj-Wska7x>U4J zUb;<~|4YBjD1giEC*t<}#ID|3J$!l}J zRI@}7_X!7&u9pKpp1h8SF%fFss%);MXXa;`MfhvD3U&x9WH&3G<4O6ia!!Q zXh5iXX@>#jKT3+aY~M4#`j_>mNK2hJ@SpKkMLq~PSJrNjN$h(!Ed9b3o3SnRZO%QN-Ak_P%7{x`X>rVR4}M=+Ie2rk%G@ z1E=n&-}L7u*Oa`Eg0tR`cCPEqCTO|Qr;yl+NS=> zR|($3VEsl5J~k9tr%E?WE7v;E$Xp})9B}^L|0oozJv~1?IXF-i3aUFDr@_wK{a|yf zUR3C7p=>nG9!H2+0tnPKEvYBouqHH8AHF#1>M27(`KgfIJZxb z4a0u#hjmBI%MNOP>?)45#AvV{Y;Ss~VJ0ul6}k&H=}ybxO9(#vli6sNM4$XC2Z&s> zl@*xj{0{rA6u{6s%iqx-WU+9GHJ|Mo_G9N%4Jv9C>tN-ecsnyqr_L!yo!EoUY+}niJ z7)yaqs%M^r+X*aVzTOO%ZL}wzzn#Sjwn;065!h!&+C?`K#EsII;sHJCw4hxDH}cf% z>CdUXsGFxQDhvflFoCf2Wv4hTpI4tPHu$#M-DyX=#^%?dEJ}mu0e?(F`?5jMhY%1A zZ_jR@FCs;<@o=vte-QZCPqahV2=9iqobg8*ILaXUR#lr^`;l^YbIMSW1+ zKUF6LrysvFp;{VZ0G2K~34Cm7ByaKB1q5?`V<8mq=<1X%eLF$;$Ag0WJ2z*-kiX={ zWSE5Sn{`K9#SO<`4Nu)e)&+eoDaRC^d##{4P%y>umk|D5qfvN_YJ|q1#sX=Trf_`M zbNhq|&KA2LnE^GFT{^ji3)!D#;EMm%3yJ{N#7WAiiRU`m6PIp*JtY!%O3zS^7|m zFFw-?GD{vXErANISca)HUPygx_P2WKfs(nn8=}fUE~iZb8R^pGfY}l?*V($6RGQr` z2E`*8jEY{Lx+&%D2C}a|L6gh8qHm<_ZO&%oQmPD-V{cICRJ$}xMznv%9U6Gn<{Wpj zde|Y0p!>I%KPmQ?3m)BMKb~ie@s@NpL3CHf1XP{N&qSwN;THXWj%t@VJwV;x)SQ6x zgCFAVFEcM<*I0}MHK7T!P<^0x9-qcsc~A7d_zM0mQXfdNF-8&Doxs`S%+Sf$N*6I; zlv%rKCELS5kp_Ojd9g^&QYNRt2$b=yfl`Ed+~~eX&PK%Ot~ySKWJ(XJ99xEUA&nWc z$}^97yVcKT1y@NcpCl+c;F}Vu8`jApWl5c>bZCFcsp8V2%n#qT8}q-Y%JpXa6FCh{ zV;MVVYxz}6`{@cL6+(TWH89Dy4@&K(QA%dMKH}#c3fy3yO9T}8xm?))81{#P*rU8s zFQXQVG?y{d%Pgiz$Bk>k@ylN}56l0JuBeSMN4}F@p=1cHM)ns!6kFByyxupXn4*}^ z$sRS^27c!N$B9vHbIQXxubST)0ydKdp8>5am$;%H-oun${l8d$nqb|(t{l`628VnGCdMSun%%YIeKXPsoYr2{UBLhvy#YHs zLB1W+J;A8(#DcU2S3f1f7++mbZb6?m@EW6!@rIj zqkWMdUylS^OJM{+?#KKmA7D_cu{SgW`fF=T*q**=9MvlP(DJQ<>={#sqNL-g#7`>k z9i`|3iG){cQw3WdF^Wn5w#*4p_+GiRH>H{zK;&mOltx0DoD?hPMre$5^O^*%=wGX? z)Z3nhFCV&U;@Xn^Yi>b)jU@}}d8c54YZJ?YDh;I-4Px^pv9^dBB54_Qg5)kLU)@wR z_Fx^gaBGphACKtij-!asiWLMpI$PKXY`cEi^i`F^xfRTD_v*Z% z%wmHV7p+XHYdnL%6*6J|LA|VcII(xBW07|71RIW&WO3^Z>ODuiiBtX%XOBS+z6~~2 zhYFpB+h zH``%l;~h|(%3dUxhmANVxsd^ z;@4DRbBTO0_qd*&kq{%Uc2@I(9Lmlc!|jmLLvl&E3VinweEC3Qr5tW#i62@h6v z&)kSgYUqk|5e5{;G-7XqmMG)Xu;Fs^58+>>6Qd`bLOPOj8rFH@5*SZ}>Z~>#aKu!f z$IHK@f81c`y>v0SXZ6@yb;RdD;NX5cmQC0 zmP)XmFmXZ9c7d}x6O_Ns3>xJHkKUv#Lv6o=Y$x1s3#4qQt$IluLnO=xOl5m@^wJ)W z4mKfJx+lqGSYWE_ibmpZkqoK#=&k6)9_6)I%x3*ly2_ z<8?vYLJ@{>Z^6Dh-{uJxBwhZt#VrEpS(@rLG00sPt6yv~-u0mz1qQhimYI%~TFLe5 zafsEQ#gB=br3}Y>FO672QR4WD;RoWp6WihuGnn?PU~oK6V#Gbh$LH%(23kC%_ki!B z)RvWqN>E54mtpDQKyguz6>MGIuj^m=avw6N3#3>nFdJjO1X?XNX`AjqY)`!g+I2ao zM6IdRQN^@Y_4}R&;de*nJ#5QF3da~OMZEB@oWLvLh9eq|pjYJd0K4U!;HZnJE1WbI zY;fF~qq4bwCSYI5vh4V(q+1vJX|%bEW}8%O#b1SM%C`@-&LE} ze7v%(4vXjqzLF~99*CZOdM(|nsf!lrXc#m~x@9$^tCt9FE~Wg;NJzl@tg3r6_fDC% zKS7LjR}#D8Sg~@rtXyF|$RzlX`vk5;^`+)_Lewrh@b~cIc1Uh$k2}qLi<0A5cc?&_ zLWb!AwC7Zisjd5wGo=L?%V=1C=N?<1$-=VenTDUgg<+4>d02 z&a7`u5J@? z^;W?>hrVRLOELDvXO|5&wLx;TeP^jZic;?npi1T+gN8mbz0=6s;+XT754aWHifVPs zA>G6{2K0&RL<|h}a&P8+vl`-r@esxAqcuB>*{sU8@2y6!4RCUwx&HX;=}z9rm4cYA z%t@N5xOk*$w5P0@#~0;jy}VFI&P8;TxTBfAf1OUs-Ncj3ss$pY!?qO~`kRwGtQ8U0x<0eI0@B*z=T}(5+sH zWqH*I2^X_FdN00my_9P4Ea4HF%J9BbWKdzYAe_L5o;B{i^2xTx_$kM3x#2W3cj*2xRNZX>mWi zxdfj_Q!A_^JB$Lb^S73NV4dxv^0nxj-qtaka5!vGTk1we2ZWw_X&~3IPhH4nqifB8 z6i9QP47o0v&gg|+tcYCEZv~;UC)N71Xo|s`K(E=$Zu&*X(5*XQyXZF{D%p~_`zzi5 z7XVN|ufHPsD{sTn(=4*_&Cyp1rz% zruSL93|3<2;oadEFFNf1^h$ZByTBy#?=AERG55gF1I)!!zRQ)l^=kET)>d+!X9j6~ z15A*=tnEwCs|kEcf!x(oS0`H42v_Tyiaf93&%jp{Pou5i$}1eop2IvwZo;k(Mdmu? zWY%oK*SV_cKQuo`jeEH6wP!<~8^AwRgNd;UzI;!I8v7~=o$gS`fWw&i3U4ukUh7b+ z{r|$x*|!R9y%+C!enR<|J5@Y$;eV>`noiAZX}{wkr=9So{eMO7FT)+)6gZ?`itm7F ze2p7#R1Ysl>{%O3`N2}a{{I`AlBAY7`yCbDH9QIyKg7qt%X7<3wSQ}9bP>I|pY8jZ z{coa1%#wcmSrpAm+C1|r7>R*7IH)ZE?v7fRpW=ARL3DRa=KLHThTGB~8VYuTU1r8U z>fiPNn9URG<@5o3@8F!Oh+_or+qAc6$F06>DEnh3bI^wP) zu8x=@D9h-!pol4pdz*9iJnbKO+_pLAbKXB*uh;waK3DNo87*Q@N+5LN=!83w;XR%y%hM(Wi*}ua-s^rS1=_ zDhCbac;3{2G;_HnZ!LM8!*_RZ_It$gEh+Oh&p(1^6Jy(7q?Tq=kv*g+H7d_akvlC# zddU4>QtOeHa&5%)QC0qcL#0lxd)JcZx2DEUHq_N=sUuEZ-KG{>9U37fam~=9)a_+U z?vMEXOI1Y!H45#?^Vd_~H4e20rR0%PB~z1U7^>YwzVA?vI}DXiBHv34 zCI3u~UtE;uM^5?d9G@V*yZE#c*Y$jNoI}M-iz3uNyQiu7E{B@wv+osRqVB1e`qkMa zC3af!(X)}OiT4IeNzZa!y&I)2@@~G4-pyH>zC;Rwf!Y9g;&?~Q7@EnL zYN`c;^vQLMr9#}0|7h@WG$s6m{2l{WZ%WReaQ-=#@)2rwD42U)8oa@jn=}06(Fsnx z&sd7CBDPyBmF}Q+_khD`OL4DM{2KWE0Q|3~w@;c1ag9ziRN0T~Z{)u1{B0X`1@D3L zCI5`2EO-yT4W_|G#~^2h=)=dH5i7*4m6d0n6toz zcP%-cXQ=*U&e>DSmFyfihx^k%v{c7=lT}OgJop`4?jf%N&-yO7q;FwjcAZG9&r|1X4UK<>7*DknB-X@( z)YCduhG0Dmj-(DTZtq}eURcr5j7r*@p%iY7H3zH-Yf^U<&PVZsq*16Xyy;6ZK#W><-h*;kPW!r@4-t zRiSs(+I=eg`Wzgd2gmTcXD@n0n!##Jx;w6kLmiq9m>PeU7>RYXiY`(2IM4H^;lq!q z+oz?5O;r_EJ528=FIEkima&7EV)hnmfCKoC-m0+ z4!k-GJ$~Ah?21^s&Ho@MJiJrgRa*6F`dlMz(StdN^@2H9m-*)7#m)s@h#O>V5%T;CJUx zRjCgRWx!J5Kb-$na#{;Ue+lmCY4&Pz`8jdFfrf(baWEA`-@{$>>3!zZYhapyJ5T2R zYfKGP3@w1Q!Atl%92)*pue$%)CqK^`IUB5^4Y~Ih_4I{% z%lxhU9J!oFy?yA^Y1DfLD=XU03j9KZr9o$mD0p{|^li+rvsgCc{fB2QB z52@Q1mAIfrKAz>Bg1?_PRK!;l$t@7ws-DmE$>X8^w^PHes%E<#8u&aM^w4*E{$Id7 z%-%5d&Tc$GgVZB%2)H~O4VedDH&g#8`R->d^|KQl^_@ztX#Kzu^bSpRgXb*Ydzp># zeYyT*hkc9E=v#>I{Z6RJvr6DMg_fnsr~D&gXP#!zkuh-8SlFWZEdv_)B4<2aRXXZW z7!4A01RJ1s;+nh;-9Lc3lV29!G;DDOXL+t7e&)^`{cB*ZRM3FP|HHL@e&0!KV6AeY zp&<22(9cwb89Tg>CKD-C9Pk$CX+(X^> zF;qf7GWS#!d5#=hVB}%y^9Ax_PRuZ8!aMLA_>w^fJ%T^JTTQw4MGS2$z?Fv04t0#7 z0pKb6BE9}Kvm<0Ex~8hctMp_gwI<$)^(UxEZuR7CW4{0wvWF2T{F>egZa=ov$UID4 zK)&c@o>}YhGXL&k{_RFzk7d4HOB`r&kUr{Jv;nCxXKV;3Ts-50=Y#+0ZufVlKq~3ct%S#n$(0ocZPCQ-^mby@>|e9_sL_hWB>J z&(B_Fxbr85W>;|UPB=(iGd1uM4>}IMhD>Vn4zUwo1)r7%4`a-mHu{m{<0I_gqN%Cf z%qwP(FG+1||9TDD^(uIEfNN%cy4}zS*l9sWieMxhMX&HoW4&r<+`E7-^w87J)2Tl`Odp^F^t?phlK9yo^C7;j zSCytdMX6tgIz-l2BrVazwfLT0b=NshBJR5!zZ`8(&LhC}{N%VOo zvB$u6j+zhJ-t81qgS8I%Y#n`&n3*j-e=*gJjt_K#OMIRFm%iKxrohT5In=UaEZ|2c zi6?^3i?vg$U(q8xTZox8zJtH-!!>xBPW<)gNnjTbK7%iDecMoYrmA`u-WWgK*lnnl zoc!m37d%7aEp(RIV*8FFo^;sOz%tLL2O0DSV2)uZk(Z1*LRzviTXZG0n z(WA&1x>cd)a42ST{5f!V0yw;!x%dY-LJz`V!Tr5uZFT*V`lIP~4^n7!a)cc48tvR) z`+s2On|!#oeHR~ak)VcN{@yiCj(Glg^do-@b#X8|;IEfmC3HV$poJA?v7d9>Tws+s zJBjC9q>tLZLxGQ(r+AY%yh+DR6^>(miI`<-BBh-=y&Bpx)V>0F0`Mogi1`xKl(eUUve zfrbs@2?F@l;!SV@o=uWR2wlnio!^-oEzCT<0?k!WKhvw>)qP7l zRpI;KgSxqWc$uGa{=w7&4oC1YPUd8qJ+#bTSGi_`8n(ex&hlSksv%~m3s3367nkgP z6uw5#vv>kLXUH25)WHmCWu`ibXOZ1^nBLT)&GYP2EqIZP?d{i@%2Qu4YeV=Tq0uw! zO6}}uUjF7s!?O7BsU3LMk1Y7f=R3WM2kp8C<#VRnc*JdTrDnnZJ4_~a&fo+cMR z^CoU{6OD|2XzLi?F^d9sgM*K>wEy@AnNvK+e>&F%mS^$=zNv-2wfqhZV#l%PVTIgF z|6bOinOD)11-#G=4!PJFvcwo?4;r3FgIs7kxebz61H5f|(OinFay;4zww&N|9RJ+T z{=f21%olRW5NG0-)bo9I+dT}$(UOKM;Vyf7&#TP)JF4&pzQ2OrqZJGInNIfE%0<+N z8g_z-B&$_@3wWX5S#-{S2=!w|l*rFD%e=aQ=X?ci!jHA0F>ZWpbR+sGIDg&-O}++( z(6u@Hod&$idFlN_%jbLWnceim2VU~ugGb%e?iBFDZl7g08a~T`e(^hVIL<7KvLlWj z=};S-$ijjA+Nu_htI<4hI&VWG;BIOaGmKgdfVB|$R){%qWlYH@`q`POCv&cVM{+U4 zn?6Rv!A0R7aP+OXQsZa_dKtl=Cc$%@XLwhc3Kku5;}ZhtPAzk~z&*CFD=||GTc{H| zf?ca1bI1n=Qt(e`oO^`lkf*>=_FOp1J~B>iXV+4TcWnK|(}UyTkL`U7eAw?H@t6s? zI}SE_>{)?-Z>G+I-%a~3q5E! z3@q4|{%_0gc_zVo6S|uq4)?CA;-BDcct+HIH^!dX$SyO=Ebl_sVqhr2ely=;Xe^Ca zuF@YoQ~e(O>JFlZ8GI*NnWv_4Fk1RGe*bWA4Hri_)5~6)Whs*ctHXZRRVxL-=K|5C0PEa1l7<#aouyx5NA4 zVc82Z%!b0b=*R=q6`tAeAMO3SC%#4Lhx#@Acla_#AK+>Jel(hWc@iwg@kar?`M{}$ z>cGw%-!*SF6`L@YIf}<}($^7ms*U>9ZwK>NffM{nux;fK8np4X>2ylpEn%z&RZbaNOVw#Y1+L3;-KzyN$0 zVSX&&(cA7dm0_3h;G?|USEi3X^sAelYoQ$-`m(XK%Zz}Xsegh^dlzLl7^iMU_KX>{ z*w6FbXs_FT&+&V%xAhE-@KN$vINhw}U0?vsUrbQ*Q;em)xj3)+b@Z7F0`vAe5q2K5 zX^ffb=j=h^&Cpv9{j+CxlzAKL2Z#8xbdf{hK>k;jZR*v5x1V8N4DMBBk2ACqPZC2n z!gwOB;Tl^{w`;l@eaG9zsaH0*LE%>N$A2}N>~dV|V-|*c!65!6 zhgOxtrfNB7{DYX99_~|tIbM&1+8R9mNAMl!RdQ3GB8jS|Er&AXGeD1{A6QH8RWoR9 z8`uC^s<4w3_e4K0x0X2RrYD6P$rWr&({uly@dB4{&US~o&t|UOqoo%x_PfXowQKkd z++lu%52t_N(sMq(fZDlu<~a4MW49`zS4liwEj}QGCQLf*clBW9D6|yJPfgkHxWMbt zedx*Z{*ebuZD?6}ivGg295c4e+@53~Yr+fJI$4^*$6rIg4(1wWm3^+y_Rs9WsUM=_ z)70GF4bY@wY1t#DRuVU!t%QHq<><_%eJbrw?}$5%zSqL-Fta$z-ZW0!74qupAU5V* zGx&4j|2=q_PHK@j$j}6y)PoMyUkG-Y`JIO`k4|M5w%<>(Ylf%sMV~UX_%&0h2Mu9C zq}5=Cct`Qsb7;%_N%(Z)cK?Ta?}o=*GahAE!w0qVdwzFKM+1&29%rlR2kV8HVO1nnm9_!vNuhmk14R<0f##AY3{>zEBc00Q83+2o#S}0sk68yOHJmhs>4&(<0azXK!1D6bV*dvhS~9(zOX)|B7}8I0FZ<23FEciB5+*OOY-qchF$I6ut2%r*Zf z{Cm@8HF>U77xN*8hj6hw79M4UI#O%fY%PP zGbPyzLtsAf-Yyk_E7Un;@5E>)aSx%tN`l#^!4(=*!Z(#)gI8dAjCt5b9^L4wH)m=L zjqhR?isR>VTbFZBph>Q$z##ic)0-PqgxmHzhXw9&!Xcf{C(9nalXJ+!%O2+A>?Sy0 zV$WsG^&m>hQG8N~y}hZ9d$&?+>QU$iCp=>qUp%>pE}(-h_>;Mm zS;Jf^aB(;?h44(kx54%FnK|GEV}3k+aud1;mvb3%gGZsF@n5T`OI>)JC7FMG z>+}7^&nFE!>$b373(7Letto;cs0sInm?Ef&>9x4}ouH_#`!25ZE^Bk?rYO1zirDQY z*Ln$Rg0i5laEpxGE!#Sa>8{R-pvZNTeD?fYe|Yq^N#38=`@GKUobw9h8&|5;+polf z0}6guDxOu-P_`+H`F&|s^$BV|D2jqBZd?= z6}{P1>EEX2-jUkBZz_0%lsMm1@)Sd^w@h_>gTK$!R4VZ7y$#LoF9qMLsk6b*#7k9q zw;Iar#~E%@)1MoPaG$YT47Khlb=6Cqx0z~am!|fqDY~2F+hi#FFf~VNay??G^kXpC zR+HmRDf3oU{?`nZ?=|Io$&`0jUi-VLN}Hj%FPq9yr|Wf7^B+k`?pN5rzk{5~9f}u8 zq1C4JPg4`?rI|ayhO-;a;OCUo@jX-R8EN_jQ!`aly(dWZ@0tpKojTvAo{eCzmlQaJ z&pgfVJBx>#@_|9~_0p{0ROZy00@U0B=Fx9d6n0 zuYl$4{6{_0^m>Blx~Dnk^Q!7iLxEHH?AHthxmTKcgU9jS&$!dUV7?3e+pt36bEL+@ z`TV<8xxa5H@w$||tEOBNSf4AUUo+JO)~>OdDp5m)SHK%w>cKosp90;6+|=%9mBQZ! ziw;9=2A}y*>iY+HT@R)Su%_-})>L6XL(!Y)CEST|PA+At3>GPB%7gXXCslc#FjVQ| zK42N_s%c`D{?LE-5Bc}^Z0bITe(>7jQbX-4`S;d{rk@>@bCW6i?ow$1eu8Hux558u@8W!E0@DtTNU4mel-5FfW-(&D7Mr zmb<_?=QkzCd8S4;P~*q+X%C(QP8l%pQrB{LGxrSShTi7c zDJj~+dnP$!OHDwfC^gL8V=8umH1;h0;OrjGY6f5TN6gs4@biz7{er4;n{AqB7QKIf ztG&$qr(pIOxL*nQWepSFd@g4A6L`&)o#Rlj-d?^Q+yA|Mg zCC{VQnHRa^1z>xBO`}U~8oiu-)$&K?<&ig<3ClEIUSsP zZ3><*mCpss-Qh6&axu5A`{Ca{hK8G%%l?W++HLAP*pL(5m^*Hb=J#Hwh1<|6dR%$a z)Z`7@<@$kLr9n8mj^~3#9GnB-THt5S1#e%gDRi!(^fjL=x^XA@sU@`=dh%{XiJk0f znWv6dYVvZI=nVHnA3DIlrA6vaQXhJlxe~lDFqC(I)%vPZZkx*Q^Q=pG?Q!%C%>Cb{ z&OgIVYRl7GQD>ip$Dz#IWc^Z6U;92Fi~eJ|0$en zl?wk04}h-;yu*zS_<5iI4tjkr_@S--m+2+iFuGc5+Yr&h4TBnA0!KU%>5`b-$Cx`j zAutT~3AAzn{BPkWIJo$H9*=4Z(}S&$9zE$j*`{&c-+E9@nRj^JZD4&Nwf+X4o{fi~ zmGx+Q&mT;AZsttROKoHh&Sc)Le#}q{XO?2j&MDlRN0p-Vo4&evPiQ;7L`}1=fK3O_ zTnQeJ^B-F7zXNRXzVH_CTU}G!W@sFZ=mKy55BU2+RnZh@{l!pf-q@jq!M~b{qbD)+ zr40}1eFFSW$BS)MWjoNg%ju^bPSKMEI8{6X0b0wfeKH8gq^3zYcCbxOIG$!+Gt8(1 zzBGQ*Q1{XBg!*RS_~>1xLQ8obeQ^#N^8IbQ(i`iPgm+Oqs-3>r(S!iA5df32VJJ>- zvYhL?3K0GSeuo*FKB*=@bKu^@><_~`G%5k!^9R9Mw8s4*zU(yAz+9y9*2Y!z2~AtL ztqM=@m5`wk{2}o(xN<+U`yu?k5DnY7LYd!!FW#0opIM^6^+y`&3ef}jTV~!q^%Q=5H1qx^xQ_Nu{)qW_p{BOIY-)&A@mrfh;Ozvv(0SZp zg8Z`vt>FFjZn)BDQ(zmSgBUOfY@|{0@R6 zcu1Mdl?0zLW;nKt`T7^n{RcHLqr-z$&A)+nF%Qku+wgW(vE$*%JkLMY(rfO|eAubi z1J9eOVFLZ10;5Ix;n`&6n~I{}vSGyBo4@;~$Y8JHt83}im!;k)oLJBM@Mv$oBF8B< z#bR)cd*sot3V!F^1AH&$-ryjjsazz|LE!23Pa=b-ngKXY%qtOX8?!lyX%6^EOBWJS+ToV#MBoXmWHTBGpH z2X99A)OHKK{6$66AJ?gjkNV)~7&ATg zOY{~@ngWKJsjWobaiWRwleiyqSx?V~Z-rxc*znJJFZm|BKyBoL@f>rGe!Gt{Gz9+h zH=+&n)!D>sj+ttsH#u@dlB4GE%HT&eC3gZ}X10vZ7QV%~&%UgD>VSM&S?wa|+!cgtDc(LgVr zMSC`rtC`WR%W9fiZ)ob?s!Gf7aD1xgKx+N3nOeXX(+Y|7y)l*Y}XJnsYR&QXQ;Z+&G8lHwXzu+!tO3}({XR;S;u4-;jgdp+HY`qLqD_qeH=c3f!+l`}XIK z%wj!RCxPD1pRWGcWGxw>gz{u(6?54?IoMK?q;@_r7YP# zOg2cMPbqvPmt@v1qmN_`H~O9cr^KcGDn9UYmB?o%vDH$~;L1E6`VP=^=~e@$^y@ZsI+S9(oQR3hZb>vRn(!j8Tj3h>m+#>7c$SNu#JMG+ z+3yZ&guTScc?I@^?!)OX`-KZn4)eL5Uvn>B7x^PaKNf4~;HNe9puYutJpxDCAEss} z82=6&(S7%U5ych<6)CXeuC}R%8A;=@$=hv8umjksCAt4f1&*z${eC>=x4cfi@S#CM zo06+)is1!aFOloXKfUN^F%7@)t`>HM-oL_GYmcR0PV#-2nXfnjq`zRj|dw^d#@6cV)6hl=t?r zFE*3)iu7S*n|9>mIX>UnPH!H?N7(&+HTX-oD!|W@rDM}Pm;9c73qL)-s^+bAnnZKM z8@||UvgoNJf@hPZ7V=f4@%2eE^fY%|#2f8op?Unyjef<@)tPs|ks3T>+JW~dnR*7# z9!K{gPw@F&ZDfxKel@5r_SdQXjU9ZO`Zl%f44!9JHT-y;I&WaUc}9s$m}ftlVLx*9 z@N<`ynnrtL%tz!9ONaZ}69$yPuN{x0*YBdsc)j~3GM}GpL46^zRgpPLbnqVb>M*-t zh^$^h3#{*E;$+V}bKb$3CGyR5T}>@y#rPxW0H2)+z%Mvcd6YA%Ym{8Fj=7|#jc^^@ zXW1jmWS3^LdiFu)4Q_TlM~(P(ju{-;$I4OQL7wn(&zXzhD!Dbp{4A0;-1H@hS9|E6 zmpd1V;Dx6LsKx3ZG9WKC_|aDfeDB?2`8WAv$fgQ;vha1gy2*izgIdW>-c`08cN`+) z_K|Zswndb9Z$Pdxd+dwo`+P)|QwO!UJF}4C9`M~m9-V`i3A}E0*NVbEGAf$sJ%Q(v zk=dCX0Q%MwVSbkJHTi z6gi-kdj!yF?-2dMUtT&4` zyOi3&xFCa3?1klckOazu2kE%&OqG zi)_|5fe)jVdAQv4Kt=i;9PF;s^8L_5>h<8m*7sw6bfJhJjl`KxGWQhE&4FDKZ3;dB z$NTx6-KHV*MK&#SrUN*N*7p+Rf&~5al8Z;k52JA1kH&Z5Yc2Sihn$tb>OJtFG6lBu zI)4$G7^ZHEv*>;ky()>Gvd=c6BW>hAJ%NVv5GPtYLvC$nk4`d6LZ32Zj}V%i-itdm z)G1AV4w7NYc!m3tnmnJQ%SSTTqxAJGxIBrLu+P+^F`i6KtzoeJXqlq;o0lCjKn~2H zwGQz0!6gTC@4bF4d451m?}HI}y7y2+0kpUA4Dd$-#<C|O=zy$ z`o8T|X0H`*Vqa)_%qDw1oFupSt?!7hC4a2qeQ?C_IPYb@Y|GTZ%9Dl!kyBtgHDwAhN$kujrqL~>?u$vYyqrc2e zXix5qkMy3(tm2#XVCa5%g?#9J{f+F_oHNNDw;bJ>$Lqbn0%LlXYT#aE;W!+aMaLT8 zpNC8|L`_Bf+|4}pS=_er54sVw_`sf3g5O0vbb{v>(WTLwIpa8H1AS}ZY}Z`B!p{t- zegnM!g#38l8ik@Uby?s2;1N;wz7o%~!{cT+8D(|?WLwWJ*7xw7jTX(LWv&ycGe|?| zy@&Zt4D%Ye`0(3NG}A-2=>qq_cKE{{=7Dc5>@x8%vjcZ#!F&1)ya7J;@VN?SyR7eN zzE#o8RGk{hZF3{!0dj%JP$iz3sW3NWq{KG%4Q9WDu{AS03#T&&;FEw}L$@*i>;>(3 zONQG0aKnkF1liFFJ(A3a_E8_vPI^cS?5{lJID zWrvs#t9M|T+)B>akG_)^qVQvs9d;I9Y6O!W2mE<)g}UMG2z5EgjA`rt-O#5LzU5|D z4l;|`yA92ujl<|>4n4`T=UCs}U=bP{t7`sJ^zD0m#=chN2L}`&L;FiLHU5%ZMqjhf z)zo|p`D!(HrT-&j;c0e?cpaZdU#IxJ<8nAfzlWJ`A9ov}#vE$o0$}VR| zU?+83zQjG|!8BR`8|n`o0$b@ z+|~MSvlTx3@$Mp-p_|;=+KpD+xkkPx*QrD<$zIT}7W(UA|LLLM6Kgpe{qe2jC;7^O z=G0G8D_l-7i>3CedP8+e!H*I%*hv0~yon$3vk~oCzPhI24RDy}mmel)qRTU28N+`U zt?xzfW!E2T=m~u$8`ZNbrFQz_JMJR;aDmVDk>@(?Xf`{HpS&^s4l}?VyT~#<)^3R} z*y%|pHKe(pn1his4f)wgJn+y-4hh{zZM1Hd|5C zM=*#Q1cP7@#7cFLtSnJeV$D{{Aa|ui8x?EFWOp_R2BEVW1ZfR|s4Rn&S#sN9gF)&B z;f@M|v^%?kL1@^SbMEIP_j&etlAM|IJHOxie*e8^m20olQHe2e8O7*))#b21J4@uJ%T>qUEnXpv0 zm&qTNeC3A3ck92Cs#~Sh)uw_M8cIbic}H3Qu7W}x4pn#Z-8U^wa;^4%EmdD*Y2q@e zb-9#pvD80esdpPoZR=1n(k<73r4pB+`G1;9?_nr##JGGBojorGY<7V-K@tr!-$I z4Q(@3x>f3Vn)6&J1^48L&sb{OPs;qj^Ix~rcDJQzk2LU~EPJU0lU@CQ$)Id#h2TGpr+0XP3${x9c!cVgHH>Lt^&bOx}*Di(a8G69}n>J9l<4u*nY^do+oL%hm3rlkmQ`0=hnc}{^dDc7B zq|wsUAE;TgH2NZYKRK`PrXsbsvo7_D{f+O^!`=&)Yp&bU%rfdp&8OLujYj*&R(+=-+?t=1}~+Y|4=E$KK&l{ zzRpnFlqqMx(DeD#=yGZ1XHp~Q&)i|E`glWAe*^9QbjW2=ANHOsm>M<=HGjpvc4mz^?t6uvUTP?_*^*}m)>_H) zN)44f0*+ZX4o(8#vHv^H2xe1G_B>0S?}tn9X9ynE(wF2asbrOof(I=bzJDg%Jdo=X1;q!c+Y~+c3O%@un*M>B!i5C*O8*b5*+PhC{jf2hd4L7;+wHl^B6r|VX5^7 zt}g+bJg*;omp=;+ujKh~qkWB3cC7vUuwn;x%M0G}?@_mBz}z!DBLZIF<0SBGJl)c0 zg0Bmg&g^n410?Ji(V_ z4VuR~HK)PL68dp1pM#l+Ke6v;EsY-mrqH0s5_r6ko}J9^@3Y?r?0Q1i-Konv>;+sW zsBwOmf~vPW)b|EhM|;}gWeLx2Gr-y5VD}NeN9`m3s8uja-_}!1bS1|9Y5I|aqcw0f zKWA$0GtPjH`yXUqXHxU^@M&j+<=bx6{?1TC5qss_Jx3d84Rr()mDf@aJY(p1?n5); zaMBl`r+GuIS99hO@Dc^?@0Ez8YHUEac@F-jho;LA3a6fo6XF+3U zshb;a58!WQ_p&crTj}}8Z_!EbcD0TbtLJQbj0W~x2;baA3Q&WVo6tQM+5jIX&*1?MfZd*^<2$y(@4I_oDxy$Jh%36^3hJn-h;0)cmYN3A~`<6*yBc*Jx20(i!DJYzq4NY7i~X*JkrX#k72nwmM+ zp;RHTBeP zi2n7U2hC`1Kin@vBcgm>YWEdSv;8@}!BE!6`glofM=-F)&@6jtyOpz6vj=oPhEC)T z;U_(u?t%d1rTqQu|H$QJmar0Ep`qZ*MP182VNc+(ls;7bhx3AidG4*hrBklsx)%E8 zNrH1cGjlN5LFZ-XG$qI1NgHGnqvpm^$^ceI6 z&lv+(18=bp^k{AxEzcJ;l;im;*)O>p8Q1Vea6>I^ZI8W%|NRxrk@v#*?=1XFe`;#v zB0~e5cYxkx;Lg-vsUum-OMYyFuW_^`M22W1TY2G3^=GX2!1t=&dqr1v{Lllg`pDe2X8ayF~jW4e#t5G$DxyC6c2R_{hl-4ihr@+W_#v=5AGQI zM&l!BN{rt7c3P|Gk=+WtPj+GkaJ~dC?M&Wlskntb;0KN_V`mX^#OM`h+3D1S z8jPWxVfa;zUbd3qJzNjLi+1+kLVc3>M9SN;B_WVwcz7u(91 zAIK6>@@ec#WE7kv4_cgeBWTzld0>>!LU_qIIjfi4TaR|dsZsVsQ~tHgRP*$c%+-uf z`PVFF1?NT73GImd1f9CwP}MrSR;Fq;QJ*>Xmdh(uT%-`XGDROJ_ho;q>6;{%pb5QS z87liZwb@!w@K8h54fb3GU*S*_nSF@7AK$lA@hjFV^hZNs=7m}?IrO%PhruH>%=Z&8 z#_v^dWD@L`vHk?TY<-I7p9jVdL%Sn}CYf8Z@Miv8`VRNn&NbC?GWRjJ^ju-8{Yiee zpGmfNPM8|Ps{b%l1DZN?d zkeBCA?@C4tI5f-})nMDh%+ofCHqq-@&QN+Avqf*0M&ON~9OBx{O!ZcsTFI`J%zH!q zaGNuZjZ!-}o&qx&=H3vQEQZHdpwUgt?h!bdB|nK-Ziao9+3!EdEgtHh%GzeWU8aCbjm0_geIMW))t^ndX_L zs_|jx8+?8`wIKVX$&)S2Z7KExtAD%7z zWIm3<)tNgT3LnWn(75_#2H2+8^snI&>T&qu3?u%PlX+@dxd+XdT0yo)CuV_HAN8qX zpKa)1?xs5O3tYo1V;->eqM;xj6-S@bU?_)f1jv^O_%-pbu>4E9mBtfhxjqB`8_2Zd zcv&tTQ{xox@nLqLKXiLK{QfIRC7#^7&25a`aB=SPwP3rRr z*vJ2S$r_x2CS-jTYJA?ztb_4_!OUFj=5_g#Y~z;zEZigW)? z1wTR?nNLF0Jc3vBG9R{~J9#{y`6#%v51cuvQ{}I8se<`7cn0^PMS<_}XXG^m_C3s~ z13MaOT*(D6*BCESx&6Lz7wU1sS`GQT)Wf>X_B+RE_{JVX#*lR(OG*vy^P6rf5|?-soZ0ay`vn zE;clEprKmu6@SQD$U`mstme5nbY+UH*K2cp*X8(WL76&->hbl)FQ@~&(_`?4wIa2~ zLcVsN#WTncLAmS36^(emf4){WkYuQuP3p{UQr=0JCL)M9*-C4ZQ(+!~Xm)_Mzj+}fe~iXQplPK?>4 z4SflLw+6In6mO}9xAQ-PKiBc|A^854six2A7g;?2D%d2ij8T*9sQv#IJd5m>e2k36 zzP!y~<0CZd<1htVr^q{Slv%3ent}!&Wdl#xFvOaPtI;sDrjlp6eQV`!g=>jT9CjpTX3q_>&-aiSneUT-t&G7cjhrE(CXYS2KRwxss3_Er@z^v%hBF-d zS=T6WB5G1vd`l#%V+cAY*&0A9^2MnmKmcGglPvjV9D5 z{*L0qTSv}WGav6^N`HO37Pem~DDb0ybhqR-W2xwY#n5?L3;LRRQn*XmZ2PGa_S9Y0F$ozNsKN3ViCB)6 zuQ>35s#kr2)E=Sw5CuBr33}I@zGOW`&>_ATQuUm!X7T@C)+Z7m$9WDdb~f$nJSn^i zDuM?|r<5o}w=J-(iO45=0-HnQU+=uq@Ywl~`T8j{N9^b_W3#Z?LlpMpS5n6f<;y`O ztt>W0PLUSvT);muHv2y+V!xyP63rw$&IPl6J|_Qv>IO5DDxGz?e}fZ5?ZuM_u2G*R zd8n2oRHGong3i94>YQG0VUwV4I|-%9StiAUw+{P^*s@~`k6d+{OM?J~xw`b4iZ zin0Oe{FgRC&j$=ArBZ|qf?JH&kZpIomkT~k^gIjqe&hiCk?!%K5&wB*NJ4AmheA_Y z;qr4)zl!3jYF$ci^lMXHW9U=~U>vhz14DYfuOS#_++j2nZbU$2aFk~{nZ8Ig{fy=? z9u{Z*t!^tCSnv$t&FcITR=ou5dbWKjZm=t(d7?KRN@;mSG+$JV4IZdgUF8!|1Un(t^%37uM&$S1X)-2i1BOG|8TT>o%xA{-yfZqR(sYoc zdodZo<;%OnMyKF~DT-XJ0m3=qggV1VQkWeg`Q)OY#TWGt z7#a|S3n-x9YV-bv+tCbo>E|=!1;;p^iL2|*Ci_=Hd>`kO*0Q4>`T6{5q+*|&{XD1s zD7&RNHmtan0nt+(>g2oy{X6#P&52E2x@*<7pocuFQM&?iin<<)5Hs_Oz9btUfs~=* zQMJ^h>k6GRT9v-#EFV4TW@-G; z(Y|rakfA}rI2y0e5U`8PD*mA}_$&2*SI-e5i#{wwAb;&`09?I9w{;&GFDIyT6VojGEEfIiGumm$W~9$Ah_9JhChhUETW91uyDK zybO$X9`RV3eAz#eF9lw2q_&J8N5SD|e6D9aTz*$3=#194W6d>Zb~y;%E1|qKN4E*A zSvksoMz6{`mM^q26sI&vCT~gmv!&_N!O;#u#faW<<{$`F9qG!ne5M%bpfV5rW7%g% zExBogK|NItjx-*8_4=!c%?x_OEH;mmsf-+>!CX-o3#GkO@ieVvO{gVcqMef@dcjFx z6(sY5^*O$wE{M)QxcD|H2DzqUfBiXYH8!j(Jyk$g?I9HEFu3?}Cd3FHXDUt>JN2$i zKNA4X>2Gc+vWHsp95oi|uVDISXYJ#L4=g4O}{A@-H99)GyR@$lb)&z%TvP}Sy9S@HKbJLqg4R^M`@J!>M z%@}_CvrATET5fM*)6af+&bIf-zXsh_Q?gJT`-v2x=8Bn-k~BrIg=WE~Q2fHIoq87W zseeaCNm!oBp6281ZE)8!izH39xZPvr7ml@|oE|1yF)AjIh-jAinjqsVhdqMV$(}qu zk-v~1Gd>_q8IH8t44`FwdGN;5q^ZCZ(tP$7d<=t;tD8wX?+6IjJ5K?nE~mhWNKnmu z3+geRta*M0(dj}<_l0B^m0{dyuF9{ecp3%~;m1I=Izmm>K3N!2ss2}ECNqM;$=NUQ zZ>uLF2Ww{ibu^QRdtO|Q7{_ti2Hq?g+t$1Y1&{?Um||YJ#Aaz{6)jM!MU7u=(@-5l z(oZ4@h96fwN}YoH=V^&M@|8PeY$+?q>ONV3%P??NqroCRGdSQZE(=Sb_Wh&SJHq3K z1?;t}7X+T&*1{A9pu2rPA!3GOBZoB#;?p0I4IT*)9IDW00uKbvH4!&CwZUOQf^B*VD@q zO&wM=FV^#@`fLl19P!?ONdd1iXH!H1aO0lfNmfUvU`IOw+2wldpIA5wEV1WhqB~Di z!re=g%+1W!Bno`y1o~vHm6W2#fA@3a3@@I$I^X@U>y^jGls2^S8@+Qr@_WC9A01VS zQ@(Nt zctiIzTpHWmt(zSob5R_;bEn@l+lFRyRBVZ z{$t6}%J)h0T0a~6`A7wlubiX?<#lk+c6{;5mq?x87g=4VE%!PV9qava=A^s4V`_7b z;gJ3$2GL1N4G*D}er6VN?A_#mz_O5QR+P48_9(U>d{nA9h|5?-`5SJy#0L`rCH+uj zXQ4Hq;#Cp|)-KoZD~yJT%x{82Ed%+OK3UAQd9#EIk=+(Ruu!l=2EC-@RhtWE<$uq5 z2roxlkNn-xr&S0AuvBNmEBrMv^tIXXLagRYTr5MpBwhHK?W@>B^EhJJ(rA=NK%2(K z(&&LyOP1Xq@C}hN-@TyYa~WS$zND&`r$|lP-_bnXeYMC`;M-v9p0^QC$$B7Fowo1} zvpSakI+-%2Ag8%oWKZ*)P~>Pg3~R^L7F1psE7zI{PVabvGjTn0uV-bkiskLhQ|dL- zIxTJ+CaAZt8WnQMR`GD71Fl2KPs9a_!|@*{iFloW;_Qb0(BN za5jI*!v=3pw?oZNyA!| z55tTOt7TM0r!PO1n&VidJy?1R!Q`DIjbv|rR7X^+mz1X65 z3);!=I?>9ivpanrYjq>^(&?VKa@U`^X}2#-z-v|wv(BZ~QBZ8BOepY))r^-FL%rTp z5>|SiJ%Cn_k8ARGp)2Ccpf=0!k5nM*a!dX;tYW+9$EvU&5z~+Z-$6et{Y+BqC;Gk2Z)m4Zm#12 zXGx=9!$BUAXZ`!1iWv`ms8~P5oOBXf(!wMzvRHEFcGJ_(91czskFb7Bf^saaLx(7D znJMj-F_%ydeVxt@jX-tg(p5-xOFUxWbK%{9)c(wR!7$hqQ|tCVw@-gqpP?)Rxyn~p zUQo0-mUm+r-VV(`i0*DWa)^l-Pm{1(K1^6TVVmOBil?b3L{uhkV*Y)w^RIDVYFC@? zb$$Q=#hcR&G2ugB4QD3PpnByGLb6Q{3sqcPXbHwIwG(^GmNgY)X0Y7Ss>558Z~i>k zuJD$@p344>)=pdyGRJ+~Wi;mBD`My8t_kD!-2h&&+Tp@aZy1(efQLUM1U#4^bumO# z)9(ci9xyp-Ce*p&PXu6s>K~$->KK)D!zgHJ#L%Z+AKynI2B{efv8%mQ))& z`axzN2Bc}NZ4-Vm6j~5nm>ggb1fR7#Ql75P5&HxeN>f`-6mN0mH@O99(uAPO_uT|>(+TENE!DTgc`kBl?`CL1AY=~u{1p0UmB41rwk?&5o zOYFYs%N`mRL}MycKSTbg=(=2Px3xwy^lP`{_xHV>Yt8FPydAlkdS3pA9>GZNVI{W3 zhp1YTJIF6VxfyA3Ws7wzQwwjpkix{HG?5hk&fpBbT^Bg8QxV89ymMuht7-2}YtZ`~ zJhVnn$!f#KT@AaS=Vrm0bl~wuJHN52WOB>jJeb2Xm_CHRXY496Jn%*iIPlRJU0pfz7!)fu0mU%i$NNp*_F!sjjYJ`(#cxtOm!3S9CC-SIW?Z@^D3 zm$C12KQtcNtB8gTPq$u^!rL*pWZ$RBiW%4sQ0sUhfBL}kSHtg}cD-_kGQl~ob8E0p zs`FIQ?GMwkSA=3OTlbF4$#%3pvnOoy_PunMPKGiBqn!sXsYWq$cyt_hEkl}#!MjQk+=`( zBw_73oRnSYvC7U(@8|~ZpQp*s5-BT~42bwpCRRlg|VnWUhR*LHDEn@2&4T4uWC#t&3pC)lr?Xy#5zDmv%_2+z? z>ye9Va_S3PCv8LvD{6oFHEcc`$|h~T89e)PHDVR>T@;cYJw-dT^dqYP+BcqRy1nRP z_Zc=rq&oN(y!|erbn@xw6`OWL>DEpiq}i#@Iw4Cl(DCB;HMiKsYWh$O{fg|1Q4M@* zJIz<#t=d*rM$NthrbLC1Kcblh_?#fPLzS8?hK6nC@Pi**;i0dRzMt7f@2K;DMQdCLK(F>$b$BPyh*nn$^anB6k< z^Uo(@c+mZ`c4s_R%D^!THB5KUBfXN8j|*Rli>T8X?RCh zsRm9)#Vt(#t{NxJ31C6fDd z>NBe-@vy_Dr3!~blm@$O{chTxTbyFf5`@-W)RPW#_-2C+w|Em+DEauHi5sM=p3`;@ zR|UaZ5*2E}C6m2Eicx7#m3w0_dY#Tyk3$R?hEU0gs*Csmi?PRUq9pnkL5^ zr&2W!+ulFZZC%!A$c`Q@3LLIzKf?Otn87-nC7PcaH{T~?N}w%@dg@8Fon`{~>@J%i zcTXzQxH4!IH>>r%)HBrruhoIxW5dsdE1#WG|9u|* zQ}#jFF4BdLET4ECuxQNFbd3iTJS2P!|RtvpqSK{Q6OucdP1b z^g?jsRrPj>GX6ZdH1?3Xg|`dU%_vaPnOgja-YkIB<=wzJAd=X3x=IiH8qL$j2@_5S zp4vo&UOn5(sE%@hdcc`CT%&i2qBTzU`5^jpD62lBHsLJq=Y6ySt(TgW+N(r^KSnb+ zU~4b5`Bxd-#4gCFwxb2Jh95iR&eaRjc|tmRHi*I-IYp3Npr7cal+yP5mkGaNcC0o} zZ;6Jql^NS+ZepYxFmj4&u4ORiLE^Ve)xTL`As7NvXQY6!aUcz+sw;Q%o1=WVmfKqO zrkYsbQb2R5ulUJ@8EixU`5bfT0c)Ahg~{SNwZNZJe%&;6lTA+|r*@+d3-h>uK2lZ>+8< z_>+AsmvcLATh8BCZ-!^(KqHTWa*owp#k`bG)=Ry1HRwr{s#erHGSf*d>5>BVmVUwJ zrW?_2=pi5-RWowZkrxpvfF9h?SR~SA70v z6Orz3WxteJj5^P@>8x8jX=46%=)1GC)=QMvKFPOxEOi$NH)c)tF-E0VY)r~+(1=US zxlR>0`;jWL0B=OKPj@;kZ&LKM+U^HOkHrdl;^nZe^QLQ6?aT|11oPx7aT8o-(hG5h z=ma2G^&+{&<;%SOPdfwrsi$jSIf$frLg2=3q!Kl$w}MgU;bkRz?kqQ@f(4#f_D_K92Z;eVmx_9;)i>I6M8j%0V^W9c*l-kX$rwOP>6_1%^} z8_dtuvVmE~#F_QCe8Cr7MN)FE-;RS_T)&BX8-XWrP5PF8zY#5lFj1e=Ts*$l(co z(%OV5r5WB<7}}P8=4~?M?td{|Qu2GoG2e&vaGm$eXJW6p4Nq>r@La!@H>H zl8ggcg#fiGX!L3VVpY^=d=JlYbRQzTHfCho%rV$ zjq69uFoZyy^9)+0HjY_j2DV9?KpzqGGZW)>&n}@?%{2>)jw!WN5!7S{#4@F$pqtpK znXy!*DsdzY{KaGjSm|2ZdszY#k{m6#cWEYN3z7MJYDFK zFA+(gy?G#!*jVh!dNkMBv=5pdRy8%d$S)N01Y3{~nK0Dx`Hm5D6sSW_F@)E0JrUFs z$ouW53KxuM!$fPH&zjGlO)Ha)Ttlx+nwc8ArfF{W`MO`4gcsdy`XZ(aJXjjXGN>bU zV@^U-|Da!(mUegSC3JANC4pv*BD2e223GpQ^e73h%vD+A)?KDs%mW6=IuNwN!Z+hZ zweE3k^>Yu=N;c%FxPNd8pTKoe7=m>l6}5X*#HP#5ISQeunt8ZzTLzXMhTZ3Z3O2vfKHzw@hC%Re(;~)^;$t)Pc#nw; z7^?M2D?i9Lz8G9NGpHM^t5fv#@6XGQy&EAD`)-9Y1qsppLYi&a4L}*Y#g}h^gRs}4 zM{J_rcesKl4bOzTTiYSX`&-sJNvmulZXqXmwj3=dz9>LdmuQ%`F>_rz1A5wh@vU;F zRWD2+Jxf!mTJ6-$p0-_mW;oFZLrSZ$5gWWD~e_)JcWi z@Yj60V-U{B68+U4nSP?@L?aP2pRjkG42c)QV044d2j434CdRbC%q`3ink)3&wgNd! zoUVpKb=!^um|=FPbOLAuB{-Pr1lM7xH}B8lm{7gLsdIZGpxZ{2A=RXlT zgs`c#^gIiW(HuwWl1jU8I>~Fo1pF2Zog9UgEzOKWItsT+u~?f(R++jBu{jm=zJl%A zN)fUPF!ur!c65ka>SA_dN`qGvqxAlA3ODRk2|+K;g2#@2o!@Jskmac2I)*eqYa>T) zR%cYl3s*+|=_$*to*Tt+&Og|5`yg)xnn3dHH$O{TP1<8Vz0IrZnCf39?fB|KSYCla zv8A5XbeW?3q@s>m)Lw7~UpOtQdH4DoYAF!TbNR_iEMH={%z>D*f)VSU*MYJV zmt{j1R7u6m%2Di)4j%<0$u71klgR1v?ICY5g~G$9F(IRdA~}B=->^g@o~}r0kJy8z z!%7wG7pu|^N3nNK#~yz#BEE~8%FTO|Il9|$2{%iQoPR#2rJh7CNX3O8KRN~kaP~`9 zrWTjq*$yw%YY79905#CLXCpTCxyMJTbQWZ)=dDx$zgS0#yB{-aid-F{nt||+6uE7y zKXq8SO)afOjXc!eyXowDY+tD-eLpZ8kcZazR%9D=)oL(`eCwyg+k^T6VcqcJOYzk& z#wWon%USPnj_8ZC?>0H{YtvdRHCMF(Og~}0s?P21BSjF;2+zZYT^5;FCuP1JSUaP0 zM|`*L1L^suwEE2BqYfu}0V4E1Ygp_JocFwyMO?z}db4g@SX+VA32)9PSW&m>F<)>f zgTG4L`KKXGmtmrCb6w=zj=cX}68u#elk1KMzBa5p3`k^t~b&|wNfxJ4Y{?Kq;nT-nZi zI^8r}+m5emCaLNv#wILE8V?87-40%QsH!7Q^4U@F=YlOr5G?_v%z+izZe>#}D^rfX z!v5z{YF<;Ye!~^Pl30Xxe6W;2&qgz~H1O|IZ1m}2-sIQ3n2Ei4wNrKa%%!yJnD^l; z%pEvJS%-M?_+yM&ef=_l(`LP!XY|_mz z7}w|pfj8+2kS%NEaC0Qf$iyo}Yvnom+7Ek)P2xl+w)5{zj7(s0y~0hZN^80}vn_)i zvc{=tWXKR!Jp9`Jc?6^b+yD5FfMm*fq_6(X=f?z5nQ6}mhLyen3BRRZacRtS*nCPg z6z|-H1p=ic^x@9yl*t@gPs@>AuX|Sne0}wQUl*}AqxVhQlhQIU?lbD^Tk~xAa?I~< ze!JD%uldNyiP5u}NhAvZl!s zs=eK!gA2Q0!22mE=hsr#@bt&T};- zrzGGt8qH!mw^XH}4xOaELK+x%^thUB5#dUxu5?R~bH(bgF*ew%Ty)}6&)H7e(;d%KR~i9tMu`Ulj*K%-+nzzkA8kI9 z+h`zxjnu*B_AFOTdleV?439LOuJY9wSDO*Rt?bAm{vdmw4ma@LOM3kS?0%<~@h@&o z#)HV66Lf!B{pkI<0@8}f?tI&26UI_2#v%9 zvK>kv+{v09v^pG6I34YWZBoNEe7K!t4GOeSA`R==IVuwi2KwJb16BPkY@cs8KgP3l z^~Gu9<{iREuTpJ2`s_i892*&&!cl9hj%2PVTTNq|x0gpkO>xh^ah!;Jk+R&v@WdL2!dW>+9KG-9iMTJ!{(oa=Rtd zuvKzt{JcmWx`~Df*(su9tKKV>`LeEGR#>)~JQ=N231vo5d1Psgl-ShP6qn$_HW0Qx z)XZ7X7%rlyF%h~b6BZT~I$8KZT?7gml-W(Q&BV7urBvE8zng3@P^miYsg79xiiq9E zUXk-k$!&o3>I5F%%qqHQDf3)w9oO-p8lG=|k3IAmk%Wae9$h_@IaKWhx4jH0%+%|F z&W~<*N+`-#UvZu4F6ZkgRk-?e{wxKoh;8~|8+K%S$h@IjHDWrCJJ$9XMYcu{ zw)X+;33lq1v6n&+Q#%WtvJe=JN%$rK|$Mdb@NE8PS~?Rahe(E*-Ltg!3W{SIqW+`{INlU&a#k$ z^AX+W0hQ{4eQ!VYOnM#F_AG}$@11w8?Q1E1l(tKyH`ep2Wtb%u%u|F;QTBJ@Z#T}% za0Z2BMKb+`i7tNEZ+x28Lj~N{1uJyYh&;cW`^0FX%4Clje4!?ilp4f2$D;P=Oz>gI z_O=6#(q6_%HBtKoYsuX%Dx&*D_Dl#*b^TnStb#-^_i7L@iRV`95X0+QIn`yyR%dl& zyO{^b#1VZS0)RKobKpV9Y8^ph5Nv8iKpH;7j=cz<>p5N7&fz{RQmA~YX%LaDZ_GB$ zG`u*);ao-&%H6@hs_ky0OTfO7I`x@4=S@0;&^2))AFZFwqzYboU9*~=gK>9-g*uH+ z2H4kzd9kY6uW!#xIP`UXyTz;+O}^gb!`YO*?%YkK{&`Bf6jKP^h_^eg@7K*3J<$)e zg9(&7)%DAFC3t>Xn?Anf^fzp);Jz(pm#;o7-dZlrnm7|Bh%e6Mb|rLFWX4zUW7xm~ z8_#hxyEBR8Cmpw#F`o|5V$>XA=nA>ywt^KQkUq;ukIFZWy|xE7G>1#HP0y}J8?^i> z>}96BRKI=^=G;0~oO7&ua5prFNuqR~QE68;D((lV8qLQimjv#h(~LV9QX6W9a-$aK zxG;2B)bF^bI(YGw?jw1hSPN|r`wDgb?_mRm%Vx%UXytYRON@JeTwnGl&hu{wya-Vx z52XKndO-b>U~N04B@yGCE)1j;cQ;_MErW=EmAh-v&+8G;p(J1dpMqZ`Lf7uDq!bSw z@#Tf9Z2wgII=B-tgDXFSWJv{7&3w1bKYC=Gi=-uZd=(24W!R=nB-b2 zRmAoZ7sT6u_1td9ZA=+_JwC|FwuJMSC1$Fmcb)G`!N~B2%#i1914{NL4J(_JYr{#eLlH3x1SWGzCcFNSNT2r;uMsXwrg?Y=CIdNmg5(q zcI6w`GPVqGTJwn6=S03*+7R08C9b&M?WXj})L8wVNNpsQD@Sb84pAq{{N}IsSiUyM zxPfbiH4Tc;`q&ERfR72^KVUqUJY;w;V`DD{E#4z=>pFn*)^45Rz=egjcgdujoRRDmB20#S>0Fo$1^)I_q znEGG7PGOpV`2vOM|K)#~IcL1O&jvBy2iSto7@-sDn zi^6v){Ld(12LMVb2BTO8#i}TlNwIGfW4{6b3{igEn{oh`6aY}G2>|Gt0RU$n06_X1 z0N`HEf4>6&FuV6(`XB#4@&DV8(z9#)GxICn>_7e=ruxqcQ&x)hzf4(@|HPuO}QCwab@c#gtn{B87 literal 0 HcmV?d00001 diff --git a/python/plugins/processing/tests/testdata/custom/grass7/float_raster.tif.aux.xml b/python/plugins/processing/tests/testdata/custom/grass7/float_raster.tif.aux.xml new file mode 100644 index 000000000000..1bc16ac0895b --- /dev/null +++ b/python/plugins/processing/tests/testdata/custom/grass7/float_raster.tif.aux.xml @@ -0,0 +1,20 @@ + + + + + -310.575 + 14854.575 + 1000 + 0 + 0 + 8|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|15|15|15|16|15|15|8 + + + + 14847 + 7272 + -303 + 4373.7169547194 + + + diff --git a/python/plugins/processing/tests/testdata/custom/grass7/raster_5class.tif b/python/plugins/processing/tests/testdata/custom/grass7/raster_5class.tif new file mode 100644 index 0000000000000000000000000000000000000000..61d5a425a30bdce2a9f6d373e771e8533c5d1902 GIT binary patch literal 6599 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 ZBl@pY2Rt#Nc?Q^d)b^@-xdFt1{{ymh#vA|u literal 0 HcmV?d00001 diff --git a/python/plugins/processing/tests/testdata/custom/grass7/raster_5class.tif.aux.xml b/python/plugins/processing/tests/testdata/custom/grass7/raster_5class.tif.aux.xml new file mode 100644 index 000000000000..a5b37b9a743f --- /dev/null +++ b/python/plugins/processing/tests/testdata/custom/grass7/raster_5class.tif.aux.xml @@ -0,0 +1,27 @@ + + + + + 0.6 + 5.4 + 5 + 0 + 0 + 2999|921|2110|4010|5000 + + + + 5 + 1.000000e+00 1.800000e+00 255 255 0 0 255 0 + 1.800000e+00 2.600000e+00 0 255 0 0 255 255 + 2.600000e+00 3.400000e+00 0 255 255 0 0 255 + 3.400000e+00 4.200000e+00 0 0 255 255 0 255 + 4.200000e+00 5.000000e+00 255 0 255 255 0 0 + GRASS GIS 7.0.3 + 5 + 3.4714760638298 + 1 + 1.4943102023375 + + + diff --git a/python/plugins/processing/tests/testdata/custom/grass7/raster_6class.tif b/python/plugins/processing/tests/testdata/custom/grass7/raster_6class.tif new file mode 100644 index 0000000000000000000000000000000000000000..984703074e8c7bda76f150a61ae8b2280eac00d5 GIT binary patch literal 7239 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 + + + + 0.5833333333333333 + 6.416666666666667 + 6 + 0 + 0 + 1999|920|2110|3010|1960|5001 + + + + 5 + 1.000000e+00 2.000000e+00 255 255 0 0 255 0 + 2.000000e+00 3.000000e+00 0 255 0 0 255 255 + 3.000000e+00 4.000000e+00 0 255 255 0 0 255 + 4.000000e+00 5.000000e+00 0 0 255 255 0 255 + 5.000000e+00 6.000000e+00 255 0 255 255 0 0 + GRASS GIS 7.0.3 + 6 + 4.1343333333333 + 1 + 1.7411551401935 + + + diff --git a/python/plugins/processing/tests/testdata/expected/grass7/i.gensig.txt b/python/plugins/processing/tests/testdata/expected/grass7/i.gensig.txt new file mode 100644 index 000000000000..e69a1b880105 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/grass7/i.gensig.txt @@ -0,0 +1,17 @@ +# +# +300 +7043.06 +1.89772e+07 +# +1700 +7405.93 +2.01921e+07 +# +6000 +7287.21 +1.90221e+07 +# +7000 +7246.81 +1.90226e+07 diff --git a/python/plugins/processing/tests/testdata/expected/grass7/i.gensigset.txt b/python/plugins/processing/tests/testdata/expected/grass7/i.gensigset.txt new file mode 100644 index 000000000000..1f23b4d354aa --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/grass7/i.gensigset.txt @@ -0,0 +1,124 @@ +title: +nbands: 1 +class: + classnum: 1 + classtitle: + classtype: 1 + subclass: + pi: 0.22802 + means: 9764.5 + covar: + 3.02234e+06 + endsubclass: + subclass: + pi: 0.495197 + means: 5208.5 + covar: + 7.97435e+06 + endsubclass: + subclass: + pi: 0.118668 + means: 997.979 + covar: + 686411 + endsubclass: + subclass: + pi: 0.158115 + means: 13401 + covar: + 702077 + endsubclass: +endclass: +class: + classnum: 2 + classtitle: + classtype: 1 + subclass: + pi: 0.177696 + means: 13267.4 + covar: + 1.06971e+06 + endsubclass: + subclass: + pi: 0.172485 + means: 3861.81 + covar: + 2.35556e+06 + endsubclass: + subclass: + pi: 0.48383 + means: 8696.48 + covar: + 7.62681e+06 + endsubclass: + subclass: + pi: 0.165989 + means: 1052.05 + covar: + 692627 + endsubclass: +endclass: +class: + classnum: 3 + classtitle: + classtype: 1 + subclass: + pi: 0.135816 + means: 1158.36 + covar: + 934515 + endsubclass: + subclass: + pi: 0.187835 + means: 11847.9 + covar: + 1.74438e+06 + endsubclass: + subclass: + pi: 0.0829488 + means: 14066.2 + covar: + 268579 + endsubclass: + subclass: + pi: 0.5934 + means: 6298.74 + covar: + 8.66516e+06 + endsubclass: +endclass: +class: + classnum: 4 + classtitle: + classtype: 1 + subclass: + pi: 0.278456 + means: 3394.59 + covar: + 2.33337e+06 + endsubclass: + subclass: + pi: 0.0849227 + means: 13998.4 + covar: + 256746 + endsubclass: + subclass: + pi: 0.102818 + means: 686.099 + covar: + 379342 + endsubclass: + subclass: + pi: 0.222279 + means: 11683.6 + covar: + 1.75208e+06 + endsubclass: + subclass: + pi: 0.311523 + means: 7849.26 + covar: + 3.21781e+06 + endsubclass: +endclass: diff --git a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml index 76a2ac6b8761..2e3e84d5fca5 100644 --- a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml @@ -236,3 +236,231 @@ tests: output: hash: 270bbef9dd111af5df23a819cb0848e104e0cf4949e794a67fa0b3f2 type: rasterhash + + - algorithm: grass7:i.group + name: GRASS7 i.group + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + type: multi + params: + - name: custom/grass7/raster_6class.tif + type: raster + - name: custom/grass7/raster_5class.tif + type: raster + - name: custom/grass7/raster_4class.tif + type: raster + results: + group: + hash: e1a433546cc1fdf7061adc0d9b77676c9d66ee8e0773d471bdb39a37 + type: rasterhash + + - algorithm: grass7:i.cluster + name: GRASS7 i.cluster + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + classes: 5 + convergence: 98 + input: + params: + - name: custom/grass7/raster_6class.tif + type: raster + - name: custom/grass7/raster_5class.tif + type: raster + - name: custom/grass7/raster_4class.tif + type: raster + type: multi + iterations: 30 + min_size: 17 + separation: 0 + results: + signaturefile: + type: regex + name: expected/grass7/i.cluster.sig.txt + rules: + - '#Class 1' + - '1.83333 1.53759 3.31579' + - '#Class 2' + - '2.11045 4.35498 3.32266' + - '#Class 3' + - '5.32655 1.72558 3.32713' + - '#Class 4' + - '4.34567 4.36522 3.30235' + - '#Class 5' + - '6 4.55734 3.30291' + + - algorithm: grass7:i.oif + name: GRASS7 i.oif + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + params: + - name: custom/grass7/float_raster.tif + type: raster + - name: custom/grass7/raster_6class.tif + type: raster + - name: custom/grass7/raster_5class.tif + type: raster + - name: custom/grass7/raster_4class.tif + type: raster + type: multi + results: + output: + type: regex + name: expected/grass7/i.oif.txt + rules: + - '118773.1947' + - '4541.9055' + - '4369.2930' + - '128.6900' + + - algorithm: grass7:i.fft + name: GRASS7 i.fft + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + name: custom/grass7/float_raster.tif + type: raster + results: + imaginary: + hash: 94249384dd8b6019f0024501bc9a093cba9dd025c183d3fb46d77027 + type: rasterhash + real: + hash: 09ab93c65aa2dde4da422b62a5ed3e38208e2da072cec2b0eb837a47 + type: rasterhash + + - algorithm: grass7:i.segment + name: GRASS7 i.segment + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + params: + - name: custom/grass7/raster_6class.tif + type: raster + - name: custom/grass7/raster_5class.tif + type: raster + - name: custom/grass7/raster_4class.tif + type: raster + type: multi + iterations: 20 + memory: 300 + method: 0 + minsize: 1 + similarity: 0 + threshold: 0.5 + results: + goodness: + hash: 5cb3cc31a68c03ea76578559b04ffa5f81331a4232abb38d09b29ea4 + type: rasterhash + output: + hash: b65992a5d48b867d4a32a533f38e7a72cb1ba18f1e261c6be132baca + type: rasterhash + + - algorithm: grass7:i.gensig + name: GRASS7 i.gensig + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + params: + - name: custom/grass7/float_raster.tif + type: raster + type: multi + trainingmap: + name: custom/grass7/raster_4class.tif + type: raster + results: + signaturefile: + type: file + name: expected/grass7/i.gensig.txt + + - algorithm: grass7:i.gensigset + name: GRASS7 i.gensigset + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + params: + - name: custom/grass7/float_raster.tif + type: raster + type: multi + maxsig: 5 + trainingmap: + name: custom/grass7/raster_4class.tif + type: raster + results: + signaturefile: + type: file + name: expected/grass7/i.gensigset.txt + + - algorithm: grass7:i.rgb.his + name: GRASS7 i.rgb.his + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + blue: + name: custom/grass7/raster_6class.tif + type: raster + green: + name: custom/grass7/raster_5class.tif + type: raster + red: + name: custom/grass7/raster_4class.tif + type: raster + results: + hue: + hash: d82c717b0aca5c7bb49d6f2b086a188a6fbdc397c533734911261f74 + type: rasterhash + intensity: + hash: 6d75d7a40460611301a1f2c2b162d08ae20fb5527d80509f19748b3c + type: rasterhash + saturation: + hash: 07578ad38cf948473a519f040acb0235f60a592904ac8ef46aa607e1 + type: rasterhash + + - algorithm: grass7:i.pansharpen + name: GRASS7 i.pansharpen + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + blue: + name: custom/grass7/raster_4class.tif + type: raster + green: + name: custom/grass7/raster_5class.tif + type: raster + method: 2 + pan: + name: custom/grass7/float_raster.tif + type: raster + red: + name: custom/grass7/raster_6class.tif + type: raster + results: + blueoutput: + hash: b8f6f6d11751ec26eb93daed87611e473efe6146ad3e84bce13c3393 + type: rasterhash + greenoutput: + hash: 522356ef99242f7be6ad65c23af9148f5a807deca89c1efec1db38c1 + type: rasterhash + redoutput: + hash: c6b99e12c2eab3016bbf8d15888c353c3fdb1b84674deac78e3e2bfc + type: rasterhash + +# Don't work, needs to handle external files in tests code + - algorithm: grass7:i.smap + name: GRASS7 i.smap + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + blocksize: 1024 + input: + params: + - name: custom/grass7/float_raster.tif + type: raster + type: multi + signaturefile: + type: file + name: expected/grass7/i.gensigset.txt + results: + goodness: + hash: 26c3f407312e8a9e03e91c32e526f71ea9cfa78037a90a5f78f0818e + type: rasterhash + output: + hash: f9e99ac3891b23c650add0478bb5225444183c61c6a4c321a7c2a16f + type: rasterhash From 222935b45fda9f006ab51daaf3cd9f397a745421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Fri, 6 May 2016 14:44:04 +0200 Subject: [PATCH 38/55] Expand external file support to graphical unit test generator --- python/plugins/processing/gui/TestTools.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py index 4bdacd903290..eb0a0c3303c2 100644 --- a/python/plugins/processing/gui/TestTools.py +++ b/python/plugins/processing/gui/TestTools.py @@ -47,7 +47,8 @@ from processing.core.parameters import ( ParameterRaster, ParameterVector, - ParameterMultipleInput + ParameterMultipleInput, + ParameterFile ) @@ -151,6 +152,17 @@ def createTest(text): if not schema: p['location'] = '[The source data is not in the testdata directory. Please use data in the processing/tests/testdata folder.]' + params[param.name] = p + elif isinstance(param, ParameterFile): + filename = token[1:-1] + schema, filepath = extractSchemaPath(filename) + p = { + 'type': 'file', + 'name': filepath + } + if not schema: + p['location'] = '[The source data is not in the testdata directory. Please use data in the processing/tests/testdata folder.]' + params[param.name] = p else: try: From 48d45fbd2244df063703d0164875dc2eebd11d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Fri, 6 May 2016 15:05:49 +0200 Subject: [PATCH 39/55] Fix multiple input parameter type detection for graphical unit test generator --- python/plugins/processing/gui/TestTools.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py index eb0a0c3303c2..8e863d09d701 100644 --- a/python/plugins/processing/gui/TestTools.py +++ b/python/plugins/processing/gui/TestTools.py @@ -139,10 +139,18 @@ def createTest(text): elif isinstance(param, ParameterMultipleInput): multiparams = token[1:-1].split(';') newparam = [] + + # Handle datatype detection + dataType = param.dataType() + if dataType in ['points', 'lines', 'polygons', 'any vectors']: + dataType = 'vector' + else: + dataType = 'raster' + for mp in multiparams: schema, filepath = extractSchemaPath(mp) newparam.append({ - 'type': 'vector', + 'type': dataType, 'name': filepath }) p = { From b40d2ff3ba8a1eec984439377cadcd1f2139f5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Fri, 6 May 2016 15:50:35 +0200 Subject: [PATCH 40/55] Add last set of tests --- python/plugins/processing/algs/grass7/TODO | 75 +++++++++- .../tests/testdata/custom/grass7/i.atcorr.txt | 8 + .../testdata/custom/grass7/i.cluster.txt | 31 ++++ .../testdata/grass7_algorithm_tests.yaml | 137 +++++++++++++++++- 4 files changed, 243 insertions(+), 8 deletions(-) create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/i.atcorr.txt create mode 100644 python/plugins/processing/tests/testdata/custom/grass7/i.cluster.txt diff --git a/python/plugins/processing/algs/grass7/TODO b/python/plugins/processing/algs/grass7/TODO index 8255922a422f..8222096186c9 100644 --- a/python/plugins/processing/algs/grass7/TODO +++ b/python/plugins/processing/algs/grass7/TODO @@ -1,9 +1,70 @@ -* http://trac.osgeo.org/grass/wiki/Grass7/NewFeatures#Replacedandremovedmodules -* TODO: Merged modules - r.average: merged into G7:r.statistics, G7:r.statistics2, G7:r.statistics3 - r.bilinear merged into G7:r.resamp.interp - r.median: merged into G7:r.statistics, G7:r.statistics2, G7:r.statistics3 - r.sum: merged into G7:r.statistics, G7:r.statistics2, G7:r.statistics3 -* http://trac.osgeo.org/grass/wiki/Grass7/NewFeatures#Renamedmodules +TODO List for GRASS7 algorithms support into QGIS Processing + +Unit tests +========== + +i.* modules: +------------ + +* i.albedo: needs better data +* i.aster.toar: needs OutputDir support in tests +* i.atcorr: broken implementation +* i.biomass: OK (basic implementation) +* i.cca: needs OutputDir support in tests +* i.cluster: OK (full implementation) +* i.colors.enhance: needs other raster data +* i.eb.eta: OK (basic implementation) +* i.eb.evapfr: needs better data +* i.eb.hsebal01: OK (basic implementation) +* i.eb.netrad: OK (basic implementation) +* i.eb.soilheatflux: OK (basic implementation) +* i.emissivity: OK (basic implementation) +* i.evapo.mh: OK (basic implementation) +* i.evapo.pm: OK (basic implementation) +* i.evapo.pt: OK (basic implementation) +* i.evapo.time: broken (don't know why, should work) +* i.fft: OK (full implementation) +* i.gensig: OK (full implementation) +* i.gensigset: OK (full implementation) +* i.group: OK (full implementation) +* i.his.rgb: needs better data +* i.ifft: needs specific raster data +* i.image.mosaic: OK (basic implementation) +* i.in.spotvgt: needs probably a true NVDI SPOT file (quite huge for tests). +* i.landsat.acca: needs better data +* i.landsat.toar: needs OutputDir support in tests +* i.maxlik: OK (full implementation) +* i.modis.qc: OK (full implementation) +* i.oif: OK (full implementation) +* i.ortho.camera: not implemented in Processing +* i.ortho.elev: not implemented in Processing +* i.ortho.rectify: not implemented in Processing +* i.pansharpen: OK (full implementation) +* i.pca: needs OutputDir support in tests +* i.rectify: needs OutputDir support in tests +* i.rgb.his: OK (full implementation) +* i.segment: OK (full implementation) +* i.smap: OK (full implementation) +* i.spectral: not implementable in Processing +* i.target: not implementable in Processing +* i.tasscap: needs OutputDir support in tests +* i.topo.corr.ill: OK (basic implementation) +* i.topo.corr: needs OutputDir support in tests +* i.vi: OK (basic implementation) +* i.zc: OK (basic implementation) + +r.* modules +----------- + +Need to write everything + +v.* modules +----------- + +Need to write everything + +Other +===== + * TODO: decide what to do with nviz: nviz_cmd -> G7:m.nviz.image diff --git a/python/plugins/processing/tests/testdata/custom/grass7/i.atcorr.txt b/python/plugins/processing/tests/testdata/custom/grass7/i.atcorr.txt new file mode 100644 index 000000000000..794cd76da541 --- /dev/null +++ b/python/plugins/processing/tests/testdata/custom/grass7/i.atcorr.txt @@ -0,0 +1,8 @@ +8 +2 19 13.00 -47.410 -20.234 +1 +1 +15 +-0.600 +-1000 +64 diff --git a/python/plugins/processing/tests/testdata/custom/grass7/i.cluster.txt b/python/plugins/processing/tests/testdata/custom/grass7/i.cluster.txt new file mode 100644 index 000000000000..014f2554a01e --- /dev/null +++ b/python/plugins/processing/tests/testdata/custom/grass7/i.cluster.txt @@ -0,0 +1,31 @@ +#produit par i.cluster +#Class 1 +1596 +1.83333 1.53759 3.31579 +0.823615 +-0.22884 0.584793 +0.0131661 0.015707 0.563537 +#Class 2 +3341 +2.11045 4.35498 3.32266 +0.790493 +-0.109278 0.472751 +0.0125567 -0.0124767 0.565921 +#Class 3 +3491 +5.32655 1.72558 3.32713 +0.671555 +0.189352 0.758483 +-0.00255721 -0.0248173 0.544533 +#Class 4 +3324 +4.34567 4.36522 3.30235 +0.22625 +0.0708273 0.484688 +0.00740409 0.00750866 0.56369 +#Class 5 +2991 +6 4.55734 3.30291 +0 +0 0.246795 +0 0.00235825 0.573099 diff --git a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml index 2e3e84d5fca5..3086f364bee8 100644 --- a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml @@ -443,7 +443,6 @@ tests: hash: c6b99e12c2eab3016bbf8d15888c353c3fdb1b84674deac78e3e2bfc type: rasterhash -# Don't work, needs to handle external files in tests code - algorithm: grass7:i.smap name: GRASS7 i.smap params: @@ -464,3 +463,139 @@ tests: output: hash: f9e99ac3891b23c650add0478bb5225444183c61c6a4c321a7c2a16f type: rasterhash + + - algorithm: grass7:i.maxlik + name: GRASS7 i.maxlik + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + params: + - name: custom/grass7/raster_6class.tif + type: raster + - name: custom/grass7/raster_5class.tif + type: raster + - name: custom/grass7/raster_4class.tif + type: raster + type: multi + signaturefile: + type: file + name: custom/grass7/i.cluster.txt + results: + output: + hash: a7b63bdda06fdcac715b8fe22e440238594a3dd0e189ca2328a3d694 + type: rasterhash + reject: + hash: 87b3d0be1315a040a61c4d4dec2bd5837881ad207a4140a89a28997a + type: rasterhash + +# - algorithm: grass7:i.evapo.time +# name: GRASS7 i.evapo.time +# params: +# GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' +# eta: +# params: +# - name: custom/grass7/raster_5class.tif +# type: raster +# - name: custom/grass7/raster_4class.tif +# type: raster +# type: multi +# eta_doy: +# params: +# - name: custom/grass7/raster_6class.tif +# type: raster +# - name: custom/grass7/raster_5class.tif +# type: raster +# type: multi +# eto: +# params: +# - name: custom/grass7/float_raster.tif +# type: raster +# - name: custom/grass7/raster_6class.tif +# type: raster +# type: multi +# results: +# output: +# hash: a361b729bf0b0628fa66822297dc57e71e0b2e56331f7145c3cfdce6 +# type: rasterhash + + - algorithm: grass7:i.eb.hsebal01 + name: GRASS7 i.eb.hsebal01 + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + aerodynresistance: + name: custom/grass7/raster_6class.tif + type: raster + frictionvelocitystar: 0.32407 + netradiation: + name: custom/grass7/raster_4class.tif + type: raster + soilheatflux: + name: custom/grass7/raster_5class.tif + type: raster + temperaturemeansealevel: + name: custom/grass7/float_raster.tif + type: raster + vapourpressureactual: 1.511 + results: + output: + hash: 2706c64a65a6a603857937554a5e8787a1429e1ca6b8551b0dea7b8f + type: rasterhash + + - algorithm: grass7:i.modis.qc + name: GRASS7 i.modis.qc + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + name: custom/grass7/float_raster.tif + type: raster + productname: 8 + qcname: 5 + results: + output: + hash: 0439e59b8c142a7f58cdece674c058891b64aafb1273ed6ce5e3ecf9 + type: rasterhash + + - algorithm: grass7:i.image.mosaic + name: GRASS7 i.image.mosaic + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + params: + - name: custom/grass7/raster_6class.tif + type: raster + - name: custom/grass7/raster_5class.tif + type: raster + - name: custom/grass7/raster_4class.tif + type: raster + type: multi + results: + output: + hash: 651bde2da0f150c0dbe7790da98f371e56de20a3f1cbb13ef6a69657 + type: rasterhash + + - algorithm: grass7:i.eb.hsebal01.coords + name: GRASS7 i.eb.hsebal01.coords + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + aerodynresistance: + name: custom/grass7/float_raster.tif + type: raster + column_dry_pixel: '50' + column_wet_pixel: '10' + frictionvelocitystar: 0.32407 + netradiation: + name: custom/grass7/float_raster.tif + type: raster + row_dry_pixel: '50' + row_wet_pixel: '10' + soilheatflux: + name: custom/grass7/float_raster.tif + type: raster + temperaturemeansealevel: + name: custom/grass7/float_raster.tif + type: raster + vapourpressureactual: 1.511 + results: + output: + hash: f564acd642f6f0ea76cbe55d3565d083e055d2e81dde97365f786bad + type: rasterhash From 6b8f9430222c2884b9c65bc320b226b9505acacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Fri, 6 May 2016 16:03:57 +0200 Subject: [PATCH 41/55] Fix i.atcor algorithm --- .../algs/grass7/description/i.atcorr.txt | 13 +++++++------ .../tests/testdata/grass7_algorithm_tests.yaml | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/python/plugins/processing/algs/grass7/description/i.atcorr.txt b/python/plugins/processing/algs/grass7/description/i.atcorr.txt index 41868a58c645..47671c4d6597 100644 --- a/python/plugins/processing/algs/grass7/description/i.atcorr.txt +++ b/python/plugins/processing/algs/grass7/description/i.atcorr.txt @@ -2,14 +2,15 @@ i.atcorr Performs atmospheric correction using the 6S algorithm. Imagery (i.*) ParameterRaster|input|Name of input raster map|False -ParameterBoolean|-a|Input from ETM+ image taken after July 1, 2000|False -ParameterBoolean|-b|Input from ETM+ image taken before July 1, 2000|False +ParameterRange|range|Input imagery range [0,255]|0,255|True ParameterRaster|elevation|Input altitude raster map in m (optional)|True ParameterRaster|visibility|Input visibility raster map in km (optional)|True ParameterFile|parameters|Name of input text file|False|False -ParameterRange|range|Input imagery range [0,255]|0,255 -ParameterBoolean|-o|Try to increase computation speed when altitude and/or visibility map is used|True +ParameterRange|rescale|Rescale output raster map [0,255]|0,255|True OutputRaster|output|Atmospheric correction -ParameterBoolean|-f|Output raster is floating point|False -ParameterRange|rescale|Rescale output raster map [0,255]|0,255 +*ParameterBoolean|-i|Output raster map as integer|False +*ParameterBoolean|-r|Input raster map converted to reflectance (default is radiance)|False +*ParameterBoolean|-a|Input from ETM+ image taken after July 1, 2000|False +*ParameterBoolean|-b|Input from ETM+ image taken before July 1, 2000|False + diff --git a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml index 3086f364bee8..14bf81d4fc63 100644 --- a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml @@ -599,3 +599,21 @@ tests: output: hash: f564acd642f6f0ea76cbe55d3565d083e055d2e81dde97365f786bad type: rasterhash + + - algorithm: grass7:i.atcorr + name: GRASS7 i.atcorr + params: + GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' + input: + name: custom/grass7/raster_6class.tif + type: raster + visibility: + name: custom/grass7/raster_5class.tif + type: raster + parameters: + type: file + name: /home/medspx/projects/QGIS/python/plugins/processing/tests/testdata/custom/grass7/i.atcorr.txt + results: + output: + hash: fc3dbc04793ecba0a24400d489f3ebd0660400232602487e6861306c + type: rasterhash From 1aa0bbd25876679d6e81969c9c7057e4202805b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Fri, 6 May 2016 16:05:04 +0200 Subject: [PATCH 42/55] Update TODO file --- python/plugins/processing/algs/grass7/TODO | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/algs/grass7/TODO b/python/plugins/processing/algs/grass7/TODO index 8222096186c9..e05bf57ebe3d 100644 --- a/python/plugins/processing/algs/grass7/TODO +++ b/python/plugins/processing/algs/grass7/TODO @@ -8,7 +8,7 @@ i.* modules: * i.albedo: needs better data * i.aster.toar: needs OutputDir support in tests -* i.atcorr: broken implementation +* i.atcorr: OK (basic implementation) * i.biomass: OK (basic implementation) * i.cca: needs OutputDir support in tests * i.cluster: OK (full implementation) From cd7047cc7c7ff7156af14f02b36224b385e5ccd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Thu, 19 May 2016 10:15:55 +0200 Subject: [PATCH 43/55] Try this exec modification --- python/plugins/processing/tests/AlgorithmsTestBase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index 16bc8fc1b869..04da19e1d2d8 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -99,7 +99,7 @@ def check_algorithm(self, name, defs): expectFailure = False if 'expectedFailure' in defs: - exec(('\n'.join(defs['expectedFailure'][:-1])), globals(), locals()) + exec('\n'.join(defs['expectedFailure'][:-1]), globals(), locals()) expectFailure = eval(defs['expectedFailure'][-1]) def doCheck(): From 03a8a8e50767ac13b553c5ecebb760f4cd21c71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Thu, 19 May 2016 14:15:13 +0200 Subject: [PATCH 44/55] Try without subfunction --- python/plugins/processing/tests/AlgorithmsTestBase.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index 04da19e1d2d8..8d0e50845de0 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -102,20 +102,17 @@ def check_algorithm(self, name, defs): exec('\n'.join(defs['expectedFailure'][:-1]), globals(), locals()) expectFailure = eval(defs['expectedFailure'][-1]) - def doCheck(): - alg.execute() - - self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) - if expectFailure: try: - doCheck() + alg.execute() + self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) except Exception: pass else: raise _UnexpectedSuccess else: - doCheck() + alg.execute() + self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) def load_params(self, params): """ From bc4b3a20a76699502ae1f668d6d5a618834f592d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sat, 21 May 2016 10:48:08 +0200 Subject: [PATCH 45/55] Fix i.at.corr test --- .../processing/tests/testdata/grass7_algorithm_tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml index 14bf81d4fc63..f9e1473d7f2a 100644 --- a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml @@ -612,7 +612,7 @@ tests: type: raster parameters: type: file - name: /home/medspx/projects/QGIS/python/plugins/processing/tests/testdata/custom/grass7/i.atcorr.txt + name: custom/grass7/i.atcorr.txt results: output: hash: fc3dbc04793ecba0a24400d489f3ebd0660400232602487e6861306c From 8884cd715ac00f6de46bc22087502ab8470d5d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Mon, 23 May 2016 16:49:16 +0200 Subject: [PATCH 46/55] Add ugly debug prints to try to find what is travis-ci problem... --- python/plugins/processing/tests/AlgorithmsTestBase.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index 8d0e50845de0..5f6ec8520828 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -83,7 +83,8 @@ def check_algorithm(self, name, defs): :param defs: A python dict containing a test algorithm definition """ QgsMapLayerRegistry.instance().removeAllMapLayers() - + print('DEBUG on {}'.format(name)) + print('Params = {}'.format(defs)) params = self.load_params(defs['params']) alg = processing.Processing.getAlgorithm(defs['algorithm']).getCopy() @@ -168,7 +169,6 @@ def load_layer(self, param): Loads a layer which was specified as parameter. """ filepath = self.filepath_from_param(param) - if param['type'] == 'vector': lyr = QgsVectorLayer(filepath, param['name'], 'ogr') elif param['type'] == 'raster': @@ -185,13 +185,14 @@ def filepath_from_param(self, param): prefix = processingTestDataPath() if 'location' in param and param['location'] == 'qgs': prefix = unitTestDataPath() - + print('DEBUG filepath_from_param: {} -> {}'.format(param['name'], os.path.join(prefix, param['name']))) return os.path.join(prefix, param['name']) def check_results(self, results, expected): """ Checks if result produced by an algorithm matches with the expected specification. """ + print('DEBUG check_results: results {}\n\texpected {}'.format(results, expected)) for id, expected_result in expected.items(): if 'vector' == expected_result['type']: expected_lyr = self.load_layer(expected_result) From 1945facd1dba3ee2b2087796c9900b5f3b2808fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Tue, 24 May 2016 17:12:58 +0200 Subject: [PATCH 47/55] Temporary make Grass7Utils more verbose for more travis debug --- python/plugins/processing/algs/grass7/Grass7Utils.py | 5 +++++ python/plugins/processing/tests/AlgorithmsTestBase.py | 7 +++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/python/plugins/processing/algs/grass7/Grass7Utils.py b/python/plugins/processing/algs/grass7/Grass7Utils.py index d2885ba84f99..ae44e76ef8f2 100644 --- a/python/plugins/processing/algs/grass7/Grass7Utils.py +++ b/python/plugins/processing/algs/grass7/Grass7Utils.py @@ -261,6 +261,7 @@ def executeGrass7(commands, progress, outputCommands=None): loglines.append(Grass7Utils.tr('GRASS GIS 7 execution console output')) grassOutDone = False command, grassenv = Grass7Utils.prepareGrass7Execution(commands) + print('Debug: executeGrass7\n---------------\ncommands: {}\n\ncommand: {}\n\nenv: {}'.format(commands, command, grassenv)) proc = subprocess.Popen( command, shell=True, @@ -271,6 +272,7 @@ def executeGrass7(commands, progress, outputCommands=None): env=grassenv ).stdout for line in iter(proc.readline, ''): + print(line) if 'GRASS_INFO_PERCENT' in line: try: progress.setPercentage(int(line[len('GRASS_INFO_PERCENT') + 2:])) @@ -290,6 +292,8 @@ def executeGrass7(commands, progress, outputCommands=None): if not grassOutDone and outputCommands: command, grassenv = Grass7Utils.prepareGrass7Execution(outputCommands) + print('Debug: executeGrass7 outputCommands\n=============\ncommands: {}\n\ncommand: {}\n\nenv: {}'.format(commands, command, grassenv)) + proc = subprocess.Popen( command, shell=True, @@ -300,6 +304,7 @@ def executeGrass7(commands, progress, outputCommands=None): env=grassenv ).stdout for line in iter(proc.readline, ''): + print(line) if 'GRASS_INFO_PERCENT' in line: try: progress.setPercentage(int( diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index 5f6ec8520828..9dd75f6a78cf 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -83,8 +83,7 @@ def check_algorithm(self, name, defs): :param defs: A python dict containing a test algorithm definition """ QgsMapLayerRegistry.instance().removeAllMapLayers() - print('DEBUG on {}'.format(name)) - print('Params = {}'.format(defs)) + params = self.load_params(defs['params']) alg = processing.Processing.getAlgorithm(defs['algorithm']).getCopy() @@ -185,14 +184,14 @@ def filepath_from_param(self, param): prefix = processingTestDataPath() if 'location' in param and param['location'] == 'qgs': prefix = unitTestDataPath() - print('DEBUG filepath_from_param: {} -> {}'.format(param['name'], os.path.join(prefix, param['name']))) + return os.path.join(prefix, param['name']) def check_results(self, results, expected): """ Checks if result produced by an algorithm matches with the expected specification. """ - print('DEBUG check_results: results {}\n\texpected {}'.format(results, expected)) + for id, expected_result in expected.items(): if 'vector' == expected_result['type']: expected_lyr = self.load_layer(expected_result) From f6710de670f25a8a6c4b1c715a3fbd2dbaa4d64a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Thu, 26 May 2016 10:44:29 +0200 Subject: [PATCH 48/55] Fix rasterhashes value with osgeo4travis values --- .../processing/algs/grass7/Grass7Utils.py | 5 --- .../testdata/grass7_algorithm_tests.yaml | 36 +++++++++---------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/python/plugins/processing/algs/grass7/Grass7Utils.py b/python/plugins/processing/algs/grass7/Grass7Utils.py index ae44e76ef8f2..d2885ba84f99 100644 --- a/python/plugins/processing/algs/grass7/Grass7Utils.py +++ b/python/plugins/processing/algs/grass7/Grass7Utils.py @@ -261,7 +261,6 @@ def executeGrass7(commands, progress, outputCommands=None): loglines.append(Grass7Utils.tr('GRASS GIS 7 execution console output')) grassOutDone = False command, grassenv = Grass7Utils.prepareGrass7Execution(commands) - print('Debug: executeGrass7\n---------------\ncommands: {}\n\ncommand: {}\n\nenv: {}'.format(commands, command, grassenv)) proc = subprocess.Popen( command, shell=True, @@ -272,7 +271,6 @@ def executeGrass7(commands, progress, outputCommands=None): env=grassenv ).stdout for line in iter(proc.readline, ''): - print(line) if 'GRASS_INFO_PERCENT' in line: try: progress.setPercentage(int(line[len('GRASS_INFO_PERCENT') + 2:])) @@ -292,8 +290,6 @@ def executeGrass7(commands, progress, outputCommands=None): if not grassOutDone and outputCommands: command, grassenv = Grass7Utils.prepareGrass7Execution(outputCommands) - print('Debug: executeGrass7 outputCommands\n=============\ncommands: {}\n\ncommand: {}\n\nenv: {}'.format(commands, command, grassenv)) - proc = subprocess.Popen( command, shell=True, @@ -304,7 +300,6 @@ def executeGrass7(commands, progress, outputCommands=None): env=grassenv ).stdout for line in iter(proc.readline, ''): - print(line) if 'GRASS_INFO_PERCENT' in line: try: progress.setPercentage(int( diff --git a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml index f9e1473d7f2a..f3d75faf7070 100644 --- a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml @@ -12,7 +12,7 @@ tests: results: output: type: rasterhash - hash: 0850127d19d5098ff7cc7bb5991b7881d792e4a64aed42b811cb031b + hash: 80fe9e022cff3d4cad57fbad40aa7f547b3621a4eec94629d69e16ed - algorithm: grass7:i.biomass name: GRASS7 i.biomass @@ -38,7 +38,7 @@ tests: type: raster results: output: - hash: 2359a7d0db659ea48272248e663ead0b9cb11e6e97b3fcb14db8bd95 + hash: edbbf2a0866a2fdfa01996d5bef5a06862df2a54c020fc8b1e4aa51a type: rasterhash - algorithm: grass7:i.eb.eta @@ -56,7 +56,7 @@ tests: type: raster results: output: - hash: ea1b463ce6d426eb3b6d40dbd5b475a010bede837f5a3e3faf3dfb40 + hash: 5143d76a4d32395f4cfaf22aab33c5ae47f2c219eebbb888663a4e85 type: rasterhash - algorithm: grass7:i.eb.netrad @@ -92,7 +92,7 @@ tests: type: raster results: output: - hash: d6fc0efa1ebff447b8b04a1bd222acd9e37b1aa3a655ad5b1d69a49f + hash: b38052d5d446aad62dfe46c84d0fb949b9ba0b6f43aa7c95e3de2c92 type: rasterhash - algorithm: grass7:i.eb.soilheatflux @@ -117,7 +117,7 @@ tests: type: raster results: output: - hash: b8a09be667e73465a3d06e3c1a1d99a8663f21be1918f498e7b49eca + hash: aa195846f7a93d4141c01170751afad055ab6d1caa74cc888c4ad398 type: rasterhash - algorithm: grass7:i.evapo.mh @@ -139,7 +139,7 @@ tests: type: raster results: output: - hash: 182223a6c8da7b51118794de826c0bc53f288f377351d62aecc63837 + hash: 9bdbd441804b613f0fdb2449572b69fdc3431113810330693c45b87f type: rasterhash - algorithm: grass7:i.evapo.pm @@ -166,7 +166,7 @@ tests: type: raster results: output: - hash: 53ad3d55bb9593decb21a6ee10802dd79a42cb17f067ce38ac286ab2 + hash: 7d649cefbf6b0d5581c9cafb285c0598827198fd6f313909325979d7 type: rasterhash - algorithm: grass7:i.evapo.pt @@ -188,7 +188,7 @@ tests: type: raster results: output: - hash: 182223a6c8da7b51118794de826c0bc53f288f377351d62aecc63837 + hash: 9bdbd441804b613f0fdb2449572b69fdc3431113810330693c45b87f type: rasterhash - algorithm: grass7:i.topo.coor.ill @@ -202,7 +202,7 @@ tests: zenith: 50 results: output: - hash: d3da5e4b9d088760d01d70de956d2cd35433c96d175ca68394d4dee8 + hash: d1eb7882b3d68f2bc5785dc052b1c870d84e9a232b72607ca4d7da45 type: rasterhash - algorithm: grass7:i.vi @@ -219,7 +219,7 @@ tests: viname: 10 results: output: - hash: 182223a6c8da7b51118794de826c0bc53f288f377351d62aecc63837 + hash: 9bdbd441804b613f0fdb2449572b69fdc3431113810330693c45b87f type: rasterhash - algorithm: grass7:i.zc @@ -323,7 +323,7 @@ tests: type: raster results: imaginary: - hash: 94249384dd8b6019f0024501bc9a093cba9dd025c183d3fb46d77027 + hash: 0a64ae8d6ba603cbedeee7706df0942f4d7221e394bc76939f33a3dc type: rasterhash real: hash: 09ab93c65aa2dde4da422b62a5ed3e38208e2da072cec2b0eb837a47 @@ -350,7 +350,7 @@ tests: threshold: 0.5 results: goodness: - hash: 5cb3cc31a68c03ea76578559b04ffa5f81331a4232abb38d09b29ea4 + hash: 597d8962276c360ef1efc0b8647c3bd773b5e98898d4e56a2d82928f type: rasterhash output: hash: b65992a5d48b867d4a32a533f38e7a72cb1ba18f1e261c6be132baca @@ -434,13 +434,13 @@ tests: type: raster results: blueoutput: - hash: b8f6f6d11751ec26eb93daed87611e473efe6146ad3e84bce13c3393 + hash: 75bac89afee968cd5d7027de1acfff3b02a7517eb136f3b73e49b2fa type: rasterhash greenoutput: - hash: 522356ef99242f7be6ad65c23af9148f5a807deca89c1efec1db38c1 + hash: e11274dddcd5f169f03957f41ece1fde7625b8ace342de258ba2873b type: rasterhash redoutput: - hash: c6b99e12c2eab3016bbf8d15888c353c3fdb1b84674deac78e3e2bfc + hash: 9fc943dcf4fcead4f2c662bb6e3f0455caed5df1d9f46ae763955001 type: rasterhash - algorithm: grass7:i.smap @@ -538,7 +538,7 @@ tests: vapourpressureactual: 1.511 results: output: - hash: 2706c64a65a6a603857937554a5e8787a1429e1ca6b8551b0dea7b8f + hash: 10a7144d393e716f721265057d1d1603baa627ceb7c6b29e30865a58 type: rasterhash - algorithm: grass7:i.modis.qc @@ -597,7 +597,7 @@ tests: vapourpressureactual: 1.511 results: output: - hash: f564acd642f6f0ea76cbe55d3565d083e055d2e81dde97365f786bad + hash: 77e31601b511602b998168f715756a87a3735b07131a99b27e2de241 type: rasterhash - algorithm: grass7:i.atcorr @@ -615,5 +615,5 @@ tests: name: custom/grass7/i.atcorr.txt results: output: - hash: fc3dbc04793ecba0a24400d489f3ebd0660400232602487e6861306c + hash: 46dea0dc5e5a3fed82aed98aa1cb6fe82deb8f1ac0347e930d4bbbeb type: rasterhash From 5f3b60c935440f8eefca01f6ea2fcb38ed385a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Thu, 26 May 2016 11:50:11 +0200 Subject: [PATCH 49/55] Add libfftw3-3 to travis-ci environment (fingercross: should pass) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c6d593e3739a..295b4aec6ec0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ matrix: - flex - flip - libfcgi-dev + - libfftw3-3 - libpq-dev - libqscintilla2-dev - libqt4-dev From c867123f18e13b4c7542eecdadada25676f1a02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Thu, 26 May 2016 17:36:38 +0200 Subject: [PATCH 50/55] Fix exec statement for AlgorithmsTestBase.py On travis-ci environment, Python version seems to be affected by [this bug](https://bugs.python.org/issue21591). One way to fix it is to use the old statement instead of exec() function. --- .../processing/tests/AlgorithmsTestBase.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index 9dd75f6a78cf..b4266914c922 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -85,6 +85,7 @@ def check_algorithm(self, name, defs): QgsMapLayerRegistry.instance().removeAllMapLayers() params = self.load_params(defs['params']) + alg = processing.Processing.getAlgorithm(defs['algorithm']).getCopy() if isinstance(params, list): @@ -99,20 +100,23 @@ def check_algorithm(self, name, defs): expectFailure = False if 'expectedFailure' in defs: - exec('\n'.join(defs['expectedFailure'][:-1]), globals(), locals()) + exec '\n'.join(defs['expectedFailure'][:-1]) in globals(), locals() expectFailure = eval(defs['expectedFailure'][-1]) + def doCheck(): + alg.execute() + + self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) + if expectFailure: try: - alg.execute() - self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) + doCheck() except Exception: pass else: raise _UnexpectedSuccess else: - alg.execute() - self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) + doCheck() def load_params(self, params): """ @@ -168,6 +172,7 @@ def load_layer(self, param): Loads a layer which was specified as parameter. """ filepath = self.filepath_from_param(param) + if param['type'] == 'vector': lyr = QgsVectorLayer(filepath, param['name'], 'ogr') elif param['type'] == 'raster': @@ -191,7 +196,6 @@ def check_results(self, results, expected): """ Checks if result produced by an algorithm matches with the expected specification. """ - for id, expected_result in expected.items(): if 'vector' == expected_result['type']: expected_lyr = self.load_layer(expected_result) From 3f6e84f959620a0985b09340226cc018126e961c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Thu, 26 May 2016 17:47:33 +0200 Subject: [PATCH 51/55] fix .travis config indentation --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 295b4aec6ec0..ea0beb99431f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,7 @@ matrix: - flex - flip - libfcgi-dev - - libfftw3-3 + - libfftw3-3 - libpq-dev - libqscintilla2-dev - libqt4-dev From 235f986517168b955e8aaaa76cf6d2741cb64744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20Ribreux?= Date: Fri, 27 May 2016 12:52:59 +0200 Subject: [PATCH 52/55] Fix qt5 travis build and update README for Processing tests --- .travis.yml | 1 + .../processing/tests/AlgorithmsTestBase.py | 14 ++++++-------- python/plugins/processing/tests/README.md | 17 +++++++++++++++++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea0beb99431f..4862b2996079 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,6 +84,7 @@ matrix: - graphviz - libpq-dev - libfcgi-dev + - libfftw3-3 - pkg-config - poppler-utils - txt2tags diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index b4266914c922..42cd20f4d61b 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -100,23 +100,21 @@ def check_algorithm(self, name, defs): expectFailure = False if 'expectedFailure' in defs: - exec '\n'.join(defs['expectedFailure'][:-1]) in globals(), locals() + exec('\n'.join(defs['expectedFailure'][:-1])) in globals(), locals() expectFailure = eval(defs['expectedFailure'][-1]) - def doCheck(): - alg.execute() - - self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) - if expectFailure: try: - doCheck() + alg.execute() + self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) except Exception: pass else: raise _UnexpectedSuccess else: - doCheck() + alg.execute() + self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) + def load_params(self, params): """ diff --git a/python/plugins/processing/tests/README.md b/python/plugins/processing/tests/README.md index 0bb8e91e6c99..7bed12810e0d 100644 --- a/python/plugins/processing/tests/README.md +++ b/python/plugins/processing/tests/README.md @@ -54,6 +54,23 @@ The above translates to name: expected/polys_densify.gml ``` +For GRASS 7 raster outputs +-------------------------- + +If you want to create a test for a GRASS 7 module which exports a raster output, you will need extra steps. There isa great chance that your GRASS 7 installation will be different from the Travis-Ci environment (for the moment, it is provided by [osgeo4travis archive]( and it is based on GRASS 7.0.2). It means that your raster hashes will be different from the ones produced in Travis-Ci (GRASS adds a metadata which contains the GRASS version in the GTiff). + +Here are a few instructions (for Debian based GNU/Linux distributions) to assure you use the same version of GRASS than in Travis-Ci: + +* Install osgeo4travis binaries into your home directory: + `curl -L https://github.com/opengisch/osgeo4travis/archive/qt4bin.tar.gz | tar -xzC ${HOME} --strip-components=1` +* Modify the grass70 script which incorporates a hardlink to /home/travis: + `sed -i 's#/home/travis#'${HOME}'#g' ${HOME}/osgeo4travis/bin/grass70` +* Launch QGIS with the following command: + `PATH=${HOME}/osgeo4travis/bin:$PATH LD_LIBRARY_PATH=${HOME}/osgeo4travis/lib:$LD_LIBRARY_PATH qgis` + +From now, you will be using osgeo4travis GRASS 7 installation in QGIS Processing and you can proceed with the above method to easily produce unit tests. + + Params and results ------------------ From ec6b29aa8ca9ed8fbf42e280a94650ef02c34d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sat, 28 May 2016 13:44:14 +0200 Subject: [PATCH 53/55] Remove NaN values from raster before hash calculation --- python/plugins/processing/gui/TestTools.py | 5 ++- .../processing/tests/AlgorithmsTestBase.py | 7 ++-- .../testdata/grass7_algorithm_tests.yaml | 36 +++++++++---------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/python/plugins/processing/gui/TestTools.py b/python/plugins/processing/gui/TestTools.py index 8e863d09d701..aae1ed2c579c 100644 --- a/python/plugins/processing/gui/TestTools.py +++ b/python/plugins/processing/gui/TestTools.py @@ -32,6 +32,8 @@ from osgeo import gdal from osgeo.gdalconst import GA_ReadOnly +from numpy import nan_to_num + from qgis.PyQt.QtCore import QCoreApplication, QMetaObject from qgis.PyQt.QtWidgets import QDialog, QVBoxLayout, QTextEdit @@ -195,7 +197,8 @@ def createTest(text): elif isinstance(out, OutputRaster): filename = token[1:-1] dataset = gdal.Open(filename, GA_ReadOnly) - strhash = hashlib.sha224(dataset.ReadAsArray(0).data).hexdigest() + dataArray = nan_to_num(dataset.ReadAsArray(0)) + strhash = hashlib.sha224(dataArray.data).hexdigest() results[out.name] = { 'type': 'rasterhash', diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index 42cd20f4d61b..1e15e982f7ff 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -34,6 +34,7 @@ import tempfile from osgeo.gdalconst import GA_ReadOnly +from numpy import nan_to_num import processing from processing.modeler.ModelerAlgorithmProvider import ModelerAlgorithmProvider @@ -100,7 +101,7 @@ def check_algorithm(self, name, defs): expectFailure = False if 'expectedFailure' in defs: - exec('\n'.join(defs['expectedFailure'][:-1])) in globals(), locals() + exec('\n'.join(defs['expectedFailure'][:-1]) in globals(), locals() expectFailure = eval(defs['expectedFailure'][-1]) if expectFailure: @@ -114,7 +115,6 @@ def check_algorithm(self, name, defs): else: alg.execute() self.check_results(alg.getOutputValuesAsDictionary(), defs['results']) - def load_params(self, params): """ @@ -210,7 +210,8 @@ def check_results(self, results, expected): elif 'rasterhash' == expected_result['type']: dataset = gdal.Open(results[id], GA_ReadOnly) - strhash = hashlib.sha224(dataset.ReadAsArray(0).data).hexdigest() + dataArray = nan_to_num(dataset.ReadAsArray(0)) + strhash = hashlib.sha224(dataArray.data).hexdigest() self.assertEqual(strhash, expected_result['hash']) elif 'file' == expected_result['type']: diff --git a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml index f3d75faf7070..226ede9d1b0e 100644 --- a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml @@ -12,7 +12,7 @@ tests: results: output: type: rasterhash - hash: 80fe9e022cff3d4cad57fbad40aa7f547b3621a4eec94629d69e16ed + hash: cef69ed56f0b0f991ae2f7f2a54b8a29319eaf8b7d65653c75cbf985 - algorithm: grass7:i.biomass name: GRASS7 i.biomass @@ -38,7 +38,7 @@ tests: type: raster results: output: - hash: edbbf2a0866a2fdfa01996d5bef5a06862df2a54c020fc8b1e4aa51a + hash: 358c7745aaa5d7fbc56b34a21821fdfdc61f68e6ca79fb996a2241d8 type: rasterhash - algorithm: grass7:i.eb.eta @@ -56,7 +56,7 @@ tests: type: raster results: output: - hash: 5143d76a4d32395f4cfaf22aab33c5ae47f2c219eebbb888663a4e85 + hash: 23e65e4c5384b48d7151f781bab10e5caa398b36d363ff8c1049c917 type: rasterhash - algorithm: grass7:i.eb.netrad @@ -92,7 +92,7 @@ tests: type: raster results: output: - hash: b38052d5d446aad62dfe46c84d0fb949b9ba0b6f43aa7c95e3de2c92 + hash: 361d48c66d978d4844ae7c073c9e6d042fdc8c7739b889a1efce3c3f type: rasterhash - algorithm: grass7:i.eb.soilheatflux @@ -117,7 +117,7 @@ tests: type: raster results: output: - hash: aa195846f7a93d4141c01170751afad055ab6d1caa74cc888c4ad398 + hash: a25c4b750dd1f2f2124a117c80ac3f0e3d9b353618a86389eca794e8 type: rasterhash - algorithm: grass7:i.evapo.mh @@ -139,7 +139,7 @@ tests: type: raster results: output: - hash: 9bdbd441804b613f0fdb2449572b69fdc3431113810330693c45b87f + hash: b0211772fe8e2c3d8c713551137f014756eb63ccd12f95a322f63ce0 type: rasterhash - algorithm: grass7:i.evapo.pm @@ -166,7 +166,7 @@ tests: type: raster results: output: - hash: 7d649cefbf6b0d5581c9cafb285c0598827198fd6f313909325979d7 + hash: 419ae792b057d2324354ce76d971d01e36751cf1c45cf3d856634576 type: rasterhash - algorithm: grass7:i.evapo.pt @@ -188,7 +188,7 @@ tests: type: raster results: output: - hash: 9bdbd441804b613f0fdb2449572b69fdc3431113810330693c45b87f + hash: b0211772fe8e2c3d8c713551137f014756eb63ccd12f95a322f63ce0 type: rasterhash - algorithm: grass7:i.topo.coor.ill @@ -202,7 +202,7 @@ tests: zenith: 50 results: output: - hash: d1eb7882b3d68f2bc5785dc052b1c870d84e9a232b72607ca4d7da45 + hash: b08dc5b47f557ecadf0c125c99f249e49111c44e43f463c2444ab474 type: rasterhash - algorithm: grass7:i.vi @@ -219,7 +219,7 @@ tests: viname: 10 results: output: - hash: 9bdbd441804b613f0fdb2449572b69fdc3431113810330693c45b87f + hash: b0211772fe8e2c3d8c713551137f014756eb63ccd12f95a322f63ce0 type: rasterhash - algorithm: grass7:i.zc @@ -323,7 +323,7 @@ tests: type: raster results: imaginary: - hash: 0a64ae8d6ba603cbedeee7706df0942f4d7221e394bc76939f33a3dc + hash: 94249384dd8b6019f0024501bc9a093cba9dd025c183d3fb46d77027 type: rasterhash real: hash: 09ab93c65aa2dde4da422b62a5ed3e38208e2da072cec2b0eb837a47 @@ -350,7 +350,7 @@ tests: threshold: 0.5 results: goodness: - hash: 597d8962276c360ef1efc0b8647c3bd773b5e98898d4e56a2d82928f + hash: 4d7728e28734d2b67427a514bcd155d254d30b3424bf4e0ad8f0f0c6 type: rasterhash output: hash: b65992a5d48b867d4a32a533f38e7a72cb1ba18f1e261c6be132baca @@ -434,13 +434,13 @@ tests: type: raster results: blueoutput: - hash: 75bac89afee968cd5d7027de1acfff3b02a7517eb136f3b73e49b2fa + hash: 906de8be89e302057ed849d00eaf49332ecca73ffaba1374994f1a17 type: rasterhash greenoutput: - hash: e11274dddcd5f169f03957f41ece1fde7625b8ace342de258ba2873b + hash: 588ad1ef8360ce903fc2defb1a1728a1dc8335d737d5fa77797605ed type: rasterhash redoutput: - hash: 9fc943dcf4fcead4f2c662bb6e3f0455caed5df1d9f46ae763955001 + hash: ad80c0007faa1b0dc15c0b0c21ff4e0045ff5e67b454df0f65e68899 type: rasterhash - algorithm: grass7:i.smap @@ -538,7 +538,7 @@ tests: vapourpressureactual: 1.511 results: output: - hash: 10a7144d393e716f721265057d1d1603baa627ceb7c6b29e30865a58 + hash: cac9e91f9b5182fbad336fd46ecebcf5185327e009ae8dc4cc0367fc type: rasterhash - algorithm: grass7:i.modis.qc @@ -597,7 +597,7 @@ tests: vapourpressureactual: 1.511 results: output: - hash: 77e31601b511602b998168f715756a87a3735b07131a99b27e2de241 + hash: b0211772fe8e2c3d8c713551137f014756eb63ccd12f95a322f63ce0 type: rasterhash - algorithm: grass7:i.atcorr @@ -615,5 +615,5 @@ tests: name: custom/grass7/i.atcorr.txt results: output: - hash: 46dea0dc5e5a3fed82aed98aa1cb6fe82deb8f1ac0347e930d4bbbeb + hash: e5fada2ec43658e25a34d0486e810dec1cad289b626f6d1c4bbfea18 type: rasterhash From 8d1bec33a0e036a2123e1a7568168ca2f71e35ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sat, 28 May 2016 15:09:46 +0200 Subject: [PATCH 54/55] Exec things... --- python/plugins/processing/tests/AlgorithmsTestBase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/plugins/processing/tests/AlgorithmsTestBase.py b/python/plugins/processing/tests/AlgorithmsTestBase.py index 1e15e982f7ff..d7bc6625a5d7 100644 --- a/python/plugins/processing/tests/AlgorithmsTestBase.py +++ b/python/plugins/processing/tests/AlgorithmsTestBase.py @@ -101,7 +101,7 @@ def check_algorithm(self, name, defs): expectFailure = False if 'expectedFailure' in defs: - exec('\n'.join(defs['expectedFailure'][:-1]) in globals(), locals() + exec('\n'.join(defs['expectedFailure'][:-1])) in globals(), locals() expectFailure = eval(defs['expectedFailure'][-1]) if expectFailure: From 59faf95c1d78d546007ac3e2b0aebe74305a718e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9d=C3=A9ric=20RIBREUX?= Date: Sat, 28 May 2016 19:10:42 +0200 Subject: [PATCH 55/55] Blacklist travis-ci qt5 tests and remove i.fft test --- ci/travis/linux/qt5/blacklist.txt | 1 + python/plugins/processing/tests/README.md | 17 ---------- .../testdata/grass7_algorithm_tests.yaml | 31 ++++++++++--------- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/ci/travis/linux/qt5/blacklist.txt b/ci/travis/linux/qt5/blacklist.txt index 9f8d03127ee4..ac0a554b1475 100755 --- a/ci/travis/linux/qt5/blacklist.txt +++ b/ci/travis/linux/qt5/blacklist.txt @@ -13,3 +13,4 @@ PyQgsVirtualLayerDefinition PyQgsVirtualLayerProvider qgis_composermapgridtest qgis_composerutils +ProcessingGrass7AlgorithmsTest diff --git a/python/plugins/processing/tests/README.md b/python/plugins/processing/tests/README.md index 7bed12810e0d..0bb8e91e6c99 100644 --- a/python/plugins/processing/tests/README.md +++ b/python/plugins/processing/tests/README.md @@ -54,23 +54,6 @@ The above translates to name: expected/polys_densify.gml ``` -For GRASS 7 raster outputs --------------------------- - -If you want to create a test for a GRASS 7 module which exports a raster output, you will need extra steps. There isa great chance that your GRASS 7 installation will be different from the Travis-Ci environment (for the moment, it is provided by [osgeo4travis archive]( and it is based on GRASS 7.0.2). It means that your raster hashes will be different from the ones produced in Travis-Ci (GRASS adds a metadata which contains the GRASS version in the GTiff). - -Here are a few instructions (for Debian based GNU/Linux distributions) to assure you use the same version of GRASS than in Travis-Ci: - -* Install osgeo4travis binaries into your home directory: - `curl -L https://github.com/opengisch/osgeo4travis/archive/qt4bin.tar.gz | tar -xzC ${HOME} --strip-components=1` -* Modify the grass70 script which incorporates a hardlink to /home/travis: - `sed -i 's#/home/travis#'${HOME}'#g' ${HOME}/osgeo4travis/bin/grass70` -* Launch QGIS with the following command: - `PATH=${HOME}/osgeo4travis/bin:$PATH LD_LIBRARY_PATH=${HOME}/osgeo4travis/lib:$LD_LIBRARY_PATH qgis` - -From now, you will be using osgeo4travis GRASS 7 installation in QGIS Processing and you can proceed with the above method to easily produce unit tests. - - Params and results ------------------ diff --git a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml index 226ede9d1b0e..e966121ad370 100644 --- a/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/grass7_algorithm_tests.yaml @@ -313,21 +313,22 @@ tests: - '4541.9055' - '4369.2930' - '128.6900' - - - algorithm: grass7:i.fft - name: GRASS7 i.fft - params: - GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' - input: - name: custom/grass7/float_raster.tif - type: raster - results: - imaginary: - hash: 94249384dd8b6019f0024501bc9a093cba9dd025c183d3fb46d77027 - type: rasterhash - real: - hash: 09ab93c65aa2dde4da422b62a5ed3e38208e2da072cec2b0eb837a47 - type: rasterhash + +# this doesn't work in travis-ci environment (probably due to libfftw version). +# - algorithm: grass7:i.fft +# name: GRASS7 i.fft +# params: +# GRASS_REGION_PARAMETER: '344500.0,358400.0,6682800.0,6693700.0' +# input: +# name: custom/grass7/float_raster.tif +# type: raster +# results: +# imaginary: +# hash: 94249384dd8b6019f0024501bc9a093cba9dd025c183d3fb46d77027 +# type: rasterhash +# real: +# hash: 09ab93c65aa2dde4da422b62a5ed3e38208e2da072cec2b0eb837a47 +# type: rasterhash - algorithm: grass7:i.segment name: GRASS7 i.segment