スマートフォンのGPS情報を取得し地図上に表示するWEBアプリを作成
今回作ったもの
URLにアクセスしたスマートフォンの位置情報を取得し,地図上に表示するWEBアプリを作成した.
建前上の用途は,バスなどにスマートフォンを積ませ,停留所で待つ乗客が位置を確認できるなど...
ソースはここに置いている
github.com
実際の動き
スマートフォンからアクセスした画面はこんな感じ
緯度経度と選択された経路が表示されている(
バスの経路をイメージ).
構成
サーバ側は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接続でなければ取得できない
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を描画できるAPIのpythonラッパーが公開されていた.
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)); }