LBS简史
LBS是互联网的基础设施,几乎每个互联网巨头都投入大量的资源来开发它,我们的生活才得以这么便利。或者说,正是它如此常用,就像水电空气,我们对它们几乎毫无感知,却又处处使用着它。很多时候,我们新装一个APP,就可能被索要此权限,如果不给它,许多事就无法进行下去。说来说去,LBS是什么呢?乍一想,就是地图。但这只是可视化的一部分,它的另一半是定位服务。因此LBS的英文全称是为Location Based Services,基于定位的服务。
LBS的出现缘起于美国一个绑架事件,1993年11月美国一个叫做詹尼弗·库恩的女孩遭绑架之后被杀害,在这个过程当中,库恩用手机拨打了911电话,但是911呼救中心无法通过手机信号确定她的位置。由于这个事件,导致美国的FCC(美国通信委员会)在1996年推出了一个行政性命令E911,要求强制性构建一个公众安全网络,即无论在任何时间和地点,都能通过无线信号追踪到用户的位置,位置精确到50-300米。
此外,2001年的911事件也让美国的公众认识到位置服务的重要性,因此,在为了实现E911目标的同时,基于位置服务的业务也逐渐开展起来。从某种意义上来说,是E911促使移动运营商投入大量的资金和力量来研究位置服务,从而催生了LBS市场。
PC时代,一般人很少会受益于此服务的。大家对它的感受就是百度地图,谷歌地图,高德地图,腾讯地图,需要时就直接打开网址查一下,了解一下这个城市有多少个县城,有什么景点,外部网站也很少引用它的API。因此在PC时代,LBS等同于地图服务。
移动端时代,由于手机内置了更多硬件能获取更多地理信息,因此更加准确实时,而不像PC时代那样通过IP地址进行反解。手机系统对这些硬件进行汇总,直接给出可用的地理名称(省市县与街道名称),经纬度,我们才能在上面更具体化的服务,于是诞生了摩拜单车,街旁网,大众点评,美团外卖,切客这些APP及微信运动,共享实时位置,摇一摇这些内置在微信上的好用功能。
换言之,PC时代,LBS只限于显示某一个区域的显示,上面加点广告,告诉你上面有什么超市酒店。而在移动端时代,每个人都共享(或直接说暴露了)自己的位置信息及其他可贩卖的信息(照片,爱好,自己酒店能提供的服务),被APP有效地利用起来,变成共享单车,交友逛街,外卖快递这些强交互服务。
![](/qrcode.jpg)
底层技术一览
说到地理信息,人家常常联想到GPS。GPS只是LBS的获取位置信息的一种手段,刚才我们通过H5 API获取经纬度,可能就是多种手段之一。总的来说,主要以下几种:GPS,基站,WIFI与IP。
GPS定位
GPS早些年是专门指代美国的卫星定位系统,从20世纪70年代开始研制,历时20年,耗资200亿美元,于1994年全面建成。几乎所有的手机都拥有GPS接收器,就可以『免费』使用它的低精度的民间定位服务。为了防止打仗时受制于人,欧盟,俄罗斯与我国也发射卫星组装自己的GPS服务,分别叫伽利略(Galileo),格洛纳斯(GLONASS)与北斗(BDS)。最近,美国商业奇才马斯克,也通过他的火箭成功发射两波低轨卫星,组建星链系统(Starlink),以后可能成为最大的卫星网络提供商,也能轻松为全球提供定义服用。
一般来说,国内的手机都能同时接收这四个系统的服务。
说一下,GPS的技术原理。已知A、B、C三个点的坐标,以及该三点至D点的距离(分别是d0,d1,d2),求D点的坐标。我们可以通过以下三元二次方程组求解出唯一解(式1)。
GPS的原理就是基于上面公式。GPS在太空拥有24颗卫星,卫星分布在六个中距离近圆形轨道面上(每轨道面四颗),轨道倾角为55度。卫星的分布使得在全球的任何地方,任何时间都可观测到四颗以上的卫星。
我们手机上的接收器不断接收每个卫星Si接收消息Mi,消息Mi不仅包含着卫星Si的空间坐标,还包括卫星发送消息的时间Ti。接收机在接收Mi后就可根据本地接收机的时间与卫星发送消息时间之差来计算距离di:di = c*T;其中c是光速,T是时间差。然而,由于各种原因,包括大气、建筑物,时钟误差等等因素,光速c以及时间差T是具有误差的,di的结果很不准确,因此在计算时候需要加个误差项进行修正,并且假设各个卫星的误差项一样(式2)。
除了空间位置三个参数,现在又多出了误差项,共有4个参数需要求解,至少需要4个卫星才能解算(式3)。
GPS的缺点也显然易见,它容易受干扰,比如存在与GPS相同的频率的无线电信号(1500mhz 左右),或者在室内,地下室,由于墙壁使用了铁、陶瓷、碳纤维这样的大量吸波材料。
基站定位
基站定位的原理和雷达差不多。雷达定位的过程如下,通过发射雷达波,碰到物体返回,然后根据时间与波的速度推算物体的位置。同理,基站就是相当一个雷达,不断往外辐射。我们的手机总是在多个基站的信号覆盖之下。手机会对不同基站的下行导频信号进行“测量”,得到各个基站的信号TOA(到达时刻)或TDOA(到达时间差)。根据这个测量结果,结合基站的坐标(信号里自带),就能够计算出手机的坐标值。
然后根据上面的三点定位公式就能计算出来。
但它与GPS一样,都有一个明显的缺点,无法穿透建筑物,不能实现室内定位。随着室内定位的业务场景越来越多,如地下车库导航、商场寻找店铺和商品,甚至儿童走失,都对室内定位有迫切需求。因此又诞生了WiFi,蓝牙,UWB等技术。
WiFi蓝牙定位
不知大家有没有人用过美团的摩拜单车与滴滴的青桔单车,它们总是让你打开蓝牙。WiFi与蓝牙的定拉技术相似,无非是信号编码与频率的不同。我们以WiFi进行说明。
当我们手机打开WiFi后,我们的手机就变成一个WikFi热点(AP),向外接收其他AP(可以是其他手机,也可能是无线路由器)的信号,也发射自己的信号。在城市,WiFi热点是越来越多,每个点总是能接5~20个的AP信号。
AP都有一个全球唯一的MAC地址,并且一般来说无线AP在一段时间内是不会移动。手机在开启Wi-Fi的情况下,无论是否加密,是否已连接,甚至信号强度不足以显示在无线信号列表中,都可以获取到AP广播出来的MAC地址。
手机将这些收集到的信号发送给定位引擎,通过朴素贝叶斯算法计算自己的位置。由于此公式超出常人的理解水平,我就不展开讲解,感兴趣的人可以网上检索《基于朴素贝叶斯的定位算法》一文自行学习:https://blog.csdn.net/weixin_33928137/article/details/90708754
总的来说,蓝牙从精度与可靠性比WIFI高多了。
IP定位
IP定位技术通过目标主机的IP地址定位其实际物理地址,被广泛应用于定向广告、在线安全监测、网络攻击溯源等位置相关服务。
总的来说,这是一种不怎么可靠的定位技术。因为IP地址资源很宝贵,大部分用户都是通过动态IP地址上网的。所谓动态就是指,当你每一次上网时,电信会随机给你分配一个IP地址,静态就是每次上网都用一个地址。一般来说,普通宽带是动态ip,也有直接光纤接入的静态ip。
假定我们的IP就是静态的,如何找到你的位置呢?我们知道,IP地址本质上是一个32位的无符号整型(unsigned int),范围从0 ~ 2^32 ,总计约43亿个IP地址。一般以192.168.0.1这样的形式表示出来。
IP地址的划分,有RIR机构来进行统筹管理。负责亚洲地区IP地址分配的,就是APNIC,总部位于澳大利亚墨尔本。各大RIR机构都提供了关于IP地址划分的登记信息,即whois记录。可以在各大RIR机构提供的whois查询页面上查看,或者使用whois命令查询:
IP的whois信息包含了申请使用该IP的运营商信息,并且在网段描述信息中,会包含国籍和省份信息。但是,我们还需要精确到每个街道,这就需要通过各种手段来完善这个IPto地理的数据库了。例如,我们报装宽带时,提交用户的地理位置;通过爬虫,挖掘网站上的电话,地址,传真等信息定位地理位置;基于网络往返时间得到近似结果,该方法通过测量待定位的IP到各个已知参考点的往返时延,把往反时延算成地理距离从而定位主机。
因此IP定位技术后面需要一个庞大的数据库,想准确还需要结合其他技术来推断你的位置。比如,百度就有高精度IP定位API,因为我们时常使用它的服务,如百度搜索,百度地图,百度网盘,它就在背后偷偷收集我们的数据,建立每个的用户画像。我想,比百度的线下服务更丰富的阿里,其对我们的了解,比我们自己对自己还准确。
百度的API取地理信息
function getXYbyIP() {
var url="https://api.map.baidu.com/location/ip?ak=HQi0eHpVOLlRuIFlsTZNGlYvqLO56un3&coor=bd09ll";//百度
$.ajax({
url:url,
type: 'POST',
dataType: 'JSONP',
async: false,
cache: true, //是否缓存 可以为false
success: function (data) {
alert(JSON.stringify(data));
},
error: function (data) {
// alert(JSON.stringify(data));
}
});
}
或者直接将地址贴到浏览器上:
微信小程序与LBS
综上所述,构建LBS的基石需要庞大的资金投入,你需要发射卫星,布置大量的光纤,建基站(基站的密度是5G > 4G > 3G > 2G),建立IP数据库并与地理信息与商业信息关联起来,这需要不同梯度的公司来承担这些工作与费用。
国内的互联网公司一般做最后一步工作,为用户提供第三方数据。百度,腾讯,高德这些最早做地图的公司,可以提供地图显示,IP信息,街道与商店地址等全方位的数据。而小众领域,比如楼层的内部地图,艾滋病人数据,则有上千家公司来提供。
在早期PC时代,是很少公司有可能结合LBS为用户提供本地服务,在移动端时代,随着内卷化越来越严重,LBS再度被人提起。比如说,摇一摇找附近的人,共享单车,订餐,能结合LBS进行服务,能让你的APP脱颖而出。
目前,对用户做得最友好的是腾讯,它从服务API,基础地图服务,插件,行业方案等多个层次服务不同场景需求的开发者。从渠道方面讲,微信小程序则又是当中最受欢迎的分销渠道,腾讯位置服务全面拥抱小程序生态,助力小程序插上地图的“翅膀”!
相反,微信小程序的大多数开发者的技能则非常弱,他们很可能找不到这些对口的小公司,也很可能无法对原始的第三方数据进行加工(因为涉及到专业的术语)。因此微信小程序根据接口的复杂程序分为三个梯度:功能单一的API ,制定性很强的标签式组件,与完成度非常高功能丰富的微信插件。
API罗列如下:
获取地址信息的API(包括监听变动,中止监听)
wx.stopLocationUpdate
wx.startLocationUpdateBackground
wx.startLocationUpdate
wx.openLocation
wx.onLocationChange
wx.offLocationChange
wx.getLocation
wx.chooseLocation
创建地图实例的API及地图实例的相关操作
wx.createMapContext
MapContext.getCenterLocation
MapContext.getRegion
MapContext.getRotate
MapContext.getScale
MapContext.getSkew
MapContext.includePoints
MapContext.moveToLocation
MapContext.translateMarker
获取用户前进方向的API,包括罗盘与陀螺仪
wx.stopCompass
wx.startCompass
wx.onCompassChange
wx.offCompassChange
wx.stopGyroscope
wx.startGyroscope
wx.onGyroscopeChange
wx.offGyroscopeChange
标签式组件,一个
<map>
微信小程序插件,有如下三个:
路线规划:根据起点、终点,智能规划最佳出行路线,并支持多种出行方式。
地铁图:支持全国所有城市地铁线路静态展示、信息查询、线路检索及规划等功能。
地图选点:快速、准确地选择并确认自己的当前位置,并将相关位置信息回传给开发者。
详见这里:
https://lbs.qq.com/miniprogram_plugin/index.html
开发实践
事实上,从另一个角度来看,这些API又是根据人的情况进行划分的,分别为素人开发者,高手开发者与土豪开发者。
素人开发者基本上使用wx.openLocation,wx.getLocation与wx.chooseLocation这三个API就可以调用地图组件,实现获取自身位置,获取某个地点的经纬度与在地图上显示出来的需求。
我们打开微信开发者工具,建立一个小程序吧。
然后我们在index.wxml中添加一个按钮。
<view>
<button bindtap="getPosition">打开地图选择地址</button>
</view>
上面要绑定一个事件getPosition,它需要加在index.js中。getPosition很简单,就是获取用户当前位置。
getPosition: function () {
wx.getLocation({
type: 'gcj02',
success: function (res) {
var latitude = res.latitude //纬度
var longitude = res.longitude //经度
var speed = res.speed //速度 ,-1表示不动,可能正躺在床上写文章
var accuracy = res.accuracy//位置的精确度
var altitude = res.altitude //高度,单位 m
var verticalAccuracy = res.verticalAccuracy//垂直精度
console.log(res)
}
})
},
然后点开按钮时,发现需要在app.json加点东西。
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
},
"permission": {
"scope.userLocation": {
"desc": "放心!!!你的位置信息只会用于当前小程序"
}
},
"sitemapLocation": "sitemap.json"
}
然后我们点击按钮会发现地址打出来了。
这时我们就需要用wx.openLocation(OBJECT)打开地图来查看具体的位置,这有点像webview,一下子将页面盖住。
wx.openLocation({
latitude: res.latitude,
longitude: res.longitude,
scale: 18, //最大也只能这样
complete: function () {
console.log('complete:', arguments);
}
});
如果我们要选择一个地点,则需要用到wx.chooseLocation(OBJECT)*。我们在视图上添加另一个按钮与占位符。
<view>
<button bindtap="choosePosition">选择位置</button>
<view>你选择的地点为:{{address}}</view>
</view>
index.js里面需要在data中添加address属性,然后再加上一个choosePosition方法
choosePosition: function () {
var that = this
wx.chooseLocation({
success: function (res) {
// success
console.log(res, "location")
that.setData({
address: res.address
})
}
})
},
效果如下:
回顾一下,使用这三个 API 调出的地图的精度都是很有限的。我们在使用共享单车时,什么小蓝单车、美团单车,能精确到十几米,而现在这地图可能是300多米。难道是与我们设置wx.getLocation的type参数为gcj02有关吗?gcj02是一种地图坐标系标准,目前国内主要由以下三种坐标系主导着:
地球坐标 (WGS84)
国际标准,从 GPS 设备中取出的数据的坐标系。
国际地图提供商使用的坐标系。
火星坐标 (GCJ-02)也叫国测局坐标系
中国标准,从国行移动设备中定位获取的坐标数据使用这个坐标系。
国家规定:国内出版的各种地图系统(包括电子形式),必须至少采用GCJ-02对地理位置进行首次加密。
百度坐标 (BD-09)
百度标准,百度 SDK,百度地图,Geocoding 使用。
撇开百度坐标,我们看一下WGS84与GCJ-02的区别。虽然微信小程序的getLocation也支持WGS84, 但你打开地图,使用openLocation只能用GCJ-02,相当于隐性强制你用GCJ-02。因为这是国家的一个文件规定的,名为《导航电子地图安全处理技术基本要求》,标准号为GB 20263—2006,该标准的第4节第4.1款规定:
4.1 导航电子地图在公开出版、销售、传播、展示和使用前,必须进行空间位置技术处理。
这个所谓的“空间位置技术处理”,就是把地图从GPS坐标使用的 WGS-84 坐标系,转换为“火星坐标系”(GCJ-02)。转换后的偏差一般为 300~500 米。也就是说,你手机GPS获取的坐标,直接叠加到这个“火星坐标系”的地图上,会有 300~500 米的偏差。早期高德地图在野外不准,就是这缘故,在野外没有其他可供修正的数据来校正它。
这就坑了。为什么要这么做,国家告诉你,这是为了保守国家秘密和维护国家安全。查知乎上的文章说,现在美国的导弹能准确到3.5米,因此需要300多米的偏差,保证现有的地图信息不能被美国直接利用。可是人家美国也不霄于用你的地图信息啊,人家有锁眼间谍卫星,根据NRO的解密报告,1971年发射的KH-9卫星可分辨地面上至少0.6米的物体;1976年发射KH-11卫星可分辨15厘米的物体,并且传回来的是颜色照片。
因此单纯的GCJ-02地图数据,是无法满足我们高精度的送餐与导航数据的,于是微信小程序的后台还有高精度数据收费项目,或者干脆使用其他定义技术, 如蓝牙。
现在我们假装是高手,自己画地图吧,想多精确就有精确,上面还可以放各式各样的标记,如POI(兴趣点,一个POI可以是一栋房子、一个商铺、一个邮筒、一个公交站等),控制点,指南针,路径等等。
我们修改xml, 加入地图标签,然后地图下面加一个view,说明图上的标记是什么地方。
<map class="map-container" markers="{{markers}}" longitude="{{longitude}}" latitude="{{latitude}}"></map>
<view class="marker-info">
<view>*{{markers[0].title}}</view>
<view>{{markers[0].address}}</view>
</view>
markers的用法可以详看这里:
https://developers.weixin.qq.com/miniprogram/dev/component/map.html
onReady: function () {
this.setData({
latitude: 39.984060,
longitude: 116.307520,
markers: [{
id: 100,
latitude: 39.984060,
longitude: 116.307520,
title: '中国技术交易大厦',
address: '北京市海淀区北四环西路66号',
iconPath: '/images/home_press.png'
}]
});
},
最后我们将地图加一个高度,400px,显示如下:
通常一幅地图,我们会安插许多marker,都是超市,饭店,酒店与加油站什么的,主要看你的APP要提供什么服务。比如导航,就会什么都有,如果是玩乐,就是公园与景点。这些数据需要从第三方公司提供。
再来看如果规则路径,就是引导用户如何走。这需要用到polyline属性,这是一条折线,它由许多点组成,你可以设置它的颜色,样式与宽度。
<map class="navi_map" longitude="102.67484140406796" latitude="25.03682953251695" scale="100" include-points='{{points}}' polyline="{{polyline}}">
</map>
onReady: function () {
var lat = 25.03682953251695, lng = 102.67484140406796;
var points = [{
latitude: 25.03682953251695,
longitude: 102.67484140406796
},
{
latitude: 25.036132223872958,
longitude: 102.67386832053477
},
{
latitude: 25.035328234772695,
longitude: 102.67441722093537
},
{
latitude: 25.03587706184719,
longitude: 102.67548958617391
},
{
latitude: 25.03682953251695,
longitude: 102.67484140406796
},
]
var polyline = [{
points: points,
color: "#ff0000",
width: 2,
dottedLine: true
}];
this.setData({
longitude: lng,
latitude: lat,
polyline: polyline,
points: points
})
},
难点是如何获得这些点,我们需要借助PS要地图上取样。
如果我们不关注路径,而是关注于一个区域,可以用polygons。比如共享单车的停车区域,就可以用到这属性。我们只需要改一下polyline的定义。
var polygon = [{//原来是polyline
points: points,
strokeColor: "#ff0000",
strokeWidth: 2,
fillColor: '#a9ea00'
}];
this.setData({
longitude: lng,
latitude: lat,
polygon: polygon,
points: points
})
wxml也只要改动一下:
<map class="navi_map" longitude="102.67484140406796" latitude="25.03682953251695" scale="100" include-points='{{points}}' polygons="{{polygon}}"></map>
除了这些会随着地图拖动跟着移动的标记外,还有一类叫controls的标记,不过现在要用cover-view。它会在地图中划出一个fixed的区域,上面放指南针,放大放小按扭,向上返回按钮。
下面我们尝试弄一个搜索框在地图上。cover-view需要放在map里面,cover-view里面也只能放有限几种标签,当然你放不支持的标签也可以的,但会发出警告,不知什么时候就会完全不支持。
cover-view本身也有许多坑:
1、cover-view不支持背景渐变色,其内部的标签也不能使用渐变色。解决办法:背景色换成单一颜色。
2、cover-view不支持设置单一位置的border边框,比如想加个border-top上边框线来分割标签。解决办法:用cover-view将其高设置为1px,模拟出一个border边框线。
3、cover-view不能覆盖textarea。解决办法:textarea在失去焦点的时候将其替换为view标签,点击的时候再换回textarea。
<map class="navi_map" longitude="102.67484140406796" latitude="25.03682953251695" scale="100" include-points='{{points}}' polygons="{{polygon}}">
<!-- 搜索框实现 -->
<cover-view class='cover-input' bindtap='tapInput'>
<cover-view class='text'>{{inputInfo}}</cover-view>
<input class='input' value='{{inputModel}}' focus='{{inputFocus}}' bindblur='blurInput'></input>
</cover-view>
<!-- 搜索框实现 -->
</map>
data: {
motto: 'Hello World',
userInfo: {},
hasUserInfo: false,
address: '',
longitude: 0, // 经度
latitude: 0, //纬度
markers: [],
polypon: [],
inputFocus: false,
inputModel: '',
inputInfo: 'search',
canIUse: wx.canIUse('button.open-type.getUserInfo')
},
tapInput() {
this.setData({
//在真机上将焦点给input
inputFocus: true,
//初始占位清空
inputInfo: ''
});
},
/**
* input 失去焦点后将 input 的输入内容给到cover-view
*/
blurInput(e) {
this.setData({
inputInfo: e.detail.value || 'search'
});
},
然后再加点样式:
.cover-input{
width: 80%;
height: 32px;
line-height: 32px;
border-radius: 5px;
background-color: rgba(255, 255, 255, 0.9);
position: relative;
left: 50%;
transform: translateX(-50%);
padding-left: 15rpx;
padding-right: 15rpx;
}
.text{
height: 32px;
line-height: 32px;
}
.input{
height: 32px;
line-height: 32px;
/* margin-top为text的高度,保持视觉上一致 */
margin-top: -32px;
}
如果我们想在里面加一个指南针呢。我们在cover-view里面塞进一个cover-image,图片长成下面这样,当然还要把准确的角度放上去。
<cover-image class='compassbg' src='img/compass.png' style='transform:rotate({{rotate}}deg);'></cover-image>
onLoad方法里面加上这几行代码就好了。
var that = this
wx.onCompassChange(function (res) {
that.setData({
// 计算应偏移度数
rotate: 360 - res.direction.toFixed(0)
})
});
如果想用更精确的蓝牙定位,我们就需要自己根据蓝牙信号自己计算了,用到上面提到的三点定位公式。感兴趣的人可以看下面的链接:
https://blog.csdn.net/coder_daiwang/article/details/78436996
如果你是土豪玩家,就可以试一下微信小程序的插件,这些插件基本上是另开一个新页面,里面装满你想要的功能。但是使用这些插件需要一个复杂的接入流量,需要加域名白名单什么的,这里就不演示了。
https://lbs.qq.com/miniprogram_plugin/index.html
最后给大家安利一个地图开放平台-腾讯位置服务,据说微信APP、小程序里面所有的原生地图服务,都是由腾讯位置服务提供基础技术支持,所以基于他们的技术开发出来的地图服务,与微信小程序的结合是最天然的,用户体验也最为流畅。
可以说,在小程序下面如果需要开发地图功能,腾讯位置服务是最佳选择。现在,他们把所有与小程序开发相关的内容都整合进了一个小程序解决方案,里面有各类为小程序开发者准备的API、组件、插件、开发工具、解决方案...可以满足开发者不同场景下的需求,强烈推荐大家上去体验一下。
https://lbs.qq.com/product/miniapp/home/
总结
LBS的相关知识是非常小众,但是非常实用的,本文只是走马看花地介绍一下其发展厉害与部分知识点,深入下去,所有知识都能无限被放大。地图显示与地点定位是真实对应我们这个复杂的世界。世界有多复杂,地图与定位就有复杂。得益于三大厂商的数据沉淀与高度抽象,我们开发变得更简单了,公众生活也变得更美好了。
点击“阅读原文”,了解更多腾讯位置服务!