def grassPath(): """ Find GRASS path on the operating system. Sets global variable Grass7Utils.path """ if Grass7Utils.path is not None: return Grass7Utils.path if not isWindows() and not isMac(): return '' folder = ProcessingConfig.getSetting(Grass7Utils.GRASS_FOLDER) or '' if not os.path.exists(folder): folder = None if folder is None: # Under MS-Windows, we use OSGEO4W or QGIS Path for folder if isWindows(): if "OSGEO4W_ROOT" in os.environ: testfolder = os.path.join(str(os.environ['OSGEO4W_ROOT']), "apps") else: testfolder = str(QgsApplication.prefixPath()) testfolder = os.path.join(testfolder, 'grass') if os.path.isdir(testfolder): for subfolder in os.listdir(testfolder): if subfolder.startswith('grass-7'): folder = os.path.join(testfolder, subfolder) break elif isMac(): # For MacOSX, we scan some well-known directories # Start with QGIS bundle for version in ['', '7', '70', '71', '72', '74']: testfolder = os.path.join(str(QgsApplication.prefixPath()), 'grass{}'.format(version)) if os.path.isdir(testfolder): folder = testfolder break # If nothing found, try standalone GRASS installation if folder is None: for version in ['0', '1', '2', '4']: testfolder = '/Applications/GRASS-7.{}.app/Contents/MacOS'.format( version) if os.path.isdir(testfolder): folder = testfolder break if folder is not None: Grass7Utils.path = folder return folder or ''
def load(self): ProcessingConfig.settingIcons[self.name()] = self.icon() ProcessingConfig.addSetting(Setting(self.name(), 'ACTIVATE_GRASS7', self.tr('Activate'), True)) if isWindows() or isMac(): ProcessingConfig.addSetting(Setting( self.name(), Grass7Utils.GRASS_FOLDER, self.tr('GRASS7 folder'), Grass7Utils.grassPath(), valuetype=Setting.FOLDER)) ProcessingConfig.addSetting(Setting( self.name(), Grass7Utils.GRASS_LOG_COMMANDS, self.tr('Log execution commands'), False)) ProcessingConfig.addSetting(Setting( self.name(), Grass7Utils.GRASS_LOG_CONSOLE, self.tr('Log console output'), False)) ProcessingConfig.addSetting(Setting( self.name(), Grass7Utils.GRASS_HELP_PATH, self.tr('Location of GRASS docs'), Grass7Utils.grassHelpPath())) # Add a setting for using v.external instead of v.in.ogr # But set it to False by default because some algorithms # can't be used with external data (need a solid v.in.ogr). ProcessingConfig.addSetting(Setting( self.name(), Grass7Utils.GRASS_USE_VEXTERNAL, self.tr('For vector layers, use v.external (faster) instead of v.in.ogr'), False)) ProcessingConfig.readSettings() self.refreshAlgorithms() return True
def grassHelpPath(): helpPath = ProcessingConfig.getSetting(Grass7Utils.GRASS_HELP_PATH) if helpPath is None: if isWindows() or isMac(): if Grass7Utils.path is not None: localPath = os.path.join(Grass7Utils.path, 'docs/html') if os.path.exists(localPath): helpPath = os.path.abspath(localPath) else: searchPaths = [ '/usr/share/doc/grass-doc/html', '/opt/grass/docs/html', '/usr/share/doc/grass/docs/html' ] for path in searchPaths: if os.path.exists(path): helpPath = os.path.abspath(path) break if helpPath is not None: return helpPath elif Grass7Utils.version: version = Grass7Utils.version.replace('.', '')[:2] return 'https://grass.osgeo.org/grass{}/manuals/'.format(version) else: # GRASS not available! return 'https://grass.osgeo.org/grass72/manuals/'
def unload(self): ProcessingConfig.removeSetting('ACTIVATE_GRASS7') if isWindows() or isMac(): ProcessingConfig.removeSetting(Grass7Utils.GRASS_FOLDER) ProcessingConfig.removeSetting(Grass7Utils.GRASS_LOG_COMMANDS) ProcessingConfig.removeSetting(Grass7Utils.GRASS_LOG_CONSOLE) ProcessingConfig.removeSetting(Grass7Utils.GRASS_HELP_PATH) ProcessingConfig.removeSetting(Grass7Utils.GRASS_USE_VEXTERNAL)
def getInstalledVersion(runSaga=False): global _installedVersion global _installedVersionFound maxRetries = 5 retries = 0 if _installedVersionFound and not runSaga: return _installedVersion if isWindows(): commands = [os.path.join(sagaPath(), "saga_cmd.exe"), "-v"] elif isMac(): commands = [os.path.join(sagaPath(), "saga_cmd -v")] else: # for Linux use just one string instead of separated parameters as the list # does not work well together with shell=True option # (python docs advices to use subprocess32 instead of python2.7's subprocess) commands = ["saga_cmd -v"] while retries < maxRetries: with subprocess.Popen( commands, shell=True, stdout=subprocess.PIPE, stdin=subprocess.DEVNULL, stderr=subprocess.STDOUT, universal_newlines=True, ) as proc: if isMac(): # This trick avoids having an uninterrupted system call exception if SAGA is not installed time.sleep(1) try: lines = proc.stdout.readlines() for line in lines: if line.startswith("SAGA Version:"): _installedVersion = line[len("SAGA Version:"):].strip().split(" ")[0] _installedVersionFound = True return _installedVersion return None except IOError: retries += 1 except: return None return _installedVersion
def grassBin(): """ Find GRASS binary path on the operating system. Sets global variable Grass7Utils.command """ cmdList = [ "grass74", "grass72", "grass71", "grass70", "grass", "grass74.sh", "grass72.sh", "grass71.sh", "grass70.sh", "grass.sh" ] def searchFolder(folder): """ Inline function to search for grass binaries into a folder with os.walk """ if os.path.exists(folder): for root, dirs, files in os.walk(folder): for cmd in cmdList: if cmd in files: return os.path.join(root, cmd) return None if Grass7Utils.command: return Grass7Utils.command path = Grass7Utils.grassPath() command = None # For MS-Windows there is a difference between GRASS Path and GRASS binary if isWindows(): # If nothing found, use OSGEO4W or QgsPrefix: if "OSGEO4W_ROOT" in os.environ: testFolder = str(os.environ['OSGEO4W_ROOT']) else: testFolder = str(QgsApplication.prefixPath()) testFolder = os.path.join(testFolder, 'bin') command = searchFolder(testFolder) elif isMac(): # Search in grassPath command = searchFolder(path) # Under GNU/Linux or if everything has failed, use shutil if not command: for cmd in cmdList: testBin = shutil.which(cmd) if testBin: command = os.path.abspath(testBin) break if command: Grass7Utils.command = command if path is '': Grass7Utils.path = os.path.dirname(command) return command
def createSagaBatchJobFileFromSagaCommands(commands): with open(sagaBatchJobFilename(), 'w') as fout: if isWindows(): fout.write('set SAGA=' + sagaPath() + '\n') fout.write('set SAGA_MLB=' + os.path.join(sagaPath(), 'modules') + '\n') fout.write('PATH=%PATH%;%SAGA%;%SAGA_MLB%\n') elif isMac(): fout.write('export SAGA_MLB=' + os.path.join(sagaPath(), '../lib/saga') + '\n') fout.write('export PATH=' + sagaPath() + ':$PATH\n') else: pass for command in commands: try: # Python 2 fout.write('saga_cmd ' + command.encode('utf8') + '\n') except TypeError: # Python 3 fout.write('saga_cmd ' + command + '\n') fout.write('exit')
def checkGrassIsInstalled(ignorePreviousState=False): if not ignorePreviousState: if Grass7Utils.isGrassInstalled: return # We check the version of Grass7 if Grass7Utils.installedVersion() is not None: # For Ms-Windows, we check GRASS binaries if isWindows(): cmdpath = os.path.join(Grass7Utils.path, 'bin', 'r.out.gdal.exe') if not os.path.exists(cmdpath): return Grass7Utils.tr( 'The specified GRASS 7 folder "{}" does not contain ' 'a valid set of GRASS 7 modules.\nPlease, go to the ' 'Processing settings dialog, and check that the ' 'GRASS 7\nfolder is correctly configured'.format( os.path.join(Grass7Utils.path, 'bin'))) Grass7Utils.isGrassInstalled = True return # Return error messages else: # MS-Windows or MacOSX if isWindows() or isMac(): if Grass7Utils.path is None: return Grass7Utils.tr( 'GRASS GIS 7 folder is not configured. Please configure ' 'it before running GRASS GIS 7 algorithms.') if Grass7Utils.command is None: return Grass7Utils.tr( 'GRASS GIS 7 binary {0} can\'t be found on this system from a shell. ' 'Please install it or configure your PATH {1} environment variable.' .format('(grass.bat)' if isWindows() else '(grass.sh)', 'or OSGEO4W_ROOT' if isWindows() else '')) # GNU/Linux else: return Grass7Utils.tr( 'GRASS 7 can\'t be found on this system from a shell. ' 'Please install it or configure your PATH environment variable.' )
def findSagaFolder(): folder = None if isMac(): testfolder = os.path.join(QgsApplication.prefixPath(), 'bin') if os.path.exists(os.path.join(testfolder, 'saga_cmd')): folder = testfolder else: testfolder = '/usr/local/bin' if os.path.exists(os.path.join(testfolder, 'saga_cmd')): folder = testfolder elif isWindows(): folders = [] folders.append(os.path.join(os.path.dirname(QgsApplication.prefixPath()), 'saga-ltr')) folders.append(os.path.join(os.path.dirname(QgsApplication.prefixPath()), 'saga')) if "OSGEO4W_ROOT" in os.environ: folders.append(os.path.join(str(os.environ['OSGEO4W_ROOT']), "apps", "saga-ltr")) folders.append(os.path.join(str(os.environ['OSGEO4W_ROOT']), "apps", "saga")) for testfolder in folders: if os.path.exists(os.path.join(testfolder, 'saga_cmd.exe')): folder = testfolder break return folder
def installedVersion(run=False): """ Returns the installed version of GRASS by launching the GRASS command with -v parameter. """ if Grass7Utils.isGrassInstalled and not run: return Grass7Utils.version if Grass7Utils.grassBin() is None: return None # Launch GRASS command with -v parameter # For MS-Windows, hide the console if isWindows(): si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW si.wShowWindow = subprocess.SW_HIDE with subprocess.Popen([Grass7Utils.command, '-v'], shell=True if isMac() else False, stdout=subprocess.PIPE, stdin=subprocess.DEVNULL, stderr=subprocess.STDOUT, universal_newlines=True, startupinfo=si if isWindows() else None) as proc: try: lines = proc.stdout.readlines() for line in lines: if "GRASS GIS " in line: line = line.split(" ")[-1].strip() if line.startswith("7."): Grass7Utils.version = line return Grass7Utils.version except: pass return None
def sagaPath(): if not isWindows() and not isMac(): return '' folder = findSagaFolder() return folder or ''
def executeGrass(commands, feedback, outputCommands=None): loglines = [] loglines.append(Grass7Utils.tr('GRASS GIS 7 execution console output')) grassOutDone = False command, grassenv = Grass7Utils.prepareGrassExecution(commands) #QgsMessageLog.logMessage('exec: {}'.format(command), 'DEBUG', Qgis.Info) # For MS-Windows, we need to hide the console window. if isWindows(): si = subprocess.STARTUPINFO() si.dwFlags |= subprocess.STARTF_USESHOWWINDOW si.wShowWindow = subprocess.SW_HIDE with subprocess.Popen(command, shell=True if isMac() else False, stdout=subprocess.PIPE, stdin=subprocess.DEVNULL, stderr=subprocess.STDOUT, universal_newlines=True, env=grassenv, startupinfo=si if isWindows() else None) as proc: for line in iter(proc.stdout.readline, ''): if 'GRASS_INFO_PERCENT' in line: try: feedback.setProgress( int(line[len('GRASS_INFO_PERCENT') + 2:])) except: pass else: if 'r.out' in line or 'v.out' in line: grassOutDone = True loglines.append(line) feedback.pushConsoleInfo(line) # Some GRASS scripts, like r.mapcalculator or r.fillnulls, call # other GRASS scripts during execution. This may override any # commands that are still to be executed by the subprocess, which # are usually the output ones. If that is the case runs the output # commands again. if not grassOutDone and outputCommands: command, grassenv = Grass7Utils.prepareGrassExecution( outputCommands) with subprocess.Popen( command, shell=True if isMac() else False, stdout=subprocess.PIPE, stdin=subprocess.DEVNULL, stderr=subprocess.STDOUT, universal_newlines=True, env=grassenv, startupinfo=si if isWindows() else None) as proc: for line in iter(proc.stdout.readline, ''): if 'GRASS_INFO_PERCENT' in line: try: feedback.setProgress( int(line[len('GRASS_INFO_PERCENT') + 2:])) except: pass else: loglines.append(line) feedback.pushConsoleInfo(line) if ProcessingConfig.getSetting(Grass7Utils.GRASS_LOG_CONSOLE): QgsMessageLog.logMessage('\n'.join(loglines), 'Processing', Qgis.Info)