­

手繪地圖製作的關鍵點之「圖層覆蓋」

 前面介紹了《景區手繪地圖(電子地圖、智慧導覽系統)如何製作》以及《景區手繪地圖的繪製流程》,接下來介紹一些手繪地圖製作的關鍵點。

手繪地圖最關鍵的一點,就是把手繪地圖準確的覆蓋到地圖平台。

​手繪地圖體驗

一、地圖開放平台介面

這是首要的關鍵點。

通過前面的介紹,我們知道,手繪地圖是基於地圖平台針對某一區域進行美化及手繪,從而生產一張精美的手繪圖片文件。但這個文件並不能拿來直接使用,而必須要覆蓋到地圖平台之上才可發揮其價值。

要實現這個目的,我先簡單介紹幾個概念(這些概念在前面的文章都有比較詳細的介紹,這裡只做簡單的回顧):

1.瓦片圖

瓦片圖是256像素的正方形圖片,整個地圖都是由瓦片圖構成的。用瓦片圖原因是,可按需要載入(按螢幕顯示範圍內的區域載入),解決的問題是,伺服器及客戶端設備記憶體問題,以及網路流量問題。

2.地圖開放平台

高德、百度、騰訊、Google等等地圖平台,都有開放的介面。通過這些開放介面,便可實現讓手繪圖覆蓋到地圖平台上。

 ​高德地圖demo

二、手繪地圖覆蓋到地圖平台的方式

每個地圖開放平台都提供了多種把手繪地圖覆蓋到平台的實現方式。

此處以高德地圖為例說明。高德地圖一共提供了這麼多介面,分別都可以實現自定義圖層覆蓋到地圖平台:

AMap.TileLayer.Flexible
AMap.ImageLayer
AMap.CanvasLayer
AMap.VideoLayer
AMap.CustomLayer

根據我的經驗,在手繪地圖覆蓋這個場景,比較適用的是:

AMap.TileLayer.Flexible 
AMap.ImageLayer

這兩個介面,分別對應上文描述的「瓦片圖實現地圖」及「一整張圖實現地圖」。ImageLayer這個介面,可以實現把整張手繪地圖覆蓋到地圖平台上。事實上,依我的了解,業內也有採取這個解決方案的系統。但是就個人而言,我更推薦介面TileLayer.Flexible,此介面就是採取瓦片圖的方案來把手繪地圖覆蓋到地圖平台。

ImageLayer介面程式碼如下所示(高德官方程式碼),由其圖片參數可見,是一張整圖:

var imageLayer = new AMap.ImageLayer({
    url: '//amappc.cn-hangzhou.oss-pub.aliyun-inc.com/lbs/static/img/dongwuyuan.jpg',
    bounds: new AMap.Bounds(
        [116.327911, 39.939229],
        [116.342659, 39.946275]
    ),
    zooms: [15, 18]
});

TileLayer.Flexible介面程式碼如下所示:

var tileLayer = new AMap.TileLayer.Flexible({
    cacheSize: 30,
    zIndex: 200,
    createTile: function (x, y, zoom, success, fail) {
        var imagePath = tileHost + '/uploades/map/" + zoom + "/tile" + x + "_" + y + 'amap.png';
        var img = document.createElement('img');
        img.onload = function () {
            success(img);
        };
        img.crossOrigin = "anonymous";
        img.onerror = function () {
            fail();
        };
        img.src = imagePath; 
        success(img);
    }
}); 

此介面有createTile方法,開放了3個參數,就是至關重要的地圖層級zoom,以及瓦片圖的坐標x/y。通過這3個參數,我們就可以把整張手繪地圖切為瓦片圖來覆蓋到地圖平台之上,達到用戶使用時也按需載入的效果。

​三個參數決定瓦片圖

三、更複雜和完善的手繪地圖覆蓋的效果

高德地圖的TileLayer.Flexible介面還有一個參數,便是success這個函數。在createTile方法里,我們其實還可以自定義更加複雜的內容,然後傳入success這個函數,高德地圖會自動把傳入的對象渲染到瓦片里。

根據這個原理,我們可以實現更加複雜的需求,如:因為中國無法正常訪問Google地圖,而高德、百度等地圖在國外訪問網路延遲嚴重且地圖數據欠缺,所以對於國外的區域或景區這種場景,我們就可以通過此介面實現全球可訪問的手繪地圖。

具體的實現方式如下:

var tileLayer = new AMap.TileLayer.Flexible({
    cacheSize: 30,
    zIndex: 200,
    createTile: function (x, y, zoom, success, fail) { 
        var div = document.createElement('div');

        var img2 = document.createElement('img');
        img2.setAttribute("crossOrigin", 'Anonymous');
        img2.onload = function () {
            div.appendChild(img2);
        };
        img2.style = "position:absolute;width:256px;height:256px;z-index:-2;";
        img2.onerror = function () {
            fail()
        };
        img2.src = tileHost + '/uploades/map/map_'+ mId +"/" + zoom + "/tile" + x + "_" + y + 'amapgoogle.png';
          
        var img = document.createElement('img');
        img.onload = function () {
            div.appendChild(img);
        };
        img.style = "position:absolute;width:256px;height:256px;z-index:-1;";
        img.crossOrigin = "anonymous";
        img.onerror = function () {
            fail()
        }; 
        img.src = tileHost + '/uploades/map/map_'+ mapId +"/" + zoom + "/tile" + x + "_" + y + 'amap.png';

        success(div); 
    }
}); 

這樣就實現了載入兩個瓦片圖,重疊到瓦片區里。其中除了我們自己的手繪地圖的瓦片之外,另外的瓦片圖是Google地圖的底圖瓦片。

Google地圖在中國無法訪問,我們可以採用把它快取到伺服器的方案,這樣就可以達到中國能通過高德地圖來實現訪問Google地圖的底圖。而國外區域的用戶,則直接調用Google地圖。這樣實現中國國外均能訪問。

順便說一下,Google地圖的瓦片圖載入方案,也是大同小異的。

這裡另外有一個需要注意注意的點就是,怎麼判斷當前用戶是在中國還是國外。可以通過經緯度判斷,可以通過IP判斷。我建議通過經緯度,更加直觀明了且準確。但是中國的版圖區域,是一個很複雜的多邊形,因此,需要一些較為複雜的計算。這個點現在就不展開,後面有機會再細說。


​複雜的多圖瓦片

四、手繪地圖覆蓋到平台的關鍵技術點

通過上文可知,手繪地圖要覆蓋到平台,最關鍵的點就在於瓦片圖的層級、x/y坐標。

而這一點,其本質又是手繪地圖所在區域的經緯度所決定的。

因此,在《景區手繪地圖(電子地圖、智慧導覽系統)如何製作》以及《景區手繪地圖的繪製流程》都有所提及或強調,手繪地圖底圖的獲取,可以基於系統直接下載。然後設計師在設計手繪地圖的時候,需要特別注意,不能隨意修改手繪地圖的像素尺寸。因為修改之後,就會導致區域發生變化,然後經緯度就會出現誤差。

在下載地圖底圖的時候,系統已知地圖的經緯度,因此系統在對完成的手繪地圖進行切片的時候,就會從默認記憶的經緯度開始計算,通過地球的經緯度、墨卡托坐標、像素偏移三者的轉換關係,計算出當前手繪圖層的第一張瓦片的偏移位置。找到此關鍵之後,後面切圖就非常簡單,只需要以256像素為依據,進行累加切圖即可。

這裡需要注意的是,實際上256像素的瓦片,在手機上的顯示效果並不佳,看上去不是很清晰。這裡涉及手機螢幕和PC螢幕硬體及顯示像素的差異原理。解決此問題不難,這裡便不展開說明了。


​瓦片圖準確覆蓋

五、更好的圖層載入體驗

因為手繪地圖打開之後,要載入的瓦片圖很多,即便是只載入可視區域內的瓦片圖,數量也是不少。因此,為了用戶在使用手繪地圖時有更好的放大、縮小查看的體驗,我們可以使用預載入瓦片圖方案。

具體的方案就是,系統在完成用戶當前所在層級的瓦片圖載入之後,便可以對相鄰的大一級和小一級的瓦片圖進行載入。因為知道當前層級的經緯度,因此也能計算出相鄰的兩個級別的經緯度,進而對相應的瓦片圖進行載入。載入之後,當用戶放大或縮小手繪地圖層級時,便可快速顯示瓦片圖而不需要再臨時載入,獲得「網路更快」的體驗。

實現預載入的核心程式碼:

for (x = xMin; x <= xMax; x ++) {
    for (y = yMin; y <= yMax; y ++) {
        if (preloadPics[x + '_' + y]) {
            continue;
        }
        preloadPics[x + '_' + y] = true;
        var picUrl = tileHost + '/uploades/map/'+ zoom +'/tile'+ x +'_'+ y +'amap.png';
        (new Image()).src = picUrl;
    }
}

​預載入的兩個相鄰層級的瓦片圖

六、結尾

手繪地圖覆蓋到底圖的方式,其實還有更多,但根據我有限的經驗,覺得其他方式都沒有TileLayer.Flexible介面靈活和實用,因此不再過多贅述。

另外說一點,這個介面的複雜效果,個人經驗是高德地圖支援最完善。因此一直以來我都首推高德地圖作為手繪地圖平台。

在本文最後,衷心感謝各大地圖開放平台。他們提供了大量可免費使用的資源,使得無數企業作為服務商可以為更多各行各業的需求方提供各種各樣的解決方案,創造了難以估量的經濟價值,體現了大企業的責任和擔當。

最後,列一些開放地圖平台文檔:

高德地圖:概述-地圖 JS API | 高德地圖API

百度地圖:地圖 JS API | 百度地圖API SDK

騰訊地圖:JavaScript API | 騰訊位置服務

Google地圖://developers.google.com/maps/documentation/javascript