學習完 Vue 之後,想要透過單元測試守護你的專案,但又不知道從何起手?
別擔心,快來訂閱作者最新系列文 《小白也能輕鬆瞭解的 Vue3 單元測試!》
讓你的 Vue 專案更上一層樓!
30天好快就到了!趁著最後一天鐵人賽我們要來繪製 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 的檔案大小,並且品質還在能接受的範圍。
而這兩種檔案究竟差在哪裡呢?
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 物件形式呈現,並且底下也詳細列出了描述地圖的一些規範和代表含意說明(如位置、點、多點、線段、多邊形等等)。
由 D3.js 作者 Mike Bostock 所發明的 TopoJSON 則是以 GeoJSON 為基礎,以拓樸學的科學基礎編碼而成的格式。其最大的特色就是原先在 GeoJSON 中描述地理訊息的邊緣,會以共同邊(arcs
)所表示,並且消除掉一些較為冗贅的地理資訊後而產生。
D3.js 根據了 GeoJSON 與 TopoJSON 格式來處理地圖訊息,透過像是 SVG 中的路徑 path
,將處理好的的資訊轉換為視覺呈現,而要以 TopoJSON 檔案來繪製地圖的話,可以透過 Mike Bostock 在 TopoJSON 專案 中的 API 來解碼使用,目前專案上也提供了 CDN 連結供快速開發使用:
<script src="https://unpkg.com/topojson@3"></script>
接著如同前面幾章,我們依樣畫葫蘆開一個 Vue.js 的 HTML 版面:
<div id="app">
<svg width='500' height='500' style='border:1px solid #00000060;'>
<g class="counties"></g>
<path class="county-borders"></path>
</svg>
</div>
Vue 實體中則是開了一個 data taiwanCountry
給予預設值,待實體載入完畢時將我們前面處理好的 TopoJSON 檔 COUNTY_MOI_1080726.json
透過 fetch 的方式載入我們的地圖資料以供取用,並且呼叫 draw()
函式來繪製地圖:
let vm = new Vue({
el: "#app",
data: {
taiwanCountry: []
},
mounted() {
fetch('COUNTY_MOI_1080726.json')
.then(res => res.json())
.then(result => {
this.taiwanCountry = result
this.draw(this.taiwanCountry)
})
},
methods: {
draw(mapData) {
}
}
});
在 draw
函式中,使用 d3.geoMercator()
來定義投影模式,並以 center
定義經緯度位置,scale
定義縮放比例尺:
let projection = d3.geoMercator()
.center([123, 24])
.scale(5500);
接著加入 d3.geoPath()
(用來產生供 path
路徑標籤所使用的 d
),並且傳入剛才定義好的投影模式 projection
:
let path = d3.geoPath(projection);
最後透過 d3.js 選擇器選擇我們剛以開好的 HTML 版面,在 g.counties
產生地圖面積、path.county-borders
產生地圖輪廓:
d3.select('g.counties')
.selectAll("path")
.data(topojson.feature(mapData, mapData.objects["COUNTY_MOI_1080726"]).features)
.enter().append("path")
.attr("d", path);
d3.select('path.county-borders')
.attr("d", path(topojson.mesh(mapData, mapData.objects["COUNTY_MOI_1080726"], function (a, b) { return a !== b; })));
而裡面使用到的 topojson
API 方法說明如下:
topojson.feature
:將 TopoJSON 轉換成 GeoJSON 的格式。topojson.mesh
:將 TopoJSON 中的 geometry 物件轉換成 GeoJSON 中的線段。加上一點點 CSS 來點綴:
<style>
.counties {
fill:#33474e;
}
.counties :hover {
fill: #7f9ca7;
transition: 0.5s;
}
.county-borders {
fill: none;
stroke: #fff;
stroke-width: 0.5px;
}
</style>
最後畫面上顯示:
以上便是 D3.js 中的最後一章,同時也是象徵著完賽啦!
雖然還有很多想講的來不及說明,但我想接下來幾天忍不住應該會繼續發吧?
完賽啦,躺爆!
請問為什麼我匯入檔案之後產生出來的地圖會有一塊是紅色邊界?
要怎麼移除它?
由於目前政府資料開放平臺最新版本的 .shp 檔案在該區有線段交互(line intersection)問題,因此 mapshaper 工具主要是標示出現有這些問題的地方。
若要關閉提示可在左上角按叉叉或是再匯入 .shp 檔案時在 options 的地方開啟 snap vertices
功能,修復後就不會再出現此狀況了。
(此處的 snap vertices
)
了解謝謝!