mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
[processing] remove R provider from core distribution
This commit is contained in:
parent
b77b496755
commit
30a7e7e514
@ -5,7 +5,6 @@ ADD_SUBDIRECTORY(gdal)
|
|||||||
ADD_SUBDIRECTORY(grass7)
|
ADD_SUBDIRECTORY(grass7)
|
||||||
ADD_SUBDIRECTORY(saga)
|
ADD_SUBDIRECTORY(saga)
|
||||||
ADD_SUBDIRECTORY(qgis)
|
ADD_SUBDIRECTORY(qgis)
|
||||||
ADD_SUBDIRECTORY(r)
|
|
||||||
ADD_SUBDIRECTORY(exampleprovider)
|
ADD_SUBDIRECTORY(exampleprovider)
|
||||||
|
|
||||||
PLUGIN_INSTALL(processing algs ${PY_FILES})
|
PLUGIN_INSTALL(processing algs ${PY_FILES})
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
FILE(GLOB PY_FILES *.py)
|
|
||||||
FILE(GLOB SCRIPT_FILES scripts/*.rsx)
|
|
||||||
|
|
||||||
PLUGIN_INSTALL(processing algs/r ${PY_FILES})
|
|
||||||
PLUGIN_INSTALL(processing algs/r/scripts ${SCRIPT_FILES})
|
|
@ -1,458 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
***************************************************************************
|
|
||||||
RAlgorithm.py
|
|
||||||
---------------------
|
|
||||||
Date : August 2012
|
|
||||||
Copyright : (C) 2012 by Victor Olaya
|
|
||||||
Email : volayaf at gmail dot com
|
|
||||||
***************************************************************************
|
|
||||||
* *
|
|
||||||
* 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. *
|
|
||||||
* *
|
|
||||||
***************************************************************************
|
|
||||||
"""
|
|
||||||
from future import standard_library
|
|
||||||
standard_library.install_aliases()
|
|
||||||
from builtins import str
|
|
||||||
|
|
||||||
__author__ = 'Victor Olaya'
|
|
||||||
__date__ = 'August 2012'
|
|
||||||
__copyright__ = '(C) 2012, Victor Olaya'
|
|
||||||
|
|
||||||
# This will get replaced with a git SHA1 when you do a git archive
|
|
||||||
|
|
||||||
__revision__ = '$Format:%H$'
|
|
||||||
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
|
|
||||||
from qgis.core import (QgsApplication,
|
|
||||||
QgsProcessingUtils,
|
|
||||||
QgsMessageLog)
|
|
||||||
|
|
||||||
from processing.core.GeoAlgorithm import GeoAlgorithm
|
|
||||||
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
|
|
||||||
from processing.gui.Help2Html import getHtmlFromHelpFile
|
|
||||||
from processing.core.parameters import ParameterRaster
|
|
||||||
from processing.core.parameters import ParameterTable
|
|
||||||
from processing.core.parameters import ParameterVector
|
|
||||||
from processing.core.parameters import ParameterMultipleInput
|
|
||||||
from processing.core.parameters import ParameterString
|
|
||||||
from processing.core.parameters import ParameterNumber
|
|
||||||
from processing.core.parameters import ParameterBoolean
|
|
||||||
from processing.core.parameters import ParameterSelection
|
|
||||||
from processing.core.parameters import ParameterTableField
|
|
||||||
from processing.core.parameters import ParameterExtent
|
|
||||||
from processing.core.parameters import ParameterCrs
|
|
||||||
from processing.core.parameters import ParameterFile
|
|
||||||
from processing.core.outputs import OutputTable
|
|
||||||
from processing.core.outputs import OutputVector
|
|
||||||
from processing.core.outputs import OutputRaster
|
|
||||||
from processing.core.outputs import OutputHTML
|
|
||||||
from processing.core.parameters import getParameterFromString
|
|
||||||
from processing.core.outputs import getOutputFromString
|
|
||||||
from processing.tools import dataobjects
|
|
||||||
from processing.tools.system import isWindows
|
|
||||||
from processing.script.WrongScriptException import WrongScriptException
|
|
||||||
from .RUtils import RUtils
|
|
||||||
|
|
||||||
pluginPath = os.path.normpath(os.path.join(
|
|
||||||
os.path.split(os.path.dirname(__file__))[0], os.pardir))
|
|
||||||
|
|
||||||
|
|
||||||
class RAlgorithm(GeoAlgorithm):
|
|
||||||
|
|
||||||
R_CONSOLE_OUTPUT = 'R_CONSOLE_OUTPUT'
|
|
||||||
RPLOTS = 'RPLOTS'
|
|
||||||
|
|
||||||
def getCopy(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __init__(self, descriptionFile, script=None):
|
|
||||||
GeoAlgorithm.__init__(self)
|
|
||||||
self.script = script
|
|
||||||
self._name = ''
|
|
||||||
self._display_name = ''
|
|
||||||
self._group = ''
|
|
||||||
self.descriptionFile = descriptionFile
|
|
||||||
if script is not None:
|
|
||||||
self.defineCharacteristicsFromScript()
|
|
||||||
if descriptionFile is not None:
|
|
||||||
self.defineCharacteristicsFromFile()
|
|
||||||
|
|
||||||
def icon(self):
|
|
||||||
return QgsApplication.getThemeIcon("/providerR.svg")
|
|
||||||
|
|
||||||
def svgIconPath(self):
|
|
||||||
return QgsApplication.iconPath("providerR.svg")
|
|
||||||
|
|
||||||
def name(self):
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
def displayName(self):
|
|
||||||
return self._display_name
|
|
||||||
|
|
||||||
def group(self):
|
|
||||||
return self._group
|
|
||||||
|
|
||||||
def defineCharacteristicsFromScript(self):
|
|
||||||
lines = self.script.split('\n')
|
|
||||||
self._name = 'unnamedalgorithm'
|
|
||||||
self._display_name = self.trAlgorithm('[Unnamed algorithm]')
|
|
||||||
self._group = self.tr('User R scripts')
|
|
||||||
self.parseDescription(iter(lines))
|
|
||||||
|
|
||||||
def defineCharacteristicsFromFile(self):
|
|
||||||
filename = os.path.basename(self.descriptionFile)
|
|
||||||
self.name = filename[:filename.rfind('.')].replace('_', ' ')
|
|
||||||
self._group = self.tr('User R scripts')
|
|
||||||
with open(self.descriptionFile, 'r') as f:
|
|
||||||
lines = [line.strip() for line in f]
|
|
||||||
self.parseDescription(iter(lines))
|
|
||||||
|
|
||||||
def parseDescription(self, lines):
|
|
||||||
self.script = ''
|
|
||||||
self.commands = []
|
|
||||||
self.showPlots = False
|
|
||||||
self.showConsoleOutput = False
|
|
||||||
self.useRasterPackage = True
|
|
||||||
self.passFileNames = False
|
|
||||||
self.verboseCommands = []
|
|
||||||
ender = 0
|
|
||||||
line = next(lines).strip('\n').strip('\r')
|
|
||||||
while ender < 10:
|
|
||||||
if line.startswith('##'):
|
|
||||||
try:
|
|
||||||
self.processParameterLine(line)
|
|
||||||
except Exception:
|
|
||||||
raise WrongScriptException(
|
|
||||||
self.tr('Could not load R script: {0}.\n Problem with line {1}').format(self.descriptionFile, line))
|
|
||||||
elif line.startswith('>'):
|
|
||||||
self.commands.append(line[1:])
|
|
||||||
self.verboseCommands.append(line[1:])
|
|
||||||
if not self.showConsoleOutput:
|
|
||||||
self.addOutput(OutputHTML(RAlgorithm.R_CONSOLE_OUTPUT,
|
|
||||||
self.tr('R Console Output')))
|
|
||||||
self.showConsoleOutput = True
|
|
||||||
else:
|
|
||||||
if line == '':
|
|
||||||
ender += 1
|
|
||||||
else:
|
|
||||||
ender = 0
|
|
||||||
self.commands.append(line)
|
|
||||||
self.script += line + '\n'
|
|
||||||
try:
|
|
||||||
line = next(lines).strip('\n').strip('\r')
|
|
||||||
except:
|
|
||||||
break
|
|
||||||
|
|
||||||
def getVerboseCommands(self):
|
|
||||||
return self.verboseCommands
|
|
||||||
|
|
||||||
def createDescriptiveName(self, s):
|
|
||||||
return s.replace('_', ' ')
|
|
||||||
|
|
||||||
def processParameterLine(self, line):
|
|
||||||
param = None
|
|
||||||
line = line.replace('#', '')
|
|
||||||
if line.lower().strip().startswith('showplots'):
|
|
||||||
self.showPlots = True
|
|
||||||
self.addOutput(OutputHTML(RAlgorithm.RPLOTS, 'R Plots'))
|
|
||||||
return
|
|
||||||
if line.lower().strip().startswith('dontuserasterpackage'):
|
|
||||||
self.useRasterPackage = False
|
|
||||||
return
|
|
||||||
if line.lower().strip().startswith('passfilenames'):
|
|
||||||
self.passFileNames = True
|
|
||||||
return
|
|
||||||
tokens = line.split('=')
|
|
||||||
desc = self.createDescriptiveName(tokens[0])
|
|
||||||
if tokens[1].lower().strip() == 'group':
|
|
||||||
self._group = tokens[0]
|
|
||||||
return
|
|
||||||
if tokens[1].lower().strip() == 'name':
|
|
||||||
self._name = self._display_name = tokens[0]
|
|
||||||
self._name = self._name.lower()
|
|
||||||
validChars = \
|
|
||||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:'
|
|
||||||
self._name = ''.join(c for c in self._name if c in validChars)
|
|
||||||
return
|
|
||||||
|
|
||||||
out = getOutputFromString(line)
|
|
||||||
if out is None:
|
|
||||||
param = getParameterFromString(line)
|
|
||||||
|
|
||||||
if param is not None:
|
|
||||||
self.addParameter(param)
|
|
||||||
elif out is not None:
|
|
||||||
out.name = tokens[0]
|
|
||||||
out.description = desc
|
|
||||||
self.addOutput(out)
|
|
||||||
else:
|
|
||||||
raise WrongScriptException(
|
|
||||||
self.tr('Could not load script: {0}.\n'
|
|
||||||
'Problem with line "{1}"', 'ScriptAlgorithm').format(self.descriptionFile or '', line))
|
|
||||||
|
|
||||||
raise WrongScriptException(
|
|
||||||
self.tr('Could not load R script: {0}.\n Problem with line {1}').format(self.descriptionFile, line))
|
|
||||||
|
|
||||||
def processAlgorithm(self, context, feedback):
|
|
||||||
if isWindows():
|
|
||||||
path = RUtils.RFolder()
|
|
||||||
if path == '':
|
|
||||||
raise GeoAlgorithmExecutionException(
|
|
||||||
self.tr('R folder is not configured.\nPlease configure it '
|
|
||||||
'before running R scripts.'))
|
|
||||||
loglines = []
|
|
||||||
loglines.append(self.tr('R execution commands'))
|
|
||||||
loglines += self.getFullSetOfRCommands()
|
|
||||||
for line in loglines:
|
|
||||||
feedback.pushCommandInfo(line)
|
|
||||||
QgsMessageLog.logMessage(loglines, self.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
RUtils.executeRAlgorithm(self, feedback)
|
|
||||||
if self.showPlots:
|
|
||||||
htmlfilename = self.getOutputValue(RAlgorithm.RPLOTS)
|
|
||||||
with open(htmlfilename, 'w') as f:
|
|
||||||
f.write('<html><img src="' + self.plotsFilename + '"/></html>')
|
|
||||||
if self.showConsoleOutput:
|
|
||||||
htmlfilename = self.getOutputValue(RAlgorithm.R_CONSOLE_OUTPUT)
|
|
||||||
with open(htmlfilename, 'w') as f:
|
|
||||||
f.write(RUtils.getConsoleOutput())
|
|
||||||
|
|
||||||
def getFullSetOfRCommands(self):
|
|
||||||
commands = []
|
|
||||||
commands += self.getImportCommands()
|
|
||||||
commands += self.getRCommands()
|
|
||||||
commands += self.getExportCommands()
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def getExportCommands(self):
|
|
||||||
commands = []
|
|
||||||
for out in self.outputs:
|
|
||||||
if isinstance(out, OutputRaster):
|
|
||||||
value = out.value
|
|
||||||
value = value.replace('\\', '/')
|
|
||||||
if self.useRasterPackage or self.passFileNames:
|
|
||||||
commands.append('writeRaster(' + out.name + ',"' + value +
|
|
||||||
'", overwrite=TRUE)')
|
|
||||||
else:
|
|
||||||
if not value.endswith('tif'):
|
|
||||||
value = value + '.tif'
|
|
||||||
commands.append('writeGDAL(' + out.name + ',"' + value +
|
|
||||||
'")')
|
|
||||||
elif isinstance(out, OutputVector):
|
|
||||||
value = out.value
|
|
||||||
if not value.endswith('shp'):
|
|
||||||
value = value + '.shp'
|
|
||||||
value = value.replace('\\', '/')
|
|
||||||
filename = os.path.basename(value)
|
|
||||||
filename = filename[:-4]
|
|
||||||
commands.append('writeOGR(' + out.name + ',"' + value + '","' +
|
|
||||||
filename + '", driver="ESRI Shapefile")')
|
|
||||||
elif isinstance(out, OutputTable):
|
|
||||||
value = out.value
|
|
||||||
value = value.replace('\\', '/')
|
|
||||||
commands.append('write.csv(' + out.name + ',"' + value + '")')
|
|
||||||
|
|
||||||
if self.showPlots:
|
|
||||||
commands.append('dev.off()')
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def getImportCommands(self):
|
|
||||||
commands = []
|
|
||||||
|
|
||||||
# Just use main mirror
|
|
||||||
commands.append('options("repos"="http://cran.at.r-project.org/")')
|
|
||||||
|
|
||||||
# Try to install packages if needed
|
|
||||||
if isWindows():
|
|
||||||
commands.append('.libPaths(\"' + str(RUtils.RLibs()).replace('\\', '/') + '\")')
|
|
||||||
packages = RUtils.getRequiredPackages(self.script)
|
|
||||||
packages.extend(['rgdal', 'raster'])
|
|
||||||
for p in packages:
|
|
||||||
commands.append('tryCatch(find.package("' + p +
|
|
||||||
'"), error=function(e) install.packages("' + p +
|
|
||||||
'", dependencies=TRUE))')
|
|
||||||
commands.append('library("raster")')
|
|
||||||
commands.append('library("rgdal")')
|
|
||||||
|
|
||||||
for param in self.parameters:
|
|
||||||
if isinstance(param, ParameterRaster):
|
|
||||||
if param.value is None:
|
|
||||||
commands.append(param.name + '= NULL')
|
|
||||||
else:
|
|
||||||
value = param.value
|
|
||||||
value = value.replace('\\', '/')
|
|
||||||
if self.passFileNames:
|
|
||||||
commands.append(param.name + ' = "' + value + '"')
|
|
||||||
elif self.useRasterPackage:
|
|
||||||
commands.append(param.name + ' = ' + 'brick("' + value + '")')
|
|
||||||
else:
|
|
||||||
commands.append(param.name + ' = ' + 'readGDAL("' + value + '")')
|
|
||||||
elif isinstance(param, ParameterVector):
|
|
||||||
if param.value is None:
|
|
||||||
commands.append(param.name + '= NULL')
|
|
||||||
else:
|
|
||||||
value = param.getSafeExportedLayer()
|
|
||||||
value = value.replace('\\', '/')
|
|
||||||
filename = os.path.basename(value)
|
|
||||||
filename = filename[:-4]
|
|
||||||
folder = os.path.dirname(value)
|
|
||||||
if self.passFileNames:
|
|
||||||
commands.append(param.name + ' = "' + value + '"')
|
|
||||||
else:
|
|
||||||
commands.append(param.name + ' = readOGR("' + folder +
|
|
||||||
'",layer="' + filename + '")')
|
|
||||||
elif isinstance(param, ParameterTable):
|
|
||||||
if param.value is None:
|
|
||||||
commands.append(param.name + '= NULL')
|
|
||||||
else:
|
|
||||||
value = param.value
|
|
||||||
if not value.lower().endswith('csv'):
|
|
||||||
raise GeoAlgorithmExecutionException(
|
|
||||||
'Unsupported input file format.\n' + value)
|
|
||||||
if self.passFileNames:
|
|
||||||
commands.append(param.name + ' = "' + value + '"')
|
|
||||||
else:
|
|
||||||
commands.append(param.name + ' <- read.csv("' + value +
|
|
||||||
'", head=TRUE, sep=",")')
|
|
||||||
elif isinstance(param, ParameterExtent):
|
|
||||||
if param.value:
|
|
||||||
tokens = str(param.value).split(',')
|
|
||||||
# Extent from raster package is "xmin, xmax, ymin, ymax" like in Processing
|
|
||||||
# http://www.inside-r.org/packages/cran/raster/docs/Extent
|
|
||||||
commands.append(param.name + ' = extent(' + tokens[0] + ',' + tokens[1] + ',' + tokens[2] + ',' + tokens[3] + ')')
|
|
||||||
else:
|
|
||||||
commands.append(param.name + ' = NULL')
|
|
||||||
elif isinstance(param, ParameterCrs):
|
|
||||||
if param.value is None:
|
|
||||||
commands.append(param.name + '= NULL')
|
|
||||||
else:
|
|
||||||
commands.append(param.name + ' = "' + param.value + '"')
|
|
||||||
elif isinstance(param, (ParameterTableField, ParameterString, ParameterFile)):
|
|
||||||
if param.value is None:
|
|
||||||
commands.append(param.name + '= NULL')
|
|
||||||
else:
|
|
||||||
commands.append(param.name + '="' + param.value + '"')
|
|
||||||
elif isinstance(param, (ParameterNumber, ParameterSelection)):
|
|
||||||
if param.value is None:
|
|
||||||
commands.append(param.name + '= NULL')
|
|
||||||
else:
|
|
||||||
commands.append(param.name + '=' + str(param.value))
|
|
||||||
elif isinstance(param, ParameterBoolean):
|
|
||||||
if param.value:
|
|
||||||
commands.append(param.name + '=TRUE')
|
|
||||||
else:
|
|
||||||
commands.append(param.name + '=FALSE')
|
|
||||||
elif isinstance(param, ParameterMultipleInput):
|
|
||||||
iLayer = 0
|
|
||||||
if param.datatype == dataobjects.TYPE_RASTER:
|
|
||||||
layers = param.value.split(';')
|
|
||||||
for layer in layers:
|
|
||||||
layer = layer.replace('\\', '/')
|
|
||||||
if self.passFileNames:
|
|
||||||
commands.append('tempvar' + str(iLayer) + ' <- "' +
|
|
||||||
layer + '"')
|
|
||||||
elif self.useRasterPackage:
|
|
||||||
commands.append('tempvar' + str(iLayer) + ' <- ' +
|
|
||||||
'brick("' + layer + '")')
|
|
||||||
else:
|
|
||||||
commands.append('tempvar' + str(iLayer) + ' <- ' +
|
|
||||||
'readGDAL("' + layer + '")')
|
|
||||||
iLayer += 1
|
|
||||||
else:
|
|
||||||
exported = param.getSafeExportedLayers()
|
|
||||||
layers = exported.split(';')
|
|
||||||
for layer in layers:
|
|
||||||
if not layer.lower().endswith('shp') \
|
|
||||||
and not self.passFileNames:
|
|
||||||
raise GeoAlgorithmExecutionException(
|
|
||||||
'Unsupported input file format.\n' + layer)
|
|
||||||
layer = layer.replace('\\', '/')
|
|
||||||
filename = os.path.basename(layer)
|
|
||||||
filename = filename[:-4]
|
|
||||||
if self.passFileNames:
|
|
||||||
commands.append('tempvar' + str(iLayer) + ' <- "' +
|
|
||||||
layer + '"')
|
|
||||||
else:
|
|
||||||
commands.append('tempvar' + str(iLayer) + ' <- ' +
|
|
||||||
'readOGR("' + layer + '",layer="' +
|
|
||||||
filename + '")')
|
|
||||||
iLayer += 1
|
|
||||||
s = ''
|
|
||||||
s += param.name
|
|
||||||
s += ' = c('
|
|
||||||
iLayer = 0
|
|
||||||
for layer in layers:
|
|
||||||
if iLayer != 0:
|
|
||||||
s += ','
|
|
||||||
s += 'tempvar' + str(iLayer)
|
|
||||||
iLayer += 1
|
|
||||||
s += ')\n'
|
|
||||||
commands.append(s)
|
|
||||||
|
|
||||||
if self.showPlots:
|
|
||||||
htmlfilename = self.getOutputValue(RAlgorithm.RPLOTS)
|
|
||||||
self.plotsFilename = htmlfilename + '.png'
|
|
||||||
self.plotsFilename = self.plotsFilename.replace('\\', '/')
|
|
||||||
commands.append('png("' + self.plotsFilename + '")')
|
|
||||||
|
|
||||||
return commands
|
|
||||||
|
|
||||||
def getRCommands(self):
|
|
||||||
return self.commands
|
|
||||||
|
|
||||||
def help(self):
|
|
||||||
helpfile = str(self.descriptionFile) + '.help'
|
|
||||||
if os.path.exists(helpfile):
|
|
||||||
return True, getHtmlFromHelpFile(self, helpfile)
|
|
||||||
else:
|
|
||||||
return False, None
|
|
||||||
|
|
||||||
def shortHelp(self):
|
|
||||||
if self.descriptionFile is None:
|
|
||||||
return None
|
|
||||||
helpFile = str(self.descriptionFile) + '.help'
|
|
||||||
if os.path.exists(helpFile):
|
|
||||||
with open(helpFile) as f:
|
|
||||||
try:
|
|
||||||
descriptions = json.load(f)
|
|
||||||
if 'ALG_DESC' in descriptions:
|
|
||||||
return self._formatHelp(str(descriptions['ALG_DESC']))
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getParameterDescriptions(self):
|
|
||||||
descs = {}
|
|
||||||
if self.descriptionFile is None:
|
|
||||||
return descs
|
|
||||||
helpFile = str(self.descriptionFile) + '.help'
|
|
||||||
if os.path.exists(helpFile):
|
|
||||||
with open(helpFile) as f:
|
|
||||||
try:
|
|
||||||
descriptions = json.load(f)
|
|
||||||
for param in self.parameters:
|
|
||||||
if param.name in descriptions:
|
|
||||||
descs[param.name] = str(descriptions[param.name])
|
|
||||||
except:
|
|
||||||
return descs
|
|
||||||
return descs
|
|
||||||
|
|
||||||
def checkBeforeOpeningParametersDialog(self):
|
|
||||||
msg = RUtils.checkRIsInstalled()
|
|
||||||
if msg is not None:
|
|
||||||
html = self.tr(
|
|
||||||
'<p>This algorithm requires R to be run. Unfortunately it '
|
|
||||||
'seems that R is not installed in your system or it is not '
|
|
||||||
'correctly configured to be used from QGIS</p>'
|
|
||||||
'<p><a href="http://docs.qgis.org/testing/en/docs/user_manual/processing/3rdParty.html">Click here</a> '
|
|
||||||
'to know more about how to install and configure R to be used with QGIS</p>')
|
|
||||||
return html
|
|
@ -1,153 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
***************************************************************************
|
|
||||||
RAlgorithmProvider.py
|
|
||||||
---------------------
|
|
||||||
Date : August 2012
|
|
||||||
Copyright : (C) 2012 by Victor Olaya
|
|
||||||
Email : volayaf at gmail dot com
|
|
||||||
***************************************************************************
|
|
||||||
* *
|
|
||||||
* 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. *
|
|
||||||
* *
|
|
||||||
***************************************************************************
|
|
||||||
"""
|
|
||||||
from builtins import str
|
|
||||||
|
|
||||||
__author__ = 'Victor Olaya'
|
|
||||||
__date__ = 'August 2012'
|
|
||||||
__copyright__ = '(C) 2012, Victor Olaya'
|
|
||||||
|
|
||||||
# This will get replaced with a git SHA1 when you do a git archive
|
|
||||||
|
|
||||||
__revision__ = '$Format:%H$'
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from qgis.PyQt.QtCore import QCoreApplication
|
|
||||||
from qgis.core import (QgsApplication,
|
|
||||||
QgsProcessingProvider,
|
|
||||||
QgsProcessingUtils,
|
|
||||||
QgsMessageLog)
|
|
||||||
from processing.core.ProcessingConfig import ProcessingConfig, Setting
|
|
||||||
from processing.gui.EditScriptAction import EditScriptAction
|
|
||||||
from processing.gui.DeleteScriptAction import DeleteScriptAction
|
|
||||||
from processing.gui.CreateNewScriptAction import CreateNewScriptAction
|
|
||||||
from processing.script.WrongScriptException import WrongScriptException
|
|
||||||
from processing.gui.GetScriptsAndModels import GetRScriptsAction
|
|
||||||
from processing.gui.ProviderActions import (ProviderActions,
|
|
||||||
ProviderContextMenuActions)
|
|
||||||
from processing.tools.system import isWindows
|
|
||||||
|
|
||||||
from .RUtils import RUtils
|
|
||||||
from .RAlgorithm import RAlgorithm
|
|
||||||
|
|
||||||
pluginPath = os.path.normpath(os.path.join(
|
|
||||||
os.path.split(os.path.dirname(__file__))[0], os.pardir))
|
|
||||||
|
|
||||||
|
|
||||||
class RAlgorithmProvider(QgsProcessingProvider):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self.algs = []
|
|
||||||
self.actions = []
|
|
||||||
self.actions.append(CreateNewScriptAction(
|
|
||||||
'Create new R script', CreateNewScriptAction.SCRIPT_R))
|
|
||||||
self.actions.append(GetRScriptsAction())
|
|
||||||
self.contextMenuActions = \
|
|
||||||
[EditScriptAction(EditScriptAction.SCRIPT_R),
|
|
||||||
DeleteScriptAction(DeleteScriptAction.SCRIPT_R)]
|
|
||||||
|
|
||||||
def load(self):
|
|
||||||
ProcessingConfig.settingIcons[self.name()] = self.icon()
|
|
||||||
ProcessingConfig.addSetting(Setting(self.name(), 'ACTIVATE_R',
|
|
||||||
self.tr('Activate'), False))
|
|
||||||
ProcessingConfig.addSetting(Setting(
|
|
||||||
self.name(), RUtils.RSCRIPTS_FOLDER,
|
|
||||||
self.tr('R Scripts folder'), RUtils.defaultRScriptsFolder(),
|
|
||||||
valuetype=Setting.MULTIPLE_FOLDERS))
|
|
||||||
if isWindows():
|
|
||||||
ProcessingConfig.addSetting(Setting(
|
|
||||||
self.name(),
|
|
||||||
RUtils.R_FOLDER, self.tr('R folder'), RUtils.RFolder(),
|
|
||||||
valuetype=Setting.FOLDER))
|
|
||||||
ProcessingConfig.addSetting(Setting(
|
|
||||||
self.name(),
|
|
||||||
RUtils.R_LIBS_USER, self.tr('R user library folder'),
|
|
||||||
RUtils.RLibs(), valuetype=Setting.FOLDER))
|
|
||||||
ProcessingConfig.addSetting(Setting(
|
|
||||||
self.name(),
|
|
||||||
RUtils.R_USE64, self.tr('Use 64 bit version'), False))
|
|
||||||
ProviderActions.registerProviderActions(self, self.actions)
|
|
||||||
ProviderContextMenuActions.registerProviderContextMenuActions(self.contextMenuActions)
|
|
||||||
ProcessingConfig.readSettings()
|
|
||||||
self.refreshAlgorithms()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def unload(self):
|
|
||||||
ProcessingConfig.removeSetting('ACTIVATE_R')
|
|
||||||
ProcessingConfig.removeSetting(RUtils.RSCRIPTS_FOLDER)
|
|
||||||
if isWindows():
|
|
||||||
ProcessingConfig.removeSetting(RUtils.R_FOLDER)
|
|
||||||
ProcessingConfig.removeSetting(RUtils.R_LIBS_USER)
|
|
||||||
ProcessingConfig.removeSetting(RUtils.R_USE64)
|
|
||||||
ProviderActions.deregisterProviderActions(self)
|
|
||||||
ProviderContextMenuActions.deregisterProviderContextMenuActions(self.contextMenuActions)
|
|
||||||
|
|
||||||
def isActive(self):
|
|
||||||
return ProcessingConfig.getSetting('ACTIVATE_R')
|
|
||||||
|
|
||||||
def setActive(self, active):
|
|
||||||
ProcessingConfig.setSettingValue('ACTIVATE_R', active)
|
|
||||||
|
|
||||||
def icon(self):
|
|
||||||
return QgsApplication.getThemeIcon("/providerR.svg")
|
|
||||||
|
|
||||||
def svgIconPath(self):
|
|
||||||
return QgsApplication.iconPath("providerR.svg")
|
|
||||||
|
|
||||||
def name(self):
|
|
||||||
return 'R scripts'
|
|
||||||
|
|
||||||
def id(self):
|
|
||||||
return 'r'
|
|
||||||
|
|
||||||
def loadAlgorithms(self):
|
|
||||||
folders = RUtils.RScriptsFolders()
|
|
||||||
self.algs = []
|
|
||||||
for f in folders:
|
|
||||||
self.loadFromFolder(f)
|
|
||||||
|
|
||||||
folder = os.path.join(os.path.dirname(__file__), 'scripts')
|
|
||||||
self.loadFromFolder(folder)
|
|
||||||
for a in self.algs:
|
|
||||||
self.addAlgorithm(a)
|
|
||||||
|
|
||||||
def loadFromFolder(self, folder):
|
|
||||||
if not os.path.exists(folder):
|
|
||||||
return
|
|
||||||
for path, subdirs, files in os.walk(folder):
|
|
||||||
for descriptionFile in files:
|
|
||||||
if descriptionFile.endswith('rsx'):
|
|
||||||
try:
|
|
||||||
fullpath = os.path.join(path, descriptionFile)
|
|
||||||
alg = RAlgorithm(fullpath)
|
|
||||||
if alg.name().strip() != '':
|
|
||||||
self.algs.append(alg)
|
|
||||||
except WrongScriptException as e:
|
|
||||||
QgsMessageLog.logMessage(e.msg, self.tr('Processing'), QgsMessageLog.CRITICAL)
|
|
||||||
except Exception as e:
|
|
||||||
QgsMessageLog.logMessage(
|
|
||||||
self.tr('Could not load R script: {0}\n{1}').format(descriptionFile, str(e)),
|
|
||||||
self.tr('Processing'), QgsMessageLog.CRITICAL)
|
|
||||||
return
|
|
||||||
|
|
||||||
def tr(self, string, context=''):
|
|
||||||
if context == '':
|
|
||||||
context = 'RAlgorithmProvider'
|
|
||||||
return QCoreApplication.translate(context, string)
|
|
@ -1,245 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
***************************************************************************
|
|
||||||
RUtils.py
|
|
||||||
---------------------
|
|
||||||
Date : August 2012
|
|
||||||
Copyright : (C) 2012 by Victor Olaya
|
|
||||||
Email : volayaf at gmail dot com
|
|
||||||
***************************************************************************
|
|
||||||
* *
|
|
||||||
* 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. *
|
|
||||||
* *
|
|
||||||
***************************************************************************
|
|
||||||
"""
|
|
||||||
from builtins import str
|
|
||||||
from builtins import object
|
|
||||||
|
|
||||||
__author__ = 'Victor Olaya'
|
|
||||||
__date__ = 'August 2012'
|
|
||||||
__copyright__ = '(C) 2012, Victor Olaya'
|
|
||||||
|
|
||||||
# This will get replaced with a git SHA1 when you do a git archive
|
|
||||||
|
|
||||||
__revision__ = '$Format:%H$'
|
|
||||||
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import stat
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from qgis.PyQt.QtCore import QCoreApplication
|
|
||||||
from qgis.core import (QgsSettings,
|
|
||||||
QgsProcessingUtils,
|
|
||||||
QgsMessageLog)
|
|
||||||
from processing.core.ProcessingConfig import ProcessingConfig
|
|
||||||
from processing.tools.system import userFolder, isWindows, mkdir, getTempFilenameInTempFolder
|
|
||||||
|
|
||||||
|
|
||||||
class RUtils(object):
|
|
||||||
|
|
||||||
RSCRIPTS_FOLDER = 'R_SCRIPTS_FOLDER'
|
|
||||||
R_FOLDER = 'R_FOLDER'
|
|
||||||
R_USE64 = 'R_USE64'
|
|
||||||
R_LIBS_USER = 'R_LIBS_USER'
|
|
||||||
|
|
||||||
rscriptfilename = os.path.join(userFolder(), 'processing_script.r')
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def RFolder():
|
|
||||||
folder = ProcessingConfig.getSetting(RUtils.R_FOLDER)
|
|
||||||
if folder is None:
|
|
||||||
if isWindows():
|
|
||||||
if 'ProgramW6432' in list(os.environ.keys()) and os.path.isdir(os.path.join(os.environ['ProgramW6432'], 'R')):
|
|
||||||
testfolder = os.path.join(os.environ['ProgramW6432'], 'R')
|
|
||||||
elif 'PROGRAMFILES(x86)' in list(os.environ.keys()) and os.path.isdir(os.path.join(os.environ['PROGRAMFILES(x86)'], 'R')):
|
|
||||||
testfolder = os.path.join(os.environ['PROGRAMFILES(x86)'], 'R')
|
|
||||||
elif 'PROGRAMFILES' in list(os.environ.keys()) and os.path.isdir(os.path.join(os.environ['PROGRAMFILES'], 'R')):
|
|
||||||
testfolder = os.path.join(os.environ['PROGRAMFILES'], 'R')
|
|
||||||
else:
|
|
||||||
testfolder = 'C:\\R'
|
|
||||||
|
|
||||||
if os.path.isdir(testfolder):
|
|
||||||
subfolders = os.listdir(testfolder)
|
|
||||||
subfolders.sort(reverse=True)
|
|
||||||
for subfolder in subfolders:
|
|
||||||
if subfolder.startswith('R-'):
|
|
||||||
folder = os.path.join(testfolder, subfolder)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
folder = ''
|
|
||||||
else:
|
|
||||||
folder = ''
|
|
||||||
|
|
||||||
return os.path.abspath(str(folder))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def RLibs():
|
|
||||||
folder = ProcessingConfig.getSetting(RUtils.R_LIBS_USER)
|
|
||||||
if folder is None:
|
|
||||||
folder = str(os.path.join(userFolder(), 'rlibs'))
|
|
||||||
try:
|
|
||||||
mkdir(folder)
|
|
||||||
except:
|
|
||||||
folder = str(os.path.join(userFolder(), 'rlibs'))
|
|
||||||
mkdir(folder)
|
|
||||||
return os.path.abspath(str(folder))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def defaultRScriptsFolder():
|
|
||||||
folder = str(os.path.join(userFolder(), 'rscripts'))
|
|
||||||
mkdir(folder)
|
|
||||||
return os.path.abspath(folder)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def RScriptsFolders():
|
|
||||||
folder = ProcessingConfig.getSetting(RUtils.RSCRIPTS_FOLDER)
|
|
||||||
if folder is not None:
|
|
||||||
return folder.split(';')
|
|
||||||
else:
|
|
||||||
return [RUtils.defaultRScriptsFolder()]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def createRScriptFromRCommands(commands):
|
|
||||||
with open(RUtils.getRScriptFilename(), 'w') as scriptfile:
|
|
||||||
for command in commands:
|
|
||||||
scriptfile.write(command + '\n')
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getRScriptFilename():
|
|
||||||
return RUtils.rscriptfilename
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getConsoleOutputFilename():
|
|
||||||
return RUtils.getRScriptFilename() + '.Rout'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def executeRAlgorithm(alg, feedback):
|
|
||||||
# generate new R script file name in a temp folder
|
|
||||||
RUtils.rscriptfilename = getTempFilenameInTempFolder('processing_script.r')
|
|
||||||
# run commands
|
|
||||||
RUtils.verboseCommands = alg.getVerboseCommands()
|
|
||||||
RUtils.createRScriptFromRCommands(alg.getFullSetOfRCommands())
|
|
||||||
if isWindows():
|
|
||||||
if ProcessingConfig.getSetting(RUtils.R_USE64):
|
|
||||||
execDir = 'x64'
|
|
||||||
else:
|
|
||||||
execDir = 'i386'
|
|
||||||
command = [
|
|
||||||
os.path.join(RUtils.RFolder(), 'bin', execDir, 'R.exe'),
|
|
||||||
'CMD',
|
|
||||||
'BATCH',
|
|
||||||
'--vanilla',
|
|
||||||
RUtils.getRScriptFilename(),
|
|
||||||
RUtils.getConsoleOutputFilename()
|
|
||||||
]
|
|
||||||
|
|
||||||
else:
|
|
||||||
os.chmod(RUtils.getRScriptFilename(), stat.S_IEXEC | stat.S_IREAD |
|
|
||||||
stat.S_IWRITE)
|
|
||||||
command = 'R CMD BATCH --vanilla ' + RUtils.getRScriptFilename() \
|
|
||||||
+ ' ' + RUtils.getConsoleOutputFilename()
|
|
||||||
|
|
||||||
proc = subprocess.Popen(
|
|
||||||
command,
|
|
||||||
shell=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stdin=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.STDOUT,
|
|
||||||
universal_newlines=True,
|
|
||||||
)
|
|
||||||
proc.wait()
|
|
||||||
RUtils.createConsoleOutput()
|
|
||||||
loglines = []
|
|
||||||
loglines.append(RUtils.tr('R execution console output'))
|
|
||||||
loglines += RUtils.allConsoleResults
|
|
||||||
for line in loglines:
|
|
||||||
feedback.pushConsoleInfo(line)
|
|
||||||
QgsMessageLog.logMessage(loglines, RUtils.tr('Processing'), QgsMessageLog.INFO)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def createConsoleOutput():
|
|
||||||
RUtils.consoleResults = []
|
|
||||||
RUtils.allConsoleResults = []
|
|
||||||
add = False
|
|
||||||
if os.path.exists(RUtils.getConsoleOutputFilename()):
|
|
||||||
with open(RUtils.getConsoleOutputFilename()) as lines:
|
|
||||||
for line in lines:
|
|
||||||
line = line.strip().strip(' ')
|
|
||||||
if line.startswith('>'):
|
|
||||||
line = line[1:].strip(' ')
|
|
||||||
if line in RUtils.verboseCommands:
|
|
||||||
add = True
|
|
||||||
else:
|
|
||||||
add = False
|
|
||||||
elif add:
|
|
||||||
RUtils.consoleResults.append('<p>' + line + '</p>\n')
|
|
||||||
RUtils.allConsoleResults.append(line)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getConsoleOutput():
|
|
||||||
s = '<font face="courier">\n'
|
|
||||||
s += RUtils.tr('<h2>R Output</h2>\n')
|
|
||||||
for line in RUtils.consoleResults:
|
|
||||||
s += line
|
|
||||||
s += '</font>\n'
|
|
||||||
|
|
||||||
return s
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def checkRIsInstalled(ignoreRegistrySettings=False):
|
|
||||||
if isWindows():
|
|
||||||
path = RUtils.RFolder()
|
|
||||||
if path == '':
|
|
||||||
return RUtils.tr('R folder is not configured.\nPlease configure '
|
|
||||||
'it before running R scripts.')
|
|
||||||
|
|
||||||
R_INSTALLED = 'R_INSTALLED'
|
|
||||||
settings = QgsSettings()
|
|
||||||
if not ignoreRegistrySettings:
|
|
||||||
if settings.contains(R_INSTALLED):
|
|
||||||
return
|
|
||||||
if isWindows():
|
|
||||||
if ProcessingConfig.getSetting(RUtils.R_USE64):
|
|
||||||
execDir = 'x64'
|
|
||||||
else:
|
|
||||||
execDir = 'i386'
|
|
||||||
command = [os.path.join(RUtils.RFolder(), 'bin', execDir, 'R.exe'), '--version']
|
|
||||||
else:
|
|
||||||
command = ['R --version']
|
|
||||||
proc = subprocess.Popen(
|
|
||||||
command,
|
|
||||||
shell=True,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stdin=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.STDOUT,
|
|
||||||
universal_newlines=True,
|
|
||||||
).stdout
|
|
||||||
|
|
||||||
for line in iter(proc.readline, ''):
|
|
||||||
if 'R version' in line:
|
|
||||||
settings.setValue(R_INSTALLED, True)
|
|
||||||
return
|
|
||||||
html = RUtils.tr(
|
|
||||||
'<p>This algorithm requires R to be run. Unfortunately, it '
|
|
||||||
'seems that R is not installed in your system, or it is not '
|
|
||||||
'correctly configured to be used from QGIS</p>'
|
|
||||||
'<p><a href="http://docs.qgis.org/testing/en/docs/user_manual/processing/3rdParty.html">Click here</a> '
|
|
||||||
'to know more about how to install and configure R to be used with QGIS</p>')
|
|
||||||
|
|
||||||
return html
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def getRequiredPackages(code):
|
|
||||||
regex = re.compile('[^#]library\("?(.*?)"?\)')
|
|
||||||
return regex.findall(code)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def tr(string, context=''):
|
|
||||||
if context == '':
|
|
||||||
context = 'RUtils'
|
|
||||||
return QCoreApplication.translate(context, string)
|
|
Loading…
x
Reference in New Issue
Block a user