diff --git a/map4.js b/map4.js index a505e08..3dca566 100644 --- a/map4.js +++ b/map4.js @@ -4,6 +4,7 @@ const SUPABASE_URL = "https://ogtlmtnjkpsxsqzqlacj.supabase.co"; const SUPABASE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im9ndGxtdG5qa3BzeHNxenFsYWNqIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjMyOTU3NjUsImV4cCI6MjA3ODg3MTc2NX0.JnCE7oUQwrSgGqiu-QRbwnaLBZrO8JX1_RUb37VIMFI"; const supa = window.supabase.createClient(SUPABASE_URL, SUPABASE_KEY); + // =================================== let deviceId = localStorage.getItem("deviceId"); if (!deviceId) { @@ -23,10 +24,16 @@ // 駅マーカー用 let stationMarkers = []; +// 目的地 +let targetMarker = null; +let targetLat = null; +let targetLng = null; +let arrived = false; + const DEFAULT_LAT = 35.681236; const DEFAULT_LNG = 139.767125; -// 駅ピン用アイコン(青) +// 駅アイコン const stationIcon = L.icon({ iconUrl: "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-blue.png", @@ -38,6 +45,16 @@ shadowAnchor: [13, 41], }); +// 赤ピン(目的地) +const targetIcon = L.icon({ + iconUrl: + "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-red.png", + shadowUrl: + "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png", + iconSize: [25, 41], + iconAnchor: [12, 41], +}); + // =================================== function loadParams() { const p = new URLSearchParams(location.search); @@ -48,7 +65,7 @@ } // =================================== -// 小型ピン(自分=緑 / 他人=黄) +// 小型ピン // =================================== function createLabeledMarker(lat, lng, name, isSelf) { const pinUrl = isSelf @@ -90,6 +107,22 @@ L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, }).addTo(map); + + // ★ 目的地設定:マップをクリックすると赤ピンを置く + map.on("click", (e) => { + targetLat = e.latlng.lat; + targetLng = e.latlng.lng; + arrived = false; + + if (targetMarker) map.removeLayer(targetMarker); + + targetMarker = L.marker([targetLat, targetLng], { + icon: targetIcon, + }).addTo(map); + + const box = document.getElementById("targetInfo"); + box.textContent = `目的地を設定しました!距離を計算中…`; + }); } // =================================== @@ -132,11 +165,10 @@ const li = document.createElement("li"); const diff = (Date.now() - new Date(row.updated_at)) / 1000; - const isOnline = diff < 6; // 6秒以内ならオンライン + const isOnline = diff < 6; li.classList.add(isOnline ? "online" : "offline"); - // 自分=緑 / 他人=黄 const circleColor = row.device_id === deviceId ? "#58c16b" : "#FFD700"; const icon = document.createElement("div"); @@ -204,9 +236,6 @@ } // =================================== -// ここから駅関連 -// =================================== - // 距離計算(メートル) function distanceMeters(lat1, lng1, lat2, lng2) { const R = 6371000; @@ -214,78 +243,33 @@ const dLat = toRad(lat2 - lat1); const dLng = toRad(lng2 - lng1); const a = - Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.sin(dLat / 2)**2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * - Math.sin(dLng / 2) * - Math.sin(dLng / 2); + Math.sin(dLng / 2)**2; const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return R * c; } -// Overpass API で半径5kmの駅を取得 -async function fetchStationsAround(lat, lng) { - const query = ` - [out:json]; - node["railway"="station"](around:5000,${lat},${lng}); - out; - `; - const url = - "https://overpass-api.de/api/interpreter?data=" + - encodeURIComponent(query); +// =================================== +// 到着判定 +// =================================== +function checkArrival() { + if (!targetLat || !targetLng) return; - const res = await fetch(url); - if (!res.ok) throw new Error("Overpass API error"); - const json = await res.json(); - return json.elements || []; -} + const d = distanceMeters(lastLat, lastLng, targetLat, targetLng); + const box = document.getElementById("targetInfo"); -// 駅マーカー追加+最寄り駅表示 -async function loadStations() { - const box = document.getElementById("nearestBox"); - try { - const stations = await fetchStationsAround(lastLat, lastLng); + box.textContent = `目的地まで 約 ${(d/1000).toFixed(2)} km`; - // 既存駅マーカー削除 - stationMarkers.forEach((m) => map.removeLayer(m)); - stationMarkers = []; + if (d < 120 && !arrived) { + arrived = true; - if (stations.length === 0) { - box.textContent = "5km以内に駅が見つかりませんでした。"; - return; - } + box.textContent = "🎉 目的地に到着しました! 🎉"; + box.style.background = "#ffe4e1"; + box.style.borderLeft = "5px solid #ff4d6d"; - let nearest = null; - let nearestDist = Infinity; - - stations.forEach((st) => { - const sLat = st.lat; - const sLng = st.lon; - const name = - (st.tags && (st.tags.name || st.tags["name:ja"])) || "駅名不明"; - - // 距離 - const d = distanceMeters(lastLat, lastLng, sLat, sLng); - if (d < nearestDist) { - nearestDist = d; - nearest = { name, lat: sLat, lng: sLng }; - } - - // 駅マーカー追加 - const mk = L.marker([sLat, sLng], { icon: stationIcon }).addTo(map); - mk.bindPopup(`${name}駅`); - stationMarkers.push(mk); - }); - - if (nearest) { - const distKm = (nearestDist / 1000).toFixed(2); - box.textContent = `ここから一番近いのは「${nearest.name}駅」です!(約 ${distKm}km)`; - } else { - box.textContent = "5km以内に駅が見つかりませんでした。"; - } - } catch (e) { - console.error(e); - box.textContent = "駅情報を取得できませんでした。"; + if (navigator.vibrate) navigator.vibrate([200, 100, 200]); } } @@ -293,8 +277,7 @@ async function main() { loadParams(); - let lat = DEFAULT_LAT, - lng = DEFAULT_LNG; + let lat = DEFAULT_LAT, lng = DEFAULT_LNG; try { const pos = await getPosition(); @@ -308,13 +291,12 @@ initMap(lat, lng); renderSelfMarker(); await saveMyLocation(lat, lng); - await showOtherUsers(); - // ★ 駅読み込み(最初の位置で1回だけ) + // 駅読み込み(初回のみ) loadStations(); - // 自分の位置更新(2秒ごと) + // ★ 2秒ごとに位置更新 → 到着判定 setInterval(async () => { try { const pos = await getPosition(); @@ -322,12 +304,12 @@ lastLng = pos.longitude; renderSelfMarker(); await saveMyLocation(lastLat, lastLng); - // 位置が大きく変わったときだけ駅再取得してもよいが、 - // API負荷を考えて今回は初回のみ。 + + checkArrival(); // ←追加 } catch {} }, 2000); - // 他ユーザー位置更新(1秒ごと) + // 他ユーザーは毎秒更新 setInterval(showOtherUsers, 1000); document.getElementById("exitBtn").onclick = () =>