Zhihao's Studio.

helium-ol-map的设计取舍、权衡、实现、踩坑与npm发布

Word count: 1,582 / Reading time: 6 min
2019/07/02 Share

前言

经历了上次被百度地图封号、封IP甚至改变离线地图下载规则后,我们开始反思,是不是项目绑百度地图太紧了,如果后面需要将应用场景变到其他城市但百度离线地图不可用了,应该怎么办的问题。

为了摆脱百度地图的枷锁,我们不得不站在更高角度上考虑问题,考虑后续如何将系统做得更通用。将之前的博文中介绍的系统实现技术方案(BMap+MapV)拓展到支持更多主流地图引擎(高德地图、腾讯地图、谷歌地图等)

经调研,我们最终选定了大名鼎鼎的OpenLayer作为管理图层和绘制地图上层样式的Web GIS引擎。主要是考虑到两点。

  1. 在地图数据源方面,它支持各种类型的瓦片地图,既支持在线的,也支持离线的(这对我们很重要)。比如OSM, Bing, MapBox, Stamen, MapQuest等等;还支持各种矢量地图,比如GeoJSON,TopoJSON,KML,GML等等。
  2. OpenLayer社区活跃,可能会遇到的各种问题都可以找到解决方案,且后续更新也是有保障的。

因此,这次我们实现了一个Library并发布到了npm上,地址是:helium-ol-map

在使用这个库的过程中,我感觉这个库还是很好用的,最直观的感受就是之前十几行的代码现在仅需一两行就搞定了。经过封装,感觉像是使用了声明式的方式在编程,描述我要做一件啥事,而之前是一直在操心这这件事的实现细节。我想,应该是已经达到了做这个库的目的。

代码前后对比(注释部分为之前的业务代码,现仅需一行代码)

下文就简单介绍一下这个库的设计与实现过程中遇到问题的解决方案。

OpenLayer5

OpenLayer本身API繁多,在做这个库之前我并没用用过,我是看了一两天OpenLayers 3 Primer对OL3进行了简单的入门。这个部分我就不班门弄斧了,有需要的读者可以自行阅读理解,需要注意的是很多接口已经有所变动,以官网为准,不过了解基本的思想还是挺好的。

helium-ol-map的设计与实现

这个库的实现主要分为两步,第一步是确定使用的地图引擎(离线 or 在线);第二步图层管理方式。

确定使用的地图引擎

确定地图引擎之前,可以对比较通用的参数进行配置(同时也需要提供默认值)。比较通用的参数有:zoom/maxZoom/minZoom/center/origin/extent/elContainer等等

由于每个引擎都有自己对经纬度的标准,确定地图引擎后,就要指定经纬度的投影规则(比如百度地图是BD-09),然后对原始的经纬度向特定地图引擎规则转换,否则会造成地图的偏移。

接下来是指定TileVectorSource:

  • ol.source.Tile对应的是瓦片数据源,现在网页地图服务中,绝大多数都是使用的瓦片地图,而OpenLayers 3作为一个WebGIS引擎,理所当然应该支持瓦片。
  • ol.source.Vector对应的是矢量地图源,点,线,面等等常用的地图元素(Feature),就囊括到这里面了。这样看来,只要这两种Source就可以搞定80%的需求了。

如果是离线地图版,还需要指定离线地图的地址

好的,有了这些配置,就可以完成初始化地图了。

图层管理方式

另一个比较关键的点是对Layer的管理方式,这也是我与同事争执主要矛盾点。由于第一期的地图业务主要是我完成,最终我说服了他,将各层Layer设计为一个key/Value的Object,并提供增删改查的方法;他的方案是给地图提供一个默认Layer,然后遍历这个Layer的各个feature进行管理。

不过他一直坚持的一点是点线面的绘制应该是在地图这一层进行管理的,但由于图层管理方式采用了我的方案,也就是点线面绘制的Layer在要添加的时候不确定有没有,因此将绘制点线面的返回值设为了需要绘制的feature,到使用的时候指定加到哪个Feature上。

下面给出了一个该库使用的example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const MAP_TYPE = 'baidu-offline';
const map = new HeliumMap({
mapType: MAP_TYPE,
el: 'map',
center: [102.870848, 25.024963],
zoom: 10
});
const layerManager = new HeliumLayerManager({
map : map
});
var markerLayer = layerManager.createLayer();
layerManager.addLayer('markerLayer',markerLayer);
var marker = map.createMarker(102.870848, 25.024963);
layerManager.addFeature(marker,'markerLayer');
var lineLayer = layerManager.createLayer();
layerManager.addLayer('lineLayer',lineLayer);
var line = map.createLine([102.870848,24.024963],[102.870848,26.024963],{fillColor:"red",strokeColor:"red",strokeWidth:2});
layerManager.addFeature(line,'lineLayer');
var polygonLayer = layerManager.createLayer();
layerManager.addLayer('polygonLayer',polygonLayer);
var polygon = map.createPolygon([[102.870848,25.024963],[102.870848,25.034963],[102.880848,25.034963],[102.880848,25.014963],[102.870848,25.024963]]);
layerManager.addFeature(polygon,'polygonLayer');

遇到的一些坑

实现过程中坑肯定还是会有的,第一个坑就是批量创建feature的时候,ID如果不明确指定而采取deafault-value,那么后面的feature会因此覆盖前面的feature,因此批量初始化feature的时候要确保每个feature有独特的ID。

第二个坑是坐标转换过程中可能会出现转换失败,需要用try catch包裹住可能throw error的部分。

第三个坑是坐标偏移,这个问题在上篇博客中是实现的很好的,跟友商的实现方式进行过比较,这次跟友商回到了同一起跑线上。主要是OL3里fromLonLat这个function的默认参数不是我们想要的地图坐标标准,需要明确指定符合自己引擎的标准。

后记

短时间内上手OpenLayer也增强了我的信心,对于各种框架的使用,我越发找到感觉了。这是我离职前为公司做的最后一个项目了,也算是做到了善始善终

最后,祝福公司明天更好,祝福自己的明天更好!

参考

  1. OpenLayers 3 Primer
  2. npm:helium-ol-map
  3. OpenLayer
CATALOG
  1. 1. 前言
  2. 2. OpenLayer5
  3. 3. helium-ol-map的设计与实现
    1. 3.1. 确定使用的地图引擎
    2. 3.2. 图层管理方式
  4. 4. 遇到的一些坑
  5. 5. 后记
  6. 6. 参考