OtbAlgorithmProvider.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. """
  2. /***************************************************************************
  3. OtbAlgorithmProvider.py
  4. -----------------------
  5. Date : 2018-01-30
  6. Copyright : (C) 2018 by CNES
  7. Email : rashad dot kanavath at c-s fr
  8. ****************************************************************************/
  9. /***************************************************************************
  10. * *
  11. * This program is free software; you can redistribute it and/or modify *
  12. * it under the terms of the GNU General Public License as published by *
  13. * the Free Software Foundation; either version 2 of the License, or *
  14. * (at your option) any later version. *
  15. * *
  16. ***************************************************************************/
  17. """
  18. __author__ = 'Rashad Kanavath'
  19. __date__ = '2018-01-30'
  20. __copyright__ = '(C) 2018 by CNES'
  21. import os
  22. import re
  23. from qgis.PyQt.QtGui import QIcon
  24. from qgis.PyQt.QtCore import QCoreApplication
  25. from qgis.core import (Qgis,
  26. QgsApplication,
  27. QgsProcessingProvider,
  28. QgsMessageLog,
  29. QgsRuntimeProfiler)
  30. from qgis import utils
  31. from processing.core.ProcessingConfig import ProcessingConfig, Setting
  32. from .OtbUtils import OtbUtils
  33. from .OtbAlgorithm import OtbAlgorithm
  34. class OtbAlgorithmProvider(QgsProcessingProvider):
  35. def __init__(self):
  36. super().__init__()
  37. self.algs = []
  38. # !hack for 6.6!#
  39. self.version = '6.6.0'
  40. def load(self):
  41. with QgsRuntimeProfiler.profile('OTB Provider'):
  42. group = self.name()
  43. ProcessingConfig.settingIcons[group] = self.icon()
  44. ProcessingConfig.addSetting(Setting(group, OtbUtils.FOLDER,
  45. self.tr("OTB folder"),
  46. OtbUtils.otbFolder(),
  47. valuetype=Setting.FOLDER,
  48. validator=self.validateOtbFolder
  49. ))
  50. ProcessingConfig.addSetting(Setting(group, OtbUtils.APP_FOLDER,
  51. self.tr("OTB application folder"),
  52. OtbUtils.appFolder(),
  53. valuetype=Setting.MULTIPLE_FOLDERS,
  54. validator=self.validateAppFolders
  55. ))
  56. ProcessingConfig.addSetting(Setting(group, OtbUtils.SRTM_FOLDER,
  57. self.tr("SRTM tiles folder"),
  58. OtbUtils.srtmFolder(),
  59. valuetype=Setting.FOLDER
  60. ))
  61. ProcessingConfig.addSetting(Setting(group, OtbUtils.GEOID_FILE,
  62. self.tr("Geoid file"),
  63. OtbUtils.geoidFile(),
  64. valuetype=Setting.FOLDER
  65. ))
  66. ProcessingConfig.addSetting(Setting(group, OtbUtils.MAX_RAM_HINT,
  67. self.tr("Maximum RAM to use"),
  68. OtbUtils.maxRAMHint(),
  69. valuetype=Setting.STRING
  70. ))
  71. ProcessingConfig.addSetting(Setting(group, OtbUtils.LOGGER_LEVEL,
  72. self.tr("Logger level"),
  73. OtbUtils.loggerLevel(),
  74. valuetype=Setting.STRING,
  75. validator=self.validateLoggerLevel
  76. ))
  77. ProcessingConfig.readSettings()
  78. self.refreshAlgorithms()
  79. return True
  80. def unload(self):
  81. for setting in OtbUtils.settingNames():
  82. ProcessingConfig.removeSetting(setting)
  83. def createAlgsList(self):
  84. algs = []
  85. try:
  86. folder = OtbUtils.otbFolder()
  87. algs_txt = self.algsFile(folder)
  88. with open(algs_txt) as lines:
  89. line = lines.readline().strip('\n').strip()
  90. if line != '' and line.startswith('#'):
  91. line = lines.readline().strip('\n').strip()
  92. alg_names = []
  93. while line != '' and not line.startswith('#'):
  94. data = line.split('|')
  95. descriptionFile = self.descrFile(folder, str(data[1]) + '.txt')
  96. group, name = str(data[0]), str(data[1])
  97. if name not in alg_names:
  98. algs.append(OtbAlgorithm(group, name, descriptionFile))
  99. # avoid duplicate algorithms from algs.txt file (possible but rare)
  100. alg_names.append(name)
  101. line = lines.readline().strip('\n').strip()
  102. except Exception as e:
  103. import traceback
  104. errmsg = "Could not open OTB algorithm from file: \n" + descriptionFile + "\nError:\n" + traceback.format_exc()
  105. QgsMessageLog.logMessage(self.tr(errmsg), self.tr('Processing'), Qgis.Critical)
  106. return algs
  107. def loadAlgorithms(self):
  108. folder = OtbUtils.otbFolder()
  109. if folder and os.path.exists(folder):
  110. if not os.path.isfile(self.algsFile(folder)):
  111. QgsMessageLog.logMessage("Problem with OTB installation: cannot find '{}'".format(self.algsFile(folder)), "Processing", Qgis.Critical)
  112. return
  113. else:
  114. QgsMessageLog.logMessage("Problem with OTB installation: OTB folder is not set.", "Processing", Qgis.Critical)
  115. return
  116. version_file = os.path.join(OtbUtils.otbFolder(), 'share', 'doc', 'otb', 'VERSION')
  117. if not os.path.isfile(version_file):
  118. version_file = os.path.join(OtbUtils.otbFolder(), 'VERSION')
  119. if os.path.isfile(version_file):
  120. with open(version_file) as vf:
  121. vlines = vf.readlines()
  122. vlines = [l.strip() for l in vlines]
  123. vline = vlines[0]
  124. if 'OTB Version:' in vline:
  125. self.version = vline.split(':')[1].strip()
  126. QgsMessageLog.logMessage(self.tr("Loading OTB '{}'.".format(self.version)), self.tr('Processing'), Qgis.Info)
  127. self.algs = self.createAlgsList()
  128. for a in self.algs:
  129. self.addAlgorithm(a)
  130. self.algs = []
  131. def validateLoggerLevel(self, v):
  132. allowed_values = ['DEBUG', 'INFO', 'WARNING', 'CRITICAL', 'FATAL']
  133. if v in allowed_values:
  134. return True
  135. else:
  136. raise ValueError(self.tr("'{}' is not valid. Possible values are '{}'".format(v, ', '.join(allowed_values))))
  137. def validateAppFolders(self, v):
  138. # if user has never entered an OTB folder, they probably aren't even using
  139. # the plugin. So don't raise any errors here which have no meaning for the user.
  140. if not v:
  141. return
  142. folder = OtbUtils.otbFolder()
  143. otb_app_dirs = self.appDirs(v)
  144. if len(otb_app_dirs) < 1:
  145. raise ValueError(self.tr("'{}' does not exist. OTB provider will be disabled".format(v)))
  146. # isValid is True if there is at least one valid otb application is given path
  147. isValid = False
  148. descr_folder = self.descrFolder(folder)
  149. for app_dir in otb_app_dirs:
  150. if not os.path.exists(app_dir):
  151. continue
  152. for otb_app in os.listdir(app_dir):
  153. if not otb_app.startswith('otbapp_') or \
  154. 'TestApplication' in otb_app or \
  155. 'ApplicationExample' in otb_app:
  156. continue
  157. app_name = os.path.basename(otb_app).split('.')[0][7:]
  158. dfile = os.path.join(descr_folder, app_name + '.txt')
  159. isValid = True
  160. if not os.path.exists(dfile):
  161. cmdlist = [OtbUtils.getExecutableInPath(folder, 'otbQgisDescriptor'),
  162. app_name, app_dir, descr_folder + '/']
  163. commands = ' '.join(cmdlist)
  164. QgsMessageLog.logMessage(self.tr(commands), self.tr('Processing'), Qgis.Critical)
  165. OtbUtils.executeOtb(commands, feedback=None)
  166. if not isValid:
  167. raise ValueError(self.tr("Problem with OTB installation: no algorithms found in '{}'".format(','.join(otb_app_dirs))))
  168. def normalize_path(self, p):
  169. # https://stackoverflow.com/a/20713238/1003090
  170. return os.path.normpath(os.sep.join(re.split(r'\\|/', p)))
  171. def validateOtbFolder(self, v):
  172. # if user has never entered an OTB folder, they probably aren't even using
  173. # the plugin. So don't raise any errors here which have no meaning for the user.
  174. if not v:
  175. return
  176. if not os.path.exists(v):
  177. raise ValueError(self.tr("Problem with OTB installation: '{}' does not exist.".format(v)))
  178. path = self.normalize_path(v)
  179. app_launcher_path = OtbUtils.getExecutableInPath(path, 'otbApplicationLauncherCommandLine')
  180. if not os.path.exists(app_launcher_path):
  181. raise ValueError(self.tr("Problem with OTB installation: cannot find '{}'.".format(app_launcher_path)))
  182. def algsFile(self, d):
  183. return os.path.join(self.descrFolder(d), 'algs.txt')
  184. def descrFolder(self, d):
  185. # !hack for 6.6!#
  186. if os.path.exists(os.path.join(d, 'description')):
  187. return os.path.join(d, 'description')
  188. else:
  189. return os.path.join(d, 'share', 'otb', 'description')
  190. def descrFile(self, d, f):
  191. return os.path.join(self.descrFolder(d), f)
  192. def appDirs(self, v):
  193. return [
  194. self.normalize_path(f)
  195. for f in v.split(';')
  196. if f is not None and os.path.exists(f)
  197. ]
  198. def name(self):
  199. return 'OTB'
  200. def longName(self):
  201. return 'OTB ({})'.format(self.version) if self.version is not None else 'OTB'
  202. def id(self):
  203. return 'otb'
  204. def supportsNonFileBasedOutput(self):
  205. """
  206. OTB Provider doesn't support non file based outputs
  207. """
  208. return False
  209. def icon(self):
  210. return QgsApplication.getThemeIcon("/providerOtb.svg")
  211. def tr(self, string, context=''):
  212. if context == '':
  213. context = 'OtbAlgorithmProvider'
  214. return QCoreApplication.translate(context, string)
  215. def defaultVectorFileExtension(self, hasGeometry=True):
  216. return 'shp'
  217. def defaultRasterFileExtension(self):
  218. return 'tif'
  219. def supportedOutputTableExtensions(self):
  220. return ['dbf']