JavaScript で簡易ウォーキング・ナビをつくる

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、前回の「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情報が取得できなくなったときに実行されるコールバック関数です。

ここで、isWatchingfalseにすることで、以下の「ラベル部分」を切り替わるようにしています。

⑤ 各種設定

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つ登録しました。

登録が完了したら、右上の閉じるボタンをクリックして、元に戻っておいてください。

では、これで準備は完了です。
この「いることにする」機能を使って実際にテストしてみましょう❗

テストする

では、実際にテストしてみましょう❗
JavaScriptHTTPS アクセスができる環境にセットしてブラウザでアクセスします。

Chromeで位置情報を取得するには HTTPS 接続が必須になります。

すると、「まだ目的地は先です。」と表示されています。

なお、watchPosition()は、ブラウザが起動したときだけでなく何か変化があったときにも実行されるようになっているようです。

そのため、一度タブからフォーカスを外し、再度フォーカスを戻すともう一度GPS取得を試みてくれます。

では、先ほどの機能を使って、まずは「浅草駅にいることに」してみましょう。
Location項目のセレクトボックスを「浅草駅」へ変更します。

すると・・・・・・

はい❗

GPSのチェック数が増えました。(つまり移動したことになったからですね👍)ただし、表示は「まだ目的地は先です。」と前と同じです。

では、続いて「本所吾妻橋駅」へ擬似的に移動しましょう。

すると・・・・・・

はい。
また回数が変わるものの、テキストは同じままです。

では最後に、100メートル以内の場所として登録した「とうきょうスカイツリー駅」を選択してみましょう。

どうなったでしょうか・・・・・・??

はい❗
想定通り100メートル以内に入ったことを通知してくれました。

成功です✨😄👍

※ ちなみに、スマホで実際にやってみたところ数秒ごとに更新されてました。そのため、スマホならリアルタイムでGPS取得できると思います。

企業様へのご提案

今回の機能を使えば、ウォーキング・ナビだけでなく、例えばAさん・Bさんがお互いに近くにいることを検知することもできますし、業務システムに組み込んでおけば、自然と従業員の今いる場所を通知することができます。

また、Pusherのようなプッシュ機能を使えば、「誰がどこに到着したか」をリアルタイムで(ブラウザをリロードせず)に更新 or 通知することもできます。

つまり、位置情報の管理は、スマホのネイティブアプリでなくとも同様のことができるようになっています。

こういった機能をご希望の場合は、ぜひお問い合わせからご連絡ください。
お待ちしております。m(_ _)m

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回はブラウザだけで「ウォーキング・ナビ」ををつくってみました。

意外とシンプルなコードで実装ができるのでいろいろな応用もしやすいんじゃないかと思います。

※ ちなみに、実際にスマホで試したところ50m以内に入ったことを検知することができました。

ぜひ皆さんもいろいろと試してみてくださいね。

ではでは〜❗

「きれいなマンホールの
写真をコレクションしてます。
キン肉マンとかもあるらしい❗」

このエントリーをはてなブックマークに追加       follow us in feedly