OGC之路(2) 之 Style之谜

OGC之路(1)里我们简单介绍了WMS协议,并且实现了一个简化版的WMS客户端,WmsBrowser。我们对WMS的了解已经算是相当多。作为一般的使用者这些已经足够了。在介绍WMS的GetMap方法时我们使用过一个参数Styles,我们设定的是每一个图层支持的Style名称,除此之外我们并没有更多的工作需要做。服务器会根据我们要求的Style来绘制图层。那么这个Style到底是怎么回事呢,除了名称它还有什么秘密是我们不知道的呢。这一次我们将简单介绍一下OGC标准SLD。然后我们会实现一个简单的WMS服务器,我们将会用到SharpMap库。

Styled Layer Descriptor(SLD)是一个语言,一个用XML实现的,用来描述图层Style的语言。我们用这个语言来告诉服务商我们希望如何绘制图层。在前面介绍的WMS服务器中我们仅仅指定了Style名称,而到底这个名称代表的Style是个什么样子,我们一无所知,即便看到了返回的地图,我们同样不可能知道绘制的细节。而且,这里还有一个限制,用户无法指定自己的Style,只能使用服务器预设的。支持用户定义Style的WMS服务器我们称之为SLD WMS。可惜GeoServer目前的版本不支持。但是GeoServer的预定义Style使用了SLD来描述,所以我们还是可以继续使用它来学习。

由于SLD是基于XML的,从一个个节点的含义开始介绍将会是一件郁闷的事。我们从基本概念和例子入手。先来想象一下Style可能会怎样用到图层的绘制中。

如果我们“白手起家”自己开发GIS程序,我们就不得不面对一个基本问题,地图绘制。使用过GDI或者GDI+的朋友都熟悉Windows平台下面的绘制流程,这里就不赘述了。我们直接跳到对Feature的绘制上。GIS里面的基本元素是Feature,我最初在MapInfo里面看到这个词的时候还很是糊涂了一阵子。Feature是一个抽象概念,粗暴的解释就是一个拥有全局ID的对象或数据记录。这样的解释显然不能让人满意,但是它引出一个很重要的概念,ID。我们换一个说法,Feature is a real world phenomena。这是OGC的“简单对象访问协议”里面的解释。在GIS里Feature就是用来表示现实世界元素的。一栋房子,一条路,一个人都可以用Feature来表示。他们都有一个ID来标识。他们还有自己特有的属性,名称,性别,年龄,道路长度,道路宽度,房子的高度,层数等。此外还有一个最重要的属性,他们都有地理位置和几何形状(Geometry)。于是在绘制的时候就会衍生出许多策略。

首先我们来看看道路,我们当然不用把路的细节完全表示出来,我们一般把路抽象成一条线(Line)。我们可以调用C#的Graphics类的DrawLine(s)来绘制这条线,到此为止我们已经完成道路的绘制了。那Style是怎么起作用的呢。我们来看下绘制Line的时候我们需要设置哪些参数:颜色Color,线形LineTyle,宽度Width,还有线段顶端的渲染样式,甚至可以为线段绑定一个图片来丰富效果。我们还可能告诉程序什么时候使用什么参数设定。

所有这些数据和算法,我们称为Style。绘制一条道路时我们需要告诉程序,这条道路是Line,它的颜色是红色,宽度是2个像素,线形用实线。更进一步,我们希望程序根据道路的某个属性值来选择绘制参数,例如,为了体现道路的拥堵情况,我们需要把拥堵程度分级,然后为每一级设定一套绘制参数。渲染程序会根据道路当前的分级来选择。

下面我们来看看用SLD,我们如何描述上述信息。首先我们告诉程序,要绘制的Feature是一个Line,在SLD里面用LineSymbolizer节点表示Feature是一条线。然后我们需要告诉程序我们希望用宽度为2的红色实线来绘制。于是我们需要LineSymbolizer的子节点Stroke,Stroke下面有一点小复杂,我们先忽略细节,直接给出答案。

<LineSymbolizer>

  <Stroke>

    <CssParameter name=”stroke”>

          <ogc:Literal>#ff0000</ogc:Literal>

      </CssParameter >

    <CssParameter name=”stroke-width”>

          <ogc:Literal>2</ogc:Literal>

      </CssParameter >

  </Stroke>

</LineSymbolizer>

还没完,我们说过,程序按照道路的交通流量等级来绘制,就像GoogleMap一样。于是这里引入了第二个概念,Feature的属性访问。SLD是通过属性名(PropertyName)来访问Feature属性的。所以我们需要在Style中给出这个名称,然后我们告诉程序当这个值,“交通流量等级”,是1的时候用哪些参数,2的时候用那些参数,当这个值大于等于3以后应哪些参数。用伪代码表示就是:

var = feature.PropertyName;

if var == 1 then

  draw with style1

else if var == 2 then

  draw with style2

else if var >= 3 then

  draw with style3

我们现在要做的就是用SLD来描述上述算法。实话说,XML在这方面还真比不上自由格式语言,如果用SLD把上述算法表示出来将会是很长的一串,头都要看痛更不要说写了。但是为了文章完整我们还是硬着头皮上吧。上面的介绍并没有把SLD的完整文档给出,完整的定义可以参见SLD标准,我在这里就偷个懒吧。

LineSymbolizer实际上是包含在Rule节点中的,一个Rule就代表了一个选择条件,我们用节点Filter和ElseFilter将这些Rule串联起来。像这样:

<Rule>

  <Filter>…</Filter>

  <LineSymbolizer>…</LineSymbolizer>

</Rule>

<Rule>

  <ElseFilter>…</ElseFilter>

  <LineSymbolizer>…</LineSymbolizer>

</Rule>

Filter和ElseFilter里面是表达式,使用了OGC的另外一个标准,有些人肯定觉得烦躁了,怎么又来一个标准。没错,欢迎来到标准的世界,不管你喜欢不喜欢,世界已经“被标准了”。关于这个标准我们后面介绍,这里只是简单使用一下,算作预习。我们先把注意力集中在Filter和ElseFilter的效果上。我们想要实现的是一个if…else if…else的算法。那么这个Filter和ElseFilter如何组合来实现这个效果呢。

<Rule>

  <Filter>

    <ogc:PropertyIsEqualTo>
       <ogc:PropertyName>交通流量等级<ogc:PropertyName>
       <ogc:Literal>1</ogc:Literal>
    </ogc:PropertyIsGreaterThanOrEqualTo>

</Filter>

  <LineSymbolizer>style1</LineSymbolizer>

</Rule>

<Rule>

  <ElseFilter>

    <ogc:PropertyIsEqualTo>
       <ogc:PropertyName>交通流量等级<ogc:PropertyName>
       <ogc:Literal>2</ogc:Literal>
    </ogc:PropertyIsGreaterThanOrEqualTo>

  </ElseFilter>

  <LineSymbolizer>style2</LineSymbolizer>

</Rule>

<Rule>

  <ElseFilter>

    <ogc:PropertyIsLessThanOrEqualTo>
       <ogc:PropertyName>交通流量等级<ogc:PropertyName>
       <ogc:Literal>3</ogc:Literal>
    </ogc:PropertyIsGreaterThanOrEqualTo>

  </ElseFilter>

  <LineSymbolizer>style3</LineSymbolizer>

</Rule>

关于Filter的叙述到此为止,你肯定有许多疑问,更多的细节建议大家都去看标准。下面我们来看这种情况,很多时候我们的Feature表示了很小的细节,例如一栋房子。当你把视野拓展到全国,甚至全球的时候,如果我们有所有的房子,那将是一场灾难,渲染程序会完全死掉,甚至服务区都会死掉。因为要绘制的房子太多了,而实际上这种情况下每个房子都只会浓缩为一个点,而地图会被密集的点覆盖。所以在这种视角下,像房子这种细节根本没有绘制的必要。Rule下面有两个节点MinScaleDenominator和MaxScaleDenominator就是为这种情况设计的。我们只需要给他们设置适当的值就可以解决上述问题。

我们一直在讨论Line,那Polygon,Point怎么办。SLD除了LineSymbolizer外还有PolygonSymbolizer,PointSymbolizer,TextSymbolizer和RasterSymbolizer。分别对应Polygon,Point,文本标签和栅格图的渲染。这里简单介绍下Symbolizer。Feature的外形是用一个叫Geometroy的概念描述的,而这个外形在绘制时就被Symbol这个概念取代了。所以Symbol和Geometroy是一体的两面。(这是我自己的理解,如果有误请一定帮我指出。)。

到此SLD的介绍就基本完成了,SLD是一个很复杂的标准,这样一篇小文是不可能做到“巨细靡遗”的,想正真了解SLD的朋友必须自己阅读标准文档,另外还需要写一些代码来验证。下面我们就通过实现一款“简单到死”的Wms服务器来进一步了解这些标准。需要说明的是我们将使用到SharpMap这个GIS开源库。所以这个程序也必须符合它的协议限制,此外我本人不会再加任何限制。我们就把这个服务器命名为SharpMapServer。

  1. SharpMapServer需求

    作为一款WMS服务器,我们首先设定它必须支持GetCapabilities和GetMap两个函数,并且它不是SLD WMS。我们只提供一种GetMap返回格式,“image/png”。此外我们还要必须加些限制:只支持SLD的子集,很小一部分子集。具体如下:

    1)只支持NamedLayer,不支持UserLayer

    2)不支持LayerFeatureConstraints

    3)只支持UserStyle,不支持NamedStyle

    4)只支持ScaleDenominator,不支持Filter

    5)只支持LineSymbolizer,不支持其他Symbolizer

    6)不支持Geometry

    7)只支持CssParameter最简单的形式,“stroke”和“stroke-width”

    8)支持Mark,不支持ExternalGraphic

    服务器只支持ESRI的Shape文件格式。使用配置文件来完成图层和样式的配置。修改了配置文件不需要重启服务器。

  2. SharpMapServer设计

    Web.Config文件需要指明图层配置文件名。图层配置文件保存了服务器发布的Layer和响应的Style。Shape文件保存在目录“Layers”下面而Style文件保存在目录“Styles”下面。每次响应用户请求,程序都会去读图层配置文件,这样修改了这个文件系统就不需要重启了。

  3. SharpMapServer编码ing。。。
  4. SharpMapServer简介

    获得代码。这次没有什么设计可言了,就是为了实现功能。所有代码都集中在WmsHandler中。其实因为使用了SharpMap完成图层渲染和坐标变换,剩下的工作也就不多了。服务器的配置是手动完成的。所有配置信息保存在“wms_capabilities.xml”中,其实这个文件就是一个WMS Capability文本。我会在用户调用GetCapabilities是直接把它发送出去。你可以在“Web.config”里面修改这个文件名。“Web.config”里面还有一些关于文件路径的设置。有:a)Layers文件路径,保存shape文件;b)Styles文件路径,保存SLD文件;c)WellKnown文件路径,保存Style中使用的WellKnown图标。所以如果你需要增加一个图层,就需要把shape文件复制到Layers文件路径下面,然后在“wms_capabilities.xml”增加响应节点。如果要增加Style,就需要把SLD文件复制到Styles文件路径路径下面。注意,所有这些文件名都必须与节点中Name相同。我默认发布了3个Layer四个Style。用我们的WmsBrowser打开就是这样:

  5. 后续

    到这里,我们已经对WMS和SLD有了比较深入的理解了。我们也不再满足与只是看看图片了,我们需要其他数据,我们需要更复杂的数据访问。我们需要开始学习WFS了。

    祝大家国庆中秋双倍快乐。

posted on 2009-09-30 11:29  索夫特威尔  阅读(4583)  评论(4编辑  收藏  举报

导航