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:
'© <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();
});