iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 18
1
自我挑戰組

LINE BOT 新手村30日攻略系列 第 18

Day18 口罩查詢系統 - 3

  • 分享至 

  • xImage
  •  

口罩查詢系統 Github 連結

今天要將前兩天設計好的內容跑起來囉~
今天要完成 3 個檔案

  • main.py
  • subsys/get_mask.py
    回傳半徑 1 公里內最近的前 10 個
  • subsys/get_distance.py
    回傳兩點座標距離

get_distance.py

這裡使用的是 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

get_mask.py

透過之前整理的 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

main.py

首先處理接收與回傳訊息
與之前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

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
        ))

完工

整體測試階段

這裡再重述一次本機測試的方式

  1. 開啟 ngrok

  2. LINE Official Account 設定 webhook 連結

  3. 執行 main.py 測試

  4. 成功~

後記

終於完成拉~
這次花了很大的篇幅在說明口罩查詢系統
這也是我第一次接觸 LINE BOT 的原因
也趁著這次機會更新一下過去沒做好的內容w
不過我還在想要怎麼跟 LINE Rich Menus 結合,讓查詢更方便

實際開發過一次,可以更了解到使用者體驗coding style註解functional coding 的重要性
雖然在後疫情時代,也許口罩查詢系統沒有過去重要了
但是其中能將過去學到的內容都實作過感覺很棒w

還是希望大家能玩得開心囉

參考資料

口罩查詢系統 Github 連結
Day07 進階訊息傳送1 - Actions
Day12 LINE BOT & 天氣預報
LINE Official Account
半正矢公式


上一篇
Day17 口罩查詢系統 - 2
下一篇
Day19 Python 爬蟲 Beautiful Soup
系列文
LINE BOT 新手村30日攻略30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言