diff --git a/gps.js b/gps.js new file mode 100644 index 0000000..e8c5163 --- /dev/null +++ b/gps.js @@ -0,0 +1,323 @@ +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 += `\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(); + });