group: AI 使用指引 order: 20 reviewed_by: rd: Ronny pm:
選舉地圖:鄉鎮藍綠地圖製作指引
以「2024 年總統大選 × 新北市各鄉鎮得票分布」為例,示範如何整合歐噴資料庫的選票資料與行政區地理邊界,用 Leaflet 繪製 choropleth 地圖。
所需資料集
| 資料集 | 用途 |
|---|---|
tw.gov.cec~ref~election-event |
確認選舉代碼 |
tw.gov.cec~ref~candidates |
取得候選人清單與政黨 |
tw.gov.cec~txn~candidates-votes |
各鄉鎮得票數 |
tw.openfun~entity~geo |
鄉鎮代碼清單 + GIS 邊界 polygon |
全部 API 均需 Bearer token。申請網址:https://data.openfun.tw/user
完整流程
Step 1:確認選舉代碼
2024 年總統大選的選舉代碼為 ELC-P0-16(第 16 任總統副總統選舉,投票日 2024-01-13)。
可由 tw.gov.cec~ref~election-event 查詢確認:
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://data.openfun.tw/api/v1/datasets/tw.gov.cec~ref~election-event/records/ELC-P0-16"
Step 2:取得候選人清單與政黨
查詢 2024 總統大選的候選人,過濾 副手 欄位為空(或 N)的記錄,排除副總統候選人:
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://data.openfun.tw/api/v1/datasets/tw.gov.cec~ref~candidates/records?選舉代碼=ELC-P0-16&per_page=20"
回傳結果中每筆包含 候選人代碼(如 ELC-P0-16:00:2)和 政黨(如 民主進步黨)。以 候選人代碼 → 政黨 建立對應表。
⚠️ 候選人號次依選舉而異,務必以 API 取得的實際資料為準,不可假設固定號次。
Step 3:取得新北市各鄉鎮代碼
新北市的縣市代碼為 65000:
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://data.openfun.tw/api/v1/datasets/tw.openfun~entity~geo/records?level=town&county_id=65000&per_page=50"
回傳 29 筆,每筆的 id 為 8 碼鄉鎮代碼(如 65000010 = 板橋區)、full_name 為中文名稱。
Step 4:取得各鄉鎮的各候選人得票數
candidates-votes 的 行政區代碼 是 exact match,無法直接篩「某縣市全部鄉鎮」。做法是取全台灣鄉鎮層後 client-side 篩選:
# 2024 總統大選,全台灣所有鄉鎮層得票(約 1,100 筆)
# 若一頁裝不下,翻頁取完(用 page=2, page=3 ... 直到 total 取完)
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://data.openfun.tw/api/v1/datasets/tw.gov.cec~txn~candidates-votes/records?選舉代碼=ELC-P0-16&行政區層級=town&per_page=200"
取回後,以 行政區代碼 比對 Step 3 的代碼清單,篩出新北市 29 個鄉鎮的記錄(每鄉鎮有 3 筆,共 87 筆)。
Step 5:計算各鄉鎮藍綠比例
對每個鄉鎮,整合所有候選人得票,計算各政黨得票率:
# 以 Python 偽代碼示意
for town_id in 新北市鄉鎮代碼清單:
records = [r for r in votes if r["行政區代碼"] == town_id]
total = sum(r["得票數"] for r in records)
# 依候選人代碼對應政黨
party_votes = {}
for r in records:
party = candidate_party_map[r["候選人代碼"]]
party_votes[party] = r["得票數"]
dpp_pct = party_votes.get("民主進步黨", 0) / total
kmt_pct = party_votes.get("中國國民黨", 0) / total
winner = "dpp" if dpp_pct > kmt_pct else "kmt"
Step 6:批次取得 29 個鄉鎮的地理邊界
# 逗號分隔 29 個鄉鎮代碼(此處以省略號代表)
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://data.openfun.tw/api/v1/geo/shapes/tw.openfun~entity~geo?ids=65000010,65000020,...,65000290"
回傳 GeoJSON FeatureCollection,每個 feature 的 properties.record_id 即為鄉鎮代碼,可直接與 Step 5 的資料 JOIN。
Step 7:繪製 Leaflet choropleth 地圖
<div id="map" style="height: 600px;"></div>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
var map = L.map('map').setView([25.0, 121.6], 10);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// voteData: {行政區代碼 → {winner, dpp_pct, kmt_pct, town_name}}
var voteData = { /* Step 5 的計算結果 */ };
// featureCollection: Step 6 的 API 回傳值
L.geoJSON(featureCollection, {
style: function(feature) {
var rid = feature.properties.record_id;
var d = voteData[rid];
var margin = Math.abs(d.dpp_pct - d.kmt_pct);
return {
fillColor: d.winner === 'dpp' ? '#1b9b38' : '#0070c8',
fillOpacity: 0.25 + 0.55 * margin, // 差距越大顏色越深
color: '#ffffff',
weight: 1
};
},
onEachFeature: function(feature, layer) {
var rid = feature.properties.record_id;
var d = voteData[rid];
layer.bindTooltip(
'<b>' + d.town_name + '</b><br>' +
'綠(民進黨):' + (d.dpp_pct * 100).toFixed(1) + '%<br>' +
'藍(國民黨):' + (d.kmt_pct * 100).toFixed(1) + '%'
);
}
}).addTo(map);
</script>
延伸應用
| 目標 | 調整方式 |
|---|---|
| 擴展到全台灣 | Step 3 改取所有 22 縣市的鄉鎮;Step 4 不做縣市篩選 |
| 縣市層地圖 | 行政區層級=county;GIS ids 換成 22 個縣市代碼(5 碼) |
| 村里層地圖 | 行政區層級=village;注意 合併票數=Y 的記錄(見 candidates-votes skill.md) |
| 其他選舉 | 把 ELC-P0-16 換成對應的選舉代碼(查 election-event 資料集) |
| 加入得票率漸層 | 用 dpp_pct 數值直接映射到色彩漸層,而非二元藍綠 |
注意事項
- 代碼格式一致:
candidates-votes的行政區代碼、entity~geo的id、GIS API 的record_id三者格式完全相同(鄉鎮 8 碼純數字),可直接 JOIN,不需格式轉換。 - 副手要排除:
candidates中副手=Y的記錄是副總統候選人,candidates-votes裡他們有獨立的票數記錄,計算得票率時需排除,只保留正候選人。 - 層級不可相加:
candidates-votes的village/town/county/national是同一票的不同彙總粒度,繪製鄉鎮地圖只取town層。 - 翻頁取完所有記錄:全台 368 個鄉鎮 × 3 位候選人 ≈ 1,104 筆,
per_page=200需要約 6 頁。確認total欄位後翻頁取完,或用更大的per_page(最大值見 API 文件)。 - 授權標注:使用此資料產出的地圖,需標注「資料來源:歐噴資料庫(data.openfun.tw)/中央選舉委員會/內政部」。