D3.js、Vue 畫一個台灣地圖

/

畫一個台灣地圖需要很多資源的幫忙

最近想做一個「台灣本島 XXX 地圖」的頁面,比如台灣本島素食地圖、台灣本島氣象地圖。

原本想說就簡單的找個台灣地圖的 SVG 檔,後來發現台北、新北這二塊很多找得到的圖層沒處理好,按了新北會包含到台北那塊,然後 August 對於 Illustrator 又沒很會用,只好放棄。

接著又 Google 爬了一下網路的高手們是怎麼處理的,看了很多篇,結果發現很多篇都是從某一篇複製貼上的,內容大同小異,先從政府資料開放平台上下載,接著用 D3.js 轉TopoJSON,再處理 path 就完成了。

教學文章都寫到這,秀出載入出的台灣地圖就停了,但 August 想做的是可以讓台灣是置中的,而實際上照以上的步驟執行後,台灣本島不會在正中央,研究了 3 個多小時才找到解決辦法。

本篇筆記會紀錄在網頁上畫出一個台灣本島的步驟,包含拿資料、轉檔、用 D3.js 繪製地圖,以及把台灣本島放在區塊中央。

最後也會附上這次摸索過程中得到幫助的資源,希望也想畫一個台灣地圖的你可以避掉這些 August 踩到的眾坑們。


畫台灣地圖步驟

步驟如下:

  1. 到政府資料開放平台下載縣市界線經緯度
  2. 到 Mapshaper 將下載的資料做簡化、轉檔
  3. 製作頁面,引入 D3.js
  4. 用 D3.js append SVG path

D3.js 的部份不用擔心,因為 August 也不太會(咦?),但找到的一些教學文有列出 Demo,看了一下覺得有點像 jQuery,且實際 append path 的部份也沒說到很複雜,就是直接複製貼上 code 也行。


下載縣市界線經緯度

進到資料開放平台:

這是廣告,點擊一下可以幫本站多個一點點的廣告收入,謝謝

https://data.gov.tw/dataset/7442

點擊「SHP」的按鈕,下載 SHP 的檔案,解壓縮後會看到以下 6 個檔案:

市界線經緯度下載後的資料夾
市界線經緯度下載後的資料夾

會需要的是 .dbf、.prj、.shp、.shx 這 4 個副檔名的檔案。

這邊附上另外 2 個檔案下載的連結,如果想畫更細的區域地圖可以下載:

鄉鎮市區界線經緯度

村里界圖


SHP 轉檔 GeoJSON

.shp 轉檔有一個好用的線上工具「Mapshaper」:

https://mapshaper.org

它可以把 shp 檔轉成:shapefile、GeoJSON、TopoJSON、JSON records、CSV、SVG。

這是廣告,點擊一下可以幫本站多個一點點的廣告收入,謝謝

進到 Mapshaper 的頁面後,選擇需要的 4 個檔案,再按下 Import,就會看見台灣地圖出現了:

選擇4個檔案後按import
選擇 4 個檔案後按 Import
出現台灣地圖
出現台灣地圖

Mapshaper 還有一個好用的功能,就是簡化,按下頁面右上角的「Simplify」,勾選要的項目後,按下 Apply,就會出現一條拉霸來選擇簡化的幅度:

確認簡化功能的項目
確認簡化功能的項目
移動拉霸調整簡化幅度
移動拉霸調整簡化幅度

原本未簡化過的地圖,轉成 GeoJSON 下載時是 12.8MB,簡化到 0.06% 後,下載的 GeoJSON 是 18KB。

因為本篇筆記實作的注重在台灣本島的部份,也覺得原本的地圖很多鋸齒的邊線,就選用簡化過的版本。

確定要匯出就按下頁面右上角的 Export,會看見匯出檔案格式的選擇,這邊選用 GeoJSON。

匯出檔案格式的選單
匯出檔案格式的選單

特別說明,蠻多教學文都說改用 TopoJSON,檔案會比 GeoJSON 更小。但這篇因為是用簡化過的地圖,大小用的是 34KB,不大,再加上用了 TopoJSON 還要多引用一隻js進行轉成 GeoJSON 的步驟,如果檔案不大就不考慮多做這一步。最後 GeoJSON 的格式看比較懂,所以本筆記選擇的是 GeoJSON。

轉成 GeoJSON 下載後,本篇僅畫台灣本島,就開啟下載的檔案刪掉離島的資料就行。


製作頁面

最後會做出來的頁面長這樣:

 d3+vue 畫出的台灣地圖
D3 + Vue.js 畫出的台灣地圖

左邊是地圖,右邊是內容,本篇就做一個簡單的點了地圖後,右邊的文字會換成該縣市的中、英文名稱。

HTML

附上 Pug:

#app.container
  // 地圖
  .taiwan-map(ref="map")
  #map
    svg#svg(xmlns="http://www.w3.org/2000/svg", preserveAspectRatio="xMidYMid meet")
  
  // 內容
  .shop-list
    h1 {{ h1 }}
    h2 {{ h2 }}

ref="map" 是為了讓 SVG 能抓到寬高。{ h1 }{ h2 } 是之後用 Vue.js 的 data

很多教學文是寫用 D3.js 去 append 地圖的 SVG 出來,August 這邊改成直接把 SVG 寫在 HTML 裡,讓 D3 選擇器可以直接抓。

引用 D3.js

目前 D3.js 的版本到了 v5,但因為對 D3 還不熟,而且說明文件堪比辭海,所以這部份就是修改教學文中的 Demo,跟著引用 v3 版的。

頁面引用 D3 v3 的 CDN:

https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js

D3.js append SVG path

為了把 GeoJSON 裡的路徑變成可以投影成地圖的路徑,必須要先轉換過,轉換的方式如下:

// 座標變換函式
var path = d3.geo.path().projection(
  d3.geo
    .mercator()
    .center([121, 24])
    .scale(mercatorScale)
    .translate([width/2, height/2.5])
);

mercatorScale 的部份是指要把台灣放大多少倍,August 實驗發現,螢幕寬 1366px 以上用 11000 倍,以下用 9000,手機時用 6000,這三個倍數看比較適合。整理成判斷如下:

let mercatorScale, w = window.screen.width;
if(w > 1366) { mercatorScale = 11000; }
else if(w <= 1366 && w > 480) { mercatorScale = 9000; }
else { mercatorScale = 6000; }

translate 這個是研究了 3 個小時後領悟到的重點,D3 畫出地圖後,想讓台灣置中就要靠這個讓地圖偏移回來。

width/2, height/2.5 這個是實驗結果,這樣的偏移量可以在桌機、手機時都讓台灣維持在正中央。

有了轉換 path 的 function 後,就可以開始用 D3 抓 GeoJSON 進來,然後把路徑寫進 SVG path 裡。

以上,就可以畫出一個美美的台灣地圖了。


參考資源

感謝網路上的特級高手們無私的分享出經驗,這邊列出在研究的過程中覺得很有幫助的資源。

老闆 來點寇汀吧 讓我們來操控地圖顯示對應天氣吧!!
看完這個教學影片後,會對怎麼控制地圖上的每個縣市有了概念。也可以直接參考影片裡的方式製作一個可互動的台灣地圖。

視覺化實戰 - D3.js 地理區塊視覺化
這篇的教學文很豐富,搜尋的排名也很高,之後再 Google 其它篇教學,會看見很多內容都跟這篇一樣。

Data Man 的資料視覺化筆記
這篇PO文介紹了mapshaper這個線上轉檔功能,是個好物。

trouble with D3js world map and svg group element width
這篇在 Stack Overflow 上的解答,讓 August 理解到是可以用 translate 來偏移地圖的。


原始碼、Demo

本篇筆記整理過的原始碼放上 GitHub 了,大家可以一起來畫個台灣:

https://github.com/letswritetw/letswrite-taiwan-map-basic

Demo 在這邊:

https://letswritetw.github.io/letswrite-taiwan-map-basic/

Summary
D3.js、Vue 畫一個台灣地圖
Article Name
D3.js、Vue 畫一個台灣地圖
Description
本篇大綱:畫一個台灣地圖需要很多資源的幫忙、畫台灣地圖步驟、下載縣市界線經緯度、SHP 轉檔 GeoJSON、製作頁面、HTML、引用 D3.js、D3.js append SVG path、參考資源、原始碼、Demo。
Augustus
Let's Write
Let's Write
https://letswrite.tw/wp-content/uploads/2020/08/logo_512.jpg

隨選筆記文

API

GitHub API:取 Issue、Comments

Front-End

如何使用 Vivus.js 製作輕量快速 SVG 動畫

Front-End

GitLab Pages:3 + 2 個步驟讓 GitLab 專案產生靜態頁

Front-End

用純 CSS 寫的網頁預覽效果

API Front-End

用 Google Apps Script 寫一個 LINE 登入功能:下篇 – 三大步驟

Front-End

拿 Trello 當資料庫 建一個店家清單 – 上篇:Trello 基本使用

Google Maps

Google Maps API 學習筆記 – 5:抓目前位置、計算到各點距離

API

Video.js 使用筆記:中文介面、取值、事件、改樣式

Front-End

像 Medium 的漸近式圖片加載

Bot Telegram

Telegram Bot 學習筆記 – 6:Google 表單提交時收到通知

以下是留言,但關於留言的部份必需先讓你們知道:

本站的文章都是 August 因為覺得有趣,才會實作並整理成筆記文而後進行發表。

如果留言是希望把 Demo 改成「你想要」的樣子,或是把功能改成「符合你需求」的樣子,

Sorry~ 除非那修改是 August 也有興趣的,不然不會幫你們寫程式去面對工作或是交作業。

未來這類的留言不會再主動回覆。😎

另外,公開信箱是為了讓金流驗證用,

因為之前遇過幾次回信協助解決問題後,對方卻一聲謝謝也沒有,就這樣拿去幫工作交差。

因此決定不再回覆信件,有疑問就利用留言功能囉。