2018-10-12

淺談分析 Google 定位記錄尋人

日前香港電台節目《鏗鏘集》探討數月前的滑翔傘意外,遇難者親友披露當日的一些搜救細節,希望政府當局可以檢討將來如何善用資訊科技幫助搜救。節目中提到,親友初時翻查手機定位記錄認為失蹤者或在鳳凰山,惟直升機搜索該位置並無發現,加上風向分析,故此很快排除了鳳鳯山。其後當局利用電話訊號發射站記錄鎖定搜救範圍,惟範圍廣闊,而且天氣不穩,首數天無功而還。親友後來仔細翻查手機定位記錄,發現線段原來重疊,最後定位疑為大東山而非鳳凰山,並在失蹤後第五天從 Google Takeout 匯出定位記錄,由於警方當時未能開啟 JSON 檔案,故此片中談到親友自行上 Google 找方法分析數據,並篩選出兩個高準確度的座標,認為最終位置應為大東山地勢崎嶇的南坡高地,而兩點亦其實相距不遠,惟當時礙於種種因素未有立即根據線索行動,及至翌日在家屬多番游說後才派人前往該兩個座標,並終在其中一個座標的十餘米附近找到失蹤者。

家屬強調並非要追究責任,而是希望可以善用科技改善搜救效率,好使搜救人員日後不必如這次般那麼辛苦日以繼夜重複上山搜索。(其實現時還有不少方法方便分享定位記錄,如漁護署的郊野樂行 Apps 內的 GPS 留蹤服務,行山人士亦可在設有標距柱的郊遊徑善用無須流動數據的 50222 短訊服務,而 Google 用戶更可考慮設立 Trusted Contacts  (安全聯絡人) 以備不時之需。

今日嘗試根據節目中的方法下載及分析自己的定位數據,並將有關步驟詳列於下以供參考,不過希望不會真的有人需要用到。首先你要有相關 Google 賬戶的登入資料 (如賬戶以及密碼,還要那部電腦早已獲豁免使用2-Factor 認證,不然就要向 Google 緊急求助),然後該用戶必須已開啟定位記錄功能,若然沒開定位或行程一直接收不到流動數據訊號就真的沒法子了,而暫時測試到能夠獲取新至即日的定位記錄,沒有太大的延誤問題。
  1. 登入相關 Google 賬戶前往 https://takeout.google.com/

  2. 按 “Select None” 避免下載不必要的數據,然後向下捲找 Location History

  3. 按旁邊的掣以選擇下載定位數據 (預設檔案類型為 .JSON)


  4. 下捲至最底按 Next (其後或須輸入密碼確認)

  5. Archive Size (Max) 選擇 1GB (不過若然真的壓縮後有 1GB,解壓後隨時有 10GB,那事情就麻煩很多了,以下方法未必可行 =p)

  6. 按 Create Archive 然後等候 Google 準備資料

  7. 下載後解壓,找子資料夾 \Takeout\Location History\,內裏應有個 Location History.json 檔案,視乎檔案大小,小的(例如幾十 MB 內還可以用記事本開,比較大的話就或要利用程式再作處理)

  8. 小型的檔案,用記事本開啟後,把內容複製至: https://konklone.io/json/ (或可在 Google 上找其他 json 解析工具),網頁下方會顯示解析原始數據後的內容,如經度 (Longitude)、緯度(Latitude)、時間(timestampMs,為 UNIX Timestamp),以及定位準確度 (Accuracy, 單位為米),經緯度的小數點要自己加上,小數點右方應有7個位,如 latitude欄的 221234567 是指 北緯 22.1234567度,可下載為 CSV 檔後再作處理 (將數字除以一千萬即可)

  9. 若要將 UNIX Timestamp 轉為常用的時間格式 (公式中的 +1/3 是將 UTC 時間轉為香港時間),可在 Excel 打入以下的公式 (假設資料放在 A1 格):
    =(A1/86400/1000+1/3)+25569

    然後將該欄格式自訂為:
    d/m/yyyy h:mm:ss



  10. 越上方的記錄為越新的記錄,可以根據有關記錄的準確度 (視乎器材設定、衞星訊號接收情況,定位可準確至10米以內),以及記錄時間間距去決定有關記錄的可信程度。以下圖為例,定位準確度忽然由10米下跌至1200米,皆因小弟進了室內吃豆腐花,電話不能順利接收衞星訊號定位,以致定位可能出現龐大誤差。得到目標位置經緯度後,可將經緯度打在 Google ,它會給你顯示相應地點。要注要的是,衞星訊號接收情況極受環境因素影響,在空曠地區較佳,而在香港高樓林立的市區,則會容易得到錯誤定位,必須小心解讀有關記錄,例如剛才翻查檔案也有些明顯錯誤定位,如人在東涌道的巴士,但零星準確度低(accuracy 聲稱約一千米)的定位記錄卻指人在貝澳。


  11. 若然檔案真的比較大,可能需要利用一些小程式幫忙抽取數據,下面用了 Python 3.6 (可能需要安裝額外的 packages,建議使用 64-bit 版本),不過碰巧手上沒有大型 JSON 檔作測試,惟根據過往經驗,一、二百 MB 應該不成問題,可按照註解更改程式碼:

import json
import os
import pandas as pd
from pandas.io.json import json_normalize

## CONVERSION TOOL FOR ANALYZING THE JSON FILE (LOCATION DATA) EXPORTED FROM GOOGLE TAKEOUT ## CHANGE THE WORKING DIRECTORY TO THE APPROPRIATE DIRECTORY
os.chdir('D:\\Medium\\Takeout\\Location History')

## LOAD THE JSON FILE
with open('Location History.json', encoding='utf-8') as data_file:
    data = json.loads(data_file.read())

## READ THE JSON FILE LOADED INTO A PANDAS DATAFRAME
df1 = pd.json_normalize(data['locations'])

## EXTRACT THE MOST RECENT n ROWS FOR ANALYSIS
n = 10000
df1 = df1.head(n).copy()

## CHECK THE STRUCTURE OF THE DATAFRAME
print(df1.columns)

## DROP THE ACTIVITY COLUMN TO FURTHER REDUCE THE SIZE
df1.drop(['activity'], axis=1, inplace=True)

## ADD THE DECIMAL POINT TO THE COORDINATES
df1['latitudeE7'] = df1['latitudeE7']/10000000.0
df1['longitudeE7'] = df1['longitudeE7']/10000000.0

## CHANGE THE UNIX TIMESTAMP(IN MILLISEC) TO LOCAL TIME (GMT+8)
df1['time'] = pd.to_datetime(df1['timestampMs'],unit='ms').dt.tz_localize('UTC').dt.tz_convert('Asia/Hong_Kong')
df1.drop(['timestampMs'], axis=1, inplace=True)

## RE-ORDER AND RENAME THE DATAFRAME FOR CONVENIENCE
## PLEASE CHECK IF THE FOLLOWING COLUMNS EXIST (AND ADJUST THE LIST IF NECESSARY)

print(df1.columns)
df1 = df1[['time','latitudeE7','longitudeE7','accuracy','altitude','velocity','verticalAccuracy','heading']]
df1.rename(columns={'latitudeE7':'latitude','longitudeE7':'longitude'}, inplace=True)

## EXPORT THE DATAFRAME TO AN EXCEL FILE
df1.to_csv('output.csv')