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有效地利用起來,變成共享單車,交友逛街,外賣快遞這些強交互服務。
底層技術一覽
說到地理信息,人家常常聯想到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的相關知識是非常小眾,但是非常實用的,本文只是走馬看花地介紹一下其發展厲害與部分知識點,深入下去,所有知識都能無限被放大。地圖顯示與地點定位是真實對應我們這個複雜的世界。世界有多複雜,地圖與定位就有複雜。得益於三大廠商的數據沉澱與高度抽象,我們開發變得更簡單了,公眾生活也變得更美好了。