畫一個台灣地圖需要很多資源的幫忙
最近想做一個「台灣本島XXX地圖」的頁面,比如台灣本島素食地圖、台灣本島氣象地圖。
原本想說就簡單的找個台灣地圖的svg檔,後來發現台北、新北這二塊很多找得到的圖層沒處理好,按了新北會包含到台北那塊,然後Augustus對於illustrator又沒很會用,只好放棄。
接著又Google爬了一下網路的高手們是怎麼處理的,看了很多篇,結果發現很多篇都是從某一篇複製貼上的,內容大同小異,先從政府資料開放平台上下載,接著用d3.js轉topojson,再處理path就完成了。
教學文章都寫到這,秀出載入出的台灣地圖就停了,但Augustus想做的是可以讓台灣是置中的,而實際上照以上的步驟執行後,台灣本島不會在正中央,研究了3個多小時才找到解決辦法。
本篇筆記會紀錄在網頁上畫出一個台灣本島的步驟,包含拿資料、轉檔、用d3.js繪製地圖,以及把台灣本島放在區塊中央。
最後也會附上這次摸索過程中得到幫助的資源,希望也想畫一個台灣地圖的你可以避掉這些Augustus踩到的眾坑們。
畫台灣地圖步驟
步驟如下:
- 到政府資料開放平台下載縣市界線經緯度
- 到mapshaper將下載的資料做簡化、轉檔
- 製作頁面,引入d3.js
- 用d3.js append svg path
d3.js的部份不用擔心,因為Augustus也不太會(咦?),但找到的一些教學文有列出demo,看了一下覺得有點像jQuery,且實際append path的部份也沒說到很複雜,就是直接複製貼上code也行。
下載縣市界線經緯度
進到資料開放平台:
https://data.gov.tw/dataset/7442
點擊「SHP」的按鈕,下載SHP的檔案,解壓縮後會看到以下6個檔案:
這是廣告,點擊一下可以幫本站多個一點點的廣告收入,謝謝

會需要的是.dbf、.prj、.shp、.shx這4個副檔名的檔案。
這邊附上另外2個檔案下載的連結,如果想畫更細的區域地圖可以下載:
SHP轉檔GeoJSON
.shp轉檔有一個好用的線上工具「mapshaper」:
它可以把shp檔轉成:shapefile、GeoJSON、TopoJSON、JSON records、CSV、SVG。
進到mapshaper的頁面後,選擇需要的4個檔案,再按下import,就會看見台灣地圖出現了:


mapshaper還有一個好用的功能,就是簡化,按下頁面右上角的「Simplify」,勾選要的項目後,按下Apply,就會出現一條拉霸來選擇簡化的幅度:
這是廣告,點擊一下可以幫本站多個一點點的廣告收入,謝謝


原本未簡化過的地圖,轉成GeoJSON下載時是12.8MB,簡化到0.06%後,下載的GeoJSON是18KB。
因為本篇筆記實作的注重在台灣本島的部份,也覺得原本的地圖很多鋸齒的邊線,就選用簡化過的版本。
確定要匯出就按下頁面右上角的Export,會看見匯出檔案格式的選擇,這邊選用GeoJSON。

特別說明,蠻多教學文都說改用TopoJSON,檔案會比GeoJSON更小。但這篇因為是用簡化過的地圖,大小用的是34KB,不大,再加上用了TopoJSON還要多引用一隻js進行轉成GeoJSON的步驟,如果檔案不大就不考慮多做這一步。最後GeoJSON的格式看比較懂,所以本筆記選擇的是GeoJSON。
轉成GeoJSON下載後,本篇僅畫台灣本島,就開啟下載的檔案刪掉離島的資料就行。
製作頁面
最後會做出來的頁面長這樣:

左邊是地圖,右邊是內容,本篇就做一個簡單的點了地圖後,右邊的文字會換成該縣市的中、英文名稱。
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 }
是之後用vu.js的data。
很多教學文是寫用d3去append地圖的svg出來,Augustus這邊改成直接把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
的部份是指要把台灣放大多少倍,Augustus實驗發現,螢幕寬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
這篇在stackoverflow上的解答,讓Augustus理解到是可以用translate來偏移地圖的。
原始碼、Demo
本篇筆記整理過的原始碼放上Github了,大家可以一起來畫個台灣:
https://github.com/letswritetw/letswrite-taiwan-map-basic
Demo在這邊:
https://letswritetw.github.io/letswrite-taiwan-map-basic/


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