自建瓦片地圖服務(wù)

-1.不想看背景、設(shè)計過程觉痛、編碼思路的同學(xué)薪棒,直接移步頁尾GayHub鏈接钉鸯。

0.背景

啊唠雕,大家好钞脂,我是一頭老王啊冰啃,前天閑著沒事,在工位上刷知乎净薛。
MD被組長發(fā)現(xiàn)了肃拜。

——“老王士聪,很閑嘛~”
——“沒有沒有剥悟,剛剛看一下知乎而已……”
——“很閑就給你個任務(wù)~”
——“真沒有真沒有区岗,就看剛看……”
——“那你這么閑的話就做一下離線地圖服務(wù)吧慈缔,有個項目是部署在政務(wù)內(nèi)網(wǎng)的赂韵,不能訪問外網(wǎng)肄满,所以要離線的地圖服務(wù),Leaflet要能用哦~”
——“…………MMP……”


有一句MMP我一定要說

人在座上坐瞬测,鍋從天上來月趟。我這個小開發(fā)仔還能怎么辦孝宗?穷躁?问潭?做唄狡忙。

看看Leaflet梳虽,用的是TMS服務(wù)(地圖瓦片服務(wù))窜觉。啊北专,老王我突然靈光一現(xiàn)禀挫,只要能下載瓦片特咆,然后部署在Tomcat/Jetty/Nginx上不就得了?啥繁?


非常靠譜

Google一下下載瓦片的軟件:


誒嘿嘿~~
誒嘿嘿嘿嘿嘿嘿~~~~
……………………
大爺?shù)?=举瑰。=

又查了十幾個捣辆,水經(jīng)注,什么地圖下載器什么的此迅,我就沒見過可以用的汽畴,其中一個上來還就要收費(fèi)?人家去Piao都還是先體驗耸序,后付費(fèi)的H绦!騙錢能不能走點(diǎn)心坎怪?0瞻印?搅窿!

累覺不愛……

我老王好歹也是常年爬人家窗戶的人嘁酿,這么點(diǎn)小挫折能難倒我?痹仙??我自己寫一個總行了吧=开仰。=

1.設(shè)計

事實證明,人都是逼出來的=众弓。=
被組長、搜索引擎谓娃,還有百度網(wǎng)盤逼迫的我,設(shè)想出一個服務(wù)滨达,這個服務(wù)需要完成:

  • 輸入經(jīng)緯度矩形、地圖等級锌订、地圖類型,后端自行下載瓦片辆飘,存放入相應(yīng)的位置。
  • 下載瓦片的同時蜈项,還需要記錄下后端擁有哪些類型续挟、哪些層級紧卒、哪些范圍的瓦片庸推,用以前端調(diào)起。
  • 提供地圖服務(wù)以及下載記錄贬媒。
  • 依賴盡量少。

OK坡倔,開始編碼。

2.后端

2.0 算法基礎(chǔ)

若對Web墨卡托罪塔,Google Web墨卡托投蝉,墨卡托有疑惑的同學(xué)征堪,請移步,熟悉理論知識:https://blog.csdn.net/mygisforum/article/details/7582449

2.1 經(jīng)緯度轉(zhuǎn)Google Web墨卡托

首先佃蚜,因為瓦片地圖用的都是Google Web墨卡托的坐標(biāo),那么我們需要先將經(jīng)緯度的矩形換算成Google Web墨卡托的坐標(biāo)熟尉。
OK,Google一個經(jīng)緯度轉(zhuǎn)Web墨卡托的斤儿,我再改寫一下恐锦,轉(zhuǎn)成Google Web墨卡托:

public static LatLngInfo lonLatToMercator(double lon, double lat)
{
    double toX = lon * 20037508.34D / 180.0D;
    double toY = Math.log(Math
                .tan((90.0D + lat) * 3.141592653589793D / 360.0D)) / 0.0174532925199433D;
    toY = toY * 20037508.34D / 180.0D;

    return new LatLngInfo(toY, toX);
}

解釋一下這里的魔術(shù)數(shù)字往果,話說網(wǎng)上這個轉(zhuǎn)換代碼寫的是真的蛋疼一铅,滿篇充斥著魔術(shù)數(shù)字……

  • 20037508.34 :Web墨卡托投影的長或?qū)挼囊话耄瑔挝粸镸馅闽;
  • 0.0174532925199433:一角度為多少弧度馍迄;
  • 90,180攀圈,360:都是角度;

針對原來經(jīng)緯度轉(zhuǎn)Web墨卡托坐標(biāo)的源碼赘来,我改寫時只加了 第一行toX中的 “+ 20037508.34D”,以及return前一行 “toY = 20037508.34D - toY”嗦篱。

因為Google Web墨卡托的坐標(biāo)原點(diǎn)在經(jīng)緯度(180,90)或者說(-180灸促,90)處,也就是整個Web墨卡托投影地圖的左上角浴栽;
Web墨卡托的在(0,0)處典鸡,也就是整個Web墨卡托投影地圖的中心,因此需回歸原點(diǎn)萝玷。

2.2 使用Google Web墨卡托坐標(biāo)計算需要請求的瓦片序列號

public static LevelInfo getLevelInfo(Integer level, Double left,
                                        Double right, Double top, Double bottom)
{
    LevelInfo levelInfo = new LevelInfo();
    LatLngInfo leftTopMocPoint = CoodUtils.lonLatToGoogleMercator(left, top);
    LatLngInfo rightBottomMocPoint = CoodUtils.lonLatToGoogleMercator(right, bottom);

    Double perTileWidth = WEB_MOC_HALF_WIDTH * 2 / Math.pow(2, level);
    levelInfo.setZ(level);
    levelInfo.setxL((int)(leftTopMocPoint.getLongitude() / perTileWidth));
    levelInfo.setxR((int)(rightBottomMocPoint.getLongitude() / perTileWidth));
    levelInfo.setyT((int)(leftTopMocPoint.getLatitude() / perTileWidth));
    levelInfo.setyB((int)(rightBottomMocPoint.getLatitude() / perTileWidth));

    return levelInfo;
}

首先,得到perTileWidth亦渗,這個變量的含義為該地圖等級下,每個切片代表的實際距離為多少法精,單位與Google Web墨卡托一致痴突,為米搂蜓。

前面說了辽装,因為Google Web墨卡托的坐標(biāo)原點(diǎn)為左上角,那么拾积,得到Web墨卡托坐標(biāo)之后,就可以直接用Google Web墨卡托坐標(biāo)除以當(dāng)前等級每個切片的寬度拓巧,得到瓦片序列號的X,Y值傻唾。

Z值為地圖等級,至此冠骄,X,Y凛辣,Z值全部得知锁荔,可以進(jìn)行瓦片URL構(gòu)建蟀给。

2.3 瓦片URL構(gòu)建

// 待匹配的URL
private static String Google_Satellite_Url = "http://mt0.google.cn/vt/lyrs=y&hl=zh-CN&hl=zh-CN&gl=CN&x={x}&y={y}&z={z}&s=Gali";
private static String Google_Image_Url = "http://mt0.google.cn/vt/lyrs=m&hl=zh-CN&hl=zh-CN&gl=CN&x={x}&y={y}&z={z}&s=Gali";
private static String Google_Terrain_Url = "http://mt0.google.cn/vt/lyrs=p&hl=zh-CN&hl=zh-CN&gl=CN&x={x}&y={y}&z={z}&s=Gali";
private static String AMap_Satellite_Url = "http://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}";
private static String AMap_Cover_Url = "http://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8";
private static String AMap_Image_Url = "http://webrd03.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}";
private static String TianDiTu_Satellite_Url = "http://t1.tianditu.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}";
private static String TianDiTu_Image_Url = "http://t1.tianditu.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}";
private static String TianDiTu_Cover_Url = "http://t1.tianditu.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}";

上各個地圖跋理,打開Chrome的控制臺择克,看他們請求的URL是什么前普,將X,Y拭卿,Z值替換成{x} , {y} , {z}。

隨后構(gòu)建URL時峻厚,調(diào)用字符串替換即可。

2.4 循環(huán)請求

得到了左上角到右下角的瓦片X浦夷,Y,Z劈狐,剩下就是循環(huán)請求的事了。
請求的量比較大肥缔,我們開個多線程:

public static Boolean getLevelPic(BackgroundType type, Integer level, Double left,
                                    Double right, Double top, Double bottom)
{
    LevelInfo levelInfo = getLevelInfo(level, left, right, top, bottom);

    // 開多線程請求圖片
    Vector<Thread> threads = new Vector<>();
    for(int x = levelInfo.getxL() ; x <= levelInfo.getxR() ; x ++)
    {
        for(int y = levelInfo.getyT(); y <= levelInfo.getyB() ; y ++)
        {
            final int fX = x;
            final int fY = y;

            Thread threadGetPic = new Thread(
                    () -> getPic(type, fX, fY, levelInfo.getZ()));

            threads.add(threadGetPic);
            threadGetPic.start();
        }
    }

    // 等待所有線程執(zhí)行完畢
    for (Thread perThread : threads)
    {
        try
        {
            perThread.join();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

    return true;
}

這里的getPic方法包含了請求和請求后的保存汹来。

2.5 其余功能

記錄地圖請求過的參數(shù)、返回該參數(shù)收班。
返回地圖瓦片這些常規(guī)操作,直接下載看Gayhub的代碼吧闺阱。

3 前端

3.1 依賴

依賴AntDesign舵变,Leaflet,React纪隙,使用TypeScript。

{
    "@types/leaflet": "^1.2.7",
    "antd": "^3.6.3",
    "axios": "^0.18.0",
    "leaflet": "^1.3.1",
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-scripts-ts": "2.16.0"
}

3.2 思路

首先需要一個矩形的框绵咱,用來輸入地圖范圍,然后再填入地圖等級、地圖類型住涉,鏈?zhǔn)秸埱蠛蠖耍尯蠖瞬粩嗟墨@取瓦片舆声。

3.3 效果

設(shè)置頁面

點(diǎn)擊下載瓦片后即可得到地圖服務(wù)柳爽。


測試頁面

測試頁面會得到已有地圖服務(wù)的類型。


Q&A

Q&A頁面是常見問題與API的描述磷脯。

4.GayHub地址:

后端:https://github.com/Icemap/MapServer
前端:https://github.com/Icemap/MapServer-Front
Release版本:https://github.com/Icemap/MapServer/releases
兩個倉庫的README都有服務(wù)的使用方法。
Release版本使用:需求Java1.8+環(huán)境打毛,直接“java -jar {jarFileName}”,{jarFileName}替換為你下載的Release版本的Jar包名稱。訪問localhost:2018 即可隘冲。

順便幫忙GayHub點(diǎn)個星星绑雄,感激不盡展辞。

謝了兄弟

5.老王第一次發(fā)文万牺,行文晦澀,望大家海涵脚粟。若有論述不明、表述錯誤之處核无,請留言啦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末噪沙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子正歼,更是在濱河造成了極大的恐慌,老刑警劉巖局义,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異萄唇,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)穷绵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勾缭,“玉大人,你說我怎么就攤上這事俩由。” “怎么了幻梯?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵努释,是天一觀的道長碘梢。 經(jīng)常有香客問我伐蒂,道長,這世上最難降的妖魔是什么逸邦? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮缕减,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桥狡。我一直安慰自己搅裙,他們只是感情好裹芝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著局雄,像睡著了一般存炮。 火紅的嫁衣襯著肌膚如雪蜈漓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天融虽,我揣著相機(jī)與錄音灼芭,去河邊找鬼有额。 笑死彼绷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的寄悯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼猜旬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了洒擦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤熟嫩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后邦危,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡希坚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年陵且,在試婚紗的時候發(fā)現(xiàn)自己被綠了裁僧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慕购。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖沪悲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情殿如,我是刑警寧澤最爬,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布门岔,位于F島的核電站爱致,受9級特大地震影響寒随,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜妻往,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蒲讯。 院中可真熱鬧,春花似錦判帮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽但指。三九已至,卻和暖如春抗楔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背连躏。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拍棕,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓绰播,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蠢箩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容