今天要將前兩天設計好的內容跑起來囉~
今天要完成 3 個檔案
這裡使用的是 haversine formula(半正矢公式)
詳細可以參考維基百科
from math import radians, cos, sin, asin, sqrt
import math
def distance( lat1, lng1, lat2, lng2):
# convert decimal degrees to radians
lng1, lat1, lng2, lat2 = map(radians, [lng1, lat1, lng2, lat2])
dlng = lng2 - lng1
dlat = lat2 - lat1
# haversine formula
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlng/2)**2
c = 2 * asin(sqrt(a))
return 6371 * c # km
透過之前整理的 positions.csv
, $O(N)$ 掃過全部資料,計算當前座標點到各藥局的距離
找到其中距離在1公里內的儲存並排序回傳前10項
import csv,os
from logger import logger
from get_distance import distance
data_folder = str(os.getcwd()) + '/data/'
def get_mask(event):
if(event.message.type == 'location'):
try:
positions = open(data_folder + 'csv/positions.csv','r')
logger.info('open positions.csv => Success')
except:
logger.warning('open positions.csv => Failed')
radius = 1.0 #km
result = []
mask_data = csv.reader(positions)
lat1 = float(event.message.latitude)
lng1 = float(event.message.longitude)
for data in mask_data:
try:
lat2 = float(data[7])
lng2 = float(data[8])
except:
continue
if(distance(lat1, lng1, lat2, lng2) <= radius):
result.append(data)
result = sorted(result, key=lambda x:(int(x[4]) + int(x[5]))*-1)
return result[:10] # 10 maximun
首先處理接收與回傳訊息
與之前Day12 LINE BOT & 天氣預報 相同
我們依序讀入資料,將其放入 Bubble 中,最後 append 到 Card 當中回傳
if(message_type == 'location'):
update()
result = get_mask(event)
Card = json.load(open(data_folder + 'json/Card.json','r',encoding='utf-8'))
for i in range(len(result)):
bubble = json.load(open(data_folder + 'json/Bubble.json','r',encoding='utf-8'))
# 醫事機構代碼
Code = result[i][0]
# 藥局名稱
Name = result[i][1]
# 地址
Address = result[i][2]
# 電話
Phone = result[i][3]
# 成人口罩剩餘
AdultRemain = result[i][4]
# 兒童口罩剩餘
KidRemain = result[i][5]
# 最後更新時間
LastUpdate = result[i][6]
# 緯度
Latitude = result[i][7]
# 經度
Longitude = result[i][8]
# 標題
bubble['body']['contents'][0]['text'] = Name
# 電話
bubble['body']['contents'][1]['contents'][1]['text'] = Phone
# 地址
bubble['body']['contents'][2]['contents'][1]['text'] = Address
# 成人口罩剩餘
bubble['body']['contents'][3]['contents'][1]['text'] = AdultRemain
# 兒童口罩剩餘
bubble['body']['contents'][4]['contents'][1]['text'] = KidRemain
# 最後更新時間
bubble['body']['contents'][5]['contents'][1]['text'] = LastUpdate[5:-3]
# PostBack Action
bubble['footer']['contents'][0]['action']['data'] = 'GetMap {}'.format(Code)
Card['contents'].append(bubble)
line_bot_api.reply_message(reply_token, FlexSendMessage('查詢結果出爐~',Card))
Postback Action 詳細可以再參考 Day07 進階訊息傳送1 - Actions
我們可以指定 postback action 被觸發時回傳的 data
藉此知道這是要來取得藥局地圖資訊
在這裡,我們設定 data 的前綴為 GetMap
,後面接上醫事機構代碼
藉由代碼可以找到使用者查詢的藥局
@handler.add(PostbackEvent)
def handle_postback(event):
if(event.postback.data.startswith('GetMap')):
px = event.postback.data[7:]
positions = json.load(open(data_folder + 'json/positions.json','r'))
name = positions[px]['name']
lat = positions[px]['lat']
lng = positions[px]['lng']
address = positions[px]['address']
line_bot_api.reply_message(event.reply_token, LocationSendMessage(
title = name,
address = address,
latitude = lat,
longitude = lng
))
完工
這裡再重述一次本機測試的方式
開啟 ngrok
到 LINE Official Account 設定 webhook 連結
執行 main.py 測試
成功~
終於完成拉~
這次花了很大的篇幅在說明口罩查詢系統
這也是我第一次接觸 LINE BOT 的原因
也趁著這次機會更新一下過去沒做好的內容w
不過我還在想要怎麼跟 LINE Rich Menus 結合,讓查詢更方便
實際開發過一次,可以更了解到使用者體驗、coding style、註解、functional coding 的重要性
雖然在後疫情時代,也許口罩查詢系統沒有過去重要了
但是其中能將過去學到的內容都實作過感覺很棒w
還是希望大家能玩得開心囉
口罩查詢系統 Github 連結
Day07 進階訊息傳送1 - Actions
Day12 LINE BOT & 天氣預報
LINE Official Account
半正矢公式