OtbUtils.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. """
  2. ***************************************************************************
  3. OtbUtils.py
  4. -----------
  5. Date : August 2012
  6. Copyright : (C) 2012 by Victor Olaya
  7. (C) 2013 by CS Systemes d'information (CS SI)
  8. Email : volayaf at gmail dot com
  9. otb at c-s dot fr (CS SI)
  10. Contributors : Victor Olaya
  11. Julien Malik, Oscar Picas (CS SI) - add functions to manage xml tree
  12. Alexia Mondot (CS SI) - add a trick for OTBApplication SplitImages
  13. Rashad Kanavath (CS SI) - re-integration of provider to QGIS
  14. ***************************************************************************
  15. * *
  16. * This program is free software; you can redistribute it and/or modify *
  17. * it under the terms of the GNU General Public License as published by *
  18. * the Free Software Foundation; either version 2 of the License, or *
  19. * (at your option) any later version. *
  20. * *
  21. ***************************************************************************
  22. """
  23. __author__ = 'Victor Olaya'
  24. __date__ = 'August 2012'
  25. __copyright__ = '(C) 2012, Victor Olaya'
  26. import os
  27. import sys
  28. import re
  29. import subprocess
  30. from processing.core.ProcessingConfig import ProcessingConfig
  31. from qgis.core import (Qgis, QgsApplication, QgsMessageLog)
  32. from qgis.PyQt.QtCore import QCoreApplication
  33. class OtbUtils:
  34. # Path to otb installation folder (string, directory).
  35. FOLDER = "OTB_FOLDER"
  36. # Path to otb application folder. multiple paths are supported (string, directory).
  37. APP_FOLDER = "OTB_APP_FOLDER"
  38. # A string to hold current version number. Useful for bug reporting.
  39. VERSION = "OTB_VERSION"
  40. # Default directory were DEM tiles are stored. It should only contain ```.hgt`` or or georeferenced ``.tif`` files. Empty if not set (no directory set).
  41. SRTM_FOLDER = "OTB_SRTM_FOLDER"
  42. # Default path to the geoid file that will be used to retrieve height of DEM above ellipsoid. Empty if not set (no geoid set).
  43. GEOID_FILE = "OTB_GEOID_FILE"
  44. # Default maximum memory that OTB should use for processing, in MB. If not set, default value is 128 MB.
  45. # This is set through environment variable ``OTB_MAX_RAM_HINT``
  46. MAX_RAM_HINT = 'OTB_MAX_RAM_HINT'
  47. # ``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``.
  48. LOGGER_LEVEL = 'OTB_LOGGER_LEVEL'
  49. @staticmethod
  50. def settingNames():
  51. return [
  52. OtbUtils.FOLDER,
  53. OtbUtils.SRTM_FOLDER,
  54. OtbUtils.GEOID_FILE,
  55. OtbUtils.LOGGER_LEVEL,
  56. OtbUtils.MAX_RAM_HINT
  57. ]
  58. @staticmethod
  59. def version():
  60. return ProcessingConfig.getSetting(OtbUtils.VERSION) or '0.0.0'
  61. @staticmethod
  62. def loggerLevel():
  63. return ProcessingConfig.getSetting(OtbUtils.LOGGER_LEVEL) or 'INFO'
  64. @staticmethod
  65. def maxRAMHint():
  66. return ProcessingConfig.getSetting(OtbUtils.MAX_RAM_HINT) or ''
  67. @staticmethod
  68. def otbFolder():
  69. if ProcessingConfig.getSetting(OtbUtils.FOLDER):
  70. return os.path.normpath(os.sep.join(re.split(r'\\|/', ProcessingConfig.getSetting(OtbUtils.FOLDER))))
  71. else:
  72. return None
  73. @staticmethod
  74. def appFolder():
  75. app_folder = ProcessingConfig.getSetting(OtbUtils.APP_FOLDER)
  76. if app_folder:
  77. return os.pathsep.join(app_folder.split(';'))
  78. else:
  79. return None
  80. @staticmethod
  81. def srtmFolder():
  82. return ProcessingConfig.getSetting(OtbUtils.SRTM_FOLDER) or ''
  83. @staticmethod
  84. def geoidFile():
  85. return ProcessingConfig.getSetting(OtbUtils.GEOID_FILE) or ''
  86. @staticmethod
  87. def getExecutableInPath(path, exe):
  88. ext = '.exe' if os.name == 'nt' else ''
  89. return os.path.join(path, 'bin', exe + ext)
  90. @staticmethod
  91. def getAuxiliaryDataDirectories():
  92. gdal_data_dir = None
  93. gtiff_csv_dir = None
  94. proj_dir = None
  95. otb_folder = OtbUtils.otbFolder()
  96. if os.name == 'nt':
  97. gdal_data_dir = os.path.join(otb_folder, 'share', 'data')
  98. gtiff_csv_dir = os.path.join(otb_folder, 'share', 'epsg_csv')
  99. proj_dir = os.path.join(otb_folder, 'share', 'proj')
  100. else:
  101. env_profile = os.path.join(otb_folder, 'otbenv.profile')
  102. try:
  103. if os.path.exists(env_profile):
  104. with open(env_profile) as f:
  105. lines = f.readlines()
  106. lines = [x.strip() for x in lines]
  107. for line in lines:
  108. if not line or line.startswith('#'):
  109. continue
  110. if 'GDAL_DATA=' in line:
  111. gdal_data_dir = line.split("GDAL_DATA=")[1]
  112. if 'GEOTIFF_CSV=' in line:
  113. gtiff_csv_dir = line.split("GEOTIFF_CSV=")[1]
  114. if 'PROJ_LIB=' in line:
  115. proj_dir = line.split("PROJ_LIB=")[1]
  116. except BaseException as exc:
  117. errmsg = 'Cannot find gdal and geotiff data directory.' + str(exc)
  118. QgsMessageLog.logMessage(errmsg, OtbUtils.tr('Processing'), Qgis.Info)
  119. return gdal_data_dir, gtiff_csv_dir, proj_dir
  120. @staticmethod
  121. def executeOtb(commands, feedback, addToLog=True):
  122. otb_env = {
  123. 'LC_NUMERIC': 'C',
  124. 'GDAL_DRIVER_PATH': 'disable'
  125. }
  126. gdal_data_dir, gtiff_csv_dir, proj_dir = OtbUtils.getAuxiliaryDataDirectories()
  127. if gdal_data_dir and os.path.exists(gdal_data_dir):
  128. otb_env['GDAL_DATA'] = gdal_data_dir
  129. if gtiff_csv_dir and os.path.exists(gtiff_csv_dir):
  130. otb_env['GEOTIFF_CSV'] = gtiff_csv_dir
  131. if proj_dir and os.path.exists(proj_dir):
  132. otb_env['PROJ_LIB'] = proj_dir
  133. otb_env['OTB_LOGGER_LEVEL'] = OtbUtils.loggerLevel()
  134. max_ram_hint = OtbUtils.maxRAMHint()
  135. if max_ram_hint and int(max_ram_hint) > 256:
  136. otb_env['OTB_MAX_RAM_HINT'] = max_ram_hint
  137. kw = {'env': otb_env}
  138. if os.name == 'nt' and sys.version_info >= (3, 6):
  139. kw['encoding'] = "cp{}".format(OtbUtils.getWindowsCodePage())
  140. QgsMessageLog.logMessage("{}".format(kw), OtbUtils.tr('Processing'), Qgis.Info)
  141. QgsMessageLog.logMessage("cmd={}".format(commands), OtbUtils.tr('Processing'), Qgis.Info)
  142. with subprocess.Popen(
  143. commands,
  144. shell=True,
  145. stdout=subprocess.PIPE,
  146. stdin=subprocess.DEVNULL,
  147. stderr=subprocess.STDOUT,
  148. universal_newlines=True,
  149. **kw
  150. ) as proc:
  151. for line in iter(proc.stdout.readline, ''):
  152. line = line.strip()
  153. # '* ]' and ' ]' says its some progress update
  154. if '% [' in line:
  155. part = line.split(':')[1]
  156. percent = part.split('%')[0]
  157. try:
  158. if int(percent) >= 100:
  159. feedback.pushConsoleInfo(line)
  160. feedback.setProgress(int(percent))
  161. except Exception:
  162. pass
  163. else:
  164. if feedback is None:
  165. QgsMessageLog.logMessage(line, OtbUtils.tr('Processing'), Qgis.Info)
  166. else:
  167. if any(l in line for l in ['(WARNING)', 'WARNING:']):
  168. feedback.reportError(line, False)
  169. elif any(l in line for l in ['(FATAL)', 'ERROR:', 'ERROR']):
  170. feedback.reportError(line, True)
  171. else:
  172. feedback.pushConsoleInfo(line.strip())
  173. @staticmethod
  174. def getWindowsCodePage():
  175. """
  176. Determines MS-Windows CMD.exe shell codepage.
  177. Used into GRASS exec script under MS-Windows.
  178. """
  179. from ctypes import cdll
  180. return str(cdll.kernel32.GetACP())
  181. @staticmethod
  182. def tr(string, context=''):
  183. if context == '':
  184. context = 'OtbUtils'
  185. return QCoreApplication.translate(context, string)