Newer
Older
JAXAtools / GPS / gps.js
@HIROSE Yuuji HIROSE Yuuji on 29 Oct 2022 9 KB Add scale control
document.addEventListener("DOMContentLoaded", ()=>{
    var distanceThresh = 10;
    var gpsTryMax = 10, nTrial = gpsTryMax;// 最大試行回数を決めておく
    var gpsTimer;
    var mymap, infobox, gpsMarker, btnGet, btnSTOP;
    var direction;
    var gsiLayer = L.tileLayer(
	'//cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {
	    attribution:
	    '<a href="http://maps.gsi.go.jp/development/ichiran.html">国土地理院</a>'
	});
    var osmLayer = L.tileLayer(
	'//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
	    attribution:
	    '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'});
    let area = null;
    var baseLayers = {
	OpenStreetMap: osmLayer,
	地理院地図: gsiLayer,
    };
    var goals = {};
    function workerInit() {
	if ('serviceWorker' in navigator) {
	    navigator.serviceWorker.register('./serviceworker.js')
		.then(function() {
		    console.log('service worker registered');
		});
	}
    }
    var stoPrefix = "gps_";
    var LS = {
	"getItem": (i)=>{return localStorage.getItem(stoPrefix+i);},
	"removeItem": (i)=>{return localStorage.removeItem(stoPrefix+i);},
	"setItem": (k,v)=>{return localStorage.setItem(stoPrefix+k, v);},
	"reset": ()=>{
	    for (let p of Object.keys(localStorage)) {
		console.log(`LS=${p}`);
		if (p.startsWith(stoPrefix))
		    localStorage.removeItem(p);
	    }
	}
    }
    function loadGoals(areaname, json) {
	// uMapでレイヤをエリアごとわけてゴールとなるマーカーを設置し
	// 名称にはピンの名前、概要(description)に答となる文字列を入れる。
	// data/そのエリア.geojson に保存して上記 area 変数に入れる
	if (!json) return;
	fetch(json).then((resp)=>{return resp.json();}).then((r)=>{
	    console.log(JSON.stringify(r));
	    let goal={}, name;
	    if (r.features) {
		for (let m of r.features) {
		    if (m.properties && (name=m.properties.name)
			&& m.geometry && m.geometry.type=="Point") {
			goal[name] = {
			    description: m.properties.description,
			    coordinates: m.geometry.coordinates
			};
		    }
		}
		goals[areaname] = goal;
		if (Object.keys(area)[0] == areaname)
		    switchArea(areaname);
	    }
	});
    }
    function setAreaBounds(areaname) {
	let areaval = area[areaname];
	if (!areaval) return;
	let map  = areaval.map, bounds = areaval.bounds;
	console.log(`bounds=${bounds}`);
	if (bounds)
	    mymap.setMaxBounds(bounds);
	else if (map.getBounds)
	    mymap.setMaxBounds(map.getBounds());
	else
	    mymap.setMaxBounds(null);
    }
    function switchArea(areaname, label) {
	if (label) {
	    if (confirm(`${label}マップに切り替えます`)) {
		for (let b of document.querySelectorAll(".areaselect"))
		    b.classList.add("hidden");
	    } else
		return;
	}
	console.log(areaname);
	let newlayer = area[areaname];
	// https://groups.google.com/g/leaflet-js/c/1gQyZhOADCg
	for (let base in baseLayers) {
	    let layer = baseLayers[base];
	    if (mymap.hasLayer(layer) && layer != newlayer)
		mymap.removeLayer(layer);
	}
	newlayer.map.addTo(mymap);
	setAreaBounds(areaname);
	gpsMarker.setLatLng(mymap.getCenter());
	if (goals[areaname]) {
	    let sel = document.getElementById("goals");
	    sel.name = areaname;
	    let opts = "<option>---探すゴールを選ぶ---</option>";
	    let glist = goals[areaname];
	    for (let point of Object.keys(glist)) {
		let val = glist[point], coord = val.coordinates;
		opts += `<option value=${coord}>${point}</option>\n`;
	    }
	    sel.innerHTML = opts;
	}
    }
    function loadArea() {
	var btnList = document.getElementById("buttons");
	fetch("area.json").then((resp)=>{
	    if (resp.ok) return resp.json()
	}).then((json)=>{
	    area = json;
	    console.log(`area = ${area}`);
	    for (var a of Object.keys(area)) {
		if (a.startsWith(".")) continue;
		let btn = document.createElement("button");
		btn.setAttribute("class", "areaselect");
		btn.innerText = area[a].name;
		console.log(area[a].name);
		let x=a;
		btn.addEventListener('click', (e)=>{
		    switchArea(x, area[x].name);});
		btnList.appendChild(btn);
		// console.log(`Loading ${area[a].map}`);
		switch (area[a].map) {
		case "gsi":
		    area[a].map = gsiLayer; break;
		case "osm":
		    area[a].map = osmLayer; break;
		default:
		   // console.log(`Loading ${area[a].map}`);
		    area[a].map = L.imageOverlay(
			area[a].map, area[a].bounds)
		}
		if (area[a].map) baseLayers[a] = area[a].map;
		loadGoals(a, area[a].geojson);
	    }
	});
    }
    function mapInit() {
	infobox = document.getElementById("info");
	mymap = L.map("mymap", baseLayers).setView([38.891, 139.824], 18);
	mymap.on('click', (e)=>{onSuccess2(e)});
	L.control.scale().addTo(mymap); // 縮尺表示追加
	let arrowIcon = L.divIcon({
	    // iconUrl:	'img/arrow.png',
	    iconSize:	[53, 53],
	    iconAncor:	[26, 26],
	    popupAnchor: [0, -23],
	    className: "gps-arrow-icon",
	    html: '<div id="arrowicon"><img src="img/arrow.png"></div>'
	}); //arrowIcon.html = "FOO";
	gpsMarker = L.marker(mymap.getCenter(),{
	    icon: arrowIcon
	}).addTo(mymap).bindPopup("");
	gpsMarker.setPopupContent("foo");
	btnGet  = document.getElementById("get");
	btnSTOP = document.getElementById("stop");
	direction = document.getElementById("arrowicon");
	if (btnGet) btnGet.addEventListener("click", getGPS, false);
	if (btnSTOP)btnSTOP.addEventListener("click", stopGPS, false);
	// Create Area Selection Button
	loadArea();
	mymap.on('baselayerchange', (e)=>{
	    setAreaBounds(e.name);
	});
	/*	方角も取れるようだ
	if (DeviceOrientationEvent) {
	    window.addEventListener("deviceorientation", (e) => {
		document.getElementById("orientation").innerText =
		    `方角=${e.alpha}`;
	    });
	    }
	*/
	L.control.layers(baseLayers).addTo(mymap);
    }
    function storageInit() {
	if (location.href.endsWith("?init")
	    && confirm("ゲームデータをリセットしますか")) {
	    LS.reset();
	    alert("リセットしました");
	    location.href = location.href.replace("?init", "");
	}
    }
    function dispInfo(msg) {
	info.innerText = msg;
    }
    function clearGetGPS() {	// 位置取得ボタンを元に戻す
	btnGet.classList.remove('running');
	btnGet.disabled = false;
    }
    function getGPS(ev) {
	navigator.geolocation.getCurrentPosition(
	    onSuccess, onError, {
		maximumAge: 0, timeout: 8000, enableHighAccuracy: true
	    });
	btnGet.classList.add('running');
	btnGet.disabled = true;
	dispInfo("取得中...");
    }
    function stopGPS(ev) {
	dispInfo("GPS捕捉停止中...");
	nTrial = 1;
	clearGetGPS();
    }
    function onSuccessL(latlng) {
	nTrial = gpsTryMax;
	clearGetGPS();
	mymap.panTo(latlng);		// 地図の中心を取得した位置に
	let lat = latlng.lat, lng = latlng.lng;
	let str = `ここは北緯${lat.toFixed(5)}, 東経${lng.toFixed(5)}. `;
	gpsMarker.openPopup().setLatLng(latlng);// マーカのポイント変更
	let sel = document.getElementById("goals");
	console.log("val="+sel.value);
	if (sel && sel.value && sel.value.indexOf(",") > -1) {
	    let useGPS = LS.getItem("useGPS"),
		helpCount = useGPS ? 1+parseInt(useGPS) : 1;
	    LS.setItem("useGPS", helpCount);
	    str += ` (位置取得${helpCount}回)`;
	    let areaname = sel.name;
	    let idx = sel.selectedIndex, pname = sel.options[idx].text;
	    let goallng = sel.value.split(",")[0],
		goallat = sel.value.split(",")[1];
	    let distance = latlng.distanceTo([goallat, goallng]);
	    let popup = `${pname}まであと${distance.toFixed(1)}m.<br>`;
	    popup += (goallat < lat ? "南へ" : "北へ");
	    let northSouthDiff = latlng.distanceTo([goallat, lng]);
	    popup += `${northSouthDiff.toFixed(1)}m`;
	    popup += (goallng < lng ? "西へ" : "東へ");
	    let eastWestDiff = latlng.distanceTo([lat, goallng]);
	    popup += `${eastWestDiff.toFixed(1)}m`;
	    if (distance < distanceThresh) {
		direction.classList.add("hidden");
		let plist = goals[areaname],
		    desc = plist[pname].description, msg = "見つかった!";
		if (desc && desc > "") {
		    msg += "<br>この場所のメッセージは<br>" +
			"「"+ desc + "」<br>です。<br><br>" +
			"★見つけたアイテムは必ず回収して下さい★"
		    //console.log(`pname=${pname}, gp=${goals[pname]}`)
		}
		gpsMarker.setPopupContent(msg);
	    } else {
		direction.classList.remove("hidden");
		direction = document.querySelector('.gps-arrow-icon');
		console.log(`dirrrr = ${direction.src}`)
		let rad,
		    dy = northSouthDiff*Math.sign(goallat-lat),
		    dx = eastWestDiff*Math.sign(goallng-lng);
		if (dx>0) {	// Math.atanは分母が0でもよい
		    rad = Math.atan(dy/dx);	//第1第4象限はそのまま
		} else {			//第2,3象限は +π
		    rad = Math.PI + Math.atan(dy/dx);
		}
		console.log(`dx=${dx}, dy=${dy}, atan = ${rad}`);
		// rad = Math.PI/2-rad;	//12時からの時計回りに変換
		rad = -rad;	//3時からの時計回りに変換
		//https://www.webdesignleaves.com/pr/css/css_basic_12.html
		direction.style.setProperty('--rotation', `${rad}rad`);
		gpsMarker.setPopupContent(popup);
	    }
	} else {		// No goal selected
	    direction.classList.add("hidden");
	    gpsMarker.setPopupContent("上から探すゴールを選んで下さい");
	}
	dispInfo(str);
	console.log(latlng);
    }
    var lastloc = null;
    function onSuccess2(ev) {
	if (lastloc && ev.latlng.distanceTo(lastloc) < 10) {
	    onSuccessL(ev.latlng);	// 同一箇所を2連続タップしたら
	}
	lastloc = ev.latlng;
    }
    function onSuccess(pos) { // 成功時のコールバック。関数名は何でもよい。
	// pos.coords に位置情報が入る。LeafletのLatLngに変換する。
	onSuccessL(L.latLng([pos.coords.latitude, pos.coords.longitude]));
    }
    function onError(err) {// 失敗時のコールバック
	restN = "あと"+(--nTrial)+"回試行します。";
	gpsMarker.setPopupContent("取得失敗:"+restN).openPopup();
	dispInfo(restN);
	if (nTrial > 0) {// 残り回数があれば
	    getGPS();// 再度取得を試行する
	} else {
	    dispInfo("GPS捕捉を中止しました.")
	    clearGetGPS();
	    nTrial = gpsTryMax;
	}
    }
    workerInit();
    mapInit();
    storageInit();
});