
17792669
1、引言
目前,在笔者所从事的国土行业的大部分GIS管理系统都具有功能类似,业务却不尽相同的特性,为了达到功能上的复用和快速灵活的修改现有程序,加快项目开发进度和效率,必须有稳定的系统框架供项目开发选择。从目前主流的GIS二次开发平台来看,在开发桌面端GIS系统的时候,系统的框架大部分是相同的,这就更需要我们在实际开发中,有一个于GIS开发平台之外的系统架构,来满足不同项目对GIS平台的选择。另外,对于不同的系统目标来讲,用户的操作模式可能也不尽相同。例如,对于侧重数据编辑的系统来讲,注重的是系统编辑方式的便捷性;对于注重数据处理业务的系统来讲,注重的是业务逻辑之间的衔接关系。本文结合作者的实际工作经验,对插件式GIS系统框架设计进行了探讨。
本文首先介绍了插件的原理和实现方法,接着讲述了框架设计的原则和系统架构设计,最后是总结。
2、插件原理及实现
任何一个程序都是由不同的功能模块所组成的,一般在GIS软件中,软件许可都是按照模块来进行定制和收费的。在一些GIS软件及类似软件中,都允许添加用户自定义功能到软件中。在实现的方式上,有些就应用了插件技术,例如ArcGIS软件允许加载自定义的功能动态库(dll文件),并拖放到ArcGIS软件的工具栏上,这就是一种插件技术的应用。插件,既可以是功能按钮,也可以是功能按钮组成的工具栏,甚至可以是自定义的控件。
2.1插件原理
插件是一种遵循统一预定义接口规范编写出来的程序,应用程序在运行时通过接口规范对插件进行调用,以扩展应用程序的功能。插件最典型的例子就是ActiveX控件和COM组件,对插件进行接口管理和调用的应用程序一般称之为宿主程序。不同的软件对自己的功能插件的定义都不尽相同,但原理都一样。正是基于这一点,我们可以在GIS应用程序的开发中定义自己插件。在.NET开发环境下,可以利用反射机制来读取封装在功能模块中的插件。
2.2插件实现
要想在应用程序中实现插件功能,必须解决以下三个问题。第一,插件接口规范的定义,即定义插件;第二,实现插件的加载/卸载,即管理插件;第三,实现插件功能的调用,即调用插件。
1、插件的定义
在实际编码时,可以将插件用接口(interface)来实现,并定义插件规范所必须的一些属性和方法。以下是定义代码:
public interface IPlugCommand
{
//插件功能组名称,相当于工具栏标题
string CatalogName { get; }
//插件功能名称,相当于功能按钮名称
string Name { get; }
//功能唯一标识码
string GUID { get; }
//位图,用于按钮图标的显示
Bitmap Bitmap { get; }
//插件功能描述,相当于功能按钮提示符
string Describe { get; }
//执行插件功能
void Excute();
//插件功能是否满足执行的条件
bool Enable();
///插件功能是否执行完毕
bool Finish();
}
2、插件管理
(1)插件功能组定义
public interface IPlugCommandGroup
{
//插件组名称,相当于工具栏名称
string Name { get; }
//插件功能个数
int CommandCount { get; }
//插件路径
string PathName { get; }
//按序号获取插件
IPlugCommand GetCommandAt(int index);
/// 根据插件唯一标识guid查找插件
IPlugCommand FindCommandByGUID(string guid);
/// 根据插件名称查找插件
IPlugCommand FindCommandByCommandName(string commandName);
/// 根据插件唯一标识guid值删除命令
bool RemoveCommandByGUID(string guid);
/// 卸载所有插件
void RemoveAllCommand();
// 添加插件
bool AddCommand(IPlugCommand command);
/// 装载插件
bool LoadCommand(string strPath);
}
(2)插件管理器定义
public interface IPlugCommandManager
{
///功能组个数
int GroupCount { get; }
///添加功能组
bool AddGroupCommand(IPlugCommandGroup commandGroup);
///卸载功能组
bool RemoveGroupCommand(string groupName);
///根据功能组序号获取功能组
IPlugCommandGroup GetCommandGroupAt(int index);
/// 根据功能组名称查找功能组
IPlugCommandGroup FindGroupCommand(string groupName);
/// 根据插件唯一guid值查找插件
IPlugCommand FindCommandByGUID(string guis);
/// 从配置文件读取要加载的插件路径并加载插件
bool LoadFromXMLFile(string strFileName);
/// 将插件路径写入配置文件
bool WriteToXMLFile(string strFileName);
}
3、插件的实例化及调用
(1)插件的实例化
插件的实例化可以采用.NET的反射机制进行读取和实例化,实例化代码如下:
//根据插件实力化名称和插件名称创建插件
private IPlugCommand LoadObject(string className, string interfaceName, object[] param)
{
Type t = Type.GetType(className);
if (t == null || !t.IsClass || !t.IsPublic || t.IsAbstract
|| t.GetInterface(interfaceName) == null)
{
return null;
}
object obj = Activator.CreateInstance(t, param);
return (IPlugCommand) obj;
}
//加载给定路径的插件
public bool LoadCommand(string strPath)
{
// 获取插件接口名称
string interfaceName = typeof(IPlugCommand).FullName;
// 载入插件文件
Assembly asm = Assembly.LoadFile(strPath);
if (asm == null)
{
return false;
}
foreach (Type t in asm.GetExportedTypes())
{
// 载入插件,如果插件不符合指定的接口,则返回null
IPlugCommand plugin = LoadObject(t.AssemblyQualifiedName, interfaceName, null);
if (plugin != null)
{
//将插件加入插件组
AddCommand(plugin);
}
}
}
(2)插件的调用
当应用程序加载了插件以后,会自动生成插件对应的工具条和工具按钮,并将插件的唯一标识guid存储到功能按钮的属性中,当用户在界面上点击功能按钮时,在从属性中读取功能按钮的唯一标识guid,并根据该guid在插件管理器中查询到该插件,并调用插件的执行函数即可。
3、插件式GIS应用系统框架的设计原则
为了增加插件式GIS系统框架的柔韧性和灵活性,笔者尽量按照一定的原则去设计系统,在实际工作中,主要遵从以下几个原则:
1、GIS平台无关性
在国土信息化建设的过程中,制定总体解决方案时往往包含一系列的系统,如数据管理系统,辅助决策支持系统、督查巡查系统、电子政务系统,在信息化逐步实施的过程中,往往就决定了系统所采用的GIS二次开发平台,这就决定了项目实施方必须具备多个GIS平台的应用系统开发的能力。为了达到程序资源的最大化利用,插件式GIS框架的定义和实现需要具备与平台无关的特性,具体到某个GIS平台的二次开发时,只需要实现插件式GIS框架所规定的接口即可。
2、业务模块的低耦合性
每一个业务系统,都有系统相关的特定资源,这样就要求GIS应用系统的框架需要与具体的业务剥离开来,我们在针对某一具体的应用领域开发应用系统时,都可以基于该框架延伸出新的应用系统框架。例如当侧重于数据的编辑时,可以加入编辑所需要的系统环境,生成数据编辑处理系统框架;当侧重于数据管理时,可加入数据管理所需要的系统登录信息、数据存储信息等系统资源的初始化功能,生成数据管理系统框架;当实时处理督查巡查相关业务数据的时候,也可以加入督查巡查业务所需要的资源环境,生成督查巡查业务系统框架。
3、用户操作模式的统一性
不同的GIS软件,对于用户操作的模式是不一样的,二次开发时改变当前操作的方式也不一样。所以框架需要封装不同GIS软件的操作模式,反映出统一的用户操作体验。同时为了封装用户和界面的交互,需要提供一系列的对象供开发插件时使用。这样使得功能开发时,开发人员只需要利用框架提供的对象实现功能要素(空间对象,用户输入值等)的获取,而不必关心系统表现的形式。
4、插件式GIS系统框架的架构设计
基于上述的设计原则,笔者所实现的插件式GIS应用系统架构如下图所示:
架构设计采用分层架构的模式,共分为5层。最底层是应用程序框架定义层,接着是操作定义层,然后是框架视图和用户操作的实现层,第二层是业务组件和功能插件层,最上层是系统应用层。
4.1框架定义层
框架定义层是与GIS平台无关的层,主要实现系统框架的定义,主要包含插件定义和实现模块、插件管理和实现模块、视图定义模块以及框架定义模块,各部分功能描述如下:
插件定义和实现模块:实现插件接口的方法和属性定义,以及公共实现接口和方法类。
插件管理和实现模块:实现插件管理器接口的方法和属性定义以及实现类。
视图定义模块:实现各种应用视图的定义,包括地图窗口、打印窗口、属性窗口、图层记录集窗口等等。
框架定义模块:实现和系统应用框架接口定义,主要包括管理用户界面资源的接口和管理应用相关环境变量的接口。
4.2操作定义层
操作定义层是与具体GIS平台无关的层,实现各种操作定义模块和核心函数,主要功能模块描述如下:
操作定义模块:实现操作接口的定义,如获取地图点坐标,获取用户选择要素等一系列的操作定义。
核心函数模块:实现一些和GIS操作无关的核心函数,如解析求点,求两点之间角度等函数。
核心命令模块:实现一些GIS扩充命令的接口,以便在实现gis操作时重载。
草图绘制定义模块:实现地图草图绘制接口的属性和方法定义,主要用于用户在地图窗口绘制和修改要素时的草图(橡皮线)绘制。
4.3框架视图和用户操作实现层
框架视图和用户操作实现层适合具体GIS相关的逻辑层,系统采用什么样的GIS平台,只需要用该平台的二次开发组件实现相应接口功能即可,主要功能模块描述如下:
地图窗口控件模块:实现框架定义层定义的地图窗口接口,用于GIS数据的显示。
图层管理控件模块:实现框架定义层定义的图层管理接口,主要用于管理地图窗口中显示的GIS数据,包括图层数据的调整,图层属性编辑等。
记录集窗口控件模块:实现框架定义层定义的记录集窗口接口,主要用于显示某个图层所有的记录或者地图窗口选择的记录信息。
属性窗口控件:实现框架定义层定义的属性窗口接口,主要用于显示和编辑图形对象的坐标信息和属性信息。
专题图窗口控件:实现框架定义层定义的专题图窗口接口,主要用于地图专题图目录的显示和专题图的加载等功能。
操作实现函数模块:实现操作定义层定义的各种操作。
草图绘制实现模块:利用GIS组件实现草图的绘制功能。
核心函数模块:实现GIS操作所需要的核心函数。
4.4业务组件和功能插件层
业务组件层和功能插件层是实现具体业务功能和插件功能的逻辑层,该层主要根据不同的业务特征来实现,GIS应用系统的类型的本质区别就体现在这一层。主要模块功能描述如下:
业务功能实现模块:实现业务功能对象,为功能插件所调用。
业务功能环境定义和实现模块:实现业务环境接口定义以及功能实现类。
插件功能模块:按照插件原理实现功能插件。
4.5系统应用层
系统应用层属于用户界面交互层,主要包括底层框架定义的实现和系统主界面,是各种GIS系统共用的逻辑层。
5、总结
本文详细的介绍了插件原理和插件式GIS应用系统架构的设计,但是对于具体的实现没有过多描述,关于如何运用不同的GIS平台开发组件进行应用系统的开发不是本文的重点,也非三言两语能够表述清楚。本文仅试图将插件原理如何应用到GIS应用系统的开发做具体描述,并基于笔者的实际经验,将基于笔者愿景的插件式GIS应用系统架构描述清楚。
对于不同的GIS开发平台,由于其开发方式和系统特性的不同,在系统框架的具体实现上会有不同,但是具体的思想和系统架构是相同的,这也是框架设计的初衷之一。同时,在实际开发过程中,也会用到其他的诸如多线程,线程异步调用,XML文件读写等技术,在此也没有一一描述。
如下图所示,上图为基于ArcGIS二次开发包ArcEngine开发出的土地调查数据更新工具软件,下图为基于SuperMap Objects开发的数据编辑系统原型。两者都是在基于文中提到的架构设计的基础上实现的GIS应用系统,只需要加载不同的业务组件和功能插件层,就能组成一个新的应用系统。
实例一 基于ESRI公司ArcEngine开发的土地调查数据更新工具软件
实例二 基于超图SuperMap Objects开发的编辑系统原型
