九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、前回の「Laravel + Leaflet 地図で駅名検索できるようにする 」ではleaflet
マップを使って駅名検索ができるようにしてみました。
そして、この開発を進める中で「これまで知らなかった面白い機能」がJavaScript
で使えることを知りました。
それが・・・・・・
リアルタイムで現在地を取得できる
機能です。
詳しく言うと、JavaScript
にはwatchPosition()
という関数があって、これを使うことで現在地をウォッチし続けることができるわけです。
ということは・・・・・・・・
そうです。
本格的ではないにしても、
ウォーキング・ナビ🚶✨
をつくることができるはずですよね。
例えば、浅草寺から東京スカイツリーまで歩くとします。
そして、スカイツリーまであと100m
になったら「もうすぐ目的地です❗」と通知する、ということがブラウザだけで実装できるわけです。
そこで、今回はJavaScript
を使って「簡易的なウォーキング・ナビ」をつくってみることにします。
ぜひ何かの参考になりましたら嬉しいです。😄✨
「実際に浅草寺〜スカイツリーを
歩きましたが、近そうで
めちゃくちゃ遠かったです😂」
開発環境: Vue 3
目次
実際のコード
では、今回は準備等は必要ないのでいきなり実際のコードをご紹介します。
<html> <head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div id="app" class="p-4"> <h3>簡易ウォーキング・ナビのテスト</h3> <div class="badge bg-danger" v-if="isWatching">GPS 監視中</div> <div class="badge bg-warning" v-else>GPS 準備中</div> <div class="pt-2"> GPSのチェック数: <span v-text="updatedCount"></span> 回チェックしました<br> <div class="alert alert-info mt-2" v-text="content"></div> </div> </div> <script src="https://unpkg.com/vue@3.1.1/dist/vue.global.prod.js"></script> <script> Vue.createApp({ data(){ return { targetLocation: { // ① 目的地:東京スカイツリー latitude: 35.71006374344037, longitude: 139.8106987089502 }, content: '', isWatching: false, updatedCount: 0 } }, methods: { init() { navigator.geolocation.watchPosition( position => { // ② GPS情報の取得に成功したとき this.updatedCount++; this.isWatching = true; const location = position.coords; // ここが取得したGPS情報 const distance = this.getDistance(location, this.targetLocation); // ③ 2点間の距離を計算 if(distance <= 0.1) { // 0.1km = 100メートル以内 this.content = 'もうすぐ目的地です!(100M以内)'; } else { this.content = 'まだ目的地は先です。'; } // console.log(`取得したGPS情報: ${location.latitude}, ${location.longitude}`) }, err => { // ④ GPS情報の取得に失敗したとき this.isWatching = false; console.log(err); }, { enableHighAccuracy: true } // ⑤ 各種設定 ); }, getDistance(location1, location2) { // ③ 2点間の距離を計算 const latitude1 = location1.latitude const longitude1 = location1.longitude; const latitude2 = location2.latitude const longitude2 = location2.longitude; const R = 6371; // km const diffLatitudeRadian = this.getRadian(latitude2 - latitude1); const diffLongitudeRadian = this.getRadian(longitude2 - longitude1); const latitudeRadian = this.getRadian(latitude1); const longitudeRadian = this.getRadian(latitude2); const a = Math.sin(diffLatitudeRadian / 2) * Math.sin(diffLatitudeRadian / 2) + Math.sin(diffLongitudeRadian / 2) * Math.sin(diffLongitudeRadian / 2) * Math.cos(latitudeRadian) * Math.cos(longitudeRadian); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; }, getRadian(value) { return value * Math.PI / 180; } }, mounted() { this.init(); } }).mount('#app'); </script> </body> </html>
中身としてはシンプルですが、今回は紹介することが少ないので少し詳しく見てみましょう。
① 目的地
目的地の緯度・経度をセット(今回は、東京スカイツリー)しています。
ちなみに、
- latitude: 緯度(日本のヘソは、北緯 35度)
- longitude: 経度(日本のヘソは、東経 135度)
です。
もちろん自由に変更しても問題ありませんので、みなさんが住んでいる近くの目的地に変更してください。(ただし、テストはスカイツリーを基準にしてるのでご注意を👍)
② GPS情報の取得に成功したとき
今回のメインになるnavigator.geolocation.watchPosition()
を使っている部分で、ここが「GPSの取得に成功したら」実行されるコールバック関数になります。
position.coords
の中身としては緯度・経度だけでなく精度の情報も入っています。実際の中身は以下のようになります。
accuracy: 150 altitude: null altitudeAccuracy: null heading: null latitude: 35.7106272738807 longitude: 139.81014282106662 speed: null
そして、以下2つの位置情報から距離を割り出して目的地付近かどうかをチェックすることになります。
- 現在地の位置情報
- スカイツリーの位置情報
※ ちなみにnavigator.geolocation.watchPosition()
は、なんとIE9からでも使えるそうなので、ほぼどんなブラウザでも使えると思っていいでしょう。詳しくは こちら をご連絡ください。
③ 2点間の距離を取得する
getDistance()
が現在地とスカイツリーの距離計算をしている部分になります。
なお、計算式は「ハバーシンの公式」を使って計算しています❗
…が、私のような馬鹿には詳細は分かりかねますので、以下ページをほぼ丸コピーさせていただきました。(分かりやすく変数名などは変更しています)
📝参考ページ: Function to calculate distance between two coordinates
Thank you, stack overflow!
④ GPS情報の取得に失敗したとき
何らかの理由でGPS情報が取得できなくなったときに実行されるコールバック関数です。
ここで、isWatching
をfalse
にすることで、以下の「ラベル部分」を切り替わるようにしています。
⑤ 各種設定
navigator.geolocation.watchPosition()
で使える設定は以下の3つです。
- enableHighAccuracy: 精度を高める
- maximumAge: キャッシュが効く時間(ミリ秒)
- timeout: 終了するまでの時間(ミリ秒)なお、デフォルトは Infinity で永遠に取得するようになっています。
簡単ですが、今回はこれで作業終了です。
お疲れ様でした😄✨
テストする前に
では、テスト・・・と言いたいところですが、実際にスマホを持って移動するのはめんどうです。(私はロケ好きなので近場でやってみましたが…😂)
そこで、Google Chrome
で使える「GPSの上書き機能」を有効にしましょう。つまり、好きな場所に「いることにできる」機能ですね。
※ なお、もしかすると日本語化してくれているかもしれません。適当に読み替えて実行してください。
まず、「Ctrl + Shift + i」で検証ツールを開きます。
いつものおなじみのウィンドウが開くので、この状態で「Ctrl + Shift + p」をタイプします。
すると、以下のような検索エリアが表示されるので、「sensor」と入力し、検索結果の「Show Sensors」をクリックします。
クリックすると、ウィンドウ下部にSensors
という項目が表示されるので、ドラッグしてエリアを広げます。
すると、Location
という項目に「Manage」というボタンがあるので、これをクリックします。
以下のように、「ここにいることできる」場所のリストが表示されるので、追加ボタンをクリックしてテストする位置を登録します。
なお、今回のテストは「浅草寺からスカイツリーに近づいていく」というストーリーで実施しますので、以下3つの場所を登録します。
- 浅草駅( 35.7107940011208,139.79776148765654 )
- 本所吾妻橋駅( 35.70859866499581, 139.80456289943254 )
- とうきょうスカイツリー駅( 35.71063338211721, 139.81014390586284 )
※ なお、今回の目的地から100m
以内にあるのは「とうきょうスカイツリー駅」だけです。(ホームの端だけですが)
ということで以下のように3つ登録しました。
登録が完了したら、右上の閉じるボタンをクリックして、元に戻っておいてください。
では、これで準備は完了です。
この「いることにする」機能を使って実際にテストしてみましょう❗
テストする
では、実際にテストしてみましょう❗
JavaScript
を HTTPS アクセスができる環境にセットしてブラウザでアクセスします。
※ Chrome
で位置情報を取得するには HTTPS 接続が必須になります。
すると、「まだ目的地は先です。」と表示されています。
なお、watchPosition()
は、ブラウザが起動したときだけでなく何か変化があったときにも実行されるようになっているようです。
そのため、一度タブからフォーカスを外し、再度フォーカスを戻すともう一度GPS
取得を試みてくれます。
では、先ほどの機能を使って、まずは「浅草駅にいることに」してみましょう。
Location
項目のセレクトボックスを「浅草駅」へ変更します。
すると・・・・・・
はい❗
GPS
のチェック数が増えました。(つまり移動したことになったからですね👍)ただし、表示は「まだ目的地は先です。」と前と同じです。
では、続いて「本所吾妻橋駅」へ擬似的に移動しましょう。
すると・・・・・・
はい。
また回数が変わるものの、テキストは同じままです。
では最後に、100メートル以内の場所として登録した「とうきょうスカイツリー駅」を選択してみましょう。
どうなったでしょうか・・・・・・??
はい❗
想定通り100メートル以内に入ったことを通知してくれました。
成功です✨😄👍
※ ちなみに、スマホで実際にやってみたところ数秒ごとに更新されてました。そのため、スマホならリアルタイムでGPS取得できると思います。
企業様へのご提案
今回の機能を使えば、ウォーキング・ナビだけでなく、例えばAさん・Bさんがお互いに近くにいることを検知することもできますし、業務システムに組み込んでおけば、自然と従業員の今いる場所を通知することができます。
また、Pusher
のようなプッシュ機能を使えば、「誰がどこに到着したか」をリアルタイムで(ブラウザをリロードせず)に更新 or 通知することもできます。
つまり、位置情報の管理は、スマホのネイティブアプリでなくとも同様のことができるようになっています。
こういった機能をご希望の場合は、ぜひお問い合わせからご連絡ください。
お待ちしております。m(_ _)m
おわりに
ということで、今回はブラウザだけで「ウォーキング・ナビ」ををつくってみました。
意外とシンプルなコードで実装ができるのでいろいろな応用もしやすいんじゃないかと思います。
※ ちなみに、実際にスマホで試したところ50m以内に入ったことを検知することができました。
ぜひ皆さんもいろいろと試してみてくださいね。
ではでは〜❗
「きれいなマンホールの
写真をコレクションしてます。
キン肉マンとかもあるらしい❗」