基于MapboxGL的样式文件自动生成图例,MapboxGL是一个基于WebGL的地图绘制引擎,其地图样式采用一个json格式的文件进行描述,但是对于渲染的地图,Mapbox并没有提供图例的生成方法,因此需要自己根据这个样式描述文件来生成地图图例。
MapboxGL中数据与样式是分开的,地图的所有样式都是由这个唯一的样式描述文件来描述。并不是地图中的所有样式都需要图例,例如简单的文字注记就不需要生成图例,而且MapboxGL的样式中对同一个数据源(例如:铁路)会有多个样式图层(style layer)描述其样式(例如:灰色的实线图层加白色的虚线图层)。因此基于该样式文件绘制图例至少要先解决两个难题:
1. 从众多的样式中筛选出哪些样式需要绘制图例;
2. 从筛选出的样式中提取哪些样式描述的是同一个数据源。
一、 筛选需要绘制图例的图层
首先我们需要将所有的样式图层进行分类,按照要素类型分为点、线、面三类,分别为pointLayers、lineLayers、polygonLayers。
其次分别从中筛选出需要绘制图例的图层,删掉不需要绘制图例的图层。筛选规则很简单,对于所有要素都需要满足以下条件:
1、当前地图的级别必须在样式图层的可见级别范围之内。例如当前地图为第8级,而某一个样式的可见范围是9-12,那么这个样式是可以被忽略的。
2、图层的visibility属性设置的为“visible”,即必须可见。
理论上来说只要满足以上两个条件,那么这个图层在当前地图级别就是可见的,但是对于点状要素来说还有一个条件:
3、如果为点状要素的样式,则该样式必须具有“icon-image”属性。即该点状符号具有图标,而不是纯文字。
二、 合并同一数据源的图层
经过以上筛选后,上下的样式都是需要绘制图例的,但是有些样式可能描述的是同一个数据源,对于这些样式应该属于一个要素,因此需要合并。怎样判断哪些样式是属于统一数据源呢,基本原则如下:
1、type必须相同,都是线状或都是面状。
2、source属性必须相同,即同一数据源。
3、source-layer属性必须相同,即同一数据图层。
4、filter必须相同,具有相同的数据过滤条件。
满足以上要求被认为是描述的同一个数据要素。
三、 绘制图例
经过以上步骤得到的应该就是所有需要绘制图例的样式了,而要对图例进行可视化就需要选择对应的渲染引擎对图例进行渲染,目前我使用过的有两种渲染方法:
1、采用WebGL渲染
考虑到MapboxGL本身就是基于WebGL渲染的,而现在筛选后的图例的样式正好与地图的样式具有相同的格式,可以考虑直接用MapboxGL渲染到地图上,这时就需要创建geojson数据源。根据点线面三种类型分别创建三种geojson的模板,然后根据筛选出的图例样式的类型,分别获取对应的geojson,并利用图例样式对其进行符号化。
2、采用svg渲染
采用WebGL的方法渲染缺点是不方便编辑,而且采用的经纬度的坐标,相当于重新建立一个map。而svg则更加轻量,并且可以缩放和编辑。
但是svg的样式描述采用的dom属性的方式,因此与不能直接使用筛选的图例样式,而要转换成svg支持的属性。
附件(图例自动生成实例):
① 一个完整的Style Json文件如下:
{ "name": "新闻", "version": 8, "sources": { "data2012": { "type": "vector", "url":"https://192.9.105.205:8090/api/v1/tilesets/foxgis/admin" } }, "sprite": "https://192.9.105.205:8090/api/v1/sprites/foxgis/Hk6NFtw8/sprite", "glyphs": "https://192.9.105.205:8090/api/v1/fonts/foxgis/{fontstack}/{range}.pbf", "layers": [ { "id": "背景", "type": "background", "paint": { "background-color": "#87c2e4" } }, { "id": "亚洲面", "type": "fill", "source": "data2012", "source-layer": "asia_a", "paint": { "fill-color": "#ebecec", "fill-opacity": 1, "fill-outline-color": "#ebecec" } }, { "id": "国界", "type": "line", "source": "data2012", "source-layer": "china_l", "maxzoom": 11, "paint": { "line-color": "#e8ae59", "line-width": 5 } }, { "id":"国界光晕", "type":"line", "source":"data2012", "source-layer":"china_l", "minzoom":3, "maxzoom": 11, "paint":{ "line-offset":-3, "line-color":"rgba(255, 214, 211, 0.98)", "line-width":{ "stops":[[3,4],[4,6],[6,8]] } } }, { "id": "省界线", "type": "line", "source": "data2012", "source-layer": "boua_prov", "minzoom": 4, "maxzoom": 9, "layout": { "line-cap": "round", "line-join": "round" }, "paint": { "line-color": "rgb(150,150,150)", "line-width": 1, "line-dasharray": [1.5,3] } }, { "id": "县界", "source": "data2012", "source-layer": "boua", "metadata": { "replaceLayer": true }, "minzoom": 7, "maxzoom": 11, "type": "line", "filter": ["==","pac",320111], "layout": { "line-join": "round", "line-cap": "round" }, "paint": { "line-color": "rgb(0,0,255)", "line-width": 2, "line-dasharray": [3,3] } }, { "id":"铁路(实线)", "source":"data2012", "source-layer":"lrrl", "minzoom":7, "maxzoom":11, "filter":["any", ["==","gb",410101], ["==","gb",410102], ["==","gb",410103], ["==","gb",410200] ], "type":"line", "paint":{ "line-width": 2.4, "line-color":"#949494" } }, { "id":"铁路(虚线)", "source":"data2012", "source-layer":"lrrl", "minzoom":7, "maxzoom":11, "filter":["any", ["==","gb",410101], ["==","gb",410102], ["==","gb",410103], ["==","gb",410200] ], "type":"line", "paint":{ "line-width":1.2, "line-color":"#fff", "line-dasharray":[10,10] } }, { "id": "省名注记", "source": "data2012", "source-layer": "prov_label", "type": "symbol", "minzoom": 4, "maxzoom": 5, "layout": { "text-size": 12, "text-font": [ "STXinwei Regular" ], "symbol-placement": "point", "text-field": "{name}", "text-max-width": 8 }, "paint": { "text-color": "rgb(250,0,0)", "text-halo-color": "rgb(255,255,255)", "text-halo-width": 1.5, "text-halo-blur": 1 } }, { "id": "首都注记", "type": "symbol", "source": "data2012", "source-layer": "agnp", "filter": ["==","capname","北京"], "minzoom": 3, "maxzoom": 11, "layout": { "text-size": 14, "icon-image": "star-15", "text-font": ["SimHei Regular"], "symbol-placement": "point", "text-allow-overlap": false, "text-offset": [0,1], "text-anchor": "top", "text-field": "{capname}", "text-letter-spacing": 0.02, "text-max-width": 8 }, "paint": { "text-color": "rgb(255,0,0)", "text-halo-color": "rgb(255,255,255)", "text-halo-width": 1.5, "text-halo-blur": 1 } }, { "id": "省会城市注记", "type": "symbol", "source": "data2012", "source-layer": "agnp", "filter": ["==","class","AB"], "minzoom": 5, "maxzoom": 8, "layout": { "text-size": 15, "icon-image": "circle-11", "text-font": [ "SimHei Regular" ], "symbol-placement": "point", "text-offset": [0,0.7], "text-anchor": "top", "text-field": "{capname}", "text-letter-spacing": 0.02, "text-max-width": 8 }, "paint": { "icon-color": "#ff0000", "text-color": "rgb(20,20,20)", "text-halo-color": "rgb(255,255,255)", "text-halo-width": 1.5, "text-halo-blur": 1 } }, { "id": "地级市注记", "type": "symbol", "source": "data2012", "source-layer": "agnp", "minzoom": 7, "maxzoom": 11, "layout": { "text-line-height": 1.2, "text-size": 13, "symbol-avoid-edges": true, "icon-image": "circle-11", "text-ignore-placement": false, "text-max-angle": 38, "text-font": [ "SimHei Regular" ], "symbol-placement": "point", "visibility": "visible", "text-offset": [0,0.7], "icon-optional": false, "text-rotation-alignment": "viewport", "text-anchor": "top", "text-field": "{name}", "text-letter-spacing": 0.02, "text-max-width": 8 }, "paint": { "icon-color": "#ff0000", "text-color": "#fe001f", "text-halo-color": "rgb(255,255,255)", "text-halo-width": 1.5, "text-halo-blur": 1 } }, { "id": "县注记", "source": "data2012", "source-layer": "agnp", "filter": ["any",["==","class","AE"],["==","class","AF"]], "minzoom": 8, "maxzoom": 11, "type": "symbol", "layout": { "text-size": 13, "icon-image": "circle-stroked-11", "text-max-angle": 38, "text-font": ["SimHei Regular"], "symbol-placement": "point", "text-anchor": "top", "text-field": "{name}", "text-letter-spacing": 0.02, "text-max-width": 7 }, "paint": { "text-color": "rgb(60,60,60)", "text-halo-color": "rgb(255,255,255)", "text-halo-width": 1.5, "text-halo-blur": 1 } } ] }
② 经过第一步筛选后
从中筛选出需要绘制图例的图层,例如其中的“县注记”、“省名注记”等纯文字注记就被剔除掉了,得到的结果如下图,共有10个图层:
vcq9o6zL+dLUttTG5NH5yr29+NDQwcvXqru7o6zWu7GjwfTBy3N2Z9Do0qrTw7W9tcTR+cq9o7o8YnIgLz4NCjxpbWcgYWx0PQ=="这里写图片描述" src="/uploadfile/Collfiles/20160916/201609160934478.png" title="\" />
③ 经过第一步筛选后
以上得到的图例样式有些样式描述的是同一要素,例如铁路有虚线和实线两个图层,国界有边线和光晕两个图层,因此需要进行合并。合并后的结果如下图所示:
可以发现合并后剩下8个图层,铁路和国界的两个样式合并到一个数组。
④ SVG绘制
剩下的就是根据这个图例数组绘制svg了,绘制效果如下图:
这些图例只是地图上可能需要展示的所有图例,考虑到在实际制图中,可能有些不太重要的要素并不一定需要绘制在地图上,因此我在自动生成图例的基础上加入了人工控制,用户可以自行选择显示哪些图例,并且可以设置图例的列数、顺序、文字内容、字体、字号等属性。