D3.js 地圖
今天我們要來繪製 D3.js 人人必繪的地圖啦!
我們首先到臺灣 政府資料開放平臺 的網站中下載以縣市為界的台灣地圖 SHP 檔案:
Shapefile(SHP),是美國環境系統研究所公司(ESRI)開發的空間資料開放格式。目前 SHP 檔案格式為地理資訊軟體界的開放標準。(資料來源: SHP Wiki)
接著透過 mapshaper 網站將下載好的 SHP 檔案轉成 D3.js 能夠編寫的 GeoJson 格式(或 TopoJson 格式),直接將剛下載好的解壓縮檔案丟進去即可:
選擇 Import 匯入:
就可以畫面上顯示出預覽地圖:
而在輸出檔案前我們還要先把地圖優化一下,點選右上方的 Simplify 簡化地圖:
將地圖簡化到 30% 左右,此時可以稍微看到地圖邊緣稍微被簡化一些,但失真程度不大還能接受:
最後點選右上方 Export 輸出檔案,可以看到有各種檔案類型可供選擇,這裡可以選擇 GeoJSON 或 TopoJSON,而本章範例將會採用 TopoJSON 的格式來示範:
而為什麼範例要選擇 TopoJSON 的格式呢?我們可以來比較一下簡化後與兩種檔案格式的大小差異:
GeoJSON 100% 檔案大小:
GeoJSON 30% 檔案大小:
TopoJSON 30% 檔案大小:
我們可以看到簡化過的檔案容量降了將近快要 10MB ,而 TopoJSON 又還能比 GeoJSON 更少了約八成左右,最後檔案從 12.1MB 的大怪物被優化到剩下 622KB 的檔案大小,並且品質還在能接受的範圍。
而這兩種檔案究竟差在哪裡呢?
GeoJSON
RFC 7946:
GeoJSON is a geospatial data interchange format based on JavaScript Object Notation (JSON).
根據 RFC 7946 的定義,GeoJSON 主要是基於 JSON 編寫的一種地理交換資料格式。也就是說 GeoJSON 其實就是 JSON 格式的檔案,並非是一種新的格式,只是將地理的一些訊息描述以 JSON 規則呈現並受嚴格的定義控管。
Geometry Object
A Geometry object represents points, curves, and surfaces in coordinate space. Every Geometry object is a GeoJSON object no matter where it occurs in a GeoJSON text.
而在幾何物件(Geometry Object)的 RFC 文件章節 裡也提到:不論用來描述的點、線、面等等訊息在檔案的何處,都需要以 GeoJSON 物件形式呈現,並且底下也詳細列出了描述地圖的一些規範和代表含意說明(如位置、點、多點、線段、多邊形等等)。
TopoJSON
由 D3.js 作者 Mike Bostock 所發明的 TopoJSON 則是以 GeoJSON 為基礎,以拓樸學的科學基礎編碼而成的格式。其最大的特色就是原先在 GeoJSON 中描述地理訊息的邊緣,會以共同邊(arcs
)所表示,並且消除掉一些較為冗贅的地理資訊後而產生。
繪製地圖
D3.js 根據了 GeoJSON 與 TopoJSON 格式來處理地圖訊息,透過像是 SVG 中的路徑 path
,將處理好的的資訊轉換為視覺呈現,而要以 TopoJSON 檔案來繪製地圖的話,可以透過 Mike Bostock 在 TopoJSON 專案 中的 API 來解碼使用,目前專案上也提供了 CDN 連結供快速開發使用:
1 | <script src="https://unpkg.com/topojson@3"></script> |
接著如同前面幾章,我們依樣畫葫蘆開一個 Vue.js 的 HTML 版面:
1 | <div id="app"> |
Vue 實體中則是開了一個 data taiwanCountry
給予預設值,待實體載入完畢時將我們前面處理好的 TopoJSON 檔 COUNTY_MOI_1080726.json
透過 fetch 的方式載入我們的地圖資料以供取用,並且呼叫 draw()
函式來繪製地圖:
1 | let vm = new Vue({ |
在 draw
函式中,使用 d3.geoMercator()
來定義投影模式,並以 center
定義經緯度位置,scale
定義縮放比例尺:
1 | let projection = d3.geoMercator() |
接著加入 d3.geoPath()
(用來產生供 path
路徑標籤所使用的 d
),並且傳入剛才定義好的投影模式 projection
:
1 | let path = d3.geoPath(projection); |
最後透過 d3.js 選擇器選擇我們剛以開好的 HTML 版面,在 g.counties
產生地圖面積、path.county-borders
產生地圖輪廓:
1 | d3.select('g.counties') |
而裡面使用到的 topojson
API 方法說明如下:
topojson.feature
:將 TopoJSON 轉換成 GeoJSON 的格式。topojson.mesh
:將 TopoJSON 中的 geometry 物件轉換成 GeoJSON 中的線段。
其餘未使用到的 API 可參考 GitHub 上的 topojson 專案
加上一點點 CSS 來點綴:
1 | <style> |
最後畫面上顯示:
躺爆!