Commit 358ba3ac authored by Rashad Kanavath's avatar Rashad Kanavath
Browse files

upgdate to same code in qgis pr

parent dc71c82c
......@@ -33,22 +33,30 @@ from qgis.PyQt.QtCore import QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.core import (Qgis,
QgsMessageLog,
QgsRasterLayer,
QgsMapLayer,
QgsProcessingAlgorithm,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterDefinition,
QgsProcessingOutputLayerDefinition,
QgsProcessingParameterString,
QgsProcessingParameterNumber,
QgsProcessingParameterEnum)
QgsMessageLog,
QgsMapLayer,
QgsApplication,
QgsProcessingException,
QgsProcessingAlgorithm,
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterDefinition,
QgsProcessingOutputLayerDefinition,
QgsProcessingParameterCrs,
QgsProcessingParameterString,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterBoolean,
QgsProcessingParameterFile,
QgsProcessingParameterNumber,
QgsProcessingParameterRasterDestination,
QgsProcessingParameterVectorDestination,
QgsProcessingParameterEnum)
from processing.core.parameters import getParameterFromString
from otb.OtbChoiceWidget import OtbParameterChoice
from otb import OtbUtils
class OtbAlgorithm(QgsProcessingAlgorithm):
def __init__(self, group, name, descriptionfile, display_name='', groupId=''):
super().__init__()
......@@ -61,30 +69,13 @@ class OtbAlgorithm(QgsProcessingAlgorithm):
if not groupId:
self._groupId = ''.join(c for c in self._group if c in validChars)
self.pixelTypes = ['unit8','int','float','double']
self.pixelTypes = ['uint8', 'uint16', 'int16', 'uint32', 'int32',
'float', 'double', 'cint16', 'cint32', 'cfloat', 'cdouble']
self._descriptionfile = descriptionfile
self.defineCharacteristicsFromFile()
# def canExecute( self, msg=None):
# if len(self.parameterDefinitions()) > 0:
# return True, None
# try:
# self.defineCharacteristicsFromFile()
# except BaseException as e:
# return False, None
# return True, None
# TODO: check if this method is really required.
def checkParameterValues(self, parameters, context):
for key, value in parameters.items():
param = self.parameterDefinition(key)
if not param.checkValueIsAcceptable(value, context):
return False, "Incorrect parameter value for {}".format(param.name())
return True, None
def icon(self):
return QIcon(os.path.join(pluginPath, 'otb', 'otb.png'))
return QIcon(os.path.join(pluginPath, 'otb', 'providerOtb.svg'))
def createInstance(self):
return self.__class__(self._group, self._name, self._descriptionfile)
......@@ -112,7 +103,7 @@ class OtbAlgorithm(QgsProcessingAlgorithm):
#TODO: show version which is same as OtbAlgorithm rather than always latest.
def helpUrl(self):
return "https://www.orfeo-toolbox.org/CookBook/Applications/app_" + self.name() + ".html"
return "https://www.orfeo-toolbox.org/CookBook/Applications/app_" + self.name() + ".html"
def defineCharacteristicsFromFile(self):
line = None
......@@ -158,7 +149,7 @@ class OtbAlgorithm(QgsProcessingAlgorithm):
group_value = name.split('.')[-2]
metadata = param.metadata()
metadata['group_key'] = group_key
metadata['group_value'] = group_value
metadata['group_value'] = group_value
param.setMetadata(metadata)
#'elev.dem.path', 'elev.dem', 'elev.dem.geoid', 'elev.geoid' are special!
......@@ -171,29 +162,31 @@ class OtbAlgorithm(QgsProcessingAlgorithm):
if name in ["elev.dem.geoid", "elev.geoid"]:
param.setDefaultValue(OtbUtils.geoidFile())
# outputpixeltype is a special parameter associated with raster output
# reset list of options to 'self.pixelTypes'.
if name == 'outputpixeltype':
param.setOptions(self.pixelTypes)
param.setDefaultValue(self.pixelTypes.index('float'))
self.addParameter(param)
#parameter is added now and we must move to next line
line = lines.readline().strip('\n').strip()
except BaseException as e:
import traceback
errmsg = "Could not open OTB algorithm from file: \n" + self._descriptionfile + "\nline=" + line + "\nError:\n" + traceback.format_exc()
errmsg = "Could not open OTB algorithm from file: \n" + self._descriptionfile + "\nline=" + line + "\nError:\n" + traceback.format_exc()
QgsMessageLog.logMessage(self.tr(errmsg), self.tr('Processing'), Qgis.Critical)
raise e
def preprocessParameters(self, parameters):
valid_params = {}
for k,v in parameters.items():
valid_params = {}
for k, v in parameters.items():
param = self.parameterDefinition(k)
#If parameterDefinition(k) return None, this is considered a invalid parameter.
#just continue for loop
if param is None:
continue
#if name of parameter is 'pixtype',
#it is considered valid if it has value other than float
if k == 'outputpixeltype' and self.pixelTypes[int(v)] == 'float':
continue
# Any other valid parameters have:
#- empty or no metadata
#- metadata without a 'group_key'
......@@ -206,49 +199,70 @@ class OtbAlgorithm(QgsProcessingAlgorithm):
return valid_params
def get_value(self, v):
if isinstance(v, QgsMapLayer):
return v.source()
elif isinstance(v, QgsProcessingOutputLayerDefinition):
return v.sink.staticValue()
else:
return str(v)
def outputParameterName(self):
with open(self._descriptionfile) as df:
first_line = df.readline().strip()
tokens = first_line.split("|")
#params = [t if str(t) != str(None) else None for t in tokens[1:]]
if len(tokens) == 2:
return tokens[1]
else:
return ''
def processAlgorithm(self, parameters, context, feedback):
output_key = self.outputParameterName()
otb_cli_file = OtbUtils.cliPath()
otb_cli_file = OtbUtils.cliPath()
command = '"{}" {} {}'.format(otb_cli_file, self.name(), OtbUtils.appFolder())
outputPixelType = None
for k, v in parameters.items():
if k == 'outputpixeltype' or not v:
# if value is None for a parameter we don't have any businees with this key
if v is None:
continue
# for 'outputpixeltype' parameter we find the pixeltype string from self.pixelTypes
if k == 'outputpixeltype':
pixel_type = self.pixelTypes[int(parameters['outputpixeltype'])]
outputPixelType = None if pixel_type == 'float' else pixel_type
continue
if 'epsg' in k and v.startswith('EPSG:'):
v = v.split('EPSG:')[1]
if isinstance(v, str) and '\n' in v:
v = v.split('\n')
if isinstance(v, list):
value = ''
for i in list(filter(None, v)):
value += '"{}" '.format(self.get_value(i))
param = self.parameterDefinition(k)
if param.isDestination():
continue
if isinstance(param, QgsProcessingParameterEnum):
value = self.parameterAsEnum(parameters, param.name(), context)
elif isinstance(param, QgsProcessingParameterBoolean):
value = self.parameterAsBool(parameters, param.name(), context)
elif isinstance(param, QgsProcessingParameterCrs):
crsValue = self.parameterAsCrs(parameters, param.name(), context)
authid = crsValue.authid()
if authid.startswith('EPSG:'):
value = authid.split('EPSG:')[1]
else:
raise QgsProcessingException(
self.tr("Incorrect value for parameter '{}'. No EPSG code found in '{}'".format(
param.name(),
authid)))
elif isinstance(param, QgsProcessingParameterFile):
value = self.parameterAsFile(parameters, param.name(), context)
elif isinstance(param, QgsProcessingParameterMultipleLayers):
layers = self.parameterAsLayerList(parameters, param.name(), context)
if layers is None or len(layers) == 0:
continue
value = ' '.join(['"{}"'.format(self.getLayerSource(param.name(), layer)) for layer in layers])
elif isinstance(param, QgsProcessingParameterNumber):
if param.dataType() == QgsProcessingParameterNumber.Integer:
value = self.parameterAsInt(parameters, param.name(), context)
else:
value = self.parameterAsDouble(parameters, param.name(), context)
elif isinstance(param, (QgsProcessingParameterRasterLayer, QgsProcessingParameterVectorLayer)):
value = '"{}"'.format(self.getLayerSource(param.name(), self.parameterAsLayer(parameters, param.name(), context)))
elif isinstance(param, QgsProcessingParameterString):
value = '"{}"'.format(self.parameterAsString(parameters, param.name(), context))
else:
value = '"{}"'.format(self.get_value(v))
# Use whatever is given
value = '"{}"'.format(parameters[param.name()])
if k == output_key and 'outputpixeltype' in parameters:
output_pixel_type = self.pixelTypes[int(parameters['outputpixeltype'])]
value = '"{}" "{}"'.format(value, output_pixel_type)
# Check if value is set in above if elif ladder and update command string
if value and value is not None:
command += ' -{} {}'.format(k, value)
command += ' -{} {}'.format(k, value)
output_files = {}
for out in self.destinationParameterDefinitions():
filePath = self.parameterAsOutputLayer(parameters, out.name(), context)
output_files[out.name()] = filePath
if outputPixelType is not None:
command += ' -{} "{}" "{}"'.format(out.name(), filePath, outputPixelType)
else:
command += ' -{} "{}"'.format(out.name(), filePath)
QgsMessageLog.logMessage(self.tr('cmd={}'.format(command)), self.tr('Processing'), Qgis.Info)
if not os.path.exists(otb_cli_file) or not os.path.isfile(otb_cli_file):
......@@ -258,7 +272,16 @@ class OtbAlgorithm(QgsProcessingAlgorithm):
OtbUtils.executeOtb(command, feedback)
result = {}
for out in self.destinationParameterDefinitions():
filePath = self.parameterAsOutputLayer(parameters, out.name(), context)
result[out.name()] = filePath
for o in self.outputDefinitions():
if o.name() in output_files:
result[o.name()] = output_files[o.name()]
return result
def getLayerSource(self, name, layer):
providerName = layer.dataProvider().name()
#TODO: add other provider support in OTB, eg: memory
if providerName in ['ogr', 'gdal']:
return layer.source()
else:
raise QgsProcessingException(
self.tr("OTB currently support only gdal and ogr provider. Parameter '{}' uses '{}' provider".format(name, providerName)))
......@@ -2,12 +2,12 @@
"""
/***************************************************************************
OtbAlgorithmProvider.py
-----------------------
date : 2018-01-30
copyright : (C) 2018 by CNES
email : rkm
***************************************************************************/
OtbAlgorithmProvider.py
-----------------------
Date : 2018-01-30
Copyright : (C) 2018 by CNES
Email : rashad dot kanavath at c-s fr
****************************************************************************/
/***************************************************************************
* *
......@@ -31,28 +31,29 @@ import os
import re
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (Qgis, QgsApplication, QgsProcessingProvider, QgsMessageLog)
from qgis.core import (Qgis, QgsProcessingProvider, QgsMessageLog)
from qgis import utils
from processing.core.ProcessingConfig import ProcessingConfig, Setting
from otb import OtbUtils
from otb.OtbSettings import OtbSettings
from otb.OtbAlgorithm import OtbAlgorithm
pluginPath = os.path.split(os.path.dirname(__file__))[0]
def otb_exe_file(f):
if os.name == 'nt':
return f + '.exe'
else:
return f
class OtbAlgorithmProvider(QgsProcessingProvider):
def __init__(self):
super().__init__()
self.algs = []
#!hack for 6.6!#
self.version = '6.6.0'
self.descriptionFile = ''
def load(self):
group = self.name()
......@@ -63,34 +64,34 @@ class OtbAlgorithmProvider(QgsProcessingProvider):
OtbUtils.otbFolder(),
valuetype=Setting.FOLDER,
validator=self.validateOtbFolder
))
))
ProcessingConfig.addSetting(Setting(group, OtbSettings.APP_FOLDER,
self.tr("OTB application folder"),
OtbUtils.appFolder(),
valuetype=Setting.MULTIPLE_FOLDERS,
validator=self.validateAppFolders
))
))
ProcessingConfig.addSetting(Setting(group, OtbSettings.SRTM_FOLDER,
self.tr("SRTM tiles folder"),
OtbUtils.srtmFolder(),
valuetype=Setting.FOLDER
))
))
ProcessingConfig.addSetting(Setting(group, OtbSettings.GEOID_FILE,
self.tr("Geoid file"),
OtbUtils.geoidFile(),
valuetype=Setting.FOLDER
))
))
ProcessingConfig.addSetting(Setting(group, OtbSettings.MAX_RAM_HINT,
self.tr("Maximum RAM to use"),
OtbUtils.maxRAMHint(),
valuetype=Setting.STRING
))
))
ProcessingConfig.addSetting(Setting(group, OtbSettings.LOGGER_LEVEL,
self.tr("Logger level"),
OtbUtils.loggerLevel(),
valuetype=Setting.STRING,
validator=self.validateLoggerLevel
))
))
ProcessingConfig.readSettings()
self.refreshAlgorithms()
return True
......@@ -117,16 +118,16 @@ class OtbAlgorithmProvider(QgsProcessingProvider):
line = lines.readline().strip('\n').strip()
while line != '' and not line.startswith('#'):
data = line.split('|')
self.descriptionFile = self.descrFile(folder, str(data[1]) + '.txt')
descriptionFile = self.descrFile(folder, str(data[1]) + '.txt')
group, name = str(data[0]), str(data[1])
if name not in alg_names:
algs.append(OtbAlgorithm(group, name, self.descriptionFile))
algs.append(OtbAlgorithm(group, name, descriptionFile))
#avoid duplicate algorithms from algs.txt file (possible but rare)
alg_names.append(name)
line = lines.readline().strip('\n').strip()
except Exception as e:
import traceback
errmsg = "Could not open OTB algorithm from file: \n" + self.descriptionFile + "\nError:\n" + traceback.format_exc()
errmsg = "Could not open OTB algorithm from file: \n" + descriptionFile + "\nError:\n" + traceback.format_exc()
QgsMessageLog.logMessage(self.tr(errmsg), self.tr('Processing'), Qgis.Critical)
return algs
......@@ -152,13 +153,11 @@ class OtbAlgorithmProvider(QgsProcessingProvider):
self.addAlgorithm(a)
self.algs = []
self.create_otbcli()
def create_otbcli(self):
otb_folder = self.normalize_path(OtbUtils.otbFolder())
otb_app_path_env = os.pathsep.join(self.appDirs(OtbUtils.appFolder()))
gdal_data_dir = None
geotiff_csv_dir = None
otbcli_path = OtbUtils.cliPath()
try:
if os.name == 'nt':
app_vargs = " %*"
......@@ -168,24 +167,23 @@ class OtbAlgorithmProvider(QgsProcessingProvider):
gdal_data_dir = os.path.join(otb_folder, 'share', 'data')
geotiff_csv_dir = os.path.join(otb_folder, 'share', 'epsg_csv')
else:
app_vargs = " $@"
app_vargs = " \"$@\""
export_cmd = 'export '
first_line = '#!/bin/sh'
otb_app_launcher = os.path.join(otb_folder, 'bin', 'otbApplicationLauncherCommandLine')
lines = None
env_profile = os.path.join(otb_folder, 'otbenv.profile')
with open(env_profile) as f:
lines = f.readlines()
lines = [x.strip() for x in lines]
for line in lines:
if not line or line.startswith('#'):
continue
if 'GDAL_DATA=' in line:
gdal_data_dir = line.split("GDAL_DATA=")[1]
if 'GEOTIFF_CSV='in line:
geotiff_csv_dir = line.split("GEOTIFF_CSV=")[1]
otbcli_path = OtbUtils.cliPath()
if os.path.exists(env_profile):
with open(env_profile) as f:
lines = f.readlines()
lines = [x.strip() for x in lines]
for line in lines:
if not line or line.startswith('#'):
continue
if 'GDAL_DATA=' in line:
gdal_data_dir = line.split("GDAL_DATA=")[1]
if 'GEOTIFF_CSV='in line:
geotiff_csv_dir = line.split("GEOTIFF_CSV=")[1]
with open(otbcli_path, 'w') as otb_cli_file:
otb_cli_file.write(first_line + os.linesep)
otb_cli_file.write(export_cmd + "LC_NUMERIC=C" + os.linesep)
......@@ -195,19 +193,19 @@ class OtbAlgorithmProvider(QgsProcessingProvider):
if geotiff_csv_dir:
otb_cli_file.write(export_cmd + "GEOTIFF_CSV=" + "\"" + geotiff_csv_dir + "\"" + os.linesep)
if OtbUtils.loggerLevel():
otb_cli_file.write(export_cmd + "OTB_LOGGER_LEVEL=" + "\"" + OtbUtils.loggerLevel() + "\"" + os.linesep)
otb_cli_file.write(export_cmd + "OTB_LOGGER_LEVEL=" + OtbUtils.loggerLevel() + os.linesep)
max_ram_hint = OtbUtils.maxRAMHint()
if max_ram_hint and not int(max_ram_hint) == 128 :
otb_cli_file.write(export_cmd + "OTB_MAX_RAM_HINT=" + "\"" + max_ram_hint + "\"" + os.linesep)
if max_ram_hint and not int(max_ram_hint) == 128:
otb_cli_file.write(export_cmd + "OTB_MAX_RAM_HINT=" + max_ram_hint + os.linesep)
otb_cli_file.write(export_cmd + "OTB_APPLICATION_PATH=" + "\"" + otb_app_path_env + "\"" + os.linesep)
otb_cli_file.write("\"" + otb_app_launcher + "\"" + app_vargs + os.linesep)
otb_cli_file.write("\"" + otb_app_launcher + "\"" + app_vargs + os.linesep)
if not os.name == 'nt':
os.chmod(otbcli_path, 0o744)
except BaseException as e:
import traceback
os.remove(otbcli_path)
errmsg = "Cannot write:" + otbcli_path + "\nError:\n" + traceback.format_exc()
errmsg = "Cannot write:" + otbcli_path + "\nError:\n" + traceback.format_exc()
QgsMessageLog.logMessage(self.tr(errmsg), self.tr('Processing'), Qgis.Critical)
raise e
QgsMessageLog.logMessage(self.tr("Using otbcli: '{}'.".format(otbcli_path)), self.tr('Processing'), Qgis.Info)
......@@ -252,7 +250,7 @@ class OtbAlgorithmProvider(QgsProcessingProvider):
for otb_app in os.listdir(app_dir):
if not otb_app.startswith('otbapp_') or \
'TestApplication' in otb_app or \
'ApplicationExample' in otb_app:
'ApplicationExample' in otb_app:
continue
app_name = os.path.basename(otb_app).split('.')[0][7:]
dfile = os.path.join(descr_folder, app_name + '.txt')
......@@ -261,13 +259,15 @@ class OtbAlgorithmProvider(QgsProcessingProvider):
cmdlist = [os.path.join(
folder, 'bin',
otb_exe_file('otbQgisDescriptor')),
app_name, app_dir, descr_folder + '/']
app_name, app_dir, descr_folder + '/']
commands = ' '.join(cmdlist)
QgsMessageLog.logMessage(self.tr(commands), self.tr('Processing'), Qgis.Critical)
OtbUtils.executeOtb(commands, feedback=None)
if isValid:
utils.iface.messageBar().pushInfo("OTB", "OTB provider is activated from '{}'.".format(folder))
# if check needed for testsing
if utils.iface is not None:
utils.iface.messageBar().pushInfo("OTB", "OTB provider is activated from '{}'.".format(folder))
else:
self.setActive(False)
raise ValueError(self.tr("No OTB algorithms found in '{}'. OTB will be disabled".format(','.join(otb_app_dirs))))
......@@ -301,12 +301,8 @@ class OtbAlgorithmProvider(QgsProcessingProvider):
return os.path.join(self.descrFolder(d), f)
def appDirs(self, v):
#!hack needed for QGIS < 3.2!#
v = v.replace(';', os.pathsep)
#!hack needed for QGIS < 3.2!#
folders = v.split(os.pathsep)
app_dirs = []
for f in folders:
for f in v.split(';'):
if f is not None and os.path.exists(f):
app_dirs.append(self.normalize_path(f))
return app_dirs
......@@ -328,7 +324,7 @@ class OtbAlgorithmProvider(QgsProcessingProvider):
def icon(self):
pluginPath = os.path.split(os.path.dirname(__file__))[0]
return QIcon(os.path.join(pluginPath, 'otb', 'otb.png'))
return QIcon(os.path.join(pluginPath, 'otb', 'providerOtb.svg'))
def tr(self, string, context=''):
if context == '':
......
......@@ -29,9 +29,10 @@ from qgis.PyQt.QtWidgets import QComboBox
from qgis.core import (QgsProcessingParameterString,
QgsProcessingOutputString)
from processing.gui.wrappers import (WidgetWrapper,
DIALOG_STANDARD,
DIALOG_BATCH,
DIALOG_MODELER)
DIALOG_STANDARD,
DIALOG_BATCH,
DIALOG_MODELER)
class OtbChoiceWidgetWrapper(WidgetWrapper):
def createWidget(self):
......@@ -83,7 +84,7 @@ class OtbChoiceWidgetWrapper(WidgetWrapper):
def valueChanged(self, value):
for parameter in self.get_algorithm().parameterDefinitions():
if not 'group_key' in parameter.metadata() or parameter.metadata()['group_key'] != self.param.name():
continue
continue
name = parameter.name()
v = self.value() == parameter.metadata()['group_value']
self.setWrapperVisible(name, v)
......@@ -123,8 +124,10 @@ class OtbChoiceWidgetWrapper(WidgetWrapper):
if wrapper.wrappedLabel():
wrapper.wrappedLabel().setVisible(v)
from qgis.core import QgsProcessingParameterDefinition
class OtbParameterChoice(QgsProcessingParameterDefinition):
def __init__(self, name='', description='', options=[], default=None, isSource=False,
......@@ -150,8 +153,7 @@ class OtbParameterChoice(QgsProcessingParameterDefinition):
else:
self.value = int(value)
return True
def type(self):
#This value is written by otbQgisDescriptor.
return 'OTBParameterChoice'
......@@ -18,6 +18,7 @@ __copyright__ = '(C) CNES 2019'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
class OtbSettings(object):
"""
OtbSetting's key names
......@@ -46,7 +47,7 @@ class OtbSettings(object):
# ``OTB_LOGGER_LEVEL``: Default level of logging for OTB. Should be one of ``DEBUG``, ``INFO``, ``WARNING``, ``CRITICAL`` or ``FATAL``, by increasing order of priority. Only messages with a higher priority than the level of logging will be displayed. If not set, default level is ``INFO``.
LOGGER_LEVEL = 'OTB_LOGGER_LEVEL'
@staticmethod
def keys():
return [
......@@ -55,6 +56,5 @@ class OtbSettings(object):
OtbSettings.SRTM_FOLDER,
OtbSettings.GEOID_FILE,
OtbSettings.LOGGER_LEVEL,
OtbSettings.MAX_RAM_HINT
]
OtbSettings.MAX_RAM_HINT
]
......@@ -39,49 +39,58 @@ from qgis.core import (Qgis, QgsApplication, QgsMessageLog)
from qgis.PyQt.QtCore import QCoreApplication
from otb.OtbSettings import OtbSettings
def cliPath():
cli_ext = '.bat' if os.name == 'nt' else ''
return os.path.normpath(os.path.join(QgsApplication.qgisSettingsDirPath(),
'processing', 'qgis_otb_cli' + cli_ext))
'processing', 'qgis_otb_cli' + cli_ext))
def version():
return ProcessingConfig.getSetting(OtbSettings.VERSION) or '0.0.0'
def loggerLevel():
return ProcessingConfig.getSetting(OtbSettings.LOGGER_LEVEL) or 'INFO'
def maxRAMHint():
return ProcessingConfig.getSetting(OtbSettings.MAX_RAM_HINT) or ''
def otbFolder():
if ProcessingConfig.getSetting(OtbSettings.FOLDER):
return os.path.normpath(os.sep.join(re.split(r'\\|/', ProcessingConfig.getSetting(OtbSettings.FOLDER))))
else:
return None
def appFolder():
app_folder = ProcessingConfig.getSetting(OtbSettings.APP_FOLDER)
if app_folder:
return os.pathsep.join(app_folder.split(';'))
return os.pathsep.join(app_folder.split(';'))
else:
return None
return None
def srtmFolder():