Skip to content

Commit

Permalink
Merge pull request #2669 from volaya/otb_version
Browse files Browse the repository at this point in the history
[processing] improved version detection mechanism for OTB
  • Loading branch information
volaya committed Jan 15, 2016
2 parents df27c07 + b68ee3d commit 0b96621
Show file tree
Hide file tree
Showing 243 changed files with 158 additions and 127 deletions.
15 changes: 6 additions & 9 deletions python/plugins/processing/algs/otb/OTBAlgorithm.py
Expand Up @@ -43,7 +43,7 @@
from processing.core.ProcessingLog import ProcessingLog
from processing.core.parameters import getParameterFromString
from processing.core.outputs import getOutputFromString
from OTBUtils import OTBUtils
import OTBUtils
from processing.core.parameters import ParameterExtent
from processing.tools.system import getTempFilename
import xml.etree.ElementTree as ET
Expand Down Expand Up @@ -77,7 +77,11 @@ def getIcon(self):
return QIcon(os.path.join(pluginPath, 'images', 'otb.png'))

def help(self):
folder = os.path.join(OTBUtils.otbDescriptionPath(), 'doc')
version = OTBUtils.getInstalledVersion()
folder = OTBUtils.compatibleDescriptionPath(version)
if folder is None:
return False, None
folder = os.path.join(folder, 'doc')
helpfile = os.path.join(unicode(folder), self.appkey + ".html")
if os.path.exists(helpfile):
return False, helpfile
Expand Down Expand Up @@ -166,16 +170,9 @@ def defineCharacteristicsFromFile(self):
self.tr('Could not open OTB algorithm: %s\n%s' % (self.descriptionFile, line)))
raise e

def checkBeforeOpeningParametersDialog(self):
return OTBUtils.checkOtbConfiguration()

def processAlgorithm(self, progress):
currentOs = os.name

msg = OTBUtils.checkOtbConfiguration()
if msg:
raise GeoAlgorithmExecutionException(msg)

path = OTBUtils.otbPath()

commands = []
Expand Down
25 changes: 15 additions & 10 deletions python/plugins/processing/algs/otb/OTBAlgorithmProvider.py
Expand Up @@ -32,7 +32,7 @@
from PyQt4.QtGui import QIcon
from processing.core.AlgorithmProvider import AlgorithmProvider
from processing.core.ProcessingConfig import ProcessingConfig, Setting
from OTBUtils import OTBUtils
import OTBUtils
from OTBAlgorithm import OTBAlgorithm
from processing.core.ProcessingLog import ProcessingLog

Expand All @@ -45,7 +45,6 @@ class OTBAlgorithmProvider(AlgorithmProvider):
def __init__(self):
AlgorithmProvider.__init__(self)
self.activate = True
self.createAlgsList()

def getDescription(self):
return self.tr("Orfeo Toolbox (Image analysis)")
Expand All @@ -57,18 +56,27 @@ def getIcon(self):
return QIcon(os.path.join(pluginPath, 'images', 'otb.png'))

def _loadAlgorithms(self):
self.algs = self.preloadedAlgs
self.algs = []

version = OTBUtils.getInstalledVersion(True)
if version is None:
ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
self.tr('Problem with OTB installation: OTB was not found or is not correctly installed'))
return

folder = OTBUtils.compatibleDescriptionPath(version)
if folder is None:
ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
self.tr('Problem with OTB installation: installed OTB version (%s) is not supported' % version))
return

def createAlgsList(self):
self.preloadedAlgs = []
folder = OTBUtils.otbDescriptionPath()
for descriptionFile in os.listdir(folder):
if descriptionFile.endswith("xml"):
try:
alg = OTBAlgorithm(os.path.join(folder, descriptionFile))

if alg.name.strip() != "":
self.preloadedAlgs.append(alg)
self.algs.append(alg)
else:
ProcessingLog.addToLog(ProcessingLog.LOG_ERROR,
self.tr("Could not open OTB algorithm: %s" % descriptionFile))
Expand Down Expand Up @@ -101,6 +109,3 @@ def unload(self):
AlgorithmProvider.unload(self)
ProcessingConfig.removeSetting(OTBUtils.OTB_FOLDER)
ProcessingConfig.removeSetting(OTBUtils.OTB_LIB_FOLDER)

def canBeActivated(self):
return not bool(OTBUtils.checkOtbConfiguration())
Expand Up @@ -44,7 +44,7 @@

from processing.core.ProcessingConfig import ProcessingConfig

from OTBUtils import OTBUtils
import OTBUtils


def adaptBinaryMorphologicalOperation(commands_list):
Expand Down
240 changes: 134 additions & 106 deletions python/plugins/processing/algs/otb/OTBUtils.py
Expand Up @@ -39,125 +39,153 @@
import logging
import xml.etree.ElementTree as ET
import traceback
from processing.gui.SilentProgress import SilentProgress


class OTBUtils:
OTB_FOLDER = "OTB_FOLDER"
OTB_LIB_FOLDER = "OTB_LIB_FOLDER"
OTB_SRTM_FOLDER = "OTB_SRTM_FOLDER"
OTB_GEOID_FILE = "OTB_GEOID_FILE"

OTB_FOLDER = "OTB_FOLDER"
OTB_LIB_FOLDER = "OTB_LIB_FOLDER"
OTB_SRTM_FOLDER = "OTB_SRTM_FOLDER"
OTB_GEOID_FILE = "OTB_GEOID_FILE"

@staticmethod
def findOtbPath():
folder = None
#try to configure the path automatically
if isMac():
testfolder = os.path.join(unicode(QgsApplication.prefixPath()), "bin")
if os.path.exists(os.path.join(testfolder, "otbcli")):
folder = testfolder
else:
testfolder = "/usr/local/bin"
if os.path.exists(os.path.join(testfolder, "otbcli")):
folder = testfolder
elif isWindows():
testfolder = os.path.join(os.path.dirname(QgsApplication.prefixPath()),
os.pardir, "bin")
if os.path.exists(os.path.join(testfolder, "otbcli.bat")):
folder = testfolder
def findOtbPath():
folder = None
#try to configure the path automatically
if isMac():
testfolder = os.path.join(unicode(QgsApplication.prefixPath()), "bin")
if os.path.exists(os.path.join(testfolder, "otbcli")):
folder = testfolder
else:
testfolder = "/usr/bin"
testfolder = "/usr/local/bin"
if os.path.exists(os.path.join(testfolder, "otbcli")):
folder = testfolder
return folder

@staticmethod
def otbPath():
folder = OTBUtils.findOtbPath()
if folder is None:
folder = ProcessingConfig.getSetting(OTBUtils.OTB_FOLDER)
return folder

@staticmethod
def findOtbLibPath():
folder = None
#try to configure the path automatically
if isMac():
testfolder = os.path.join(unicode(QgsApplication.prefixPath()), "lib/otb/applications")
if os.path.exists(testfolder):
folder = testfolder
else:
testfolder = "/usr/local/lib/otb/applications"
if os.path.exists(testfolder):
folder = testfolder
elif isWindows():
testfolder = os.path.join(os.path.dirname(QgsApplication.prefixPath()), "orfeotoolbox", "applications")
if os.path.exists(testfolder):
folder = testfolder
elif isWindows():
testfolder = os.path.join(os.path.dirname(QgsApplication.prefixPath()),
os.pardir, "bin")
if os.path.exists(os.path.join(testfolder, "otbcli.bat")):
folder = testfolder
else:
testfolder = "/usr/bin"
if os.path.exists(os.path.join(testfolder, "otbcli")):
folder = testfolder
return folder


def otbPath():
folder = findOtbPath()
if folder is None:
folder = ProcessingConfig.getSetting(OTB_FOLDER)
return folder


def findOtbLibPath():
folder = None
#try to configure the path automatically
if isMac():
testfolder = os.path.join(unicode(QgsApplication.prefixPath()), "lib/otb/applications")
if os.path.exists(testfolder):
folder = testfolder
else:
testfolder = "/usr/lib/otb/applications"
testfolder = "/usr/local/lib/otb/applications"
if os.path.exists(testfolder):
folder = testfolder
return folder

@staticmethod
def otbLibPath():
folder = OTBUtils.findOtbLibPath()
if folder is None:
folder = ProcessingConfig.getSetting(OTBUtils.OTB_LIB_FOLDER)
return folder

@staticmethod
def otbSRTMPath():
folder = ProcessingConfig.getSetting(OTBUtils.OTB_SRTM_FOLDER)
if folder is None:
folder = ""
return folder

@staticmethod
def otbGeoidPath():
filepath = ProcessingConfig.getSetting(OTBUtils.OTB_GEOID_FILE)
if filepath is None:
filepath = ""
return filepath

@staticmethod
def otbDescriptionPath():
return os.path.join(os.path.dirname(__file__), "description")

@staticmethod
def executeOtb(commands, progress):
loglines = []
loglines.append(OTBUtils.tr("OTB execution console output"))
os.putenv('ITK_AUTOLOAD_PATH', OTBUtils.otbLibPath())
fused_command = ''.join(['"%s" ' % re.sub(r'^"|"$', '', c) for c in commands])
proc = subprocess.Popen(fused_command, shell=True, stdout=subprocess.PIPE, stdin=open(os.devnull), stderr=subprocess.STDOUT, universal_newlines=True).stdout
for line in iter(proc.readline, ""):
if "[*" in line:
idx = line.find("[*")
perc = int(line[idx - 4:idx - 2].strip(" "))
if perc != 0:
progress.setPercentage(perc)
else:
loglines.append(line)
progress.setConsoleInfo(line)
elif isWindows():
testfolder = os.path.join(os.path.dirname(QgsApplication.prefixPath()), "orfeotoolbox", "applications")
if os.path.exists(testfolder):
folder = testfolder
else:
testfolder = "/usr/lib/otb/applications"
if os.path.exists(testfolder):
folder = testfolder
return folder


def otbLibPath():
folder = findOtbLibPath()
if folder is None:
folder = ProcessingConfig.getSetting(OTB_LIB_FOLDER)
return folder


def otbSRTMPath():
folder = ProcessingConfig.getSetting(OTB_SRTM_FOLDER)
if folder is None:
folder = ""
return folder


def otbGeoidPath():
filepath = ProcessingConfig.getSetting(OTB_GEOID_FILE)
if filepath is None:
filepath = ""
return filepath


def otbDescriptionPath():
return os.path.join(os.path.dirname(__file__), "description")

_installedVersion = None
_installedVersionFound = False


def getInstalledVersion(runOtb=False):
global _installedVersion
global _installedVersionFound

if _installedVersionFound and not runOtb:
return _installedVersion

commands = [os.path.join(otbPath(), "otbcli_Smoothing")]
progress = SilentProgress()
out = executeOtb(commands, progress, False)
for line in out:
if "version" in line:
_installedVersionFound = True
_installedVersion = line.split("version")[-1].strip()
break
return _installedVersion


def compatibleDescriptionPath(version):
supportedVersions = {"5.0.0": "5.0.0"}
if version is None:
return None
if version not in supportedVersions:
lastVersion = sorted(supportedVersions.keys())[-1]
if version > lastVersion:
version = lastVersion
else:
return None

return os.path.join(otbDescriptionPath(), supportedVersions[version])


def executeOtb(commands, progress, addToLog=True):
loglines = []
loglines.append(tr("OTB execution console output"))
os.putenv('ITK_AUTOLOAD_PATH', otbLibPath())
fused_command = ''.join(['"%s" ' % re.sub(r'^"|"$', '', c) for c in commands])
proc = subprocess.Popen(fused_command, shell=True, stdout=subprocess.PIPE, stdin=open(os.devnull), stderr=subprocess.STDOUT, universal_newlines=True).stdout
for line in iter(proc.readline, ""):
if "[*" in line:
idx = line.find("[*")
perc = int(line[idx - 4:idx - 2].strip(" "))
if perc != 0:
progress.setPercentage(perc)
else:
loglines.append(line)
progress.setConsoleInfo(line)

if addToLog:
ProcessingLog.addToLog(ProcessingLog.LOG_INFO, loglines)

@staticmethod
def checkOtbConfiguration():
path = OTBUtils.otbPath()
libpath = OTBUtils.otbLibPath()
configurationOk = bool(path) and bool(libpath)
if not configurationOk:
return OTBUtils.tr('OTB folder is not configured. Please configure it '
'before running OTB algorithms.')

@staticmethod
def tr(string, context=''):
if context == '':
context = 'OTBUtils'
return QCoreApplication.translate(context, string)
return loglines


def tr(string, context=''):
if context == '':
context = 'OTBUtils'
return QCoreApplication.translate(context, string)


def get_choices_of(doc, parameter):
Expand Down
3 changes: 2 additions & 1 deletion python/plugins/processing/tests/PackagingTests.py
Expand Up @@ -17,6 +17,7 @@
***************************************************************************
"""


__author__ = 'Victor Olaya'
__date__ = 'May 2015'
__copyright__ = '(C) 2015, Victor Olaya'
Expand All @@ -35,7 +36,7 @@
from processing.algs.saga.SagaUtils import *
from processing.core.ProcessingConfig import ProcessingConfig
from processing.algs.grass.GrassUtils import GrassUtils
from processing.algs.otb.OTBUtils import OTBUtils
from processing.algs.otb import OTBUtils


class PackageTests(unittest.TestCase):
Expand Down

1 comment on commit 0b96621

@m-kuhn
Copy link
Member

@m-kuhn m-kuhn commented on 0b96621 Jan 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just got this:

Couldn't load plugin processing due to an error when calling its initGui() method 

AttributeError: 'NoneType' object has no attribute 'endswith' 
Traceback (most recent call last):
  File "/home/mku/dev/cpp/qgis/build/output/python/qgis/utils.py", line 315, in startPlugin
    plugins[packageName].initGui()
  File "/home/mku/dev/cpp/qgis/build/output/python/plugins/processing/ProcessingPlugin.py", line 57, in initGui
    Processing.initialize()
  File "/home/mku/dev/cpp/qgis/build/output/python/plugins/processing/core/Processing.py", line 149, in initialize
    Processing.loadFromProviders()
  File "/home/mku/dev/cpp/qgis/build/output/python/plugins/processing/core/Processing.py", line 164, in loadFromProviders
    Processing.loadAlgorithms()
  File "/home/mku/dev/cpp/qgis/build/output/python/plugins/processing/core/Processing.py", line 199, in loadAlgorithms
    Processing.updateProviders()
  File "/home/mku/dev/cpp/qgis/build/output/python/plugins/processing/core/Processing.py", line 172, in updateProviders
    provider.loadAlgorithms()
  File "/home/mku/dev/cpp/qgis/build/output/python/plugins/processing/core/AlgorithmProvider.py", line 54, in loadAlgorithms
    self._loadAlgorithms()
  File "/home/mku/dev/cpp/qgis/build/output/python/plugins/processing/algs/otb/OTBAlgorithmProvider.py", line 61, in _loadAlgorithms
    version = OTBUtils.getInstalledVersion(True)
  File "/home/mku/dev/cpp/qgis/build/output/python/plugins/processing/algs/otb/OTBUtils.py", line 138, in getInstalledVersion
    commands = [os.path.join(otbPath(), "otbcli_Smoothing")]
  File "/usr/lib64/python2.7/posixpath.py", line 70, in join
    elif path == '' or path.endswith('/'):
AttributeError: 'NoneType' object has no attribute 'endswith'

Please sign in to comment.