Newer
Older
kensho / gps.js
@太田さくら 太田さくら on 12 May 2024 12 KB 追加
document.addEventListener("DOMContentLoaded", ()=>{
        var distanceThresh = 10;
        var gpsWatchID = null, gpsCount = {};
        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
    			};
    		    }
    		}
    		// {description: "desc", coordinates: [lon, lat]}
    		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);
        }
        var vMarkers = [];
        function switchArea(areaname, label) {
    	let visibleMode = location.href.match(/\?markers$/);
    	if (visibleMode) {
    	    while (vMarkers.length > 0) {
    		vMarkers.pop().remove();
    	    }
    	}
    	if (label && !visibleMode) {
    	    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`;
    		if (visibleMode) {
    		    let lon = coord[0], lat = coord[1]
    		    let mrk = L.marker([lat, lon]).bindPopup(point).addTo(mymap);
    		    vMarkers.push(mrk);
    		}
    	    }
    	    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 clearWatchGPS() {	// 位置取得ボタンを元に戻す
    	navigator.geolocation.clearWatch(gpsWatchID);
    	gpsWatchID = null;
    	btnGet.classList.remove('running');
    	btnGet.disabled = false;
        }
        function getGPS(ev) {
    	if (gpsWatchID) {
    	    dispInfo("既に探索中です.");
    	    return;
    	}
    	gpsWatchID = navigator.geolocation.watchPosition(
    	    onSuccess, onError, {
    		maximumAge: 0, timeout: 8000, enableHighAccuracy: true
    	    });
    	setTimeout(clearWatchGPS, 10000);
    	btnGet.classList.add('running');
    	btnGet.disabled = true;
    	dispInfo("取得中...");
        }
        function stopGPS(ev) {
    	clearWatchGPS();
    	dispInfo("GPS捕捉を停止しました.");
        }
        function onSuccessL(latlng) {
    	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) {
    	    // GPSに頼った数をカウントする
    	    let useGPS = LS.getItem("useGPS"),
    		helpCount = useGPS ? parseInt(useGPS) : 0;
    	    let countUp;
    	    if (gpsCount[gpsWatchID])
    		countUp = 0;	// 一度カウントしたwatchIDではノーカウント
    	    else if (gpsWatchID == null)
    		countUp = 1;	// おそらくタップ×2の位置取得
    	    else {
    		countUp = 1;
    		gpsCount[gpsWatchID] = 1;
    	    }
    	    if (useGPS) {
    		helpCount += countUp;
    	    } else {
    		helpCount = 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) {// 失敗時のコールバック
    	gpsMarker.setPopupContent("取得失敗:"+restN).openPopup();
    	dispInfo("空がよく見える位置で試して下さい");
    	dispInfo("GPS捕捉を中止しました.")
    	clearWatchGPS();
        }
        workerInit();
        mapInit();
        storageInit();
    });