メモ_Webアプリで位置情報取得が5分で停止してしまう問題を解決

laskで位置情報を取得するWebアプリを開発中、5分で停止してしまう問題が発生しました。スマホのバックグラウンド稼働の設定の問題かと思い、設定を見直してみましたが、それが原因ではなく、以下の方法で解決しました。

 

解決策:

navigator.geolocation.watchPosition 関数の仕様により、5分を超えると自動的に位置情報の取得が停止することが原因

この解決のために、getCurrentPosition を定期的に再呼び出しすることで、位置情報の取得が5分を超えても継続できるように修正しました。以下は、詳しい対応内容です。

  1. watchPosition の代わりに getCurrentPosition を利用
    watchPosition は、連続的に位置情報を取得する方法ですが、ブラウザやOSの制限により5分以上の持続が難しい場合があります。
    そのため、getCurrentPosition を定期的に呼び出す方法に変更しました。getCurrentPosition は、位置情報を1回だけ取得する関数です。
  2. setInterval を使った再取得
    setInterval を使い、定期的に getCurrentPosition を再呼び出しすることで、位置情報を間隔を空けて取得し続ける処理を実装しました。
    例えば、10秒ごとに位置情報を取得するように設定し、5分を超えても自動的に再度位置情報を取得し続けるようにしました。
  3. 位置情報取得の間隔制御
    前回の位置情報取得時刻を記録し、前回の取得から10秒以上経過した場合にのみ次の位置情報を取得するように制御しました。このことで、過度な頻度で位置情報を取得しないようにしています。
  4. 正確な停止処理
    停止処理のために、stopButton を押したときに定期取得を停止するロジックも含まれています。このボタンを押すと clearInterval により setInterval を停止し、位置情報取得も中止されます。また、サーバへのデータ送信も行われます。

 

コード:

<!DOCTYPE HTML>
<html>
<head>
    <title>クライアント位置情報取得</title>

    {% include 'assets.html' %}

    <!-- CSRFトークンを追加 -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <!-- Leaflet.jsのCSSとJavaScript -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>

    <!-- AxiosのCDN -->
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

    <style>
        #map {
            height: 400px;
            width: 100%;
        }
        #locationData {
            margin-top: 20px;
        }
        #locationData ul {
            list-style-type: none;
            padding: 0;
        }
        #locationData li {
            margin-bottom: 5px;
        }
    </style>
</head>
<body>
    <div class="docs-container off-canvas off-canvas-sidebar-show">
        {% include 'sidebar.html' %}
        <div class="off-canvas-content">
            <div class="docs-content" id="content">
                <div class="container" id="grid">
                    <h3 class="s-title">GPS情報を連続取得してサーバに送信してMapに表示</h3>
                    <p>当ページにアクセスしているクライアントの位置情報は以下の通りです。</p>

                    <button id="startButton" class="btn btn-block input-group-btn btn-ma4">スタート</button>
                    <button id="pauseButton" disabled class="btn btn-block input-group-btn btn-ma4">中断</button>
                    <button id="resumeButton" disabled class="btn btn-block input-group-btn btn-ma4">再開</button>
                    <button id="stopButton" disabled class="btn btn-block input-group-btn btn-ma4">停止</button>

                    <div id="map"></div>

                    <div id="locationData">
                        <h2>Location Data (Timestamped)</h2>
                        <ul id="locationList"></ul>
                    </div>

                    <script>
                        let locationData = [];
                        let map;  // 地図オブジェクトをグローバルに定義
                        let locationInterval;  // setInterval用の変数
                        const interval = 10000; // 10秒間隔(ミリ秒)

                        // handleError 関数
                        function handleError(error) {
                            let errorMessage;

                            switch (error.code) {
                                case error.PERMISSION_DENIED:
                                    errorMessage = "ユーザーが位置情報の取得を拒否しました。";
                                    break;
                                case error.POSITION_UNAVAILABLE:
                                    errorMessage = "位置情報が利用できません。";
                                    break;
                                case error.TIMEOUT:
                                    errorMessage = "位置情報の取得がタイムアウトしました。";
                                    break;
                                case error.UNKNOWN_ERROR:
                                    errorMessage = "不明なエラーが発生しました。";
                                    break;
                            }

                            console.error('Geolocation error:', errorMessage);
                            alert(errorMessage);
                        }

                        // 地図の初期化
                        function initializeMap() {
                            const map = L.map('map').setView([35.6895, 139.6917], 13); // 東京の中心に設定
                            L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                                attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
                            }).addTo(map);
                            return map;
                        }

                        // ページ読み込み後に地図を初期化
                        window.addEventListener('DOMContentLoaded', (event) => {
                            map = initializeMap();
                        });

                        // 位置情報を表示し、地図上に更新
                        function displayPosition(position) {
                            const timestamp = new Date(position.timestamp).toLocaleTimeString();
                            const latitude = position.coords.latitude;
                            const longitude = position.coords.longitude;
                            const speed = position.coords.speed || 0;

                            let li = document.createElement('li');
                            li.textContent = `Time: ${timestamp}, Latitude: ${latitude}, Longitude: ${longitude}, Speed: ${speed} m/s`;
                            document.getElementById('locationList').appendChild(li);

                            locationData.push({ latitude, longitude, speed, timestamp });

                            updateMapPosition(latitude, longitude);
                        }

                        // 地図の位置を更新
                        function updateMapPosition(lat, lng) {
                            if (map) {
                                map.setView([lat, lng], 13);  // 中心点を更新
                                L.marker([lat, lng]).addTo(map);  // マーカーを追加
                            }
                        }

                        // Axiosを使ったPOSTリクエスト
                        function postLocationData() {
                            const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

                            axios.post('/save_location_data', locationData, {
                                headers: {
                                    'Content-Type': 'application/json',
                                    'X-CSRFToken': csrfToken
                                }
                            })
                            .then(function (response) {
                                console.log('Data saved successfully:', response.data);
                            })
                            .catch(function (error) {
                                console.error('Error saving data:', error);
                                alert('Failed to save location data.');
                            });
                        }

                        // ストップボタンの処理(停止時にデータをサーバに送信)
                        document.getElementById('stopButton').addEventListener('click', function () {
                            clearInterval(locationInterval);  // 位置情報取得の停止
                            postLocationData();  // データをサーバに送信

                            // リセット処理
                            locationData = [];
                            document.getElementById('startButton').disabled = false;
                            document.getElementById('pauseButton').disabled = true;
                            document.getElementById('resumeButton').disabled = true;
                            this.disabled = true;
                        });

                        // スタートボタンの処理
                        document.getElementById('startButton').addEventListener('click', function () {
                            if (navigator.geolocation) {
                                // 10秒ごとに位置情報を取得するためのsetIntervalを開始
                                locationInterval = setInterval(() => {
                                    navigator.geolocation.getCurrentPosition(
                                        (position) => {
                                            displayPosition(position);
                                        },
                                        handleError,
                                        {
                                            enableHighAccuracy: true,
                                            timeout: 20000,
                                            maximumAge: 0
                                        }
                                    );
                                }, interval);

                                // ボタンの状態を変更
                                document.getElementById('pauseButton').disabled = false;
                                document.getElementById('stopButton').disabled = false;
                                this.disabled = true;  // スタートボタンを無効化
                            } else {
                                alert('Geolocation is not supported by this browser.');
                            }
                        });
                    </script>
                    
                </div>
            </div>
        </div>
        {% include 'button_pagetop.html' %}
    </div>
</body>
</html>