基于Java的ArcSDE空间数据查询的设计与实现
2009-01-15
作者:樊昱,高红雨,廖湖声
1.引言
近年来,地理信息系统(GIS)发展迅速。为了在全球范围内实现GIS软件组件的互操作性,OGC(Open GIS Consortium)提出了空间要素服务(WFS)的实现规范[1],有利于解决基于Web的GIS软件的数据共享、互操作性和开放性等问题。
WFS是OGC提出的一种空间要素服务。它将Web服务应用于地理信息系统,允许客户端通过互联网从多个服务器端获取以地理标注语言GML(Geography Markup Language)编码的空间地理数据。
空间数据的查询是通过发送GetFeature请求来实现的。在WFS的实现规范中,一个GetFeature请求是由空间要素类型、属性和查询条件三部分组成,以XML格式定义。其中要素类型用于指定查询哪类要素,属性用于指定需获取该要素的哪些属性,而查询条件用于筛选出满足指定条件的要素。
我们的课题是需要实现一个基于J2EE的WFS系统;其中存储空间数据的数据源采用的是ArcSDE(ESRI公司开发的空间数据库软件),在查询数据源ArcSDE时使用其自身提供的客户端应用接口。由于ArcSDE应用接口与WFS应用接口之间存在着差异,因此需要对收到的WFS GetFeature请求作一些适当的变换,以适应ArcSDE的数据查询。
本文将着重介绍以WFS GetFeature请求查询ArcSDE的实现方法,以及必要的变换算法。
2.设计思想
WFS应用接口与ArcSDE应用接口之间的主要差异在于查询条件的设置。
首先,在数据格式方面,WFS GetFeature请求中的查询条件是以XML格式定义,而ArcSDE的应用接口不支持XML,因此需要对此XML字符串进行解析。
其次,在数据内容方面,GetFeature的查询条件可以分为两类,一类以要素标识符作为查询条件;另一类是由空间筛选条件,或者非空间筛选条件,或者这二者的逻辑组合组成的查询条件。每次查询只能选用其中一类查询条件,两类不能同时出现在一次查询中。对第一类GetFeature的查询条件,在ArcSDE的应用接口中提供了一种用要素标识符作为约束条件的筛选器对象来处理此类条件。对于第二类查询条件,在ArcSDE中可以通过定义SQL属性查询来处理GetFeature请求中的非空间筛选条件,而通过添加空间约束的筛选器则可处理请求中的空间筛选条件。对于空间和非空间筛选条件的逻辑组合组成的条件,则相对不易处理。由于在查询ArcSDE时空间筛选器需以数组的形式设置,使得空间筛选条件之间只能以逻辑“与”的关系进行组合。同样,由于空间和非空间筛选分开设置,使得空间与非空间筛选条件之间也只能以逻辑“与”的关系进行组合。而在实际的GetFeature请求的查询条件中,查询请求可能是由“与”、“或”、“非”三种逻辑关系任意混合组成,条件之间的组合不一定能满足上述ArcSDE筛选条件的要求。我们在这里提出了一种解决的方案:对查询条件提取析取范式,转换成多个“与”项组合(有的与项组合有可能只有一项)的并集,然后对每个“与”项组合组织一次子查询,每次得到一个结果集,最后对所有的结果集再取并集,即为整个查询的结果集。
3.算法设计
根据上面分析的结果,将整个查询变换分为两个步骤:
(1)对XML格式的查询条件进行DOM解析。DOM处理XML文档时会将XML文档加载到内存中生成一棵DOM节点树,可以方便地实现遍历。
(2)对查询条件提取析取范式,将不符合ArcSDE要求的查询条件拆分为多次。
(2.1)遍历DOM树同时生成BLRTree,用以去除Not节点,并将非空间条件转换为SQL语句,将空间条件封装为特定的类结构。
由于在GetFeature查询条件的DOM树中的可能存在着Not节点,而且根据GetFeature查询条件的格式,所有单个的空间或非空间筛选条件在树中是以一棵子树的形式存在,而不是一个节点,因此还不能直接作逻辑关系的转换,需要对这棵DOM树进行“化简”。为了不改变原树的逻辑,本文定义了一种二叉树数据结构BLRTree(二元逻辑关系树—Binary Logic Relation Tree)。在遍历原DOM树的过程中,自底向上构造这一数据结构。在BLRTree中,所有的树枝节点都是二元逻辑运算符“And”或“Or”,而所有的叶子节点都是单个的查询条件。这种处理是将Not节点作为函数递归的一个布尔值参数truth传递给下层,在没有遇到Not节点时,truth都为“true”,而如果遇到了Not节点,只是将truth取反(注意:是取反,而不是取“false”值),并不在Not节点上作任何处理操作,这样对其下层的子节点就可以根据truth的取值来进行构造相应的了BLRTree子树。原DOM树中的所有Not节点在BLRTree中已根据摩根定率降至叶子节点处,且不以节点的形式存在,而并作为条件节点的一个布尔值参数。
(2.2)遍历(后序)BLRTree同时生成O_A_C_Tree,根据分配率和结合律进行逻辑关系变换提取析取范式。
BLRTree树构造完毕后,如果发现树根是一个条件节点,则不必作逻辑关系转换。如果是逻辑操作符节点则需进行逻辑关系的转换。由于最终的结果是多个与项组合的并集(析取范式结构),即将多个与项组合“或”起来。若把每个与项组合中的所有条件项看作是同一层,而把每个与项组合之间看作是同一层,最上层的“或”关系为一层,则可将一个多个与项组合的并集用一种3层孩子兄弟树的结构来表示。因此定义了一种3层孩子兄弟树的数据结构O_A_C_Tree(Or-And-Condition Tree)来辅助转换。在后序遍历BLRTree的同时,从条件节点开始构造子树。如遇到Or节点,则通过结合律将左右子树进行合。而遇到And节点,则通过分配律将左右子树进行合。如此递归操作生成一棵完整的O_A_C_Tree,完成逻辑关系的转换。
(2.3)遍历O_A_C_Tree,将所有筛选条件放置到两个数组中,以便于访问。
完成了逻辑转换之后,原查询条件将被放置到两个数组whereclauses[]和spatialfilters[][]中。其中whereclauses[]是非空间条件组,数组中每个元素代表一次查询的非空间筛选条件。由于非空间筛选条件是以SQL语句的形式存在,一次子查询中的非空间筛选条件用逻辑“与”可合并为一个整体,因此一次子查询中的所有非空间筛选条件可以合并只用一个元素来表示。spatialfilters[][]是空间条件数组,数组中的每一维代表一次子查询的所有空间筛选条件。用whereclauses[]中的一个元素,再搭配spatialfilters[][]中的相应一组元素(如whereclauses[i]和spatialfilters[i][]可以构成第i次子查询的筛选条件)就可以构成一次ArcSDE子查询的全部筛选条件。
如此便可完成一次查询变换,根据数组的内容就可以设置相应的筛选条件,进行查询。在处理结果时,将每个取到的结果其封装成GML格式,写到输出流中。这样不必保存查询的要素对象实体,降低了系统开销。由于在对组合条件进行条件拆分的时候,将一次查询分成了多次子查询,因此每次子查询的结果集之间可能会有重复的情况,这时需要将其中重复的结果舍去。由于要素标识符都是唯一确定的,这样只需把查到过的要素标识符记录下来,下次再遇到标识符相同的要素将会跳过而不做任何操作。由于查询的数据量可能会很大,如果只将标识符的值顺序的存储在一个表中,进行查找的开销会很大,因此为了利于新值的插入和加快查找的速度,本文选择以二叉排序树为存储结构,使用了二叉树查找算法。
4.算法实现
关键算法:
算法2.1 构造二元逻辑关系树:
BLRTreeNode construct ( Node root , boolean truth ){ //root 为DOM树中的根节点
if root为and(/or) Then
对root的左子树作construct( root.Lchild , truth )得到Lchild
对root的右子树作construct( root.Rchild , truth )得到Rchild
if truth为true Then
创建一个and(/or)节点,将Lchild,Rchild作为左右孩子
Else
创建一个or(/and)节点,将Lchild,Rchild作为左右孩子
Endif
返回新创建的节点
Endif
if root为not Then //not是单目的逻辑运算符,只有一棵子树
if root的孩子也为not Then
对root的孙子树作construct(root.Grandchild, truth)
得到grandchild节点,将其返回
Else
truth取反
对root的子树作construct(root.child, truth)
得到child节点,将其返回
Endif
Endif
if root为非空间条件节点 Then
根据truth 构造非空间条件节点, 将其返回
Else if root为空间条件节点 Then
根据truth 构造空间条件节点, 将其返回
Endif
}
算法2.2 等价构造析取范式结构树:
ORNode LogicTransform ( BLRTreeNode root ){ //root 是BLRTree的根节点
if root . Lchild是条件节点 then
生成condNode节点cond1
新添一个ANDNode节点and1,孩子指针指向cond1
新添一个ORNode节点or1,孩子指针指向and1
Else 对左子树作LogicTransform (root . Lchild ),得到or1
Endif
if root . Rchild是条件节点 then
生成condNode 节点cond2
新添一个ANDNode节点 and2,孩子指针指向cond2
新添一个ORNode 节点or2,孩子指针指向and2
Else 对右子树作LogicTransform (root . Rchild ),得到or2
Endif
if root为Or节点 then
将or2节点的所有孩子添加到or1节点的孩子链上去
返回or1节点
Endif
if root为And节点 then
新添一个ORNode节点newOR
for( i = 0;i < or1节点孩子个数;i++ ) {
for( j = 0;j < or2的孩子个数;j++) {
新添一个ANDNode节点newAnd
将or1的第i个孩子的所有子孩子和or2的第j个孩子的所有子孩子都添加到newAnd节点的孩子链上去,作为newAnd的共同的孩子
为newOR的添加一个孩子newAnd
}
}
返回newOR节点
Endif
}n 下面举一实例来表现转换的全过程: 要求查找所有属于M州,且不同时满足人口大于100000和与一条起止点坐标为(-50 20,-57 22)的河流相交的城市。 查询条件如下: < gml:coordinates>-50,20 –57,22 gml:coordinates> (1)对XML的查询条件进行DOM解析后,生成DOM节点树 (2.1)遍历DOM树同时生成BLRTree: 图1 生成BLRTree (2.2)遍历BLRTree树同时生成O_A_C_Tree 图2 生成O_A_C_Tree (2.3)遍历O_A_C_Tree输出到数组中 whereclauses[0] = “StateName=‘M’ And Not Population >100000” whereclauses[1] = “StateName=‘M’” n n filters[0]置空 filters[1][0]为不满足与线相交的空间条件 5、结束语 本文在对比了WFS GetFeature查询请求的格式与数据源ArcSDE的查询的基础上,提出了一种将WFS的要素查询变换为ArcSDE查询对象的变换算法,并着重介绍了以WFS的应用接口对数据源ArcSDE进行查询实现的实现方法。 在转换的过程中为了实现数据的接口一致性,进行一系列的数据转换,这势必增加系统的负担,降低系统的效率。因此,还需要在系统的优化方面作进一步的研究。 参考文献 1. Open GIS Consortium Inc.,Feature Service Implementation Specification 1.0.0 (02-058) ,Version 1.0.0, 19-September-2002 2. Open GIS Consortium Inc.,OpenGIS Filter Encoding Implementation Specification 1.0.0 (02-059),Version 1.0.0, 19-September-2001 3. Open GIS Consortium Inc.,OpenGIS Geography Markup Language (GML) Implementation Specification (02-009),Version 2.1.1, 25-April-2002 4. ESRI,ArcSDE Developer Help 8.3 2003年 5. Don Box, Aaron Skonnard和John Lam著,卓栋涛译。XML 本质论。中国电力出版社,2003年3月第一版