diff --git a/shonai_tiiki_map_tansaku.js b/shonai_tiiki_map_tansaku.js index 453e8ba..a6369ed 100644 --- a/shonai_tiiki_map_tansaku.js +++ b/shonai_tiiki_map_tansaku.js @@ -1,52 +1,52 @@ document.addEventListener("DOMContentLoaded", () => { var mymap = L.map("locationmap").setView([38.855235,139.910744], 16); - + L.tileLayer('https://{s}.tile.osm.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(mymap); - + var playerIcon = L.icon({ iconUrl: 'images/yuusya.png', iconSize: [40, 40], iconAnchor: [20, 40], popupAnchor: [0, -40] }); - + var locmarker = L.marker(mymap.getCenter(), { icon: playerIcon }).addTo(mymap); locmarker.bindPopup("STARTを押してね").openPopup(); - + let circleRadius = 500; let goalReachRadius = 10; - + var radiusCircle = L.circle(mymap.getCenter(), { radius: circleRadius, color: 'blue', fillColor: 'blue', fillOpacity: 0.1 }).addTo(mymap); - + var watchId = null; const urlParams = new URLSearchParams(window.location.search); const cheatMode = urlParams.get("cheat") === "true"; - + let stores = []; let visitedStores = []; let storeMarkers = []; - + const canvas = document.getElementById("progressImage"); const ctx = canvas.getContext("2d"); const totalImages = 7; const images = []; - + for (let i = 0; i < totalImages; i++) { images[i] = new Image(); images[i].src = `images/irasuto${i + 1}.png`; } - + function saveProgress() { localStorage.setItem("visitedStores", JSON.stringify(visitedStores)); } - + function loadProgress() { const saved = localStorage.getItem("visitedStores"); if (saved) { @@ -56,18 +56,18 @@ } catch (e) { console.error("保存データの読み込みに失敗:", e); } } } - + function updateProgressImage() { if (!canvas) return; - + let storeTotal = stores.length > 0 ? stores.length : 1; let progress = visitedStores.length / storeTotal; - + let index = Math.floor(progress * totalImages); if (index >= totalImages) index = totalImages - 1; - + ctx.clearRect(0, 0, canvas.width, canvas.height); - + if (images[index].complete) { ctx.drawImage(images[index], 0, 0, canvas.width, canvas.height); } else { @@ -76,7 +76,7 @@ }; } } - + function renderBookmarkList() { const container = document.getElementById("bookmarkList"); container.innerHTML = ""; @@ -87,7 +87,7 @@ visitedStores.forEach((store, idx) => { const div = document.createElement("div"); div.className = "bookmark-item"; - + div.innerHTML = ` ${store.name} (訪問日時: ${store.time})
おすすめ: ${store.osusume || "〇〇"}
@@ -100,7 +100,7 @@ `; container.appendChild(div); }); - + const memos = container.querySelectorAll(".memo-textarea"); memos.forEach(textarea => { textarea.addEventListener("input", (e) => { @@ -109,25 +109,25 @@ saveProgress(); }); }); - + const uploads = container.querySelectorAll(".image-upload"); uploads.forEach(input => { input.addEventListener("change", (e) => { const index = e.target.dataset.index; const file = e.target.files[0]; if (!file) return; - + if (file.size > 5 * 1024 * 1024) { alert("ファイルサイズは5MB以下にしてください。"); e.target.value = ""; return; } - + const reader = new FileReader(); reader.onload = function(event) { visitedStores[index].image = event.target.result; saveProgress(); - + const img = e.target.parentElement.querySelector(".preview-image"); if (img) { img.src = event.target.result; @@ -137,7 +137,7 @@ reader.readAsDataURL(file); }); }); - + const deletes = container.querySelectorAll(".image-delete"); deletes.forEach(button => { button.addEventListener("click", (e) => { @@ -156,9 +156,9 @@ }); }); } - + function loadCSV() { - fetch('shonai_tiiki_map.csv') + fetch('shonai_tiiki_map_tansaku.csv') .then(res => res.ok ? res.text() : Promise.reject("CSV読み込み失敗")) .then(csvText => { const results = Papa.parse(csvText, { header: true, skipEmptyLines: true }); @@ -172,7 +172,8 @@ marker: null, redCircle: null })).filter(s => !isNaN(s.latitude) && !isNaN(s.longitude)); - + + // 既に訪問済みの店舗をマップへ復元(画像対応版) visitedStores.forEach(obj => { const s = stores.find(st => st.name === obj.name); if (s) { @@ -185,7 +186,7 @@ let popupContent = ` 訪問日時: ${obj.time}
庄内総合高校の子のおすすめは${obj.osusume || "〇〇"}
- ${s.osusumeImage ? `おすすめ画像` : ""} + ${s.osusumeImage ? `` : ""} `; let storeMarker = L.marker([s.latitude, s.longitude], { icon: customIcon }) .addTo(mymap) @@ -193,7 +194,7 @@ storeMarkers.push(storeMarker); } }); - + if (cheatMode) { document.getElementById("title").textContent = "チートモード"; mymap.on("click", e => updateLocation(e.latlng)); @@ -203,11 +204,11 @@ }) .catch(err => alert("CSVの読み込みに失敗しました: " + err)); } - + function getDistance(lat1, lng1, lat2, lng2) { return L.latLng(lat1, lng1).distanceTo(L.latLng(lat2, lng2)); } - + function getNearestStore(latlng) { let nearest = null, minDist = Infinity; for (let s of stores) { @@ -218,28 +219,30 @@ if (!nearest) return null; return { store: nearest, distance: minDist }; } - + function updateLocation(latlng) { mymap.panTo(latlng); locmarker.setLatLng(latlng); radiusCircle.setLatLng(latlng); - + stores.forEach(s => { const d = getDistance(latlng.lat, latlng.lng, s.latitude, s.longitude); - + if (visitedStores.some(c => c.name === s.name)) { if (s.marker && mymap.hasLayer(s.marker)) mymap.removeLayer(s.marker); if (s.redCircle && mymap.hasLayer(s.redCircle)) mymap.removeLayer(s.redCircle); return; } - + if (d <= circleRadius) { if (!s.marker) { - s.marker = L.marker([s.latitude, s.longitude]).addTo(mymap).bindPopup(s.name); + s.marker = L.marker([s.latitude, s.longitude]) + .addTo(mymap) + .bindPopup(s.name); } else if (!mymap.hasLayer(s.marker)) { s.marker.addTo(mymap); } - + if (!s.redCircle) { s.redCircle = L.circle([s.latitude, s.longitude], { radius: goalReachRadius, @@ -258,15 +261,15 @@ if (s.redCircle && mymap.hasLayer(s.redCircle)) mymap.removeLayer(s.redCircle); } }); - + let nearestData = getNearestStore(latlng); if (!nearestData) { locmarker.setPopupContent("すべての店舗を訪問しました!").openPopup(); return; } - + const { store, distance } = nearestData; - + if (distance <= goalReachRadius) { const now = new Date(); const options = { @@ -275,39 +278,48 @@ }; const visitTime = now.toLocaleString('ja-JP', options); const recommendation = store.osusume.trim() !== "" ? store.osusume : "〇〇"; - - locmarker.setPopupContent( - `到着!(${store.name})
訪問日時: ${visitTime}
庄内総合高校の子のおすすめは${recommendation}` - ).openPopup(); - - visitedStores.push({ name: store.name, time: visitTime, osusume: recommendation }); + + // ===画像付きポップアップに修正した部分=== + let popupHTML = ` + 到着!(${store.name})
+ 訪問日時: ${visitTime}
+ 庄内総合高校の子のおすすめは${recommendation}
+ ${store.osusumeImage ? `` : ""} + `; + locmarker.setPopupContent(popupHTML).openPopup(); + + visitedStores.push({ + name: store.name, + time: visitTime, + osusume: recommendation + }); saveProgress(); - + if (store.marker && mymap.hasLayer(store.marker)) mymap.removeLayer(store.marker); if (store.redCircle && mymap.hasLayer(store.redCircle)) mymap.removeLayer(store.redCircle); - + const customIcon = L.icon({ iconUrl: store.iconUrl, iconSize: [40, 40], iconAnchor: [20, 40], popupAnchor: [0, -40] }); - + let newMarker = L.marker([store.latitude, store.longitude], { icon: customIcon }) .addTo(mymap) - .bindPopup(`訪問日時: ${visitTime}
庄内総合高校の子のおすすめは${recommendation}`); + .bindPopup(popupHTML, {autoClose:false}); storeMarkers.push(newMarker); - + updateProgressImage(); renderBookmarkList(); - + } else if (distance <= circleRadius) { locmarker.setPopupContent(`もう少しで${store.name}!`).openPopup(); } else { locmarker.setPopupContent(`ここは lat=${latlng.lat.toFixed(5)}, lng=${latlng.lng.toFixed(5)} です.`).openPopup(); } } - + function resetProgress() { if (confirm("進行状況をリセットして最初からやり直しますか?")) { localStorage.removeItem("visitedStores"); @@ -320,10 +332,10 @@ location.reload(); } } - + loadProgress(); loadCSV(); - + if (!cheatMode) { document.getElementById("start").addEventListener("click", () => { watchId = navigator.geolocation.watchPosition( @@ -333,42 +345,42 @@ ); }); } - + document.getElementById("stop").addEventListener("click", () => { if (watchId != null) navigator.geolocation.clearWatch(watchId); watchId = null; }); document.getElementById("reset").addEventListener("click", resetProgress); - + const radiusSelect = document.getElementById("radiusSelect"); const goalSelect = document.getElementById("goalSelect"); const RADIUS_KEY = "selectedRadius"; const GOAL_KEY = "selectedGoal"; - + let savedRadius = localStorage.getItem(RADIUS_KEY); if (savedRadius) { circleRadius = parseInt(savedRadius, 10); radiusCircle.setRadius(circleRadius); radiusSelect.value = savedRadius; } - + let savedGoal = localStorage.getItem(GOAL_KEY); if (savedGoal) { goalReachRadius = parseInt(savedGoal, 10); goalSelect.value = savedGoal; } - + radiusSelect.addEventListener("change", (e) => { circleRadius = parseInt(e.target.value, 10); radiusCircle.setRadius(circleRadius); localStorage.setItem(RADIUS_KEY, circleRadius); }); - + goalSelect.addEventListener("change", (e) => { goalReachRadius = parseInt(e.target.value, 10); localStorage.setItem(GOAL_KEY, goalReachRadius); - + stores.forEach(s => { if (s.redCircle) { s.redCircle.setRadius(goalReachRadius);