Skip to content

Commit 3b87f7b

Browse files
Rashad Kanavathnyalldawson
Rashad Kanavath
authored andcommitted
[FEATURE] [needs-docs] integrate OTB provider to processing plugin
1 parent 1a8cbd1 commit 3b87f7b

File tree

10 files changed

+946
-0
lines changed

10 files changed

+946
-0
lines changed

images/themes/default/providerOtb.png

14.6 KB
Loading

python/plugins/processing/algs/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ FILE(GLOB PY_FILES *.py)
33
ADD_SUBDIRECTORY(help)
44
ADD_SUBDIRECTORY(gdal)
55
ADD_SUBDIRECTORY(grass7)
6+
ADD_SUBDIRECTORY(otb)
67
ADD_SUBDIRECTORY(saga)
78
ADD_SUBDIRECTORY(qgis)
89
ADD_SUBDIRECTORY(exampleprovider)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FILE(GLOB PY_FILES *.py)
2+
PLUGIN_INSTALL(processing algs/otb ${PY_FILES})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
***************************************************************************
4+
OtbAlgorithm.py
5+
---------------
6+
Date : June 2017
7+
Copyright : (C) 2017 by CS Systemes d'Information (CS SI)
8+
: (C) 2018 by Centre National d'Etudes et spatiales (CNES)
9+
Email : rashad dot kanavath at c-s fr, otb at c-s dot fr (CS SI)
10+
11+
***************************************************************************
12+
* *
13+
* This program is free software; you can redistribute it and/or modify *
14+
* it under the terms of the GNU General Public License as published by *
15+
* the Free Software Foundation; either version 2 of the License, or *
16+
* (at your option) any later version. *
17+
* *
18+
***************************************************************************
19+
"""
20+
21+
__author__ = 'Rashad Kanavath'
22+
__date__ = 'June 2017'
23+
__copyright__ = "(C) 2017,2018 by CS Systemes d'information (CS SI), Centre National d'Etudes et spatiales (CNES)"
24+
25+
# This will get replaced with a git SHA1 when you do a git archive
26+
27+
__revision__ = '$Format:%H$'
28+
29+
import os
30+
31+
from qgis.PyQt.QtCore import QCoreApplication
32+
from qgis.PyQt.QtGui import QIcon
33+
34+
from qgis.core import (Qgis,
35+
QgsMessageLog,
36+
QgsRasterLayer,
37+
QgsMapLayer,
38+
QgsApplication,
39+
QgsProcessingAlgorithm,
40+
QgsProcessingParameterMultipleLayers,
41+
QgsProcessingParameterDefinition,
42+
QgsProcessingOutputLayerDefinition,
43+
QgsProcessingParameterString,
44+
QgsProcessingParameterNumber,
45+
QgsProcessingParameterEnum)
46+
47+
from processing.core.parameters import getParameterFromString
48+
from processing.algs.otb.OtbChoiceWidget import OtbParameterChoice
49+
from processing.algs.otb import OtbUtils
50+
51+
52+
class OtbAlgorithm(QgsProcessingAlgorithm):
53+
def __init__(self, group, name, descriptionfile, display_name='', groupId=''):
54+
super().__init__()
55+
self._name = name
56+
self._group = group
57+
self._display_name = display_name
58+
59+
self._groupId = ''
60+
validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:'
61+
if not groupId:
62+
self._groupId = ''.join(c for c in self._group if c in validChars)
63+
64+
self.pixelTypes = ['uint8', 'int', 'float', 'double']
65+
self._descriptionfile = descriptionfile
66+
self.defineCharacteristicsFromFile()
67+
68+
def icon(self):
69+
return QgsApplication.getThemeIcon("/providerOtb.png")
70+
71+
def createInstance(self):
72+
return self.__class__(self._group, self._name, self._descriptionfile)
73+
74+
def tr(self, string):
75+
return QCoreApplication.translate("OtbAlgorithm", string)
76+
77+
def name(self):
78+
return self._name
79+
80+
def displayName(self):
81+
return self._display_name
82+
83+
def group(self):
84+
return self._group
85+
86+
def groupId(self):
87+
return self._groupId
88+
89+
def descriptionfile(self):
90+
return self._descriptionfile
91+
92+
def initAlgorithm(self, config=None):
93+
pass
94+
95+
#TODO: show version which is same as OtbAlgorithm rather than always latest.
96+
def helpUrl(self):
97+
return "https://www.orfeo-toolbox.org/CookBook/Applications/app_" + self.name() + ".html"
98+
99+
def defineCharacteristicsFromFile(self):
100+
line = None
101+
try:
102+
with open(self._descriptionfile) as lines:
103+
line = lines.readline().strip('\n').strip()
104+
self._name = line.split('|')[0]
105+
self.appkey = self._name
106+
line = lines.readline().strip('\n').strip()
107+
self.doc = line
108+
self.i18n_doc = QCoreApplication.translate("OtbAlgorithm", self.doc)
109+
#self._name = self._name #+ " - " + self.doc
110+
self._display_name = self.tr(self._name)
111+
self.i18n_name = QCoreApplication.translate("OtbAlgorithm", self._name)
112+
113+
line = lines.readline().strip('\n').strip()
114+
self._group = line
115+
self.i18n_group = QCoreApplication.translate("OtbAlgorithm", self._group)
116+
line = lines.readline().strip('\n').strip()
117+
while line != '':
118+
line = line.strip('\n').strip()
119+
if line.startswith('#'):
120+
line = lines.readline().strip('\n').strip()
121+
continue
122+
param = None
123+
if 'OTBParameterChoice' in line:
124+
tokens = line.split("|")
125+
params = [t if str(t) != str(None) else None for t in tokens[1:]]
126+
options = params[2].split(';')
127+
param = OtbParameterChoice(params[0], params[1], options, params[3], params[4])
128+
else:
129+
param = getParameterFromString(line)
130+
131+
#if parameter is None, then move to next line and continue
132+
if param is None:
133+
line = lines.readline().strip('\n').strip()
134+
continue
135+
136+
name = param.name()
137+
if '.' in name and len(name.split('.')) > 2:
138+
p = name.split('.')[:-2]
139+
group_key = '.'.join(p)
140+
group_value = name.split('.')[-2]
141+
metadata = param.metadata()
142+
metadata['group_key'] = group_key
143+
metadata['group_value'] = group_value
144+
param.setMetadata(metadata)
145+
146+
#'elev.dem.path', 'elev.dem', 'elev.dem.geoid', 'elev.geoid' are special!
147+
#Even though it is not typical for OTB to fix on parameter keys,
148+
#we current use below !hack! to set SRTM path and GEOID files
149+
#Future releases of OTB must follow this rule keep
150+
#compatibility or update this checklist.
151+
if name in ["elev.dem.path", "elev.dem"]:
152+
param.setDefaultValue(OtbUtils.srtmFolder())
153+
if name in ["elev.dem.geoid", "elev.geoid"]:
154+
param.setDefaultValue(OtbUtils.geoidFile())
155+
156+
self.addParameter(param)
157+
#parameter is added now and we must move to next line
158+
line = lines.readline().strip('\n').strip()
159+
160+
except BaseException as e:
161+
import traceback
162+
errmsg = "Could not open OTB algorithm from file: \n" + self._descriptionfile + "\nline=" + line + "\nError:\n" + traceback.format_exc()
163+
QgsMessageLog.logMessage(self.tr(errmsg), self.tr('Processing'), Qgis.Critical)
164+
raise e
165+
166+
def preprocessParameters(self, parameters):
167+
valid_params = {}
168+
for k, v in parameters.items():
169+
param = self.parameterDefinition(k)
170+
#If parameterDefinition(k) return None, this is considered a invalid parameter.
171+
#just continue for loop
172+
if param is None:
173+
continue
174+
175+
#if name of parameter is 'pixtype',
176+
#it is considered valid if it has value other than float
177+
if k == 'outputpixeltype' and self.pixelTypes[int(v)] == 'float':
178+
continue
179+
# Any other valid parameters have:
180+
#- empty or no metadata
181+
#- metadata without a 'group_key'
182+
#- metadata with 'group_key' and 'group_key' is not in list of parameters. see ParameterGroup in OTB
183+
#- metadata with 'group_key' and 'group_key' is a valid parameter and it's value == metadata()['group_value']
184+
if 'group_key' in param.metadata() and not param.metadata()['group_key'] in parameters:
185+
valid_params[k] = v
186+
elif not 'group_key' in param.metadata() or parameters[param.metadata()['group_key']] == param.metadata()['group_value']:
187+
valid_params[k] = v
188+
189+
return valid_params
190+
191+
def get_value(self, v):
192+
if isinstance(v, QgsMapLayer):
193+
return v.source()
194+
elif isinstance(v, QgsProcessingOutputLayerDefinition):
195+
return v.sink.staticValue()
196+
else:
197+
return str(v)
198+
199+
def outputParameterName(self):
200+
with open(self._descriptionfile) as df:
201+
first_line = df.readline().strip()
202+
tokens = first_line.split("|")
203+
#params = [t if str(t) != str(None) else None for t in tokens[1:]]
204+
if len(tokens) == 2:
205+
return tokens[1]
206+
else:
207+
return ''
208+
209+
def processAlgorithm(self, parameters, context, feedback):
210+
output_key = self.outputParameterName()
211+
otb_cli_file = OtbUtils.cliPath()
212+
command = '"{}" {} {}'.format(otb_cli_file, self.name(), OtbUtils.appFolder())
213+
for k, v in parameters.items():
214+
if k == 'outputpixeltype' or not v:
215+
continue
216+
217+
if 'epsg' in k and v.startswith('EPSG:'):
218+
v = v.split('EPSG:')[1]
219+
220+
if isinstance(v, str) and '\n' in v:
221+
v = v.split('\n')
222+
if isinstance(v, list):
223+
value = ''
224+
for i in list(filter(None, v)):
225+
value += '"{}" '.format(self.get_value(i))
226+
else:
227+
value = '"{}"'.format(self.get_value(v))
228+
229+
if k == output_key and 'outputpixeltype' in parameters:
230+
output_pixel_type = self.pixelTypes[int(parameters['outputpixeltype'])]
231+
value = '"{}" "{}"'.format(value, output_pixel_type)
232+
233+
command += ' -{} {}'.format(k, value)
234+
235+
QgsMessageLog.logMessage(self.tr('cmd={}'.format(command)), self.tr('Processing'), Qgis.Info)
236+
if not os.path.exists(otb_cli_file) or not os.path.isfile(otb_cli_file):
237+
import errno
238+
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), otb_cli_file)
239+
240+
OtbUtils.executeOtb(command, feedback)
241+
242+
result = {}
243+
for out in self.destinationParameterDefinitions():
244+
filePath = self.parameterAsOutputLayer(parameters, out.name(), context)
245+
result[out.name()] = filePath
246+
return result

0 commit comments

Comments
 (0)