diff --git a/map4.js b/map4.js index 3dca566..da600c3 100644 --- a/map4.js +++ b/map4.js @@ -21,10 +21,10 @@ let lastLat = null; let lastLng = null; -// 駅マーカー用 +// 駅マーカー let stationMarkers = []; -// 目的地 +// 目的地関連 let targetMarker = null; let targetLat = null; let targetLng = null; @@ -35,22 +35,16 @@ // 駅アイコン const stationIcon = L.icon({ - iconUrl: - "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-blue.png", - shadowUrl: - "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png", + iconUrl: "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-blue.png", + shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png", iconSize: [25, 41], iconAnchor: [12, 41], - shadowSize: [41, 41], - 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", + 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], }); @@ -65,14 +59,14 @@ } // =================================== -// 小型ピン +// 小型ピン(自分:緑、他人:黄) // =================================== function createLabeledMarker(lat, lng, name, isSelf) { const pinUrl = isSelf ? "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-green.png" : "https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-gold.png"; - const iconHtml = ` + const html = `
${name}
@@ -82,18 +76,20 @@ return L.marker([lat, lng], { icon: L.divIcon({ className: "custom-pin", - html: iconHtml, + html: html, iconSize: [32, 32], iconAnchor: [16, 32], }), - }); + });実際どのようにサイトを作るのか、具体的な手順を知りたい。 + + } // =================================== function getPosition() { return new Promise((res, rej) => { navigator.geolocation.getCurrentPosition( - (pos) => res(pos.coords), + p => res(p.coords), rej, { enableHighAccuracy: true } ); @@ -108,7 +104,7 @@ maxZoom: 19, }).addTo(map); - // ★ 目的地設定:マップをクリックすると赤ピンを置く + // ★目的地設定(マップをタップ) map.on("click", (e) => { targetLat = e.latlng.lat; targetLng = e.latlng.lng; @@ -121,7 +117,7 @@ }).addTo(map); const box = document.getElementById("targetInfo"); - box.textContent = `目的地を設定しました!距離を計算中…`; + box.textContent = "目的地を設定しました!距離を計算中…"; }); } @@ -148,10 +144,6 @@ selfMarker = createLabeledMarker(lastLat, lastLng, currentUser, true); selfMarker.addTo(map); - - selfMarker.on("click", (e) => { - map.panTo(e.latlng, { animate: true, duration: 0.4 }); - }); } // =================================== @@ -169,11 +161,10 @@ li.classList.add(isOnline ? "online" : "offline"); - const circleColor = row.device_id === deviceId ? "#58c16b" : "#FFD700"; - + const color = row.device_id === deviceId ? "#58c16b" : "#FFD700"; const icon = document.createElement("div"); icon.className = "member-icon"; - icon.style.background = circleColor; + icon.style.background = color; const name = document.createElement("span"); name.textContent = row.user_name; @@ -184,17 +175,6 @@ li.append(icon, name, status); - li.onclick = () => { - const marker = - row.device_id === deviceId - ? selfMarker - : otherMarkers.find((m) => m._deviceId === row.device_id); - - if (marker) { - map.panTo(marker.getLatLng(), { animate: true, duration: 0.5 }); - } - }; - list.appendChild(li); }); } @@ -209,50 +189,45 @@ latestByDevice = {}; - if (Array.isArray(data)) { - for (const r of data) { - if (!latestByDevice[r.device_id]) latestByDevice[r.device_id] = r; - } - } + data?.forEach((r) => { + if (!latestByDevice[r.device_id]) latestByDevice[r.device_id] = r; + }); showMemberList(latestByDevice); - otherMarkers.forEach((m) => map.removeLayer(m)); + otherMarkers.forEach(m => map.removeLayer(m)); otherMarkers = []; Object.values(latestByDevice).forEach((row) => { if (row.device_id === deviceId) return; const mk = createLabeledMarker(row.lat, row.lng, row.user_name, false); - mk._deviceId = row.device_id; - - mk.on("click", (e) => { - map.panTo(e.latlng, { animate: true, duration: 0.4 }); - }); - mk.addTo(map); otherMarkers.push(mk); }); } // =================================== -// 距離計算(メートル) +// 距離(メートル) +// =================================== function distanceMeters(lat1, lng1, lat2, lng2) { const R = 6371000; - const toRad = (d) => (d * Math.PI) / 180; + const toRad = (d) => d * Math.PI / 180; + const dLat = toRad(lat2 - lat1); const dLng = toRad(lng2 - lng1); + const a = Math.sin(dLat / 2)**2 + Math.cos(toRad(lat1)) * - Math.cos(toRad(lat2)) * - Math.sin(dLng / 2)**2; - const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - return R * c; + Math.cos(toRad(lat2)) * + Math.sin(dLng / 2)**2; + + return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); } // =================================== -// 到着判定 +// ★到着判定 // =================================== function checkArrival() { if (!targetLat || !targetLng) return; @@ -260,11 +235,10 @@ const d = distanceMeters(lastLat, lastLng, targetLat, targetLng); const box = document.getElementById("targetInfo"); - box.textContent = `目的地まで 約 ${(d/1000).toFixed(2)} km`; + box.textContent = `目的地まで 約 ${(d / 1000).toFixed(2)} km`; if (d < 120 && !arrived) { arrived = true; - box.textContent = "🎉 目的地に到着しました! 🎉"; box.style.background = "#ffe4e1"; box.style.borderLeft = "5px solid #ff4d6d"; @@ -274,6 +248,66 @@ } // =================================== +// ★駅検索(Overpass API) +// =================================== +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); + + const res = await fetch(url); + if (!res.ok) throw new Error("Overpass API error"); + const json = await res.json(); + return json.elements || []; +} + +// 最寄り駅表示 +async function loadStations() { + const box = document.getElementById("nearestBox"); + + try { + const stations = await fetchStationsAround(lastLat, lastLng); + + stationMarkers.forEach(m => map.removeLayer(m)); + stationMarkers = []; + + if (stations.length === 0) { + box.textContent = "駅が見つかりませんでした。"; + return; + } + + let nearest = null; + let nearestDist = Infinity; + + stations.forEach(st => { + const name = st.tags?.name || "駅名不明"; + const sLat = st.lat; + const sLng = st.lon; + + 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); + }); + + const distKm = (nearestDist / 1000).toFixed(2); + box.textContent = `最寄り駅:${nearest.name}(約 ${distKm} km)`; + + } catch (e) { + console.error(e); + box.textContent = "駅情報を取得できませんでした。"; + } +} + +// =================================== async function main() { loadParams(); @@ -293,10 +327,10 @@ await saveMyLocation(lat, lng); await showOtherUsers(); - // 駅読み込み(初回のみ) + // 駅読み込み(最初だけ) loadStations(); - // ★ 2秒ごとに位置更新 → 到着判定 + // ★2秒ごとに位置更新&到着判定 setInterval(async () => { try { const pos = await getPosition(); @@ -309,13 +343,12 @@ } catch {} }, 2000); - // 他ユーザーは毎秒更新 setInterval(showOtherUsers, 1000); document.getElementById("exitBtn").onclick = () => (location.href = "index.html"); - setTimeout(() => map.invalidateSize(), 400); + setTimeout(() => map.invalidateSize(), 300); } window.addEventListener("DOMContentLoaded", main);