分享一下如何製作專業的手繪電子地圖。
一、智慧導覽系統介紹
手繪電子地圖,就是把手繪地圖覆蓋到地圖上,遊客或者普通使用者,可以在手機上通過地圖的連結(或者現在流行的小程式)開啟使用。是一種使用非常方便,集「視、聽、路徑規劃、實時導航」等諸多功能於一體的智慧導覽系統。也是現在很多景區為遊客提供的增加便捷性和遊玩體驗的一項功能。
最重要的兩點,我認為是:
1.手繪圖本身
手繪圖的美觀度、清晰度、完整度、準確度,決定了圖的檔次格調的高低,也是手繪設計師的技術水平、設計能力、規劃能力、溝通能力等綜合性體現。
2.實時導航功能
如果說手繪圖本身是面子,讓人對地圖有第一印象和直觀感受,那麼實時導航功能則是地圖的靈魂和裡子。沒有實時定位和動態路徑規劃導航功能的地圖,只是一個可觀而不可用的花瓶,沒有使用價值。沒有準確的而高效的實時導航演演算法,就不能把地圖價值在遊客手裡發揮到最大。
因為多年的從業經驗,我從一個純技術的角度來分享一下如何實現這樣的「智慧導覽系統」。
二、智慧導覽系統功能
先看一個參考範例圖(根據實際情況,只新增了必要的功能):
參考範例圖
1.基礎功能
地圖功能的設計,包括需要哪些功能,需要怎麼展示,地圖點位的圖示等細節,不一而足。每個需求方可能要的也不一樣。但總體來說,可能包含如下:
景點介紹:這是智慧導覽系統最基礎的功能之一。在系統後臺可以在地圖上新增景點標註,並完善相應的介紹資訊,包括圖片、文字、圖文、語音、視訊等內容。
服務設施:在地圖上新增區域內的服務設施,如廁所、遊客中心、停車場等內容。
商家資訊:在地圖上新增區域內的商家,如酒店、餐飲、特產等商家。使用者可檢視商家介紹、商家產品,並可支付下單購買。
景區公告:在後臺新增景區最新公告,如閉園通知、開園通知、最新活動等資訊。
線路推薦:在後臺設定推薦的線路及景點,遊客在地圖上可點選檢視,並在地圖上直觀的規劃出推薦的路徑,模擬導覽遊覽。非常直觀明瞭。
諮詢電話:可在後臺設定景區的諮詢、客服的聯絡方式,如電話、微信、微博等。
景區720:可拍攝製作景區的720全景,並在地圖上標註展示,更直觀的向遊客展示景點的實景。
2.增強功能
智慧導覽系統還應該具備如下增強功能,才能更好的增強客戶的服務能力、滿足使用者的使用需求。這也是當前電子手繪地圖系統的重點和難點。
實時定位:基於使用者端的位置獲取介面(如上文,可能大部分是微信環境裡,或者瀏覽器環境),實時獲取使用者當前的位置資訊。當然,位置的精度可能會受到諸多方面因素的影響,如天氣、建築物遮擋、裝置本身的效能或GPS模組的精度、網路狀況等。在理想的情況下,精度甚至可達到10到20米左右的偏差。這種精度,在民用已經算是極致了。為什麼不給出更精確的精度,可能一方面是裝置、技術等因素限制,另一方面,從安全形度來看,可能這也超出民用的範疇。當然,話說回來,從我們的實際使用場景來看,這樣的精度其實已經遠遠的滿足我們的需求了。這裡說個題外話,為什麼我們在手機上使用地圖APP導航的時候,不管是駕車還是步行,看上去都能做到精準無誤呢?(如駕車,在路口轉向時,基本上達到米級的精度)關於這一點,我是這樣思考的:地圖APP是基於裝置的定位,然後再結合當前使用者的導航方式、路徑規劃、裝置朝向、速度等多方面綜合的因素,用強大的後臺演演算法為使用者的當前位置做了智慧的「糾偏」。那為什麼我們智慧導覽系統不做這樣的糾偏呢。從實際出發,這一方面是我們的資料量遠遠不夠豐富完善,另一方面是「價效比」不高。在園區內的導航,不需要這樣的「糾偏」已經可以滿足需求,而要做這樣的「錦上添花」的功能,則需要付出指數級甚至更高的成本和代價。
動態路線規劃:基於前端裝置「實時定位」的能力,智慧導覽系統實時計算使用者當前位置和指定目的地的路徑。指定的目的地,可以是地圖上標註的任意一個點位,如景點、服務設施、商家等。這一點,和地圖APP的導航功能非常類似。園區內很多情況下,沒有地圖APP平臺採集路線,所以需要我們人工在智慧導覽系統後臺標註和實際情況一致的路線,或者通過智慧導覽系統提供的路線採集工具,安排人員現場採集。其實,地圖平臺的路線採集也是這樣做的。各大地圖平臺,都有國家發放的專業牌照,然後通過路線採集人員駕車或步行,採集、更新每個城市的道路(仔細想想,這是一個多大的工作量和成本,我們每個人都可以免費的使用這些服務,感謝他們的付出成本和辛苦)。
自動觸發:基於「實時定位」和「動態路線規劃」能力,使用者達到系統設定的點位附近時,可以自動觸發後臺設定的各種功能,如自動播放景點的語音講解、展示圖文內容,或推播商家的優惠券等。為使用者提供便利、人性化的智慧服務。
多語言:多語言是智慧導覽系統滿足國際化需求的一個增強功能。系統具備基本的漢語、英語選擇,還具備很便捷的擴充套件其他語種的設計和功能。
3.地圖個性化
智慧導覽系統不應該是一個單純的功能性的系統,還應該有更多豐富的、多元化的功能,為使用者提供更多個性化、趣味化的服務。
個人中心:系統具備使用者個人中心的功能。使用者可以對地圖上的景點進行點贊、評論等操作,然後可在個人中心檢視。
行銷、廣告:後臺可在地圖上新增固定的行銷、廣告等資訊,增強能力和滿足運營需求。
優惠券:後臺可設定地圖上的商家傳送優惠券,吸引、導流遊客進店,帶動消費。
尋寶遊戲等活動:後臺可在地圖上新增尋寶指引點位,引導遊客到指定地點掃碼積星對話獎品。
標註圖示動效:所有標註點的圖示,可設定一些動效,增加地圖的趣味性和個性化。
三、技術棧的選擇
從這裡開始後面的內容,有一定的行業背景或經驗的人,能更好的理解。我儘量說得通俗易懂一些。如果你覺得一些專業名詞不明白是什麼意思,可以直接跳過。
現在絕大部分業務系統的開發,都是基於一個成熟的技術棧來實現。這樣可以極大的節約基礎設施的成本,而且效率得到極大的提高。甚至某些系統或行業的應用及系統,不基於一些成熟的技術棧,想要完全自主開發,幾乎是不可實現的。
智慧導覽系統,建議基於這樣的一個技術棧:
1.伺服器
使用成熟的雲平臺,國內成熟好用的幾家,業內人都知道。
2.前端平臺
如今最大的前端平臺是微信小程式,還有就是瀏覽器直接通過連結開啟。不過就智慧導覽系統來說,大部分情況下,微信小程式也是基於Webview控制元件來呼叫,本質上和瀏覽器的直接開啟沒什麼區別,就是HTML5+JavaScript+CSS3來實現。但是微信的生態內,會有一些微信開放的額外的一些介面和功能,比如說:可以使用微信提供的位置獲取介面,來為遊客獲取更精準的定位。
3.地圖平臺
智慧導覽系統只能基於地圖的開放平臺來實現。目前國內幾家大而成熟的地圖平臺:高德地圖、百度地圖、騰訊地圖。建議首選高德地圖,次選百度地圖。為什麼呢?
地圖有個「層級(Zoom)」的概念,就是使用者開啟地圖,放大縮小,就是顯示的不同的層級。現在地圖平臺開放的層級,高德地圖最大為19級,百度地圖為18級,而同級別的情況下,高德地圖也更大一些。因此,建議選擇高德地圖,可以做更精細化的地圖。騰訊地圖多不做介紹。
另外,如果涉及國外的景區或地點,那谷歌地圖是不二的選擇。國內的地圖在國外沒有完整的內容,而且存取速度也很隨緣。
四、開發介紹
具體開發內容,這裡主要介紹涉及地圖核心的部分。其他比如資料庫設計、後臺管理系統等不做過多介紹。
1.開發語言選擇
後端開發:目前流行的開發語言如Java、PHP、Python等都是不錯的選擇,每個語言都有自己的特色。就個人而言,我比較喜歡Java和PHP,Java純物件導向,適合大型專案,執行速度較快;PHP靈活簡單,開發效率很高,現在利用有Swoole這樣的擴充套件,也可以做到執行速度很快。
前端開發:前端開發老生常談,JavaScript+HTML5+CSS3,千年不變的三板斧。但是可選擇的框架或者庫現在比較多,JavaScript有之前非常流行的JQuery庫,也有現在很火的Vue等框架。總之,做為開發者,你最擅長的可能就是最適合的選擇。當然,你也可以選擇學習新的知識和技術代替曾經最擅長的。
2.地圖基礎知識
這一點,是核心,基礎中的基礎,原理也比較複雜。因為地球是一個球體,是立體的,並不是天圓地方的一個平面,所以,首先,需要引入一個叫「座標系統」的概念。
當前常見的座標系主要有三種:
地球座標系:WGS84,常見於GPS裝置,Gooogle地圖(非中國區域)等國際標準的座標體系 。
火星座標系:GCJ-02,中國國內使用的被強制加密後的座標系,如高德地圖、騰訊地圖、谷歌地圖(中國區域)的座標就屬於此類座標系。
百度座標系:BD-09,百度地圖所特有的座標體系,它在火星座標系的基礎上又進行了一次加密處理。
但是我們使用地圖的時候,卻又只能看到平面的地圖,因此,又有了另外一個概念:墨卡託投影。
墨卡託投影的主要功能,就是讓三維立體的地圖座標能在二維平面上顯示。有點類似於《三體》裡的二向箔的功能,把三維繫統二維化。
二維化之後的平面,會被分割為一片一片的小圖,或者換句話說,由一片一片的小圖拼成了二維的地圖。而這個小圖,叫「瓦片圖」。這又是一個重要的概念。後文會繼續細說。
而瓦片圖的開始點(最左上角,或最左下角)在地球的什麼地方呢,因為座標系的不同,每個地圖可能也不一樣。所以,同一個經緯度,在不同的地圖平臺上,對應的瓦片圖的序號可能都是不一樣的。其中詳細的原理和規則演演算法,這篇文章說得比較詳細:
國內主要地圖瓦片座標系定義及計算原理
3.地圖平臺介紹
高德、百度、騰訊、谷歌地圖開放平臺介紹。
首先祭出開放平臺檔案,這是基於平臺開發的基礎:
高德地圖:https://lbs.amap.com/api/javascript-api/summary
百度地圖:https://lbsyun.baidu.com/index.php?title=jspopular3.0
騰訊地圖:https://lbs.qq.com/webApi/javascriptGL/glGuide/glBasic
谷歌地圖:https://developers.google.com/maps/documentation/javascript
每個地圖的API,大同小異。從細節來說,騰訊地圖的介面和高德地圖差別最小。
值得一提的是,在地圖上畫線(主要是導航的線路規劃標識),谷歌地圖沒有直接給出繪製虛線的介面,而國內的地圖平臺都有。這一點也體現了國內和國外的一種思維的差異。
另外還有一個細節,標註圖示旋轉(比如導航時,箭頭圖示跟隨人的方向旋轉)介面,高德地圖的旋轉的中心點不是圖示中心,而是圖示外層父元素的點位,因此轉向時,給人的感覺是自身的位置也在畫一個圓圈,而百度地圖沒有這個問題。當然這個問題也不是無法解決,我們可以通過自己編寫轉向的CSS,利用JavaScript來控制圖示以中心點來轉向。
圖示圍繞父元素邊點轉向
圖示圍繞中心點正常轉向
每個地圖平臺在手機端的表現和體驗也有些差異。我個人覺得高德地圖最流暢順滑,百度地圖次之。
還有另外一點細節,就是關於瓦片圖(後文細說),一樣的圖,在騰訊地圖上會有非常細微的差別(騰訊地圖允許級別Zoom為小數,在兩個級別之間,還可以有多個過度值,而其他地圖只能是整數,這是一個更人性化的設計,但卻導致了瓦片圖變得模糊了一點)。
4.瓦片圖覆蓋到地圖上
瓦片圖是尺寸為256px*256px的正方形圖片。這樣的圖片,像瓦片蓋房一樣,覆蓋為整個地圖,所以稱為「瓦片圖」。
瓦片圖的設計是一個非常精妙的設計,解決了地圖圖片太大的根本性問題,節約了伺服器、使用者端裝置的記憶體,按視覺範圍內載入圖片,也節約了網路流量。
這是一種前端的「懶載入」思想的體現,也是和前端的「雪碧圖」剛好相反的設計(關於「懶載入」和「雪碧圖」,這裡不做過多介紹)。
因此我們可以發現,不管什麼解決方案,都有相應的使用場景,也有相反的侷限性。不可脫離場景,一概而論。
瓦片圖覆蓋到地圖,這是整個手繪電子地圖最核心、最基礎的設施和功能。因為此,我們自己繪製的精美地圖,才能夠覆蓋到地圖平臺上,做成我們個性化需求的地圖。具體的實現,並沒有想象的那麼複雜。當然,經驗豐富的程式設計師,可以設計出更科學的演演算法和載入邏輯。這裡拋磚引玉,做一個範例(高德地圖):
1 var fileHost = 'https://yourfilehost.com/';
2 var tileLayer = new AMap.TileLayer.Flexible({
3 createTile: function (x, y, zoom, success, fail) {
4 var imagePath = fileHost + '/tilefile/' + zoom + '/x + '_' + y + '.png';
5
6 var div = document.createElement('div');
7 var img = document.createElement('img');
8 img.onload = function () {
9 div.appendChild(img);
10 };
11 img.crossOrigin = "anonymous";
12 img.onerror = function () {
13 fail()
14 };
15 img.src = imagePath;
16
17 success(div);
18
19 }
20 });
21 tileLayer.setMap(map);
這裡AMap.TileLayer.Flexible方法是核心,這是高德地圖提供的使用瓦片圖的一個介面。他提供了一個div層(className為「amap-layer amap-flexible」)覆蓋在底圖之上,然後允許此方法返回仁義的元素,填充在256*256的瓦片圖的方格里。因此,這裡其實也可以更簡單的直接返回一個img元素而不用div:
1 var fileHost = 'https://yourfilehost.com/';
2 var tileLayer = new AMap.TileLayer.Flexible({
3 createTile: function (x, y, zoom, success, fail) {
4 var imagePath = fileHost + '/tilefile/' + zoom + '/x + '_' + y + '.png';
5 var img = document.createElement('img');
6 img.onload = function () {
7 success(img);
8 };
9 img.crossOrigin = "anonymous";
10 img.onerror = function () {
11 fail()
12 };
13 img.src = imagePath;
14 }
15 });
16 tileLayer.setMap(map);
以上為高德地圖的範例,其他地圖原理相差不大,因此不再贅述。當然,這裡只給了最基礎的載入瓦片圖的邏輯。事實上,根據實際情況,這裡面還會做很多必要的其他業務邏輯的判斷,比如,系統應當儲存當前地圖的瓦片圖範圍,超出範圍的,就不要載入圖片,或者載入一張透明的小圖等。
這裡列一下地圖平臺瓦片圖的介面名,便於有需要的搜尋使用:
AMap.TileLayer.Flexible // 高德地圖
BMap.TileLayer // 百度地圖
TMap.ImageTileLayer // 騰訊地圖
google.maps.ImageMapType // 谷歌地圖
//需要注意的是,一些地圖的介面允許傳入引數:瓦片圖的尺寸。不過建議預設為256較好,畢竟這是通用的預設尺寸。
5.瓦片圖的製作
既然瓦片圖是基礎,那麼我們如何從一張完整的手繪圖製作成為256*256的多張瓦片圖呢?可能設計師都能想到,直接用Photoshop切圖即可,很簡單。是的,常理來說是這樣,但這有2個問題:
(1)我們設計的圖,往往不是剛好為256px的倍數,那麼第一張切圖,從什麼地方開始?(即便是剛好為256的倍數,也不能從0畫素開始切,後文細說)
(2)我們切圖出來之後,結合前文的地圖基礎知識,通過上面範例程式碼可見,最關鍵的是每個圖的檔名,要和地圖的級別Zoom、X軸的數值x、Y軸的數值y相對應。否則切出來的圖,沒有任何意義。
這兩個問題,是製作有用的瓦片圖的根本問題。
其實,在我們繪製手繪圖片檔案之前,就已經清晰的知道,我們繪製的內容到底是處於地圖的什麼區域。然後應當記錄這個區域的起始點的經緯度。
前面說了瓦片圖可能從地圖的最左下角或左上角開始。具體情況是這樣的:高德、谷歌、騰訊地圖是從左下角,高德地圖是從左上角開始。
然後通過經緯度、抹卡託、可見區域畫素三者的轉換演演算法,計算出當前手繪圖分別在X軸和Y軸的第二張瓦片圖的偏移畫素,然後從此畫素位置開始切圖,並把計算得到的層級(Zoom)、X座標(x)、Y座標(y)的值作為對應的檔名儲存切圖。因此,說到這裡,我們便都知道,這切圖工作沒辦法由設計師來人工執行,只能由設計的專門演演算法的系統執行。
這裡因為一些商業保密和其他的原因,我不能對這個切圖演演算法做更仔細的講解,非常抱歉。
瓦片圖切好之後,放到專門的檔案伺服器,然後前端程式碼便可呼叫,實現瓦片圖覆蓋於地圖底圖上展示。個性又漂亮的手繪地圖便基本成型了(如上圖「參考範例圖」)。
6.動態規劃路徑的實現
漂亮個性的手繪電子地圖完成之後,這還只是一個純純的地圖展示,談不上功能性的使用,更別說「智慧」。因此,我們還需要增加各項智慧能力的功能。這其中,最基礎的又應當是「實時定位」及「動態路徑規劃」了。
其實這兩項功能,就是地圖APP的基礎的導航功能。但是因為大部分我們的園區內根本沒有地圖的路線,或者不全面,因此需要我們自己來實現這個功能。
具體如何實現,原理很簡單。前端通過定位介面獲取到使用者當前的位置(需要注意的是,為了支援更全面的使用場景,可以考慮相容微信及HTML5原生介面),然後系統計算當前位置與指定的點位之間的路線。
因此,這就需要我們根據實際情況,事先在系統裡標註園區內的點位和路線,路線越詳細越好。而通過這些點位和路線,要計算得到最短路徑,關鍵則在於最短路徑的「尋路演演算法」。演演算法可以自己設計,而目前很多偉大的科學家公開的很多最短路徑演演算法則更為推薦。因為自己設計的演演算法難免可能會出現一些不可預料的BUG,而這些公開的演演算法經過嚴密的證明和大量使用的驗證,使得計算效率以及可靠性都有很好的保證。
這也是我的個人經驗,曾經我們自己設計的一個演演算法,在大部分情況下都能計算出正確的最短路徑結果,但在某些情況下,卻得到意外的結果,或發生計算消耗較多時間,效率低下的情況。
我推薦「迪傑斯特拉」演演算法,或「佛洛依德」演演算法。
1 //C語言版本的迪傑斯特拉演演算法
2 int a[1000][1000];
3 int d[1000];//d表示源節點到該節點的最小距離
4 int p[1000];//p標記存取過的節點
5 int i, j, k;
6 int m;//m代表邊數
7 int n;//n代表點數
8 int main()
9 {
10 scanf("%d%d",&n,&m);
11 int min1;
12 int x,y,z;
13 for(i=1;i<=m;i++)
14 {
15 scanf("%d%d%d",&x,&y,&z);
16 a[x][y]=z;
17 a[y][x]=z;
18 }
19 for( i=1; i<=n; i++)
20 d[i]=max1;
21 d[1]=0;
22 for(i=1;i<=n;i++)
23 {
24 min1 = max1;
25 for(j=1;j<=n;j++)
26 if(!p[j]&&d[j]<min1)
27 {
28 min1=d[j];
29 k=j;
30 }
31 p[k] = 1;
32 for(j=1;j<=n;j++)
33 if(a[k][j]!=0&&!p[j]&&d[j]>d[k]+a[k][j])
34 d[j]=d[k]+a[k][j];
35 }
36 for(i=1;i<n;i++)
37 printf("%d->",d[i]);
38 printf("%d\n",d[n]);
39 return 0;
40 }
當然這些公開演演算法,都是最基本的核心和原理,要能讓我們可在地圖上做最短路徑尋找,還需要我們自己根據實際業務和邏輯,做相應的變種。
根據對當前使用者的實時定位,通過最短路徑演演算法,便能實現動態路徑的規劃。這便使得我們的手繪電子地圖具備了最基礎、最重要的導航功能。
7.自動觸發的實現
通過實時定位獲取到使用者當前位置,系統判斷位置是否和後臺設定的點位接近。當距離已小於設定值的時候,電子地圖系統便自動展示當前點位的介紹資訊、語音講解,或者商家推播的優惠券等。因此,我們的電子地圖系統便越來越智慧了。
而在此基礎之上,我們可以相當的所有功能,都可以載入到電子地圖系統上。由此我們的系統便優化成為真正的「智慧導覽系統」。其他的更多的功能的實現,我便不再贅述,只對下列三個比較特別的功能再做一些簡述。
8.多語言的實現
多語言是一個老生常談的需求,也是一個系統國際化所必備的功能。對於智慧導覽系統而言,我覺得有兩個值得注意的點:
(1)地圖本身的文案,包括提示資訊、按鈕文字等。這可以在程式碼層面做多語言的設定。
(2)地圖示注點位相關的內容,包括圖文介紹、語音講解等內容,這裡需要在後臺系統設計可新增多語言內容的管理。
9.實現國內國外可同時存取地圖
這一點需求,是根據實際情況來提的。因為國內和國外的地圖基本上不能互通,因此我們的智慧導覽系統應當解決這一痛點。
10.地圖路線的採集和標註
系統的「動態路徑規劃」功能需要園區的路線資料為基礎,因此係統應當提供路線採集和標註的配套工具,方便工作人員從現場採集路線的經緯度等資訊。
五、結尾
本文是我個人基於「智慧導覽系統」開發經驗的一個大致的概括。整體來講,寫得比較粗陋和簡略。很難作為一個方案或者教學,只當是我個人做一個總結罷了。如有不當之處,歡迎指正。也歡迎有興趣的一起討論。