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 數值直接映射到色彩漸層,而非二元藍綠

注意事項

  1. 代碼格式一致candidates-votes行政區代碼entity~geoid、GIS API 的 record_id 三者格式完全相同(鄉鎮 8 碼純數字),可直接 JOIN,不需格式轉換。
  2. 副手要排除candidates副手=Y 的記錄是副總統候選人,candidates-votes 裡他們有獨立的票數記錄,計算得票率時需排除,只保留正候選人。
  3. 層級不可相加candidates-votesvillage/town/county/national 是同一票的不同彙總粒度,繪製鄉鎮地圖只取 town 層。
  4. 翻頁取完所有記錄:全台 368 個鄉鎮 × 3 位候選人 ≈ 1,104 筆,per_page=200 需要約 6 頁。確認 total 欄位後翻頁取完,或用更大的 per_page(最大值見 API 文件)。
  5. 授權標注:使用此資料產出的地圖,需標注「資料來源:歐噴資料庫(data.openfun.tw)/中央選舉委員會/內政部」。

📄 查看原始 Markdown