高德地图和D3js的结合(上)

Posted by Chris Lu on 2019-05-24

内容太多所以分成高德地图篇(上)、D3js篇(下)两个部分,本篇是对高德地图使用的总结。

国内Web端用到的地图引擎,基本是高德、百度和腾讯三家中的一个。
我最后选择了高德,虽然我司不是阿里系,但是腾讯的文档不完善,接口也不够丰富。至于百度嘛,不知道为啥,做的时候根本没有想起来要去对它做调研,就这样忘记它吧~

完成这次项目用到了:

通过看官方文档就能上手的部分就不啰嗦了,这里面值得一提的是图层、自建图层和搜索服务。


1. 麻点图层 MassMarks

需求说明:
将3000多个数据,以图标的方式渲染到地图上;
图标的颜色和形状取决于接口返回的字段;
图标的尺寸根据地图当前的缩放级别动态调整。

因为高德地图已经有高效渲染海量点数据的 API,直接用就是了。
需要自己稍微处理一下的部分就是图标的形状、颜色和尺寸。

1.1 图标尺寸

设置尺寸很容易,根据缩放级别设置相应的图标尺寸,修改配置项中 style 下的 size 属性即可(size支持数组或者AMap.Size类)。

1.2 图标形状

设置图标形状对应配置项中 style 下的 url 属性。
文档中只说 url 是图标地址,string 类型,看示例用的是网络图片地址。
经过测试摸索后发现支持 svg 字符串,为了方便配置图标颜色,svg 当然是比图片灵活许多。
最后的实现方案:

  1. 接口返回的数据包含该点的图标名称
  2. 前端静态维护名称和 path 的映射
  3. 渲染图标的时候,根据图标名称找到对应的 path,动态生成 svg 字符串

贴一下前端动态生成 svg 字符串的代码:

1.3 图标颜色

图标是什么颜色是接口返回的,在动态生成 svg 字符串的时候,同时传入颜色字段就可以了。


2. 行政区图层 DistrictLayer

需求说明:
页面可以进行城市切换;
地图上显示当前城市地图,并绘制行政区域边界线。

2.1 覆盖物

最开始的方案,是先用 DistrictSearch 根据城市名称获得城市的 adcode,再用城市 adcode 和 level 去查询下面的行政区边界,最后绘制成AMap.Polygon类。
优点:可以自定义边界线的粗细程度。
缺点:耗费时间,大概要 600ms。

2.2 图层

后来发现高德 2018-09-12 就发布了简易行政区图层插件。
这个插件用起来很简单,重点是特别快,大概 200ms 完成绘制。
但是它真的太简单,只支持到大部分市级的行政区边界,太仓、辛集都没有,重庆绘制出来不包括郊县。
优点:绘制高效,调用简单。
缺点:覆盖的地区不全,不能调整边界线的粗细程度。

2.3 覆盖物结合图层

综合上面两种方式的优缺点,最后采用二合一方式,贴一个简单的代码:


3. 自建图层 CustomLayer

需求说明:
自定义图层内容;
图层状态可以同步地图的缩放、平移、尺寸变化状态。

高德的自定义图层功能必须给点个赞,这个功能给地图产品带来更多的可能。除了 CustomLayer,还有 TileLayer、ImageLayer、CanvasLayer、VideoLayer。不是很明白细化这些图层的原因是什么,感觉有个万能的 CustomLayer 就够了。

使用起来也简单:

  1. 加载CustomLayer插件
  2. 创建CustomLayer实例,初始化render方法,此方法会在地图状态变化时被调用
  3. 添加自定义图层到地图实例

需要注意的是,render 方法会有被频繁调用可能,最好是做一个防抖判断


4. 搜索服务 DistrictSearch

这个服务提供行政区信息的查询,使用该服务可以获取到行政区域的区号、城市编码、中心点、边界、下辖区域等详细信息,为基于行政区域的地图功能提供支持。

要注意两个点:

  1. 高德的行政级别划分跟我们的认知不一致。
    举个例子,重庆市我认为是市级别,但是高德是省级;太仓市我认为是市级别,实际又是区级。
  2. 个别省市的行政区划分很特殊,目前发现的是重庆和苏州,重庆由重庆郊县和主城区组成,苏州由苏州工业园区和主城区组成。

封装了两个搜索的方法,自我感觉并不是很满意,再慢慢优化吧。
下面的代码引入了ramda工具库,缩写为R。

4.1 搜索城市信息

  • searcher:DistrictSearch 实例
  • keyword:关键字(城市名称或 adcode)
  • level:搜索级别

4.2 搜索区域边界线

  • searcher:DistrictSearch 实例
  • others:更多配置
    • keyword:关键字(城市名称或 adcode)
    • level:搜索级别
    • subDistrict:是否获取下一级的行政区边界 1-返回,0-不返回

5. 问题碎片

有些琐碎的、我觉得挺坑的点,也顺便记录在这里。

  1. OverlayGroup 的作用很鸡肋
    我还以为AMap.OverlayGroup是批量绘制覆盖物的对象,实际测试下来并不是,我估计它的底层只是代替开发者做了一个循环,因为对性能没有一点儿帮助。

  2. 获取 Bounds 中心点存在问题
    创建一个AMap.Rectangle时需要传入矩形的 bounds 参数,文档是说用东北-西南角坐标。亲测下来,这样会在创建完矩形后,获取 bounds 中心点时,经度为负数。
    解决办法就是使用西北-东南角坐标。

  3. moveend 事件触发的时机和文档不符
    给地图绑定事件的时候,意外发现 ToolBar 控制、键盘控制地图缩放时,moveend 事件没有被触发,但是鼠标滚轮控制缩放时就会触发 moveend 事件,真的是奇怪。
    解决办法是全局添加一个标记值 isZoomEnd,绑定 zoomend 事件,触发时置 isZoomEnd 为 true,在 moveend 回调中阻止重复处理,并改 isZoomEnd 为 false。

  4. clearMap接口效率奇低
    清除地图上所有覆盖物的API clearMap,能把人逼疯。我们的业务场景下地图上绘制了成千上万的覆盖物,切换城市时这些覆盖物需要一次性清除掉,绘制新的。8000 个覆盖物要耗费十几秒!实在恼火,且无能为力。

  5. 绘制Polygon传入的path参数会被更改
    创建一个AMap.Polygon时需要传入多边形的 path 参数,讲道理,函数不应该直接修改传引用的参数,亲测发现这里 path 会被改写。
    解决办法就是 path 传入时使用副本。


高德只支持了点数据的海量绘制,需要海量绘制覆盖物的时候,性能就下滑得难以让人接受了,不得不另寻出路。
借助 CustomLayer,就可以自定义覆盖物的绘制方式,canvas 也好,svg 也好。

下面一篇文就继续总结高德地图配合 D3js 的开发经验。