最近武漢肺炎疫情,又想起了2002年,讀國中的時候遇到 SARS 疫情時期的那份恐懼,上課或是出門到哪邊都要先量體溫,許多店門口都放酒精消毒用品,第一次知道什麼叫N95口罩。

但現在是2020年,醫護人員一樣血汗,但時代不一樣了,行動上網普及,資訊工具與開發方式百家爭鳴,政府還公發醫療口罩?又有什麼新梗呢?大概是口罩地圖之亂吧!

2020/2/2 的時候大家瘋傳一個超商口罩掃貨地圖……呃不是,是超商口罩現況回報的網站,畢竟都 2020 年了,超商POS機的數據已經連網了,超商這麼佛心把資料放出來給大家查? 結果想得美,原來是一個台南的大大寫的,超商口罩庫存量的資料來源,是仰賴老百姓對資訊工具的使用熟悉度和公德心。

那個網站當天晚上就連不上了,後來就看到新聞科技防疫|自製「超商口罩地圖」的工程師:地圖上線6小時,我收到60萬Google帳單,隔天 2/3 晚上政府宣布超商不賣口罩,並改成實名制購買,7天內有限購買數量。

2/4 起超商也不賣口罩了,2/6變成藥局實名制口罩時代,大家都變成改在藥局排隊尋找 ONE PIECE。宣布改成藥局販售的時候,有些部落客手腳很快,例如硬是要學-全國健保藥局地圖用免費的 Google 我的地圖 功能把健保藥局名單匯了上去。

看到全國健保藥局地圖的時候,突然有個大膽的想法:
– 之前自己有做過一個佛系寶可夢玩家-寶可夢VIP道館,每週從 Google 地圖的 URL 爬 VIP 道館的發券資料,再處理轉換成自己看的資料。
– 有時候到不認識的地方,也可以查到附近有哪些VIP 道館,所以有做過查詢附近VIP道館的功能。
– 也做過幾個網站,顯示分店銷售據點。

上述這些製作經驗,這不就跟找藥局的工具差不多嘛?於是也用 Google Maps Place API ,把幾千間藥局的中文地址轉成經緯度座標,弄了一個 懶人找口罩 – 離我最近的口罩購買處的網頁,自己的工具自己動手做。

v 1.0 初版靜態資料

我這個路癡+方向感白癡很討厭看地圖,所以操作畫面上根本沒有打算要放地圖,初版就是透過 HTML5 的 Geolocation API 取得裝置當下位置,然後再換算直線距離(純粹用上網找的球體上求兩點距離算法,在用戶端的瀏覽器上運算,沒有使用 Google Maps 的 Distance Matrix API),把附近藥局資料,用卡片清單介面顯示出來而已。

自己用來查一下應該很方便。不必忍受別人的程式上面可能有廣告,被追蹤,還是有什麼多餘的步驟或流程。

v 1.1 改接 Open Data 的藥局資料與口罩庫存量

後來 2/6 藥局口罩正式開賣,也從 g0v 口罩供需資訊平台 看到政府放出來的 Open Data,真是官方逼死同人,上面已經幫人處理好了每間藥局的經緯度座標,還有各藥局的口罩庫存量?

許多資料工具標榜「即時」,但有點資訊素養就知道,要先釐清資料從哪裡來的?多久更新一次?才知道是真即時,還是商人的騙人話術。

那麼藥局的口罩庫存量的數據是哪來的?原來也不是即時更新,而是隔一段時間才會從一個系統同步到另一個系統:
– (官方)口罩售出,藥局刷健保卡之後,健保局的系統會扣庫存
– (官方)每隔一段時間,會將上述的庫存數量,更新到一份「健保特約機構口罩剩餘數量明細清單」的 csv 檔案
– (民間)再隔一段時間,一些民間開發者會把剛剛的 CSV 分流成 JSON 格式或 GraphQL 的資料
– (民間)大家再接這邊的資料,寫成各種口罩地圖應用。

所以說從口罩售出之後,要經過上述這麼多段資料同步處理時間的延遲,然後還有發號碼牌作業、藥局有「我流銷售法」的情況,真的就算口罩地圖看到有庫存,還是有滿大機會買不到的!做資訊程式就是這麼可悲,常常要假裝自己有解決問題…

這邊開始程式又變肥了,乖乖把程式改成接上口罩庫存量的資料,多了爬蟲程式去爬 Open Data 的資料然後存在資料庫。後續 Open Data 還多了一個 updated 的欄位,顯示每家店資料是啥時更新的,但是如今大家都知道,這些更新時間、庫存量,也只是顯示身體健康的…

v 1.2 增加防疫新聞資訊

做來自己玩玩而已,也沒有登錄到什麼公開的地方,但是這時候在 g0v 的登錄清單上,民間開發者做的相關應用已經有二三十種了,操作畫面多半是以地圖為主,還有一堆篩選器功能,然後除了網頁版的,還有 Line 聊天機器人, Telegram 聊天機器人, iOS 捷徑的,更厲害的還有幫藥局設計叫號系統的,實在是非常厲害。大家真的對防疫這麼有責任感嗎?大概是因為這些開放資訊資料僅憑單純的 URL 就可以存取,使用者使用時也不用什麼註冊登入之類的步驟,程式有問題也不會怎樣。

之前做「附近的VIP道館」的時候,無意發現這類玩意有一個有趣的應用,當初因為想要分析有多少人使用「查詢附近的桃園 VIP道館」功能,以及都是哪裡的人在查,所以是有記錄一些操作資訊。

也就是說,如果小明開發類似服務,貼連結叫我試玩一下,發生以下的對話:
2020/2/30 13:50 小明:我做了個東西,來試玩一下 網址是…
2020/2/30 13:51 我:酷喔,還滿準的,我附近的藥局都有顯示
2020/2/30 13:52 小明:你現在在幹嘛呀?
2020/2/30 13:52 我:我在上班呀
2020/2/30 13:52 小明:你是做啥工作的?
2020/2/30 13:52 我:資訊業的

如果程式並且有做使用記錄機制+手機那時候的定位很準+特定時間點(甚至只有我在用,那根本不用時間點了),上述種種條件滿足之下,從本段範例對話,小明就可以從系統記錄得知我在哪邊上班。除非那個地方是一棟辦公大樓,而且從1~10樓有不同的資訊公司,小明只有1/10的機率猜到我在哪間公司上班。這個問題就跟共享機車服務某種程度上可以用來窺探隱私一樣…

其他還有一些有趣的用法,例如:
– 弄一個會員註冊功能,號稱可以把某藥局加到最愛,更方便查詢,或是連續登入一週可以抽口罩之類的,如此一來就可以更方便蒐集到單一使用者的歷史行蹤。
– 科技水平沒這麼高的,弄個 google 表單還是用 chatisfy 弄個 FB chatbot 來騙人留個資抽口罩,更是零資訊建置成本,騙個資馬上就上手。
– 號稱有「口罩進貨提醒通知」,Android 手機可以做網頁推播,或是 FB/Line/Telegram 弄個聊天機器人,然後在選舉的時候搖身一變,推播一些有的沒的訊息。

細思極恐,不可不慎呀!口桀口桀,所以我的懶人口罩地圖就沒有特別埋記錄了,我也不太敢用別人的口罩地圖查詢工具。

口罩地圖上線沒幾天,某天晚上突然聽到許多北部人收到國家級警報 http://bit.ly/2SpSxeT ,1月31日曾於北北基重要景點與鑽石公主號郵輪旅客密切接觸者,請進行自我健康觀察,政府的訊息竟然還用國外的縮網址?看到 bitly 的反射動作就是在後面接個加號 http://bit.ly/2SpSxeT+ 查詢點擊的概況,然後如果縮網址公司裡面有匪區工程師,又發生「技巧性資料遺失」,讓轉址轉到其他網站去呢?

口罩查詢需求衍生出的各種可怕的邪惡應用,實在讓人不寒而慄,於是網站上又增加了一塊防疫新聞資訊的連結。

v1.3 加上休息時間

我是只有一三五才可以買公發口罩的,根本沒預料到口罩這麼難搶,平常下班後口罩都賣光了,唯一的希望只有禮拜天了。

但是禮拜天下午出門找口罩,使用敗口罩的 Line bot,或是當初那個做超商口罩地圖的大大做的新版網站,卻遇到很大的問題!地圖上一看滿滿的綠色的庫存充足標記,根本只是幻覺,一堆藥局禮拜天沒營業啊!還要多點一下看營業時間然後才發現沒營業,有的連營業時間都沒顯示,存心想讓人白跑一趟,增加交通工具的碳排放量。

痛定思痛,我的口罩地圖上也是只有顯示附近有哪些店,沒有顯示營業時間,只好來修改一下,看了一下 Open Data ,至少有4個相關欄位:
– available 欄位的資料是像「禮拜天上午休診」、「禮拜天晚上休診」一大串中文
– note 跟 service_note,看起來最像人話,就是亂七八糟的註記,後來 service_note 活沒多久又被砍了。
– service_periods ,資料是 21 個字的 NNNNNNYYYYYYYYYYYYYYY,前7位數代表周一到周日的早上,N代表有開,Y代表沒開…

想知道每家店禮拜幾+幾點開始營業+幾點有營業,就能做出查詢的當下,沒開的店就直接不要顯示了,但不知道下午休診的「下午」究竟是幾點到幾點?還有早上有營業,但是下午才開始賣口罩的,店家就這麼喜歡有人打電話去問他們幾點上班賣口罩?資料亂七八糟,要顯示出完美的答案,似乎是不太可能。

做資訊程式就是這麼可悲,整天聽別人說要上太空,被人使喚把時間浪費在一些無用的地方,然後許多基本的資料都沒人整理…

總之後來又把東西通通加上去,並顯示哪時候休息的資訊。

v1.4 加上 Open Street Map 顯示地圖

自製的口罩地圖一開始只有卡片介面條列附近的店家資訊跟距離,但有些店家看似距離很近,看路名卻完全不知道在哪?雖然我不喜歡看地圖,看來還是得放個地圖,比較方便吧。

Google Maps JavaScript API 用太多要錢,發現有個叫 Leaflet 的地圖套件,裡面是用佛心免費的 Open Street Map,親手試了一下,以 HTML5 Geolocation API 拿到的位置為中心點,並在地圖上打上藥局的位置,口罩數量>10的就打上綠點,點了會跑出 infoWindow,程式碼還比 Google Maps 的API 精簡很多。

Pokemon GO 的遊戲也是從 2017 年起改用這套 Open Street Map (OSM),所以玩家都很熟悉 OSM 的「圖資問題」,有些地方明明沒有路,但地圖上卻有路;有些地方明明有路,在地圖上卻是一大坨空曠區域,反正地圖只是參考輔助用,就不要計較太多了。

把藥局的點打上地圖就發現,資料視覺化就是一個照妖鏡,來源的 Open data 幾乎只有中文地址是正確的,其餘則有各種狀況:
– 經緯度座標點跟實際位置誤差達幾百公尺的(例如得康藥局:桃園市中壢區龍岡路二段89 號的位置不在24.939096,121.231292,差了近800公尺,)
– 只有地址對,藥局名稱&經緯度座標都不對的(例如24.947143,121.228543的同慶大藥局)
– 在地圖上找到庫存充足的藥局,卻有千百個已售完的情況,還有還沒開賣就已售完的(不是發號碼牌的),非常混沌。

本來以為藥局的經緯度座標有誤,是因為用 Google Maps Place API 直接轉換中文地址後沒有再人工校對,但是比對我之前用 Google Maps Place API 轉出來的資料,Google Maps Place API 的轉出來的經緯度座標都是有正確對應到中文地址的,那麼政府 Open data 裡的藥局經緯度座標錯誤資料,究竟是哪來的?後來去 github 上發 issue 回報藥局的正確位置,來源資料也就修正了。

也許直接幫每間藥局建一個聊天室,有啥奇怪的狀況直接讓民眾在上面回報,使用者邊排隊還可以邊聊天或是玩小遊戲,這樣可能會更實際一點,但這聊天室的管理,又要仰賴老百姓對資訊工具的使用熟悉度和公德心…

v1.5 加上點選地圖重新定位功能

如果是電腦開啟網頁, HTML5 Geolocation API 拿到的裝置定位資訊,誤差非常大,於是又增加了一個點選地圖,以點選的位置重新計算的功能。

這中間又遇到一個插曲,每半小時爬回來的藥局和庫存資料,都擺在一個虛擬主機(共享主機)的 MS SQL 裡面,DB 容量剩不到 200MB,想說到時候可以依據歷史資料,做一些比較有用的資料分析,例如通常每天幾點會補庫存、每天通常幾點會賣光、推測郵差補貨路線,然後用 LINE Notify 通知我,結果才跑個幾天,資料庫空間竟然爆了!

看來店家資料跟庫存量應該要分開存才對,但懶得處理,後來又改了一次爬蟲程式,沒用到的資料就不存了,舊資料也不留了。

v1.6 藥局+衛生所時代

藥局口罩時代的第二個週末來臨,政府又有新的作為,宣布衛生所也開賣公發口罩。網頁上本來共XXXX間藥局的字樣,通通都要修改了,地圖程式要改嗎?

打開地圖一查,尬電姆!為什麼附近的衛生所都沒顯示?難道 Open Data 裡把衛生所擺在不同的 json 節點?從政府的公告查,原來不是所有的衛生所都有賣,桃園地區有賣的衛生所也才開放3間,所以衛生所很難找是正常的,不要緊張。

v1.7 網購口罩時代

發文後快二個多月又回來更新,從2月到現在又有不少變革。3月中開始開放網購訂購口罩,4月初也變成不限身分證單雙號,因為手機之前就有裝健保快易通的APP,所以就改用線上訂購了,不用再一家一家跑,連口罩地圖也不用看了,還能減少藥局人員辛苦包裝與販售口罩的工作量。

生活中充滿各種數位落差,例如線上會議不能用 zoom 要用啥軟體?盜版影音網站收了要去哪追劇看電影?每天都在玩手機但不知道怎麼買口罩?周遭仍有許多需要幫助的人,而我們卻無能為力…。程式每天仍在排程自動更新庫存資料,但現在還沒想到網購口罩時代,口罩地圖要怎麼轉型,使用者需要知道的資料只有開賣時通知我、下次什麼時候可以再訂、哪邊取貨、哪種付款方式最優惠,(繳費跟取貨時官方會通知),還有各種疫情謠言澄清,好像也沒甚麼功能好做了。

結論

防疫人人有責,這次的低水平懶人玩具製作經驗也受益匪淺,知道如何用爬蟲把 DB 灌到爆掉,用免費的地圖 API 等等。

寫這篇文章的時候,g0v上的口罩地圖的相關應用列表已經有將近百種。可惜在藥局口罩時代,口罩是用健保卡實名制購買的,不然這些口罩地圖通通都會接上金流,保留更多使用者個資,變成口罩代購/預購工具了。