GPS情報の取得

ここからの説明は、実際にGPSセンサ付のデバイスでのみ動かせる。 まず、HTTPS配付可能なディレクトリにて、HTML 文書ファイルと JavaScript プログラムを作成し、それを GPS センサつきモバイルデバイスで確認するという手順が必要である。

https://www.w3.org/TR/geolocation-API/

GPSはアメリカによる測位衛星システムの固有名詞であり、 近年では衛星による位置を取得する一般名詞としてGNSS (Global Navigation Satellite System) を使うのが推奨されているが 本講ではGPSという語を用いる。

Geolocation API

HTML5 Geolocation APIでは次の3つのインターフェースが用意されている。

getCurrentPosition(successCallback[, errorCallback[, option]])

センサからの位置情報を1度だけ取得試行し、成功したら successCallback を、失敗したら errorCallback を呼ぶ。 成功時の関数には Potision オブジェクトが、 失敗時の関数には PotisionError オブジェクトが渡される。

watchPosition(successCallback[, errorCallback[, option]])

位置情報取得を繰り返す。引数は getCurrentPosition() と同様。

返却値としてIDが返るので、この値を保存しておき、GPS 追跡を止めたいときに次の clearWatch() に渡す。

clearWatch(watchId)

指定したIdの追跡を止める。止めない限り追跡は続き、 成功・失敗によって指定した関数が呼ばれるため、 止める処理を盛り込む必要がある。

情報取得を試行する2つの関数は、いずれも第1、第2引数にそれぞれ 取得成功時に呼ぶ関数、失敗時に呼ぶ関数を指定する。このように 将来何かのタイミングで呼び返してほしい関数のことをコールバック関数 という。

第3引数に指定するのは挙動を制御するオプションで、 以下のプロパティが設定できる。

enableHighAccuracy 高精度の測定をするかどうか。false はしない。false 以外であればする。デフォルトは false。
timeout 位置情報が取得できるまでの最大待ち時間(ミリ秒)。
maximumAge 以前に取得してキャッシュしてある位置情報の有効期限(ミリ秒)。 0(デフォルト)に指定するとキャッシュを利用せずかならず測定を試みる。

位置情報取得手順

たとえば、1回のみの情報取得を試みるコードは以下のようになる。

// ここまでに変数 mymap にLeaflet地図オブジェクトが入っているとする。
var gpsmarker = L.marker(mymap.getCenter()).addTo(mymap);
// マーカを作り、取得に関する情報をポップアップ表示する用途に用いる。
gpsmarker.bindPopup("取得中...").openPopup();

function tryGetGPS() {	// 取得を開始する処理を行なう関数の定義
    navigator.geolocation.getCurrentPosition(
	onSuccess, onError,{	// 下で定義する2つの関数をコールバック指定
	    maximumAge: 0, timeout: 2000, enableHighAccuracy: true
	});
}
function onSuccess(pos) { // 成功時のコールバック。関数名は何でもよい。
    // pos.coords に位置情報が入る。LeafletのLatLngに変換する。
    var latlng = L.latLng([pos.coords.latitude, pos.coords.longitude]);
    mymap.panTo(latlng);		// 地図の中心を取得した位置に
    gpsmarker.setPopupContent(		// ポップアップ表示を
	"ここは "+latlng+"です."		// 更新後の緯度経度に変え、
    ).openPopup().setLatLng(latlng);	// ポップアップし、ポイントも変更する
}
var nTrial = 10;			// 最大試行回数を決めておく
function onError(err) {			// 失敗時のコールバック
    restN = "あと"+(--nTrial)+"回試行します。";
    gpsmarker.setPopupContent("取得失敗:"+restN).openPopup();
    if (nTrial > 0) {			// 残り回数があれば
	tryGetGPS();			// 再度取得を試行する
    }
}
tryGetGPS();

成功時のコールバックには Position オブジェクトが渡る。 Position は2つのプロパティを持つ。

coords位置に関する情報
timestamp取得時刻

coords プロパティはさらに以下のプロパティを持つ。

latitude緯度(10進)
longitude経度(10進)
altitudeWGS84高度(メートル)(取れなければnull)
accuracy緯度経度の測定誤差(精度)
altitudeAccuracy高度の測定誤差(精度)
heading移動方向(北から反時計回り360度まで)
speed水平移動速度(m/s)

モバイルデバイスを用いての実験はこのURLから:
https://www.yatex.org/lect/gps/gps-get.html https://www.yatex.org/lect/gps/gps-get.html

https://www.yatex.org/lect/gps/gps-watch.html https://www.yatex.org/lect/gps/gps-watch.html

機種によって異なるが、次のようなことが気になる場合がある。

また、watchPosition() の場合は捕捉に成功するまで続くが逆に

という問題があり、デバイスによっては電力消費が激しくなることもある。

作成練習

gps-chase.html(実例)

gps-chase.html

<!DOCTYPE html>
<html lang="ja">
<head>
<title>GPS宝探し</title>
<link rel="stylesheet" type="text/css" href="../src/leaflet.css">
<script type="text/javascript" src="../src/leaflet.js"></script>
<script type="text/javascript" src="gps-chase.js"></script>
<style type="text/css">
<!--
div#gpsmap {width: 90vw; height: 75vw; margin: auto;}
-->
</style>
</head>

<body>
<h1>たからさがし<span id="title"></span></h1>
<p>
<button id="start" type="button">START</button>
<button id="stop" type="button">STOP</button>
<a href="../src/gps-chase.js">src</a>
</p>
<div id="gpsmap"></div>


</body>
</html>

gps-chase.js

document.addEventListener("DOMContentLoaded", () => {
    var mymap = L.map("gpsmap").setView([38.891, 139.824], 16);
    L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', {
	attribution:
	'&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> \
		contributors'
    }).addTo(mymap);

    var gpsmarker = L.marker(mymap.getCenter()).addTo(mymap);
    gpsmarker.bindPopup("STARTおしてね").openPopup();

    var nTrial = 100
    var watchId = null;		// 最初はnullにしておく
    function stopGPS() {	// watchが動いていたら止めてnullにする
	console.log("watchId="+watchId);
	if (watchId != null) {	// nullかどうかで比較しないとだめ(初期値0)
	    navigator.geolocation.clearWatch(watchId);
	    document.getElementById("title").textContent = "stop";
	    gpsmarker.setPopupContent("停めました");
	}
	watchId = null;
    }
    function tryWatchGPS() {
	stopGPS();		// 二重で動かないように注意する
	watchId = navigator.geolocation.watchPosition(
	    onSuccess, onError,{
		maximumAge: 0, timeout: 3000, enableHighAccuracy: true});
	document.getElementById("title").textContent = "START!!";
    }
    function onSuccess(pos) {	// GPS信号が取れたらここに来る
	var latlng = L.latLng([pos.coords.latitude, pos.coords.longitude]);
	mymap.panTo(latlng);// 地図の中心をそこにする
	console.log(latlng);
	// ★★★★★ ここから
	gpsmarker.setLatLng(latlng).setPopupContent(
	    "ここは lat="+latlng.lat+", lng="+latlng.lng+" です."
	).openPopup();
	// ★★★★★ ここまでの latlng.lat と latlng.lng を
	// if文などで判定して、特定の場所に近づいたら「GOAL!」と
	// 表示するように変えてみよ。
    }
    function onError(err) {
	restN = "あと"+(--nTrial)+"回試行します。";
	gpsmarker.setPopupContent("捕捉失敗:"+restN).openPopup();
	if (nTrial <= 0) {
	    navigator.geolocation.clearWatch(watchId);
	}
    }
    // STARTボタンに開始を仕込む
    document.getElementById("start").addEventListener("click", tryWatchGPS);
    // STOPボタンに停止を仕込む
    document.getElementById("stop").addEventListener("click", stopGPS);
}, false);

★★★★★★