D3 除了繪製圖表外還可以拿來繪製地圖,而這篇的目標是繪製一個世界地圖,那就開始吧!
繪製地圖前我們要先了解一下會使用到的資料格式,比較常看見的會有 SHP、GeoJSON 與 TopoJSON,而其中 D3 可用的格式為 GeoJSON 與 TopoJSON,詳細的就讓我們往下看
ESRI Shapefile(shp),或簡稱 shapefile,是美國環境系統研究所公司(ESRI)開發的空間資料開放格式,目前該檔案格式已經成為了地理資訊軟體界的開放標準。
shapefile 檔案用於描述幾何體物件:點、折線與多邊形。例如其可以儲存井、河流、湖泊等空間物件的幾何位置。除了幾何位置,shp檔案也可以儲存這些空間物件的屬性,例如河流的名字、城市的溫度等等。
而 Shapefile 又有分為好幾種格式,我們會用到的是副檔名為 .shp
的格式,可以將其轉換成 D3 可用的格式,也就是 GeoJSON 與 TopoJSON
GeoJSON 是一種基於 JSON 的地理空間數據交換格式,它定義了幾種類型 JSON 對象以及它們組合在一起的方法,以表示有關地理要素、屬性和它們的空間範圍的數據。
GeoJSON 同樣是屬於 JSON 的一種,只不過是對它的名稱進行規範後,專門用於表示地理信息的格式,此格式為 D3 主要使用的格式
TopoJSON 是 GeoJSON 的擴展,由 D3 的作者所發明,透過將共享邊(arcs)整合的方法組成,消除了一些 GeoJSON 冗餘的部分,使檔案大小大幅縮小,縮小幅度約可達 80% 左右
首先我們先找到提供 shp 的網站將檔案下載下來,裡面會有 Shapefile 的各種格式,我們需要的是副檔名為 shp 的檔案,再來就是將它轉換成 GeoJSON 或是 TopoJSON 了
線上轉換可使用 mapshaper 的服務,它除了轉換還可以預覽,可以說是非常好用
首先安裝 shapefile 這個套件,它可以幫我們將 .shp
轉換成 GeoJSON,可以使用它的 API 或是直接使用終端機轉換,我們這邊使用終端機的方式
$ npm install -g shapefile
$ shp2json ne_110m_land.shp -o map.json
<!-- 將 ne_110m_land.shp 轉換成 map.json -->
$ shp2json ne_110m_land.shp -o map.json --encoding big5
<!-- 將 ne_110m_land.shp 轉換成 map.json,使用 big5 編碼 -->
TopoJSON 無法直接於 D3 使用,所以作者也另外提供了套件,讓我們將 TopoJSON 轉換為 GeoJSON 的格式,安裝一樣可以使用 npm 或是 CDN 方式載入,有興趣可以看看,這邊就不做示範了
用上述方法得到 GeoJSON 資料後就可以開始繪製地圖了,這邊先介紹幾個會使用到的方法~
d3.geoMercator
:將地圖以麥卡托投影法繪製center
:設定地圖中心點座標
scale
:設定地圖縮放倍率
d3.geoPath
:將投影資料轉換為 path
的路徑// 設定 svg 的寬高
const map = d3
.select('.map')
.attr('width', 500)
.attr('height', 500);
// 獲取 ne_110m_land.json 的檔案資訊 ( GeoJSON ),完成後執行 draw 函式
fetch('./ne_110m_land.json')
.then(res => res.json())
.then(data => draw(data));
function draw(mapData) {
// 設定投影中心點與縮放倍率
const projection = d3
.geoMercator()
.center([200, 10])
.scale(70);
// 將投影資料轉換為路徑
const path = d3.geoPath(projection);
// 繪製地圖
map
.selectAll('path')
.data(mapData.geometries)
.enter()
.append('path')
.attr('d', path)
.attr('stroke', 'black')
.attr('stroke-width', '0.7')
.attr('fill', 'steelblue')
.on('mouseover', function() {
d3.select(this).attr('fill', '#007bff')
})
// 滑鼠碰到後改變顏色
.on('mouseleave', function() {
d3.select(this).attr('fill', 'steelblue')
})
// 滑鼠離開將顏色變回
}
繪製地圖並不難,只要有地圖的資料,並且把握好資料格式的轉換,相信對大家來說是輕而易舉的事情~