基于MapboxGL的样式文件自动生成图例

MapboxGL是一个基于WebGL的地图绘制引擎,其地图样式采用一个json格式的文件进行描述,但是对于渲染的地图,Mapbox并没有提供图例的生成方法,因此需要自己根据这个样式描述文件来生成地图图例。
MapboxGL中数据与样式是分开的,地图的所有样式都是由这个唯一的样式描述文件来描述。并不是地图中的所有样式都需要图例,例如简单的文字注记就不需要生成图例,而且MapboxGL的样式中对同一个数据源(例如:铁路)会有多个样式图层(style layer)描述其样式(例如:灰色的实线图层加白色的虚线图层)。因此基于该样式文件绘制图例至少要先解决两个难题:
1. 从众多的样式中筛选出哪些样式需要绘制图例;
2. 从筛选出的样式中提取哪些样式描述的是同一个数据源。

一、 筛选需要绘制图例的图层

首先我们需要将所有的样式图层进行分类,按照要素类型分为点、线、面三类,分别为pointLayerslineLayerspolygonLayers
其次分别从中筛选出需要绘制图例的图层,删掉不需要绘制图例的图层。筛选规则很简单,对于所有要素都需要满足以下条件:
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":"http://192.9.105.205:8090/api/v1/tilesets/foxgis/admin"
    }
  },
  "sprite": "http://192.9.105.205:8090/api/v1/sprites/foxgis/Hk6NFtw8/sprite",
  "glyphs": "http://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个图层:
共有10个图层
这里我是打算采用svg的绘制方式,所以对其样式进行了转换,只保留了svg需要用到的样式:
这里写图片描述

③ 经过第一步筛选后
以上得到的图例样式有些样式描述的是同一要素,例如铁路有虚线和实线两个图层,国界有边线和光晕两个图层,因此需要进行合并。合并后的结果如下图所示:
这里写图片描述
可以发现合并后剩下8个图层,铁路和国界的两个样式合并到一个数组。

④ SVG绘制

剩下的就是根据这个图例数组绘制svg了,绘制效果如下图:
这里写图片描述

这些图例只是地图上可能需要展示的所有图例,考虑到在实际制图中,可能有些不太重要的要素并不一定需要绘制在地图上,因此我在自动生成图例的基础上加入了人工控制,用户可以自行选择显示哪些图例,并且可以设置图例的列数、顺序、文字内容、字体、字号等属性。
这里写图片描述

这里写图片描述

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值