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

d3、vue 畫一個台灣地圖
d3、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
訂閱
通知
guest

11 Comments
最舊
最新
Inline Feedbacks
看所有留言
startgo
startgo
3 年 之前

有離島(金門、連江縣)含台灣本島的嗎?

Jason
Jason
回覆給  startgo
3 年 之前

嗯~ 金門、澎湖、連江…….,我也覺得需要!
畢竟這個台灣圖是我目前看過 d3.js 畫出來最精緻的,
真的該給些掌聲。

Jason
Jason
3 年 之前

1.請問Taiwan Map 放置中央的問題,costs u take 3 hrs to solve it !
 可否詳述該問題成因。(我想這是交換學習經驗心得的關鍵)
2.augurio-taiwan.min.js 等 .js 檔,可否提供非壓縮版(反去空白文字),
 以方便閱讀及debug。
3.另有關元宵節的放天燈程式,據您所述需具物理、數學的一些基礎科學
 的知識根基,可否略舉一二。
 (曾看到大陸學生用vb語言寫MD5加密演算法,將部落格密碼存入資料庫
 ,當時覺得DBMS有現存好用的函數做好此事,所以不覺得有啥好奇之處)  

jason
jason
3 年 之前

請問您的javascript (d3、Vue) 程式是在MS VisualStudio 的 IDE 整合介面上開發的嗎?

jason
jason
3 年 之前

prove coding ability with age 60 …..
(1) on click 改 on mouseover 
(2) countyeng 改 population (geojson檔 另加一個欄位)
(3) augurio-taiwan.min.js 反向工程改成易讀版,加空白及換行 (做苦工)
(4) 背景顏色黑色 改 淺灰 (研究中)

jason
jason
回覆給  August
3 年 之前

感謝,上述問題皆已解決(只修改 HTML、CSS 屬性)。
進階問題較瑣碎…for ur reference on Github.

jason
jason
回覆給  August
3 年 之前

我已將無法解決的程式上傳到 github ,thanx !

charles
charles
2 年 之前

下載『鄉鎮市區界線經緯度』,用您提供的程式 mouse 就無法移動。
可否 Demo 一下此部分。

James
James
2 年 之前

鄉鎮市區界線圖可畫出來(很慢),但滑鼠無法定位
某區域。