在上一篇中介绍了sextante的架构,这次主要介绍如何在sextante中自定义算法。sextante自定义算法途径有二:1)编写脚本文件;2)编写算法类。
编写脚本文件
from qgis.core import * from PyQt4.QtCore import * from PyQt4.QtGui import * from sextante.core.QGisLayers import QGisLayers from sextante.core.SextanteVectorWriter import SextanteVectorWriter然后定义输入输出参数,如下:
##[Example scripts]=group ##input=vector ##output=output vector虽然该算法中先导入所需文件再定义参数和输出,实际上这两者的顺序如何毫不影响。脚本文件中定义输入参数和输出有一定的规则,正如前篇所言,也即被模板化了,或者说对象化了,每一种输出都是一种对象。具体参照QGIS 帮助文档Sextante部分。
- raster,栅格文件;
- vector,矢量文件;
- table,表单;
- number,数字,必须提供默认值,例如dept =number 2.4;
- string,字符串,必须提供默认值,例如name=string xiluoduyu;
- boolean,布尔值,可选True或False,例如,verbose=boolean True,默认为False;
- multiple raster,栅格数据集合,即可以同时选择输入多个栅格文件;
- multiple vector,矢量数据集合,即可以同时选择输入多个矢量文件;
- field,矢量数据属性中的字段,在field后面必须跟着矢量文件名,例如若在前面有选择输入矢量文件:myLayer=vector,则可通过myfield=field myLayer来选择矢量数据中的要素;
- folder,文件夹;
- file,文件名。
def createDescriptiveName(self, s): return s.replace("_", " ")
- output raster,输出栅格文件;
- output vector,输出矢量文件;
- output table,输出表格文件,可用于输出矢量数据属性;
- output html,输出html文件,在生成帮助等信息时可用;
- output file,输出文件,格式不定,具体用法未明;
- output number,输出数字;
- output string,输出字符串,最直观的就是用来显示执行信息。
#首先获取输入的矢量文件。输入的文件input只是文件对应的路径, #因而需要通过Sextante.getObjectFromUri()将input转换为QGIS矢量图层对象. #实际上Sextante.getObjectFromUri()只是简单调用了QGisLayers.getObjectFromUri(input)而已。 vectorLayer = QGisLayers.getObjectFromUri(input) #接着创建输出文件,可通过SextanteVectorWriter对象来进行,该对象实为模板化的矢量输出类 provider = vectorLayer.dataProvider() writer = SextanteVectorWriter(output, None, provider.fields(), provider.geometryType(), provider.crs() ) #然后将选择的字段添加到输出文件 selection = vectorLayer.selectedFeatures() for feat in selection: writer.addFeature(feat) del writer
通过脚本文件创建的算法会自动加载到QGIS里面去,在modeler中脚本算法也会得到相应的处理。sextante的开发者们对这已经做了足够多的工作让它好用,不用我们操心。脚本算法执行的效果如下:
def processParameterLine(self,line): param = None out = None line = line.replace("#", ""); ... tokens = line.split("="); desc = self.createDescriptiveName(tokens[0]) if tokens[1].lower().strip() == "group": self.group = tokens[0] return ... elif tokens[1].lower().strip().startswith("number"): tokens[0] = tokens[0].strip() #新增语句,修改是为了剔除参数名部分不必要的空格,解决定义数字参数执行时提示变量未定义的问题 default = tokens[1].strip()[len("number")+1:].strip() param = ParameterNumber(tokens[0], desc, default=default)#tokens[0]为参数名,desc为描述信息,用于显示 return脚本算法的执行过程也是首先生成算法调用命令行字符串,然后调用Python的exec()函数执行有效的script脚本代码,也即是说咱们编写的脚本代码会被当做一行行的python代码进行处理,与平时编写正常的python代码一样,不同之处在于脚本代码中的未定义的参数的值是从“参数字典”这一全局名字空间中获取,源码如下:
def processAlgorithm(self, progress): script = "import sextante\n" #导入sextante文件 #------------生成算法调用命令行字符串--------------# ns = {} ns['progress'] = progress for param in self.parameters: #script += param.name + "=" + param.getValueAsCommandLineParameter() + "\n" ns[param.name] = param.value for out in self.outputs: ns[out.name] = out.value #script += out.name + "=" + out.getValueAsCommandLineParameter() + "\n" script+=self.script #-----------------执行算法----------------# exec(script) in ns #将参数字典作为全局名字空间 #将输出结果保存到算法的输出列表中,这样后面sextante即可自动将算法输出结果加载到QGIS中 for out in self.outputs: out.setValue(ns[out.name])
编写算法类
编写算法类自定义算法,首先得继承GeoAlgorithm基类进行定义,然后定义对应的算法提供者基类AlgorithmProvider的派生类,也可以不定义AlgorithmProvider派生类而只是将算法分到已有的provider中去,但此时得注意算法类中的provider属性必须与其所属的provider的名称name对应,否则会有问题。为了介绍完整的自定义算法类流程,虽然只是定义了一个算法类,我仍然为其定义了对应的provider类。完整的自定义算法需要定义几个必要的文件:- __init__.py,定义该文件主要是为了进行provider加载前的某些操作,但sextante中一般第三方应用程序算法提供者,如OTB、SAGA、Grass等均只是简单的定义了该文件而没有实现任何内容。这也说明,该文件只是sextante的硬性规定,相当于协议而已,目前实际作用不明显;
- [算法类名(Algeorithm)].py,该文件定义了算法类及其参数等属性;
- [算法提供者(AlgorithmProvider)].py,该文件定义了算法提供者类及其相关属性;
- CMakeList.txt,该文件声明需要编译那些文件;
__init__.py
CmakeList.txt
FILE(GLOB PY_FILES *.py) FILE(GLOB DESCR_FILES description/*.txt) FILE(GLOB HELP_FILES help/*.html) PLUGIN_INSTALL(sextante preprocess ${PY_FILES}) PLUGIN_INSTALL(sextante preprocess/description ${DESCR_FILES}) PLUGIN_INSTALL(sextante preprocess/help ${HELP_FILES})
testalg.py (算法基类派生类文件)
# -*- coding: utf-8 -*-'''描述信息,省略...'''from sextante.core.GeoAlgorithm import GeoAlgorithmfrom sextante.outputs.OutputVector import OutputVectorfrom sextante.parameters.ParameterVector import ParameterVectorfrom qgis.core import *from PyQt4.QtCore import *from PyQt4.QtGui import *from sextante.core.QGisLayers import QGisLayers#自定义算法案例class testalg(GeoAlgorithm): '''这是个算法样例,算法实现的是从矢量数据中提取感兴趣的要素。该算法样例的目的主要是向开发者说明在sextante中自定义算法的流程, 开发者可以参照该算法样例定义自己的算法,同时你可以在sextante中像使用其他算法一样使用自己定义的算法,不需要做任何额外的工作。''' #输入参数和输出的常量引用,在算法被第三方算法或QGIS python控制台调用时会用到。 OUTPUT_LAYER = "OUTPUT_LAYER" INPUT_LAYER = "INPUT_LAYER" #************************************************************************# # 必须定义的两个函数: # # 1)defineCharacteristics(self),该函数用于设置算法参数等属性 # # 2)processALgorithm(self,progress),该函数定义算法的执行 # #************************************************************************# def defineCharacteristics(self): '''在此定义算法的输入输出以及其他属性''' #在Toolbox算法树节点显示的算法名 self.name = "Save selected features" #Toolbox算法树中子目录名(组名) self.group = unicode("测试算法",'utf-8') #添加矢量文件输入参数,在此因为该参数是必须的,也即对用户是可见的,因而最后一个参数必须设为False,即hiden=False self.addParameter(ParameterVector(self.INPUT_LAYER, "Input layer", ParameterVector.VECTOR_TYPE_ANY, False)) # 添加矢量文件输出 self.addOutput(OutputVector(self.OUTPUT_LAYER, "Output layer with selected features")) def processAlgorithm(self, progress): '''在此定义算法的执行过程''' #首先获取用户的输入内容 inputFilename = self.getParameterValue(self.INPUT_LAYER) output = self.getOutputFromName(self.OUTPUT_LAYER) #将输入的文件路径转换为QGIS对象 vectorLayer = QGisLayers.getObjectFromUri(inputFilename) #开始执行 #创建输出矢量文件 provider = vectorLayer.dataProvider() writer = output.getVectorWriter( provider.fields(), provider.geometryType(), provider.crs() ) #获取选择的矢量要素数据并添加到结果矢量文件中 features = QGisLayers.features(vectorLayer) total = len(features) i = 0 for feat in features: writer.addFeature(feat) progress.setPercentage(100 * i / float(total)) i += 1 del writer
PreprocessAlgorithmProvider.py(算法提供者基类派生类文件)
# -*- coding: utf-8 -*-'''描述信息,省略...'''import osfrom PyQt4 import QtGuifrom sextante.core.SextanteLog import SextanteLogfrom sextante.core.AlgorithmProvider import AlgorithmProviderfrom sextante.preprocess.testalg import testalgclass PreprocessAlgorithmProvider(AlgorithmProvider):
"""必须重定义以下函数以提供算法提供者派生类的相关信息"""#初始化函数,类似C++中的构造函数 def __init__(self): AlgorithmProvider.__init__(self) self.active = False #可在此设置在配置窗口中显示的算法提供者的相关设置,例如OTB、SAGA的文件夹路径等,默认添加“是否激活”设置, #即配置窗口目录树下Active一项,在此仅添加默认设置。 def initializeSettings(self): AlgorithmProvider.initializeSettings(self) #卸载算法提供者函数,类似C++中的析构函数 def unload(self): AlgorithmProvider.unload(self) #设置/获取算法提供者名称 def getName(self): return "preprocess" #设置/获取算法提供者在Toolbox算法目录树中的节点名称 def getDescription(self): return unicode("自定义预处理算法",'utf-8') #设置对应的算法树节点图标 def getIcon(self): return QtGui.QIcon(os.path.dirname(__file__) + "/../images/preprocess.png") #加载算法提供者下属算法,在此添加自定义的算法testalg def _loadAlgorithms(self): self.alglist = [testalg()] self.algs = self.alglist #设置/获取是否支持非文件类型的结果输出 def supportsNonFileBasedOutput(self): return True 以上定义的几个文件中的函数都是自定义算法流程中不可缺少的,除此之外,我们当然还可以自定义其他函数或者重载基类中可以被重载的函数,具体请参考core文件夹下GeoAlgorithm.py文件中GeoAlgorithm基类的定义。
1、在文件开头引入自定义provider文件:
from sextante.preprocess.PreprocessAlgorithmProvider import PreprocessAlgorithmProvider2、在initialize函数中添加provider:
Sextante.addProvider(PreprocessAlgorithmProvider())至此,大功告成!
【Toolbox显示】 【算法配置信息】