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: '国土地理院' }); var osmLayer = L.tileLayer( '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap 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 = "---探すゴールを選ぶ---"; let glist = goals[areaname]; for (let point of Object.keys(glist)) { let val = glist[point], coord = val.coordinates; opts += `${point}\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: '
' }); //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.
`; 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 += "
この場所に隠されているものは
" + "「"+ desc + "」
です。
" + "★見つけたアイテムは必ず回収して下さい★" //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(); });