wide and deep

スマートフォンのGPS情報を取得し地図上に表示するWEBアプリを作成

今回作ったもの

URLにアクセスしたスマートフォンの位置情報を取得し,地図上に表示するWEBアプリを作成した.
建前上の用途は,バスなどにスマートフォンを積ませ,停留所で待つ乗客が位置を確認できるなど...
ソースはここに置いている
github.com

実際の動き

スマートフォンからアクセスした画面はこんな感じ
緯度経度と選択された経路が表示されている(
バスの経路をイメージ).
https://user-images.githubusercontent.com/37448236/66890234-59ef8580-f020-11e9-836c-4c4648df0028.jpg

表示される地図はこんな感じ
指定座標にピンを立て,infoには経路情報を表示している.
https://user-images.githubusercontent.com/37448236/66890059-a6869100-f01f-11e9-9360-03a708781e43.png

構成

サーバ側はpython(flask),クライアント側は大体Javascriptでどうにかしている.

  • python(flask)
    • サーバを建てる
    • 位置情報を保存
    • 地図を描画しピンを立てる(flask_googlemaps)
  • Javascript
    • 位置情報を取得
    • 位置情報をサーバに送信

新しく知ったこと

GPS情報はJavascriptで取得できる

標準のnavigator.geolocation.watchPositionを使って各種情報を取得できるらしい.かんたん.
今回は緯度経度・精度しか使っていないが,他にもspeedとか色々あるらしい.
developer.mozilla.org

// GPS値が変化したら実行される
navigator.geolocation.watchPosition((position) => {
    var lat = position.coords.latitude;
    var lng = position.coords.longitude;
    var acc = position.coords.accuracy;
    sendLocation(lat, lng, acc, route_id);
}, (error) => {
    alert('GPS情報が取得できません.権限を確認してください')
}, {
    enableHighAccuracy: true
});

GPS情報はhttps接続でなければ取得できない

参考↓
www.flying-h.co.jp

httpsでサーバを建てるためにオレオレ証明書を発行し,flaskに読み込ませた.
まずssl証明書を/certに作成

$ sudo apt install openssl
$ sudo apt install python3-openssl
$ mkdir cert
$ cd cert
$ openssl genrsa 2048 > server.key
$ openssl req -new -key server.key > server.csr
$ openssl x509 -days 365 -req -signkey server.key < server.csr > server.crt

作成した証明書をflaskに読み込ませる.

from flask import Flask
import ssl

app = Flask(__name__)
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain('cert/server.crt', 'cert/server.key')
...
if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True, ssl_context=context)

GoogleMapはflask_googlemapsから描画できる

JavascriptでGoogleMapを描画できるAPIpythonラッパーが公開されていた.
github.com

使い方は直感的でわかりやすい.
Mapオブジェクトに色々設定したあと(ここでは変数mymapに格納),それをrender_templateでmymap=mymapとして渡してやる.

from flask import Flask, render_template
from flask_googlemaps import GoogleMaps
from flask_googlemaps import Map

app = Flask(__name__)
GoogleMaps(app, key="GOOGLE_MAP_API_KEY")

def mapview():
    df = pd.read_pickle('./df.pkl')
    subset = df[['lat', 'lng', 'info']]
    locations = [tuple(x) for x in subset.values]
    # マップを作成
    mymap = Map(
        identifier = "view",
        lat = 31.581319,
        lng = 130.544519,
        markers = [(loc[0], loc[1], loc[2]) for loc in locations],
        fit_markers_to_bounds = len(locations) > 1,
        style = "height:800px; width:80%; margin:auto; text-align:center;",
        region = "JPN"
    )
    return render_template('map.html', mymap=mymap)

map.html側では,ヘッダで{{mymap.js}}を受け取り,body(表示したい場所)で{{mymap.html}}を受け取る.

<!DOCTYPE html>
<html>
<head>
    {{mymap.js}}
</head>

<body>
    <h1 style="text-align: center;">Flask Google Maps</h1>
    {{mymap.html}}
</body>
</html>

とても簡単に使えるので今後使っていきたい.
あとGOOGLE_MAP_API_KEYを取得する必要がある.
このあたりを参考に
nendeb.com

クライアント → サーバのデータ送信

サーバ側にPOST用のページを作成しておく.
ここでは緯度経度,精度,経路情報を受け取るようにしてある.
受け取るデータはjson.loads(request.data.decode('utf-8'))のようにしなければならない.
request.data.decode('utf-8')自体はString形式を返すらしいのでちゃんとJSON形式に変換してやる

# POSTされた情報を受け取るページ
@app.route('/send-location', methods=['POST'])
def send():
    data = json.loads(request.data.decode('utf-8'))
    addr = request.remote_addr
    lat = data["lat"]
    lng = data["lng"]
    acc = data["acc"]
    route_id = data["route_id"]
    route_name = data["route_name"]
    ...
    return ''

クライアント側のデータ送信部分
上記/send-locationに各情報をまとめた辞書をJSON化して(JSON.stringify(data)))POSTする

// 取得したデータをサーバへ送信
function sendLocation(lat, lng, acc, route_id, route_name) {
    var data = {
        "lat": lat,
        "lng": lng,
        "acc": acc,
        "route_id": route_id,
        "route_name": route_name
    }
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/send-location");
    xhr.send(JSON.stringify(data));
}

終わりに

今回はスマートフォンGPS情報を取得し地図上に表示するWEBアプリを作成した.
大学院の特別研究と称された,外部研究室での研究として取り組んだが,とても興味を引くことをやらせていただいた.
今までの知識も活用することができ,早く簡単に作成することができた.