WebGIS最佳实践-3 为GeoServer编写漂亮的Style
凡是用过的人都知道,在GeoServer里面创建美观的Style是一件痛苦的事(如果有人觉得是种享受请不要反驳,当我哗众取宠就是了)。GeoServer直接支持OGC的SLD作为样式(Style)的描述。SLD是用XML描述的,而XML是由许多相互嵌套的“<"和">"搭建的。我很不喜欢手动编写XML,尤其是从空白文件开始编写,这是其一。其二,在GeoServer的Style页面编写Style无法立刻看到效果,经常需要重复几个枯燥的动作才能看到,大大降低了工作效率。这在“所见即所得”肆虐的年代,简直就是大逆不道。本文中我会把我配图的方法和常用的工具拿出来与大家分享,虽不是什么火箭科学,但也算是工作经验的总结,希望对战斗在配图第一线的童鞋有所帮助。另外也是想抛砖引玉,如果你有自己的经验也请发布出来共同进步。写了这么多虚伪的文字(汗),下面进入正题。
先上图
【这张图的配色我参考了Google Map,说白了就是把它的颜色一点点抠下来,虽然很费事但是效果我还是很满意的】
矢量数据来自http://sms.webmap.cn/find.asp?status=%CD%EA%B3%C9&accessOption=%CF%C2%D4%D8,大家需要自行下载。
下表是数据之间的对照关系
shp文件名 | 数据名称 | Style名称 | Layer名称 | |
bou2_4p | 1:400万国界与省界 | bou2_ply | bou2_4p | |
bou2_4l | 1:400万国界与省界 | bou2 | bou2_4l | |
hyd2_4p | 1:400万一级河流 | lake | hyd2_4p | |
hyd2_4l | 1:400万一级河流 | river | hyd2_4l | |
roa_4m | 1:400万主要公路 | road | roa_4m | |
rai_4m | 1:400万主要铁路 | rail | rai_4m | |
res2_4m | 1:400万地市级以上居民地 | china_city | res2_4m |
这些图层会按照表中顺序组合为LaueyGroup,就可以了。
这里提供GeoServer的样式文件打包下载:https://files.cnblogs.com/sillyemperor/webgis_best_practices_3_slds.7z。具体用法很简单,解压缩然后复制到"[GeoServer]\data_dir\styles"路径下面,重启服务即可。至于Layer和LayerGroup的发布,请参看网上资料,这里不赘述。之前提到一点,我们的数据是shp文件,所以发布成Store之后记得把编码改成“GB2312”
介绍第一个工具uDig,它的作用有二:一、提供“所见即所得”的配图环境;二、帮助创建Style的模板。需要说明的是,在有些情况下,uDig创建的样式不能直接复制到GeoServer的Style页面中。我会在介绍到具体内容时再细说。
第二个工具是一个在线服务:http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php它的作用是把RGB分量转换成CSS需要的16进制字符串。当然类似工具绝不只一个,反正我用的是这个。
第三个是参考文献:http://portal.opengeospatial.org/files/?artifact_id=16700这是我们将要用到的SLD中关于符号化语言的标准文档,遗憾的是全英文,我没找到过翻译版,只能将就一下了。
武器都备齐了,现在开始我们的征程。首先启动uDig,把数据加载到新的地图中,记住要按照上面的顺序。
1、先从最简单的开始,地区和国家的样式“bou2_ply”。右键选中“bou2_4p”图层选择“ChangeStyle”菜单打开“Style Editor”对话框。在XML选项中我们可以看到uDig为多边形提供的缺省样式,其中最重要的就是
<sld:PolygonSymbolizer> <sld:Fill> <sld:CssParameter name="fill">#F7EFEF</sld:CssParameter> <sld:CssParameter name="fill-opacity">0.5</sld:CssParameter> </sld:Fill> <sld:Stroke/> </sld:PolygonSymbolizer>
先简单介绍下这个“PolygonSymbolizer”,在SLD中符号(Symbolizer)是一个很重要的概念,它代表地图元素会被如何显示。例如:点元素(Point)使用PointSymbolizer,多边形就是PolygonSymbolizer。再来看“Fill”,它是填充样式命令,如果不提供则表示该样式不需要填充,也就是中空。命令参数CssParameter 则定义填充的各项参数值,如“<sld:CssParameter name="fill">#F7EFEF</sld:CssParameter> ”就是在指定填充颜色。最后来说说“Stroke”命令,它代表画线,也可以用CssParameter 来制定具体参数值,我们这里希望国界没有边框所以直接把它删掉。再回来看看填充色,我前面提到过配色是从GoogleMap来的,现在来看看怎么做。先打开GoogleMap,缩放到需要的位置,然后截图,将截图复制到操作系统自带的画板中,然后用取色工具选取背景颜色,再打开“编辑颜色”菜单,从里面复制出RGB分量。打开 http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php网页,将RGB分量复制到
界面中获得“Hex Code”,再把这个值复制到“<sld:CssParameter name="fill">#F7EFEF</sld:CssParameter> ”里替换原来的值。然后点击“Style Editor”对话框右下角按钮“Apply”就可以看到效果了。一口气说了这么多,希望你没有不耐烦。毕竟这是操作步骤,我只会在这里说一次,后面就是重复类似动作了。
2、稍微复杂点的,铁路样式“rail”。首先说明,SLD语法允许多个Symbolizer顺序组合出新的效果,这里我们会用到3条线来实现铁路的样式,从下到上依次是
具体的SLD代码见文件rail.sld。这三条线叠加的效果就是
。这里还要引入另外一组命令”MaxScaleDenominator/MinScaleDenominator”,这个命令实现这样一种效果:当地图缩放到一定的比例范围时图元才会显示,否则就不显示。如果没有这个效果我们的地图就会挤成这样
3、打大BOSS了,城市样式“china_city”。需要简单说明以下城市数据,有一个字段叫“ADCLASS”将城市分成1,2,3,9四级。我希望不同级别的城市用不同的图标,并且在不同的显示级别出现。
1级城市用 ;2级城市用
;3级城市用
,9级城市用
。这些样式不复杂,就不多说了,重点放在两个问题上:使用过滤器(Filter)和使用中文。
先看代码
<ogc:Filter> <ogc:PropertyIsEqualTo> <ogc:PropertyName>ADCLASS</ogc:PropertyName> <ogc:Literal>1</ogc:Literal> </ogc:PropertyIsEqualTo> </ogc:Filter>
这是“china_city.sld”中的一段,“Filter”命令定义一个判断语句,只有当这个判断语句结果为真时它所在的样式才会起作用,从这点看“MaxScaleDenominator/MinScaleDenominator”可以认为是特殊的“Filter”。上面这段语句用伪代码写出来就是:feature.ADCLASS == 1。也就是当字段“ADCLASS”的值为“1”时为真。使用Filter我们可以构造出复杂的渲染样式,不过实话说这样的代码风格实在太不直观了。
最后来看这段代码
<sld:TextSymbolizer> <sld:Label> <ogc:PropertyName>NAME</ogc:PropertyName> </sld:Label> <sld:Font> <sld:CssParameter name="font-family">宋体</sld:CssParameter> <sld:CssParameter name="font-size">12.0</sld:CssParameter> <sld:CssParameter name="font-style">normal</sld:CssParameter> <sld:CssParameter name="font-weight">bold</sld:CssParameter> </sld:Font>...此处省略若干字 </sld:TextSymbolizer>
这段代码定义了标签符号,也就是显示文字,我们想把字段”NAME”的值用“宋体”显示出来。如果你直接把这个SLD从uDig里复制到GeoServer里然后点“Validate”按钮,你很可能会收到这条警告:java.io.UTFDataFormatException: Invalid byte 2 of 2-byte UTF-8 sequence.解决方法其实很简单,把XML开头的“<?xml version="1.0" encoding="UTF-8"?>”改成“<?xml version="1.0" encoding="gb2312"?>”就OK了。同样不要问我为什么。
说了这么多终于可以结束了,在结束前我有点小小的不高兴,虽然以上方法在实践中证明是可行的,但是未免太麻烦了,要不断在多个程序和界面之间换来换去,在uDig里面配好的地图还要到GeoServer里面再做一次,这不是程序员解决问题的方式,我觉得应该有工具能让我把uDig配好的地图直接发布到GeoServer上,而不是像这样给大家津津乐道一大堆注意事项和小技巧。好了,再次感谢大家,我们下期节目再见。