123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- """
- /***************************************************************************
- OtbAlgorithmProvider.py
- -----------------------
- Date : 2018-01-30
- Copyright : (C) 2018 by CNES
- Email : rashad dot kanavath at c-s 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__ = 'Rashad Kanavath'
- __date__ = '2018-01-30'
- __copyright__ = '(C) 2018 by CNES'
- import os
- import re
- from qgis.PyQt.QtGui import QIcon
- from qgis.PyQt.QtCore import QCoreApplication
- from qgis.core import (Qgis,
- QgsApplication,
- QgsProcessingProvider,
- QgsMessageLog,
- QgsRuntimeProfiler)
- from qgis import utils
- from processing.core.ProcessingConfig import ProcessingConfig, Setting
- from .OtbUtils import OtbUtils
- from .OtbAlgorithm import OtbAlgorithm
- class OtbAlgorithmProvider(QgsProcessingProvider):
- def __init__(self):
- super().__init__()
- self.algs = []
- # !hack for 6.6!#
- self.version = '6.6.0'
- def load(self):
- with QgsRuntimeProfiler.profile('OTB Provider'):
- group = self.name()
- ProcessingConfig.settingIcons[group] = self.icon()
- ProcessingConfig.addSetting(Setting(group, OtbUtils.FOLDER,
- self.tr("OTB folder"),
- OtbUtils.otbFolder(),
- valuetype=Setting.FOLDER,
- validator=self.validateOtbFolder
- ))
- ProcessingConfig.addSetting(Setting(group, OtbUtils.APP_FOLDER,
- self.tr("OTB application folder"),
- OtbUtils.appFolder(),
- valuetype=Setting.MULTIPLE_FOLDERS,
- validator=self.validateAppFolders
- ))
- ProcessingConfig.addSetting(Setting(group, OtbUtils.SRTM_FOLDER,
- self.tr("SRTM tiles folder"),
- OtbUtils.srtmFolder(),
- valuetype=Setting.FOLDER
- ))
- ProcessingConfig.addSetting(Setting(group, OtbUtils.GEOID_FILE,
- self.tr("Geoid file"),
- OtbUtils.geoidFile(),
- valuetype=Setting.FOLDER
- ))
- ProcessingConfig.addSetting(Setting(group, OtbUtils.MAX_RAM_HINT,
- self.tr("Maximum RAM to use"),
- OtbUtils.maxRAMHint(),
- valuetype=Setting.STRING
- ))
- ProcessingConfig.addSetting(Setting(group, OtbUtils.LOGGER_LEVEL,
- self.tr("Logger level"),
- OtbUtils.loggerLevel(),
- valuetype=Setting.STRING,
- validator=self.validateLoggerLevel
- ))
- ProcessingConfig.readSettings()
- self.refreshAlgorithms()
- return True
- def unload(self):
- for setting in OtbUtils.settingNames():
- ProcessingConfig.removeSetting(setting)
- def createAlgsList(self):
- algs = []
- try:
- folder = OtbUtils.otbFolder()
- algs_txt = self.algsFile(folder)
- with open(algs_txt) as lines:
- line = lines.readline().strip('\n').strip()
- if line != '' and line.startswith('#'):
- line = lines.readline().strip('\n').strip()
- alg_names = []
- while line != '' and not line.startswith('#'):
- data = line.split('|')
- 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, 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" + descriptionFile + "\nError:\n" + traceback.format_exc()
- QgsMessageLog.logMessage(self.tr(errmsg), self.tr('Processing'), Qgis.Critical)
- return algs
- def loadAlgorithms(self):
- folder = OtbUtils.otbFolder()
- if folder and os.path.exists(folder):
- if not os.path.isfile(self.algsFile(folder)):
- QgsMessageLog.logMessage("Problem with OTB installation: cannot find '{}'".format(self.algsFile(folder)), "Processing", Qgis.Critical)
- return
- else:
- QgsMessageLog.logMessage("Problem with OTB installation: OTB folder is not set.", "Processing", Qgis.Critical)
- return
- version_file = os.path.join(OtbUtils.otbFolder(), 'share', 'doc', 'otb', 'VERSION')
- if not os.path.isfile(version_file):
- version_file = os.path.join(OtbUtils.otbFolder(), 'VERSION')
- if os.path.isfile(version_file):
- with open(version_file) as vf:
- vlines = vf.readlines()
- vlines = [l.strip() for l in vlines]
- vline = vlines[0]
- if 'OTB Version:' in vline:
- self.version = vline.split(':')[1].strip()
- QgsMessageLog.logMessage(self.tr("Loading OTB '{}'.".format(self.version)), self.tr('Processing'), Qgis.Info)
- self.algs = self.createAlgsList()
- for a in self.algs:
- self.addAlgorithm(a)
- self.algs = []
- def validateLoggerLevel(self, v):
- allowed_values = ['DEBUG', 'INFO', 'WARNING', 'CRITICAL', 'FATAL']
- if v in allowed_values:
- return True
- else:
- raise ValueError(self.tr("'{}' is not valid. Possible values are '{}'".format(v, ', '.join(allowed_values))))
- def validateAppFolders(self, v):
- # if user has never entered an OTB folder, they probably aren't even using
- # the plugin. So don't raise any errors here which have no meaning for the user.
- if not v:
- return
- folder = OtbUtils.otbFolder()
- otb_app_dirs = self.appDirs(v)
- if len(otb_app_dirs) < 1:
- raise ValueError(self.tr("'{}' does not exist. OTB provider will be disabled".format(v)))
- # isValid is True if there is at least one valid otb application is given path
- isValid = False
- descr_folder = self.descrFolder(folder)
- for app_dir in otb_app_dirs:
- if not os.path.exists(app_dir):
- continue
- for otb_app in os.listdir(app_dir):
- if not otb_app.startswith('otbapp_') or \
- 'TestApplication' in otb_app or \
- 'ApplicationExample' in otb_app:
- continue
- app_name = os.path.basename(otb_app).split('.')[0][7:]
- dfile = os.path.join(descr_folder, app_name + '.txt')
- isValid = True
- if not os.path.exists(dfile):
- cmdlist = [OtbUtils.getExecutableInPath(folder, 'otbQgisDescriptor'),
- 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 not isValid:
- raise ValueError(self.tr("Problem with OTB installation: no algorithms found in '{}'".format(','.join(otb_app_dirs))))
- def normalize_path(self, p):
- # https://stackoverflow.com/a/20713238/1003090
- return os.path.normpath(os.sep.join(re.split(r'\\|/', p)))
- def validateOtbFolder(self, v):
- # if user has never entered an OTB folder, they probably aren't even using
- # the plugin. So don't raise any errors here which have no meaning for the user.
- if not v:
- return
- if not os.path.exists(v):
- raise ValueError(self.tr("Problem with OTB installation: '{}' does not exist.".format(v)))
- path = self.normalize_path(v)
- app_launcher_path = OtbUtils.getExecutableInPath(path, 'otbApplicationLauncherCommandLine')
- if not os.path.exists(app_launcher_path):
- raise ValueError(self.tr("Problem with OTB installation: cannot find '{}'.".format(app_launcher_path)))
- def algsFile(self, d):
- return os.path.join(self.descrFolder(d), 'algs.txt')
- def descrFolder(self, d):
- # !hack for 6.6!#
- if os.path.exists(os.path.join(d, 'description')):
- return os.path.join(d, 'description')
- else:
- return os.path.join(d, 'share', 'otb', 'description')
- def descrFile(self, d, f):
- return os.path.join(self.descrFolder(d), f)
- def appDirs(self, v):
- return [
- self.normalize_path(f)
- for f in v.split(';')
- if f is not None and os.path.exists(f)
- ]
- def name(self):
- return 'OTB'
- def longName(self):
- return 'OTB ({})'.format(self.version) if self.version is not None else 'OTB'
- def id(self):
- return 'otb'
- def supportsNonFileBasedOutput(self):
- """
- OTB Provider doesn't support non file based outputs
- """
- return False
- def icon(self):
- return QgsApplication.getThemeIcon("/providerOtb.svg")
- def tr(self, string, context=''):
- if context == '':
- context = 'OtbAlgorithmProvider'
- return QCoreApplication.translate(context, string)
- def defaultVectorFileExtension(self, hasGeometry=True):
- return 'shp'
- def defaultRasterFileExtension(self):
- return 'tif'
- def supportedOutputTableExtensions(self):
- return ['dbf']
|