Google Maps API 學習筆記 – 6:畫新冠肺炎分佈圖

Google Maps API 學習筆記 - 6:畫新冠肺炎分佈圖
Google Maps API 學習筆記 - 6:畫新冠肺炎分佈圖

2020.04.14 更新:昨天讀到了一篇文章:Why I’m not making COVID19 visualizations, and why you (probably) shouldn’t either。想了一下後覺得,確實,其實現有資料很難說是準確的,再加上不論確診人數增加或減少,我們一般人看到其實都是讓自己的心情七上八下而已。今天開始決定關閉 Demo 頁,這篇就當作是 Google Maps API 的筆記教學。

2020.03.30 更新:根據資料來源 JHU CSSE公告,他們不再更新 recovered 的資料,因此 Demo 頁面中,August 在今天也刪掉了所有「康復」的部份。

2020.02.13 更新:
新增段落「城市、國家名英翻中」。
新增段落「繪製圖表」。
新增段落「infowindow 監聽按鈕 click 事件」。


2020.02.10 更新:因為資料源今天公告改更新在 GitHub上,Google Sheets 只會更新到今天以前,因此本篇針對取資料、整理資料的部份,分為上、下二大段。
上段是接 Google Sheets 的、下段是接 GitHub 上 CSV 格式的。
因為前陣子 Sheets 的部份常有變動,不是增加列數就是減個欄位,上段用 Sheets 的截圖會跟實際有所出入,在此告知。

有用到的筆記文列表

這一篇是整合了之前幾篇筆記文,最後會實作出在 Google Map 上丟標記,標記這次新冠肺炎目前的分佈圖。

整理一下有用到的技術,都是之前寫過的幾篇筆記文,條列如下:

Google Maps API 部份

取得資料部份

繪製圖表部份


Google Sheets 部份

取得、整理資料

本段的資料來源是 Google Sheets,用 Google Apps Script 來取得資料。

Sheets 表格是長這樣:

資料來源的表格
資料來源的表格

Google Maps API 要丟上 Marker,必須要有經緯度,我們要存下經緯度。

表共有三個 Sheets:Confirmed、Recovered、Death。

處理資料時就要三個表的資料都取得、整理後,再回傳給 Client 端。

August 寫在 Google Apps Script 的程式碼是這樣子的,處理了要資料、整理資料、回傳資料這三個部份:

部署為網路應用程式後,用 Postman GET 產出的網址,得到的結果如下:

取得整理過的資料
取得整理過的資料

確認有了經緯度跟三個數值,接下來就是把經緯度丟到 Google Map 上。


將資料寫進 Google Maps API

這邊實作產出的 Google Map,是一般地圖 + 標記 + 熱圖,有幾個考量:

  1. 調用 API、畫出地圖要時間,在頁面加上一層 loading,才不會一進來看到一片白。
  2. 這次最嚴重的地方在湖北,map 的中心點就設為湖北。(2020.3 月改成義大利)
  3. 視覺重心在分佈的城市,因此地圖樣式要客製,隱藏大部份的景點標示 + 介面按鈕。

loading 效果

loading 的效果做了一個呼吸燈的樣子,先畫出一個蓋版的漸層,animationkeyframe 就是改變這個漸層的透明度。

CSS 如下:

繪製地圖、客製樣式

首先,確認地圖中心點在湖北(2020.3 月改成義大利):

let center = {
  lat: 30.97564,
  lng: 112.2707
 };

接著開始把資料寫給 API:

把控制地圖的 UI 關掉,只留全螢幕的按鈕。

styles 是要寫客製的樣式,因為很長,這邊不列出來,最後可以從原始碼裡看到。

styles 的設定可以參考官方的說明文件:featureType

地圖上放置標記、info window、熱圖

這三件事寫在一起,因為都是在同一個迴圈內一起完成的。

由於資料的格式是 Object,先用了一行:

let keys = Object.keys(res);

把所有的 Key 都先用成陣列,在用這個陣列跑 forEach 迴圈。

這邊是丟資料給 API 的程式碼:

一併在迴圈中處理要給 Heat Map 的資料,這邊是接 Heat Map API 的程式碼:

最後要記得把 loading 給拿掉,就完成了這次的實作。

看了圖後才發現,這次竟然連芬蘭都有1個確診,也太可怕。


原始碼

接 Google Sheets 當資料部份的原始碼放在 Gist 上了,記得替換掉 token:

https://gist.github.com/letswritetw/f386028c675c43250722ed49d5d572b6


GitHub CSV 部份

取得資料

對方更新在 GitHub 上的資料格式是 CSV,進到 GitHub 的 專案 後會看到二個資料夾,都是這次肺炎的全球資料,只是整理的性質不同:

Github專案
GitHub 專案

daily_case_updates 點進去,會是按日期來分檔案。

time_series 點進去,會像上段取 Google Sheets 一樣分為 confirmed、recovered、death 三個檔,這個是本篇實作會用的,所以選擇這個資料夾:

time_series的資料夾
time_series 的資料夾

比方我們要取 time_series_2019-ncov-Confirmed.csv 這個的資料,點進去後,會看到 GitHub 把 CSV 檔給顯示成完整的表格:

time_series_2019-ncov-Confirmed.csv
time_series_2019-ncov-Confirmed.csv

要怎麼取得這個 CSV 的 URL 呢?點選上圖標紅框的「Row」就可以了,就會進到這個網址:

https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/time_series/time_series_2019-ncov-Confirmed.csv

原本 August 也疑惑這個網址能不能取得資料,後來用 Postman 先用個 GET,竟然就真的取到了:

用postman取得Github上的資料
用 Postman 取得 GitHub 上的資料

既然 Postman 取得到,那用 Google Apps Scripts也行,要煩惱的就是如何將 CSV 檔轉成 JSON 的格式。

轉換 CSV 成 JSON

如果 Google 一下,會看見很多搜尋結果都指向同一種寫法,如下:

接著就會看見地圖開始報錯,畫不出來,原因就出在這一行:

var headers = lines[0].split(',');

資料源裡面的城市名,有些本身就帶有 , ,像是:Seattle, WA、Chicago, IL。

一用 , 來切,就會一起切了出去。

由於 August 目前對正則表達式還沒有很熟,所以就用了比較笨的方法,就是先把 , 給替換成 -- 後,再來 split(',')。修改過的並在 GAS 上執行的程式碼如下:

有了把 CSV 轉成 JSON 的 function 後,接下來在 GAS 上就是取得資料、整理並回傳。在 GAS 上寫的完整程式碼如下

一樣部署為網路應用程式後,用 Postman GET 的結果如圖:

整理資料後取得的JSON
整理資料後取得的 JSON(點擊看原圖)

可以看到跟在 Google Sheets 取到的資料排法不太一樣,每個城市/國家就是一個陣列。

將資料寫進 Google Maps API

這一段大致上跟上段 Google Sheets 的部份相同,不同的點在於 Client 取得 GAS 回傳的資料後,怎麼來整理?

以下是 August 整理的方式,可以參考:

上段的程式碼處理了整理資料、丟資料到 Google Maps API。

需要說明的是,這邊改用 Vue.js 來寫,才會看到 map 改成了 this.map,很多變數也都加了 this,因為都用成了 Vue.js 的 data

另外實作產出的頁面,這幾日也完成了右下方新增列表的卡片,點擊卡片上的城市/國家名時,會移到該城市/國家,並打開 infowindow 看數據的功能。才會多寫一個 responseData 來依照確診數排序。


城市、國家名英翻中

資料源的城市、國家名都是拼英的,不是中文,所以常常看到一些地名會沒概念是在哪裡,在地圖上看還好,因為還可以從地圖上標示的中文看到,但如果是看列表的話就不方便。

原本想直接找個套件來用,但由於套件得包含大部份的文字,因此往往很大一包,最小的一個在 npm 上有看到 3 點多 MB 的。

最後決定用手動整理對照表的方式來翻,笨了點,但檔案才不會過大拖垮讀取速度。

首先是列出一個陣列,裡面是一個個的物件,Key 是拼英,Value 是中文,列完後是這樣:

https://gist.github.com/letswritetw/3e9b4040e752bacacc57233f05cb4e70

接著,在取資料時,就寫一個翻譯的 function:

這種自己手寫對照表的,好處是檔案小,讀取快,壞處就是如果資料源有新增城市/國家,就每次都要再補。

為了防止資料面有新增時,程式這邊沒有列進對照中而出現空值,因此最後也寫了對照不到就回傳原拼音,以免列表中看不見文字。

2020.03.12 更新:
因為城市數愈來愈多,今天看破四百了,都把對照表寫進頁面裡會很長。
從今日開始,把對照表寫進 Google Sheets 裡,頁面實際是抓 Sheets 裡的資料來對照翻譯。
August 先用 Sheets 原有的翻譯功能來翻,翻起來很怪的再去 Google 查城市中文名。四百多筆有點多,如果有看到很怪的中文名,就代表還未更新到,請見諒。


繪製圖表

最後開發完成的,是點擊地圖上的標記後,加入一個「開啟圖表」按鈕,點下去會生成一個照日期來繪製的圖表,像這樣:

圖表的部份,這邊直接使用套件,推薦一套很好用又開源的 Chart.js

它的參數好懂,又有許多的範例可以參考,繪製出來的圖表也漂亮。

圖表這邊比較麻煩的部份,就是要把資料的格式整理成圖表要的。以 Chart.js 來說,需要的格式是這樣:

data: {
  labels: ['January', 'February', 'March'],
  datasets: [{
    label: 'My First dataset',
    backgroundColor: 'rgb(255, 99, 132)',
    borderColor: 'rgb(255, 99, 132)',
    data: [0, 10, 5]
  }]
}

先建出一個 labels,是圖表下方的說明,接著在 datasets.data 寫出對應這個 labels 的值。像是 August 的圖表預計放上確診、康復、死亡三個,那在 datasets 的陣列出就得寫三個物件進去。

整理資料成圖表要的格式就不列出了,用幾個迴圈整理就行。

這邊要特別寫的是,因為「開啟圖表」的按鈕是放在 infowindow上,要怎麼讓 infowindow 中的按鈕可以監聽 click 事件?

infowindow 監聽按鈕 click 事件

在跑迴圈時,infowindow 的 content 就支援 HTML,因此寫成以下:

button 的部份直接寫入 id="info-btn-${id}",讓每一個 infowindow 中的按鈕都可以有一個獨立的 id。

接著,是要監聽 infowindow 本身的事件,infowindow 有一個事件叫「domready」,就是這個 infowindow 在 DOM 上就緒時,我們就寫一個當 domready 後,聽這個按鈕的 click 就行了,如下:

就這樣幾行就完成了監聽 infowindow 中的按鈕 click 事件。


Google Maps API 學習筆記系列

  1. 地圖、標記、客製樣式
  2. 在地圖上畫個日本結界
  3. 用熱圖(Heat Map)製作全台 12 小時雨量分佈圖
  4. Place API 自動完成地址、地點評論摘要
  5. 抓目前位置、計算到各點距離
  6. 畫新冠肺炎分佈圖
Summary
Google Maps API學習筆記-6:畫新冠肺炎分佈圖
Article Name
Google Maps API學習筆記-6:畫新冠肺炎分佈圖
Description
本篇大綱:有用到的筆記文列表。Google Maps API部份。取得資料部份。Google Sheet部份。取得、整理資料。將資料寫進Google Maps API。原始碼。Github CSV部份。取得資料。轉換CSV成JSON。將資料寫進Google Maps API。
Augustus
Let's Write
Let's Write
https://letswrite.tw/wp-content/uploads/2020/08/logo_512.jpg
訂閱
通知
guest

2 Comments
最舊
最新
Inline Feedbacks
看所有留言
cpliang
cpliang
3 年 之前

請問有沒有抓取csv資料版本的程式碼?