天下雜誌2018台灣選舉地圖製作分享:技術的部分

Andy Lin
8 min readNov 27, 2018

--

天下雜誌在2018年九合一大選,推出「台灣選舉地圖」專題,用中選會的開票資料,搭配地理圖資,繪製全台細到村里層級的政黨得票率地圖。在投票日前一週,先開放2014年大選的投票資料,作為下周投票的參考,之後在投票日隔天中午,放上2018年大選的投票資料,馬上就能查詢昨天的投票情況。

天下雜誌2018「台灣選舉地圖」專題

這次的選舉地圖專題,整個製作團隊成員主要有5人:

  1. 我:資料工程師兼前端工程師,負責資料取得和清理、設計地圖機制、根據地圖結果寫分析文章
  2. 另一位前端工程師:負責設計地圖的搜尋功能,優化地圖操作
  3. 網頁設計師:負責整個網頁的視覺設計、切版、套版、UI/UX設計
  4. 兩位編輯兼PM:負責掌控專題製作進度、發想網頁文案、擔任各成員間的溝通角色、蒐集

資料取得和清理

這次地圖使用的資料來源主要有兩個,一個是中選會,另一個是社會經濟資料庫。中選會提供的資料有:

  1. 2014年縣市長選舉開票結果:中選會資料庫網站即可下載
  2. 2018年縣市長選舉及公投開票結果:事前打電話問中選會,如何最快拿到2018年大選開票資料,被告知需要代表媒體提出申請書,即可獲得資料下載帳密,在投票當天使用。
  3. 2018年九合一大選投開票所資訊:由於中選會在開票完成後馬上丟出來的資料,是以投開票所為最小單位,資料裡面只有記載該開票所是某縣市第XXX號開票所,如果要對應到開票所屬於哪個村里,需要另一份投開票所對應村里的資料。因此我打電話給中選會,說明天下雜誌製作地圖需要這份資料後,麻煩對方提供後,馬上就收到這份資料。
  4. 2014年和2018年的縣市長候選人名單:由於地圖tooltip需要呈現各縣市候選人,因此從中選會網站取得名單。

從社會經濟資料庫取得的資料有:

  1. 2014年全台村里所得、年齡、教育程度統計:由於我需要在地圖上呈現所得高、高齡、教育程度高等等條件的村里如何投票,我需要先找出哪些村里所得高等等。社會經濟資料庫就是取得這些資料的最佳去處,不僅各種社經資料應有盡有,還會附贈shp地圖檔,供繪製地圖使用。
  2. 2018年全台村里所得、年齡、教育程度統計:2018年相同的資料也要載下來,供2018年版地圖使用。

這些資料要怎麼串在一起?我用的是「R語言」,專門拿來清理、分析資料的程式語言。

在清理資料的過程中,最難的反而不是寫程式,而是抓電腦無法辨識的字和異體字,例如「糖廓里」的「廓」,就很容易無法辨識,變成「糖?里」這樣就無法把開票結果資料的糖廓里,接到社經資料的糖廓里。或者「公『館』里」和「公『舘』里」,其實是同一個里,但只要出現異體字就無法串接。

這些例外情況需要手動檢查,程式只能抓出這些異常。在全台約7700個村里中,約一百個村里有這樣的問題,清理這些村里花的時間,反而比處理其他部分來得多。

此外,中選會開票結束後的開票結果,是以投開票所為單位,需要把同一個村里的票加總,才能得到村里的投票結果。首先,需要把開票結果與投開票所地址資料串接,再根據村里資訊分類投開票所,加總各政黨得票,最後算出政黨得票率。

準備地理圖資

有了資料之後,要把資料轉化成互動地圖。但第一步,我們需要先準備地圖圖資。之前有提到,社會經濟資料庫的資料,會附贈shp地圖檔,我們可以直接用現成的。但是有一個問題,社會經濟資料庫的shp檔,經緯度的座標系統是TWD97,不是一般常見的WGS84。為了方便之後製作地圖轉換投影,需要先把TWD97轉成WGS84,這時候就需要使用QGIS。

QGIS是免費使用的GIS軟體,相對於收費版ArcGIS功能比較陽春,不過用來轉換座標系統綽綽有餘。轉換的步驟簡單來說就是

  1. 讀取SHP檔,在座標系統選擇TWD97
  2. 另存新檔,在座標系統選擇WGS84

兩個步驟就完成了。

但在兩個步驟之間,我有額外做一個步驟

1.5 調整離島位置和刪去無人居住的島嶼

為什麼要做這個步驟呢?因為選舉地圖的重點,是有人居住的地區如何投票,但地圖檔裡有許多無人居住的島嶼,這些島嶼在互動地圖中是不需要出現的資訊,去除可以讓地圖更簡潔。另外把金門、馬祖兩個縣市,移動到較靠近台灣本島的位置,也能讓整個地圖更好操作,不用移來移去。

搞定座標系統之後,還要搞定另一個問題。由於寫網頁的程式語言Javascript,無法讀取shp檔,需要把shp檔轉成json格式,才能給Javascript畫成地圖。

把shp轉成json,用的是node.js。由於過程比較複雜,我把我參考的教學附在這裡,有興趣的讀者可以自行閱讀。

教學是由互動地圖大神Mike Bostock撰寫,十分好懂:

  1. Command-Line Cartography, Part 1
  2. Command-Line Cartography, Part 2
  3. Command-Line Cartography, Part 3
  4. Command-Line Cartography, Part 4

這些教學一共教了如何把shp轉成geojson、預先計算投影、串接geojson和開票資料、把容量大的geojson轉成容量小的topojson、進一步輕量化topojson等等,都是我這次有用到的技巧。

有了地理圖資跟開票、社經資料後,就能正式開始畫地圖了。

製作互動地圖

製作互動地圖,就需要Javascript,製作地圖最常被使用的Javascript套件就是D3.js。

D3.js的學問博大精深,用途也很多元,畫地圖只是眾多用途中的其中之一。用D3.js畫地圖,簡單可以是一張靜態填色圖,複雜可以是切換年份、可放大縮小、變換地圖分割層次的互動地圖,作法天差地遠。

由於實際製作過程過於繁雜,先分享一些我拿來參考、別人製作的D3.js地圖:

  1. Map Pan & Zoom II:Mike Bostock製作的世界地圖可縮放版,可以看到最經典的縮放功能是怎麼寫的。
  2. van Wijk Smooth Zooming:Mike Bostock製作的順暢版縮放。因為地圖中會有縮放到孤島選區的功能,如何讓縮放效果最順暢,是重要的技術細節。Mike Bostock參考資料視覺化論文,寫了一個順暢縮放functiond3.interpolateZoom,直接拿來用就可以了。
  3. Zoomable Choropleth:Chris Given製作的可縮放美國地圖,點擊州就會縮放到整個州填滿視窗,外加繪製該州的郡分界填色圖。由於我設計上想要先呈現整個台灣地圖,以縣市分界,點擊縣市後才會跳出鄉鎮市區分界,再點擊鄉鎮市區後才會跳出村里分界,以降低網頁運算負擔,因此這個版本是我很重要的參考對象。

以上三個作品,是我這製作這次選舉地圖的主要參考對象。但要修改的程度還是很大,例如Zoomable Choropleth只有州、郡兩個層次,我要做縣市、鄉鎮市區、村里三個層次;讓使用者自行縮放地圖會和地圖自動縮放到孤島選區相衝突。解決這些問題需要數不清的Google搜尋和自行摸索。

在做好互動功能後,另一個需要處理的問題是:加入切換年份的效果。

切換年份不是單純寫一個按鈕,按下去而已,還要考慮到:

  1. 重複切換地圖會不會讓手機效能承受不了:我採取了預設2014年和2018年的地圖都先畫好,但是一個先隱藏(z-index改成-1和visibility改成hidden),另一個不隱藏,按切換年份按鈕可以互換。這樣就不會每次按切換年份按鈕都要重新畫好整個地圖,浪費手機效能。
  2. 2014年沒有公投要怎麼切換:最後我們決定在2018年的公投頁面,把2014年的切換按鈕隱藏。
  3. 2018年跟2014年的行政區劃有些許不同該怎麼辦:為了畫出不同的行政區劃,我們準備2014年和2018年的地理圖資,雖然需要讀取的資料多了一倍,但是因為都有使用node.js輕量化,因此不會造成讀取時間過久。
  4. 切換年份後新地圖怎麼維持在原本的位置:在比較不同年份的地圖時,會希望維持在同一個地區,不要每次切換後就跳回整個台灣。解決的方法是在切換前紀錄當時的x、y、縮放比等資訊,切換後把這些資訊套用到新呈現的地圖上。

需要先解決這些問題,才能做出可切換年份的互動地圖。

最後,2018年版的地圖還不能直接上線,需要等2018年大選結果出爐後才能亮相。因此我預先寫好處理2018年大選資料的R語言腳本,等中選會一釋出資料,就可以套用上線。

但11月25日出現另一個插曲。投票結果公布後,我先用開票資料繪製測試版,發現台北市因為三名候選人瓜分選票,導致政黨得票率都偏低,畫出來的顏色太淡,當場決定改色階。為了趕在下午1點之前上線,我和網頁設計卯足全勁調整,是當時製作地圖時無法預測的突發狀況。

關於視覺設計、UI/UX的部分,由於是網頁設計師負責,在此就不贅述。這些大概就是這次選舉地圖的技術層面,提供讀者參考。對於專題如何發想有興趣的讀者,可以看另一篇文章

--

--