1# -*- coding: utf-8 -*-
2
3"""
4***************************************************************************
5    SagaAlgorithmProvider.py
6    ---------------------
7    Date                 : August 2012
8    Copyright            : (C) 2012 by Victor Olaya
9    Email                : volayaf at gmail dot com
10***************************************************************************
11*                                                                         *
12*   This program is free software; you can redistribute it and/or modify  *
13*   it under the terms of the GNU General Public License as published by  *
14*   the Free Software Foundation; either version 2 of the License, or     *
15*   (at your option) any later version.                                   *
16*                                                                         *
17***************************************************************************
18"""
19
20__author__ = 'Victor Olaya'
21__date__ = 'August 2012'
22__copyright__ = '(C) 2012, Victor Olaya'
23
24import os
25from qgis.PyQt.QtGui import QIcon
26from qgis.PyQt.QtCore import QCoreApplication
27from qgis.core import (Qgis,
28                       QgsProcessingProvider,
29                       QgsProcessingUtils,
30                       QgsApplication,
31                       QgsMessageLog,
32                       QgsRuntimeProfiler)
33from processing.core.ProcessingConfig import ProcessingConfig, Setting
34from processing.tools.system import isWindows, isMac
35
36from .SagaAlgorithm import SagaAlgorithm
37from .SplitRGBBands import SplitRGBBands
38from . import SagaUtils
39
40pluginPath = os.path.normpath(os.path.join(
41    os.path.split(os.path.dirname(__file__))[0], os.pardir))
42
43REQUIRED_VERSION = '2.3.'
44BETA_SUPPORT_VERSION = '7.'
45
46
47class SagaAlgorithmProvider(QgsProcessingProvider):
48
49    def __init__(self):
50        super().__init__()
51        self.algs = []
52
53    def load(self):
54        with QgsRuntimeProfiler.profile('SAGA Provider'):
55            ProcessingConfig.settingIcons[self.name()] = self.icon()
56            ProcessingConfig.addSetting(Setting("SAGA", 'ACTIVATE_SAGA',
57                                                self.tr('Activate'), True))
58            ProcessingConfig.addSetting(Setting("SAGA",
59                                                SagaUtils.SAGA_IMPORT_EXPORT_OPTIMIZATION,
60                                                self.tr('Enable SAGA Import/Export optimizations'), False))
61            ProcessingConfig.addSetting(Setting("SAGA",
62                                                SagaUtils.SAGA_LOG_COMMANDS,
63                                                self.tr('Log execution commands'), True))
64            ProcessingConfig.addSetting(Setting("SAGA",
65                                                SagaUtils.SAGA_LOG_CONSOLE,
66                                                self.tr('Log console output'), True))
67            ProcessingConfig.readSettings()
68            self.refreshAlgorithms()
69
70        return True
71
72    def unload(self):
73        ProcessingConfig.removeSetting('ACTIVATE_SAGA')
74        ProcessingConfig.removeSetting(SagaUtils.SAGA_LOG_CONSOLE)
75        ProcessingConfig.removeSetting(SagaUtils.SAGA_LOG_COMMANDS)
76
77    def isActive(self):
78        return ProcessingConfig.getSetting('ACTIVATE_SAGA')
79
80    def setActive(self, active):
81        ProcessingConfig.setSettingValue('ACTIVATE_SAGA', active)
82
83    def canBeActivated(self):
84        version = SagaUtils.getInstalledVersion(True)
85        if version is not None and (version.startswith(REQUIRED_VERSION) or version >= BETA_SUPPORT_VERSION):
86            return True
87        return False
88
89    def warningMessage(self):
90        version = SagaUtils.getInstalledVersion(True)
91        if version is not None and version >= BETA_SUPPORT_VERSION:
92            return self.tr('SAGA version {} is not officially supported - algorithms may encounter issues').format(version)
93        return ''
94
95    def loadAlgorithms(self):
96        version = SagaUtils.getInstalledVersion(True)
97        if version is None:
98            QgsMessageLog.logMessage(self.tr('Problem with SAGA installation: SAGA was not found or is not correctly installed'),
99                                     self.tr('Processing'), Qgis.Critical)
100            return
101
102        if not version.startswith(REQUIRED_VERSION) and not version >= BETA_SUPPORT_VERSION:
103            QgsMessageLog.logMessage(self.tr('Problem with SAGA installation: unsupported SAGA version (found: {}, required: {}).').format(version, REQUIRED_VERSION),
104                                     self.tr('Processing'),
105                                     Qgis.Critical)
106            return
107
108        self.algs = []
109        folder = SagaUtils.sagaDescriptionPath()
110        for descriptionFile in os.listdir(folder):
111            if descriptionFile.endswith('txt'):
112                try:
113                    alg = SagaAlgorithm(os.path.join(folder, descriptionFile))
114                    if alg.name().strip() != '':
115                        self.algs.append(alg)
116                    else:
117                        QgsMessageLog.logMessage(self.tr('Could not open SAGA algorithm: {}'.format(descriptionFile)),
118                                                 self.tr('Processing'), Qgis.Critical)
119                except Exception as e:
120                    QgsMessageLog.logMessage(self.tr('Could not open SAGA algorithm: {}\n{}'.format(descriptionFile, str(e))),
121                                             self.tr('Processing'), Qgis.Critical)
122
123        self.algs.append(SplitRGBBands())
124        for a in self.algs:
125            self.addAlgorithm(a)
126
127    def name(self):
128        return 'SAGA'
129
130    def longName(self):
131        version = SagaUtils.getInstalledVersion()
132        return 'SAGA ({})'.format(version) if version is not None else 'SAGA'
133
134    def id(self):
135        return 'saga'
136
137    def defaultVectorFileExtension(self, hasGeometry=True):
138        return 'shp' if hasGeometry else 'dbf'
139
140    def defaultRasterFileExtension(self):
141        return 'sdat'
142
143    def supportedOutputRasterLayerExtensions(self):
144        return ['sdat']
145
146    def supportedOutputVectorLayerExtensions(self):
147        return ['shp', 'dbf']
148
149    def supportedOutputTableExtensions(self):
150        return ['dbf']
151
152    def flags(self):
153        # push users towards alternative algorithms instead, SAGA algorithms should only be used by experienced
154        # users who understand and can workaround the frequent issues encountered here
155        return QgsProcessingProvider.FlagDeemphasiseSearchResults
156
157    def supportsNonFileBasedOutput(self):
158        """
159        SAGA Provider doesn't support non file based outputs
160        """
161        return False
162
163    def icon(self):
164        return QgsApplication.getThemeIcon("/providerSaga.svg")
165
166    def tr(self, string, context=''):
167        if context == '':
168            context = 'SagaAlgorithmProvider'
169        return QCoreApplication.translate(context, string)
170