地图服务在前端页面的某些场景下,尤其是交通和旅游相关业务中会经常见到。前端在进行地图相关业务的开发时,会遇到各种各样的问题,今天我结合一下自己之前业务开发中所遇到的一些地图相关的问题,跟大家分享一下前端地图开发过程中的一些经验。这里会省略掉一部分地图基础知识的介绍,有需要的小伙伴可以自行查阅一下相关资料。
一、选择哪个地图服务
前端进行地图相关业务开发时,需要根据具体的业务需求选择地图服务商。
对于某些地图属性需求较弱的地方,比如单纯展示一个地点在地图上的定位点,可以考虑使用静态的地图图片进行展示,这样可以直接调用地图服务的接口获取静态图片,而无需为此启用一整个地图服务。
具体选择哪一家地图服务商,可以大致参照下面的方法进行选择。
如果地图业务需要在APP原生页面运行,APP内已经有很成熟的解决方案了,直接参考APP内的实现方案进行选择;
如果为web端地图业务,需要进行具体的分析考虑,比如地图是不是涉及到海外数据信息的展示、业务的预估访问量、地图使用的成本等问题。
如果地图需求涉及到海外地图服务,那么目前来看,可选择的就只剩下谷歌地图、百度地图,当然,高德地图和腾讯地图目前也提供海外地图服务,只是目前仍处于试用阶段。最好的选择当然是谷歌地图,只是谷歌地图还有一些其他限制,由于某些众所周知的原因,谷歌地图在国内只能走cn域名的接口,而且这个接口只能在http协议下才能调用,不能走https协议。
如果地图仅仅涉及到国内地图服务,那么直接选择百度地图或者高德地图;
关于使用费用问题,需要结合具体的业务访问量、收益等因素综合考量之后进行选择。
二、关于地图的坐标系
坐标系维护着经纬度与地点的对应关系,同一地点在不同坐标系下的经纬度可能不同。因此在调用地图服务时要明确当前地图的坐标系和经纬度数数据的坐标系,可以通过做坐标系转换逻辑一致化数据。在数据存储中,建议中国大陆及港澳地区使用GCJ-02坐标系,其他地区使用WGS-84坐标系,并且在每个数据单元中包含一个坐标系类型字段。常见的地图坐标系有以下几种:
WGS-84
世界大地测量系统 (World Geodetic System, WGS) 是一种用于地图学,大地测量学和导航 (包括全球定位系统) 的大地测量系统标准。全球定位系统 (Global Positioning System, GPS) 使用的就是WGS-84参考系。Google和高德地图定位的的经纬度(国外)都是基于WGS-84坐标系的;但是在国内是不允许直接用WGS84坐标系标注的,必须经过加密后才能使用。
GCJ-02
又名“火星坐标系”,是我国国测局独创的坐标体系,由WGS-84加密而成,出于国家安全考虑,此坐标系所采用的混淆算法,会在经纬度中加入看似随机的偏移。在国内,必须至少使用GCJ-02坐标系,或者使用在GCJ-02加密后再进行加密的坐标系,如百度坐标系。高德和Google(.cn域名下的服务)在国内都是使用GCJ-02坐标系,另外高德地图、腾讯地图、搜狗地图都是使用的GCJ-02坐标系。可以说GCJ-02是国内最广泛使用的坐标系。
BD-09
BD-09是百度地图使用的地理坐标系,百度坐标系是在GCJ-02坐标系的基础上再次加密偏移后形成的坐标系,只适用于百度地图。目前百度API提供了从其它坐标系转换为百度坐标系的API,但却没有从百度坐标系转为其他坐标系的API。
在使用地图服务的时候,一定要注意坐标系的问题,经纬度数据所属坐标系一定要与地图展示所需的坐标系相一致,比如在拿到手机定位信息的时候,拿到的其实是WGS-84坐标,如果目标坐标系不是WGS-84,比如百度地图(使用的是BD-09坐标系),还需要进行相应的坐标转换才能用于展示,否则展示出来的位置与地图上的实际位置会有偏差。
三、谷歌地图使用的一些心得体会
我在之前的项目里,使用过谷歌地图,下面结合我的实际使用情况,聊一下Vue项目里谷歌地图的使用。
Vue项目下,可以使用vue2-google-maps这个包,使用示例:
import * as VueGoogleMaps from 'vue2-google-maps'
Vue.use(VueGoogleMaps,{
load:{
key: 'AIzaSyD-*******************WkU49Bk53nQ', // 你的谷歌地图的key
v: '3.34', // 你希望使用的API版本
libraries: 'geometry', // 你需要使用的库,地图初始化时加载
},
loadCn: true, // 写在这里才可以请求cn域名下的谷歌地图
disableDefaultUI: true
})
这样的话在template里就可以使用组件方式进行开发了。具体代码示例可以参考github上的ReadMe文档 ,还可以结合谷歌地图的官方文档进行使用。
- 关于地图实例初始化
地图初始化的时候,一定要记住不能设置地图的所在元素隐藏,这样会造成地图初始化时候瓦片的加载。地图隐藏相当于大小为0,这个时候地瓦片无法正常渲染,会影响地图的正常展示。
- 关于导航线路
谷歌地图的Javascript API
支持实现导航线路的绘制,只是非付费用户,导航线路上的控制点(就是指定起点终点后要求中途必须经过的点)数量有限制。如果你的控制点比较多,不能只画一条线来绘制出导航线路,这时候可以通过下边的方式解决:
后台使用谷歌地图Web Service 下的 Direction Service API
获取导航线路数据,然后存起来,统一传给前端,前端一段段的绘制在地图上,这样还可以对每一段polyline进行单独的控制,比如颜色、线宽等等。
后台使用谷歌地图Web Service获取导航线路数据,返给前端做线路渲染,前端使用谷歌地图Javascript API是无法直接渲染导航线路的,这个时候只需要拿到结果里的overview_polyline下的points字段,然后前端使用 google.maps.geometry.encoding.decodePath()
方法解析points字段可以获取到一系列坐标点,最后前端直接调用polyline方法可以把这些点绘制成导航线路,这样就可以一段一段地把导航线路画到地图上了。
当你的线路是固定的时候,这样存储起来可以大大减少地图服务的请求次数。
自定义类似infowindow的popup层
如果你想在地图上画出这样的popup层,你可以参考谷歌地图的官方示例 ,自定义一下实现这种popup层的方法,写好之后,你只需要在代码里把需要的dom写好,然后调用方法,就可以实现这种popup层了。
- 关于谷歌地图的样式定制
当你的地图样式需要高度定制的时候,你可以在地图组件上通过 style 属性春如地图的展示样式,这个style属性值是一个数组,可以设置很多地图的属性样式。
下边是一段示例:
styles: [
{
featureType: 'administrative.locality',
elementType: 'labels.text.fill',
stylers: [{color: '#d59563'}]
},
{
featureType: 'poi',
elementType: 'labels.text.fill',
stylers: [{color: '#d59563'}]
},
{
featureType: 'road',
elementType: 'geometry',
stylers: [{color: '#38414e'}]
},
{
featureType: 'road',
elementType: 'geometry.stroke',
stylers: [{color: '#212a37'}]
},
{
featureType: 'road.highway',
elementType: 'geometry.stroke',
stylers: [{color: '#1f2835'}]
},
{
featureType: 'road.highway',
elementType: 'labels.text.fill',
stylers: [{color: '#f3d19c'}]
},
{
featureType: 'transit',
elementType: 'geometry',
stylers: [{color: '#2f3948'}]
}
]
通过这个属性的设置,你可以定制地图的展示样子,道路颜色粗细、各种级别道路的展示与否,POI类型的展示与否等等很多东西你都可以自定义,从而实现地图的高度定制化。具体的使用请看官方示例,另外可以看官方文档。另外这里介绍一个很有用的网站,有很多谷歌地图的样式,喜欢哪个可以直接复制代码使用。
自定义谷歌地图的marker
如果你需要自定义marker(一般都会,默认的样子设计师应该不会喜欢),你就需要自己画。对于那些很不规则的形状,使用简单的css很难实现。这种情况下,你可以试试下边这种方案。你可以找设计师要icon的svg格式图片,然后查看源码里的path,自己根据这个path去画icon。
例如,在我的项目中,需要实现上图中这样的marker,示例代码是这样的
map组件上
:icon = icon
// icon变量定义
icon = {
path:"m26.5,1.5l-22.5,0c-1.38071,0 -2.5,1.11929 -2.5,2.5l0,23l6,-4.5l16.5,0c1.38071,0 2.5,-1.11929 2.5,-2.5l0,-18.5z", // icon的path信息
fillColor: "#ff5b4d", // 填充颜色
fillOpacity: 1, // 填充透明度
scale: 1, // 缩放倍数
strokeColor: "#ff5b4d", // 描边颜色
strokeWeight: 3, // 描边线宽
labelOrigin: {x:14,y:12}, // 设置label中心位置,使数字在icon内居中
anchor:{x:0,y:34}, // 定位marker的左下角位置到坐标点
zIndex: 200
}
marker底图是画上去的marker,数字序号是画上去的label,label的代码是这样的:
// 地图上 :label = label
label = {
text: i , //序号
fontSize: '14px', // 字号
fontFamily:'Din-bold', // 字体
color: '#ffffff' // 颜色
}
- 地图事件监听
当你使用vue2-goole-maps
时,你可能会用到下面这些事件监听。
1.google地图对象可用(地图初始化完成)
const _map = this.$refs.myMap;
// 监听map加载完成的promise,执行需要的操作
_map.$mapPromise.then((map) => {
// do something here
})
2.地图瓦片加载完成事件监听
google.maps.event.addListenerOnce(map,"tilesloaded",function(event){
// do something here
// 一般在这里加载数据更新地图视图
});
3.地图缩放、移动完成后闲置状态
google.maps.event.addListenerOnce(map,"idle",function(e){
// addListenerOnce 表示只监听第一次
// do something here
// 上一次地图操作完成,可以进行下一次变换了
});
google.maps.event.addListener(map,"idle",function(e){
// do something here
// 上一次地图操作完成,可以进行下一次变换了
});
4.地图缩放比例发生变化
google.maps.event.addListener(map,"zoom_changed",function(zoom){
// do something here
// 地图缩放比例发生变化了
});
// 使用地图组件的话,也可以调用组件内的方法
// 监听zoom_changed事件
_map.$on('zoom_changed', (zoom) => {
if(zoom > 16){
_map.$mapObject.setZoom(12);
}
})
这里还有很多,就不一一举例子了,具体请查看官方的文档。
谷歌地图使用的注意事项
1.谷歌地图不要轻易设置
display:none
,因为地图大小为0*0的时候,你无法对地图进行各种操作,相应的,你也不能在初始化的时候在地图组件上设置v-show隐藏它,这样你的地图刚开始是加载不出来的。如果某些情况下你实在要隐藏地图,试试设置透明度为0。2.谷歌地图的
fitBounds()
方法有时候不太好使,可以试试settimeout
异步操作吧,这个通常是有效的。3.地图上的marker使用图片(例如2倍图/3倍图)会造成marker偏大,而且比较模糊,最好的实践是使用svg的path画上去,这样marker会比较清晰,设计师不会因为这个找你麻烦。
4.谷歌地图在国内只能走cn域名的接口,而且只能走http协议,这一点很麻烦,目前我们的额项目基本上都是走的https协议,所以目前的话不到万不得已web前端的地图最好别走谷歌地图。后续需要看看公司统一的地图服务平台能不能很好地解决这个问题。
四、写在最后
这里只是介绍了一下谷歌地图使用的一些经验,不过地图服务的API都很相似,熟悉一个地图后开发其他地图应该可以很快上手。web地图开发中的坑比较多,遇到问题的时候可能需要花费很长的时间才能解决。遇到问题的时候可以去Google,还有查阅官方的文档,仔细阅读官方文档真的可以帮忙解决很多问题。实在没找到很好的解决方案,可以去问一下公司其他用过地图服务的小伙伴们有没有遇到类似的问题。
能力有限,只能写到这里了,但愿这篇文章能够对你的地图开发有一丢丢的帮助。好了不说了,又来新需求了,我要去写代码了。