2015年9月7日 星期一

用向量圖磚 (vector tiles) 自製陽春離線地圖

自製陽春離線地圖 受到 臺中公車達人 的刺激, 我一直想要寫一個離線版的臺中公車地圖。 最好可以放在手機上。 我只需要大致的街道圖跟街道名稱, 不需要縮放 (zoom) 很多等級, 所以並不需要完整龐大 [而且超出我的人腦跟電腦能力範圍] 的 OSM 的圖資。 後來發現 mapzen vector tile service 提供各種向量圖磚, 完全符合我的需要。 把一二十塊圖磚抓回來之後, 就可以用 d3.js 自製陽春地圖了! (這篇並不需要寫程式也可以操作一下感受成果; 但最終還是對程式設計師比較有用。)

先下載一塊向量圖磚來玩玩看: wget http://vector.mapzen.com/osm/roads/14/13684/7059.json 然後用 jq . 7059.json | less 會看到 「臺灣大道一段」 等等街道名稱。 把它上傳到 umap 或 mapbbcode 會發現這一塊圖磚包含了臺中火車站, 切得整整齊齊的, 好漂亮啊! 如何計算網址的那些數字? 14 是縮放層級 (zoom) (0: 全球; 20: 最細微); 從臺中火車站的經度可以算出 13684; 從緯度可以算出 7059。 詳見 這一頁, 裡面有很多不同語言現成的程式碼。 所以你可以到 geofabrik 找到你有興趣的地點、 把網址的經緯度代入, 就可以得知你要的圖磚網址。

通常應該會需要很多塊圖磚。 所以我寫了一個小程式 slippy-tiles.py 讓你一口氣抓很多圖磚: slippy-tiles.py -s 20 -z 14 -d /home/ckhung/tiles/ -u 'http://vector.mapzen.com/osm/roads/{}.json' 120.631 24.128 120.737 24.165

  1. -s 20 : 每抓一塊圖磚, 就 (讓伺服器) 休息 20 秒。 答應我, 當你下載很多圖磚的時候, 請一定要讓伺服器休息; 請不要破壞公共資源。
  2. -z 14 : 縮放層級
  3. -d /home/ckhung/tiles/ : 下載回來的圖磚都放這個目錄底下。
  4. -u 'http://vector.mapzen.com/osm/roads/{}.json' : 圖磚伺服器的網址。 如果省略這個參數, 預設就是從 mapzen 以 geojson 格式下載街道圖層 (roads)。
  5. 120.631 24.128 120.737 24.165 : 你有興趣的地圖範圍的對角兩頂點的經緯度。 (先經度後緯度; 兩點順序任意。) 這幾個數值一樣可以從 geofabrik 讀出來。 (移動滑鼠的時候, 注意右下角。)

呵呵, 騙你的啦。 上面的指令不會真的下載, 只會印出下載的指令。 請改用你自己的座標、 確認這個 zoom level 包含你要的資料、 印出來的指令正確。 用力檢查無誤之後, 即可真的執行: slippy-tiles.py ... | bash

再來把所有的圖磚串成一個檔: jq -s '{"type":"FeatureCollection","features":map(.features[])}' $(find /home/ckhung/tiles/ -iname '*.json') > roads.json 。 這個恐怕太大, 無法上傳到 umap 。

然後請下載 我的 cordova-examples。 本篇只需要其中的 offline-map 這個子目錄。 裡面的原始碼只有三個檔案, 總共不到 100 列; 另外還有兩個很大的 「向量圖檔」: roads.json 跟 landuse.json, 就是用上述步驟產生的。 當你改用不同的圖層時, 程式碼 main.js 應該很容易小修改; 要改顏色請到 map.css 。

除了 geojson 之外, mapzen 還提供 .topojson/.mvt/.vtm 等等格式的向量圖磚。 至於圖層, 除了街道及土地使用狀況之外, 還有水文、 建築物、 PoI 等等選擇。 也可以用 "all" 選取所有圖層。

d3.js 超強大。 看起來有點難學; 不過學會了之後, 寫出來的程式超簡潔。 其實它有一個 d3.geo.tile.js 外掛, 讓地圖程式撰寫的門檻大幅降低。 像是 「Chrome 禁讀令」 那篇的第二個例子 tc-streets.tgz 就是從 d3.js 原作者 Mike Bostock 的範例 改來的。 我也還看不太懂, 就胡亂把遠端的 tile server 網址改成本地目錄, 竟然這樣就成功了! 不過現在這篇並不需要很多 zoom level 的圖磚, 而且目前我也還不太會用 d3, 所以沒用到 d3.geo.tile.js, 而是直接把地圖當成一個普通的 svg 圖來縮放比較簡單。 臺灣的 oxxo 大大 有一系列的 d3.js 教學文, 有空時要來 K 一下啊...

用 cordova 編譯、 放到手機上後, 縮放超不順, 有一種麥芽糖拉不開的感覺...。 手機版目前也沒有顯示街道或土地名稱的功能。 geojson 檔太大, 應該改用 topojson 比較好。 這些缺點以後若有改進我再回這裡補充說明。

2 則留言:

  1. openstreetmap 好像可以下載地圖
    但我不太清楚.

    回覆刪除
    回覆
    1. 嗯, 可以下載, 而且國網中心有映射:
      https://lists.openstreetmap.org/pipermail/talk-tw/2016-May/001289.html

      不過沒研究過怎麼用。 也許找一個現成的 docker 來用比較快。

      刪除