diff --git a/favoritesgood.html b/favoritesgood.html new file mode 100644 index 0000000..7ead88e --- /dev/null +++ b/favoritesgood.html @@ -0,0 +1,29 @@ + + + + + + + 行きたいお店リスト + + + + + + +

+ + スナパラ + +

+ +
+ + マップに戻る + +
+ + + + + diff --git a/favoritesgood.js b/favoritesgood.js new file mode 100644 index 0000000..5f8db09 --- /dev/null +++ b/favoritesgood.js @@ -0,0 +1,45 @@ +document.addEventListener('DOMContentLoaded', () => { + const favoritesList = document.getElementById('favorites-list'); + + // localStorage からデータを取得 + let favorites = JSON.parse(localStorage.getItem('favorites') || '[]'); + + // リストを描画する関数 + function renderList(items) { + favoritesList.innerHTML = ''; // リストをリセット + if (items.length === 0) { + favoritesList.innerHTML = '
  • 行きたいお店はまだ登録されていません。
  • '; + } + items.forEach((item, index) => { + const li = document.createElement('li'); + li.className = 'favorite-item'; + + // 店舗名と追加日時を表示 + li.innerHTML = ` + ${item.name} + ${new Date(item.addedAt).toLocaleString()} + + `; + + favoritesList.appendChild(li); + }); + + // 削除ボタンにイベントリスナーを追加 + document.querySelectorAll('.delete-btn').forEach(button => { + button.addEventListener('click', (event) => { + const index = event.target.dataset.index; + deleteFavorite(index); + }); + }); + } + + // 削除する関数 + function deleteFavorite(index) { + favorites.splice(index, 1); // 該当のデータを削除 + localStorage.setItem('favorites', JSON.stringify(favorites)); // 更新されたデータを localStorage に保存 + renderList(favorites); // 再描画 + } + + // 初期表示(追加日時順) + renderList(favorites); +}); diff --git a/mapgood.js b/mapgood.js new file mode 100644 index 0000000..970b08d --- /dev/null +++ b/mapgood.js @@ -0,0 +1,209 @@ +document.addEventListener('DOMContentLoaded', () => { + + // 地図オブジェクトを作成し、中心座標とズームレベルを設定 + const map = L.map('map').setView([38.9175, 139.8353], 16); + const markers = [];// 全てのマーカーを保存する配列(フィルタ用) + + // 地図タイルを読み込み(OpenStreetMap使用) + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }).addTo(map); + + // 現在地表示 + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition(position => { + const lat = position.coords.latitude; + const lng = position.coords.longitude; + L.marker([lat, lng], { + icon: L.icon({ + iconUrl: 'images/favicon-32x32-now.png', + iconSize: [24, 24], + iconAnchor: [12, 12] + }) + }).addTo(map).bindPopup('現在地'); + }); + } + + // 曜日を日本語に変換する配列 + const dayMap = ['日', '月', '火', '水', '木', '金', '土']; + const today = new Date(); + const todayStr = dayMap[today.getDay()];// 今日の曜日を文字列で取得 + + // ───── 関数:本日が定休日かどうかを判定 ───── + function isClosedToday(desc1) { + if (!desc1) return false; + const closed = desc1.replace('定休日:', '').replace(/:/g, '').split(/[、,/・\/\s]/).filter(Boolean);// 曜日や不定休を抽出 + + for (let entry of closed) { + entry = entry.trim(); + if (entry.includes('不定休')) return true; + if (entry.includes(todayStr) && !entry.match(/第\d/)) return true; + + // 第○○曜日のパターン + const nthMatch = entry.match(/第(\d)(.)曜日/); + if (nthMatch) { + const nth = parseInt(nthMatch[1], 10); + const day = nthMatch[2]; + if (day !== todayStr) continue; + + // 今月のその曜日が第何回か判定 + let count = 0; + for (let d = 1; d <= 31; d++) { + const date = new Date(today.getFullYear(), today.getMonth(), d); + if (date.getMonth() !== today.getMonth()) break; + if (date.getDay() === dayMap.indexOf(day)) count++; + if (date.getDate() === today.getDate() && count === nth) return true; + } + } + } + return false; + } + + // ───── 関数:現在時間が営業時間内か判定 ───── + function isOpenNow(desc2) { + if (!desc2) return false; + const now = new Date(); + const day = now.getDay(); + const time = now.getHours() * 60 + now.getMinutes();// 現在時刻(分単位) + + // 複数の営業時間パターン(曜日ごと)を考慮 + const segments = desc2.split(/\/|\\n|\\r\\n/); + for (let segment of segments) { + // 曜日を含む営業時間フォーマットにマッチ + const match = segment.match(/(月|火|水|木|金|土|日)(?:~(月|火|水|木|金|土|日))?[::]?\s*(\d{1,2})[::](\d{2})\s*〜\s*(\d{1,2})[::](\d{2})/); + if (match) { + const [_, startDay, endDay, sh, sm, eh, em] = match; + const startMin = parseInt(sh) * 60 + parseInt(sm); + let endMin = parseInt(eh) * 60 + parseInt(em); + const dayIndex = { '日': 0, '月': 1, '火': 2, '水': 3, '木': 4, '金': 5, '土': 6 }; + let validDay = false; + if (!endDay) { + validDay = day === dayIndex[startDay]; + } else { + const startIdx = dayIndex[startDay]; + const endIdx = dayIndex[endDay]; + validDay = startIdx <= endIdx ? day >= startIdx && day <= endIdx : day >= startIdx || day <= endIdx; + } + if (!validDay) continue; + if (endMin <= startMin) endMin += 1440;// 翌日まで営業する場合 + const currentTime = time < startMin ? time + 1440 : time; + if (currentTime >= startMin && currentTime <= endMin) return true; + } + + // シンプルな時間フォーマット(曜日なし) + const simple = segment.match(/(\d{1,2})[::](\d{2})\s*〜\s*(\d{1,2})[::](\d{2})/); + if (simple) { + const startMin = parseInt(simple[1]) * 60 + parseInt(simple[2]); + let endMin = parseInt(simple[3]) * 60 + parseInt(simple[4]); + if (endMin <= startMin) endMin += 1440; + const currentTime = time < startMin ? time + 1440 : time; + if (currentTime >= startMin && currentTime <= endMin) return true; + } + } + return false; + } + + // ───── CSVファイルから店舗情報を読み込んでマーカーを配置 ───── + fetch('snack.csv') + .then(response => response.text()) + .then(text => { + const rows = text.split('\n').filter(row => row.trim()); + const headers = rows[0].split(',').map(h => h.trim()); + + for (let i = 1; i < rows.length; i++) { + const values = rows[i].split(',').map(v => v.replace(/^"|"$/g, '').trim()); + const data = {}; + headers.forEach((key, index) => data[key] = values[index] || ''); + + const lat = parseFloat(data.latitude); + const lng = parseFloat(data.longitude); + if (isNaN(lat) || isNaN(lng)) continue; + + // 営業中アイコンか、休業中アイコンを選択 + const isOpen = !isClosedToday(data.description1) && isOpenNow(data.description2); + const iconPath = isOpen ? (data.icon || 'images/snack-icon.png') : 'images/favicon-door-32x32.png'; + + const customIcon = L.icon({ + iconUrl: iconPath, + iconSize: [32, 32], + iconAnchor: [16, 32], + popupAnchor: [0, -32] + }); + + // 店舗の説明文と画像 + const description = [data.description1, data.description2, data.description3, data.mama] + .filter(Boolean) + .map(d => d.trim()) + .join('
    '); + + const image1 = data.img1 && data.img1.trim() !== '' ? data.img1 : 'images/snacktitle.png'; + const image2 = data.img2 && data.img2.trim() !== '' ? data.img2 : 'images/snacktitle.png'; + + // ポップアップのHTML内容 + const popupContent = ` + `; + + const marker = L.marker([lat, lng], { icon: customIcon }).addTo(map).bindPopup(popupContent); + marker.isOpen = isOpen; + markers.push(marker);// フィルタ機能のために保存 + } + }) + .catch(error => { + console.error('CSV読み込みエラー:', error); + }); + + // ───── 「空いてる店だけ表示」ボタンのUIと機能 ───── + const toggleBtn = document.createElement('button'); + toggleBtn.textContent = '営業中の店を表示'; + toggleBtn.style.position = 'absolute'; + toggleBtn.style.top = '10px'; + toggleBtn.style.right = '10px'; + toggleBtn.style.zIndex = 1000; + toggleBtn.style.padding = '10px 10px'; + toggleBtn.style.background = '#f3e8ff'; + toggleBtn.style.color = '#3e004f'; + toggleBtn.style.border = '1px solid #3e004f'; + toggleBtn.style.cursor = 'pointer'; + document.body.appendChild(toggleBtn); + + let showingOnlyOpen = false; + toggleBtn.addEventListener('click', () => { + showingOnlyOpen = !showingOnlyOpen; + toggleBtn.textContent = showingOnlyOpen ? '全ての店を表示' : '営業中の店を表示'; + markers.forEach(marker => { + if (showingOnlyOpen) { + if (marker.isOpen) marker.addTo(map); + else map.removeLayer(marker); + } else { + marker.addTo(map); + } + }); + }); +}); + +// ローカルストレージに店舗名を追加(お気に入り機能) +window.addToFavorites = function(name) { + const favorites = JSON.parse(localStorage.getItem('favorites') || '[]'); + favorites.push({ name, addedAt: new Date().toISOString() }); + localStorage.setItem('favorites', JSON.stringify(favorites)); + alert(`${name}を行きたいお店リストに追加しました!`); +}; + +// 画像モーダル表示用 +window.openModal = function(src) { + const modal = document.getElementById("modal"); + const modalImg = document.getElementById("modal-img"); + modal.style.display = "block"; + modalImg.src = src; +}; + +// モーダル閉じる +window.closeModal = function() { + document.getElementById("modal").style.display = "none"; +}; diff --git a/snack.csv b/snack.csv new file mode 100644 index 0000000..b2a6690 --- /dev/null +++ b/snack.csv @@ -0,0 +1,39 @@ +name,latitude,longitude,description1,description2,description3,img1,img2,icon +スナック緑,38.917517,139.83584,定休日:月曜日,営業時間 19:00〜0:00,,,,images/snack-icon.png +スナックLink,38.917834,139.834857,定休日:なし,営業時間 20:00〜1:00,,,,images/snack-icon.png +スナックCherir,38.916895,139.83499,定休日:日曜日,営業時間 20:00〜0:00,,,,images/snack-icon.png +スナック待夢里,38.916106,139.831466,定休日:不定休,営業時間 20:00〜0:00,,,,images/snack-icon.png +アモーレ,38.9182771,139.8339734,定休日:なし,営業時間 20:00〜0:00,"店内情報:初めての方でも行きやすいお店です。店内は窓に開放感があり、働いているキャストさんの年齢層は幅広いです。",images/amore1.jpeg,images/amore2.jpeg,images/favicon-32x32.png +スナックRJ,38.919706,139.832596,定休日:日曜日,営業時間 20:30~3:00,,,,images/snack-icon.png +スナック美麗,38.919015,139.834490,定休日:日曜日,営業時間 20:00~0:00,,,,images/snack-icon.png +スナックエミ,38.917714,139.835221,定休日:日曜日,営業時間 20:00〜0:00,,,,images/snack-icon.png +スナック・ジャスミン,38.9173250,139.8361750,定休日:不定休,営業時間 19:00〜0:00,,,,images/snack-icon.png +//ラボンヌ,38.919834,139.832893,定休日:曜日,営業時間 :00〜0:00,,,,images/snack-icon.png +//セブンマキ,38.917906,139.834498,定休日:曜日,営業時間 :00〜0:00,,,,images/snack-icon.png +ドルフィン,38.9176609,139.8336603,定休日:月曜日,営業時間 20:00〜0:00,,,,images/snack-icon.png +パラダイス。,38.917986,139.834382,定休日:不定休,営業時間 月~木:19:30〜0:00/金~日:20:00~0:00,,,,images/snack-icon.png +スナック志保,38.9178898,139.8349414,定休日:日、月曜日,営業時間:20:30〜0:30,"店内情報:店内は落ち着いた雰囲気で、スナックには窓がない、置いてるお酒はママの好みで年齢層はやや高め。",images/siho1.jpg,images/siho2.jpg,images/favicon-32x32.png +スナックマーブル,38.919078,139.834543,定休日:日曜日,営業時間 19:00〜0:00,,,,images/snack-icon.png +スナックCline,38.9177181,139.834623,定休日:日曜日,営業時間 20:00〜0:00,,,,images/snack-icon.png +//スナックバタフライ,38.9180537,139.8313297,定休日:曜日,営業時間 :00〜0:00,,,,images/snack-icon.png +スナックゆーみん,38.917652,139.835209,定休日:日曜日,営業時間 19:30〜0:00,,,,images/snack-icon.png +スナックKa・Na・De,38.91762,139.83947,定休日:日曜日,営業時間 19:00〜0:00,,,,images/snack-icon.png +//スナックわかな,38.920393,139.843235,定休日:曜日,営業時間 :00〜0:00,,,,images/snack-icon.png +ゆーとぴあ〔夢想郷〕,38.921445,139.844411,定休日:第3日曜日,営業時間 19:00〜0:00,,,,images/snack-icon.png +スナック暖,38.9203021,139.8343288,定休日:日曜日,営業時間 20:00〜0:00,,,,images/snack-icon.png +スナックべに花,38.917906,139.834498,定休日:日曜日,営業時間 19:30〜0:00,,,,images/snack-icon.png +スナック・プルメリア,38.9175525,139.8314215,定休日:不定休,営業時間 月~木:20:00〜1:00/金~日:19:00~1:00,,,,images/snack-icon.png +ふらっと,38.9213477,139.8433158,定休日:不定休,営業時間 18:30〜0:00,,,,images/snack-icon.png +シーズ,38.9179562,139.8339100,定休日:日曜日,営業時間 20:00〜0:00,,,,images/snack-icon.png +Glossy★,38.9179889,139.8310129,定休日:日曜日,営業時間 19:00〜0:00,,,,images/snack-icon.png +SnackRoppongi,38.918003,139.835507,定休日:日曜日,営業時間 19:00〜0:00,,,,images/snack-icon.png +//OPENHEART,38.9201247,139.8341646,定休日:曜日,営業時間 :00〜0:00,,,,images/snack-icon.png +リヨン2,38.9180986682216,139.83401441368,定休日:日曜日,営業時間 19:30〜0:00,,,,images/snack-icon.png +K,38.9094655069939,139.835738820211,定休日:不定休,営業時間 18:00〜2:00,,,,images/snack-icon.png +シャングリラ,38.918033,139.8338931,定休日:日曜日,営業時間 20:00〜1:00,,,,images/snack-icon.png +サラ,38.9196951473271,139.832192713464,定休日:日曜日,営業時間 18:30〜0:00,,,,images/snack-icon.png +CAROL,38.9180039,139.8353658,定休日:日曜日,営業時間 20:00〜0:00,,,,images/snack-icon.png +クオーレ,38.917384,139.836265,定休日:日、月曜日,営業時間 20:00〜0:00,,,,images/snack-icon.png +//ブルーローズ,38.918003,139.835507,定休日:曜日,営業時間 :00〜0:00,,,,images/snack-icon.png +CLUB Ma Cherie,38.9178582,139.8339079,定休日:日曜日,営業時間:20:00〜0:00,"店内情報:カラオケがなく、お話がメインのお店。接待の方でも使えるお店となっており、年齢の若い子から大人のキャストもいるため話が合わないことがない。",images/ma%20cheri1.JPG,images/ma%20cheri2.JPG,images/favicon-32x32.png +//らーく,38.921695,139.84452,定休日:曜日,営業時間::00〜0:00,,,,images/snack-icon.png diff --git a/snackmapgood.html b/snackmapgood.html new file mode 100644 index 0000000..50ec159 --- /dev/null +++ b/snackmapgood.html @@ -0,0 +1,35 @@ + + + + + + + スナッキングパラダイス + + + + + + + +

    + + スナパラ + +

    +
    + + スナパラ + +
    +

    🍾=営業時間中 🚪=閉業時間中 🍸=情報なし 👇=現在地

    + +
    + + + + + diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..d26e992 --- /dev/null +++ b/styles.css @@ -0,0 +1,241 @@ +/* 全体のスタイル */ +body { + font-family: 'Arial', sans-serif; + margin: 0; + padding: 0; + box-sizing: border-box; + background-color: #f3e8ff; /* 淡い紫 */ + color: #3e004f; /* 濃い紫 */ +} + +h1 { + font-family: 'Playfair Display', serif; + font-size: 32px; + text-align: center; + margin: 0; + padding: 20px; + background-color: #6a0dad; /* 紫の背景 */ + color: white; + font-weight: 700; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + letter-spacing: 1px; + border-bottom: 4px solid #4b0082; /* 深い紫のアクセント */ +} + +a{ + color: #ff5757; +} + +.modal { + display: none; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.8); +} + +.modal-content { + margin: auto; + display: block; + max-width: 90%; + max-height: 90%; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + animation: fadeIn 0.3s; +} + +.close { + position: absolute; + top: 10px; + right: 25px; + color: white; + font-size: 35px; + font-weight: bold; + cursor: pointer; +} + +.close:hover, +.close:focus { + color: #ccc; + text-decoration: none; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* ポップアップのカスタムスタイル */ +.popup-content { + width: 250px; /* 幅を広く設定 */ + height: auto; + padding: 15px; + font-size: 18px; /* フォントサイズを大きく */ + line-height: 1.5; +} + +.popup-content h3 { + font-size: 30px; + margin-bottom: 10px; + color: #6a0dad; +} + +.popup-content p { + margin: 15px 0; + color: #333; +} + +.popup-content button { + padding: 15px 25px; + font-size: 20px; + background-color: #6a0dad; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; +} + +.popup-content button:hover { + background-color: #4b0082; +} + +/* ポップアップの全体サイズを大きく */ +.leaflet-popup-content-wrapper { + width: auto !important; /* ポップアップの幅 */ + height: auto !important; /* 高さは内容に合わせて自動 */ +} + +.leaflet-popup-tip { + display: none; /* ポップアップの三角形を非表示にする */ +} + +/* 検索欄 */ +.search-container { + display: flex; + justify-content: center; + margin: 20px 0; +} + +.search-container input[type="text"] { + width: 80%; + max-width: 600px; + padding: 15px; + font-size: 18px; + border: 2px solid #9b59b6; /* 紫色の枠線 */ + border-radius: 25px; + background-color: white; + color: #3e004f; /* 濃い紫 */ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: border-color 0.3s ease, box-shadow 0.3s ease; +} + +.search-container input[type="text"]:focus { + border-color: #6a0dad; /* フォーカス時の枠線 */ + box-shadow: 0 4px 8px rgba(106, 13, 173, 0.4); /* フォーカス時の影 */ + outline: none; +} + +/* マップエリア */ +#map { + height: 500px; + margin: 20px; + border-radius: 10px; + border: 2px solid #9b59b6; /* 紫色の枠線 */ + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); +} + +/* お気に入りリンク */ +.favorites-link, .back-link { + display: block; + text-align: center; + margin: 20px auto; + font-size: 25px; + color: #6a0dad; + text-decoration: none; + font-weight: bold; +} + +.favorites-link:hover, .back-link:hover { + text-decoration: underline; + color: #4b0082; /* ホバー時の濃い紫 */ +} + + +/* お気に入りリスト */ +#favorites-list { + margin: 20px auto; + font-size: 20px; + padding: 0; + list-style: none; + max-width: 600px; +} + +#favorites-list li { + background-color: #ffffff; /* 白背景 */ + color: #3e004f; /* 濃い紫 */ + border: 1px solid #9b59b6; /* 紫色の枠線 */ + border-radius: 10px; + padding: 10px 15px; + margin: 10px 0; + display: flex; + justify-content: space-between; + align-items: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +#favorites-list li button { + padding: 10px 10px; + font-size: 30px; + background-color: #6a0dad; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s ease; +} + +#favorites-list li button:hover { + background-color: #4b0082; +} + +/* Leafletズームコントロールのカスタマイズ */ +.leaflet-control-zoom { + transform: scale(1.5); /* サイズを縮小 (デフォルトの80%) */ + right: 10px; /* 位置を少し右に調整 (必要に応じて変更) */ + bottom: -300px; /* 位置を少し上に調整 (必要に応じて変更) */ +} + +.leaflet-control-zoom a { + width: 30px; /* ボタンの幅を調整 */ + height: 30px; /* ボタンの高さを調整 */ + font-size: 18px; /* ボタン内の文字サイズを調整 */ + line-height: 30px; /* ボタン内のテキストの位置を中央に揃える */ + border-radius: 5px; /* ボタンに角丸を付ける */ + background-color: #0078d7; /* ボタンの背景色を変更 */ + color: white; /* テキストの色 */ + transition: background-color 0.3s ease; +} + +.leaflet-control-zoom a:hover { + background-color: #005a9e; /* ホバー時の背景色 */ +} + +/* レスポンシブ対応 */ +@media (max-width: 768px) { + #map { + height: 600px; + } + + .search-container input[type="text"] { + font-size: 16px; + padding: 12px; + } +}