From e1eff22fa902d5fad898ebeae3b5666bb65a4622 Mon Sep 17 00:00:00 2001 From: Mr-T18 Date: Thu, 28 Aug 2025 01:24:35 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=E7=99=BB=E9=8C=B2=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E3=81=A7=E3=81=AE=E3=83=9E=E3=83=83=E3=83=97=E3=81=AE=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=20=E3=81=AA=E3=81=9C=E3=81=8B=E3=83=94=E3=83=B3?= =?UTF-8?q?=E3=81=8C=E3=81=95=E3=81=9B=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/javascript/controllers/map_controller.js | 68 +++++++++++++++----- app/views/admin/photo_spots/_form.html.erb | 25 +++++++ app/views/photo_spots/_map.html.erb | 9 +++ app/views/photo_spots/index.html.erb | 1 - 4 files changed, 85 insertions(+), 18 deletions(-) create mode 100644 app/views/photo_spots/_map.html.erb diff --git a/app/javascript/controllers/map_controller.js b/app/javascript/controllers/map_controller.js index 5e9e8b1..7f312b4 100644 --- a/app/javascript/controllers/map_controller.js +++ b/app/javascript/controllers/map_controller.js @@ -3,12 +3,13 @@ import L from "leaflet" // Connects to data-controller="map" export default class extends Controller { - static targets = ["container"] - static values = { onsens: Array } + // 新しいターゲットを追加しました + static targets = ["container", "latitudeInput", "longitudeInput"] + static values = { photo_spots: Array } connect() { - this.onsens = this._parseOnsensData(); - console.log(this.onsens); + this.currentMarker = null; + this.photo_spots = this._parsePhotoSpotsData(); this.map = L.map(this.containerTarget).setView([35.468, 133.0483], 11.5); @@ -17,16 +18,30 @@ export default class extends Controller { attribution: '© OpenStreetMap' }).addTo(this.map); - const onsenIcon = L.icon({ - iconUrl: '/onsen.svg', + const photo_spotIcon = L.icon({ + iconUrl: '/photo_spot.svg', iconSize: [32, 32], iconAnchor: [16, 32], }); - this.onsens.forEach(onsen => { - L.marker([onsen.geo_lat, onsen.geo_lng], { icon: onsenIcon }) + this.photo_spots.forEach(photo_spot => { + L.marker([photo_spot.geo_lat, photo_spot.geo_lng], { icon: photo_spotIcon }) .addTo(this.map) - .bindPopup(onsen.name); + .bindPopup(photo_spot.name); + }); + + // フォームに初期値があればピンを立てる + if (this.latitudeInputTarget.value && this.longitudeInputTarget.value) { + this.updateMarker(this.latitudeInputTarget.value, this.longitudeInputTarget.value); + } + + // マップクリック時のイベントリスナー + this.map.on("click", (e) => { + const { lat, lng } = e.latlng; + this.updateMarker(lat, lng); + // テキストボックスの値を更新 + this.latitudeInputTarget.value = lat.toFixed(5); + this.longitudeInputTarget.value = lng.toFixed(5); }); } @@ -37,19 +52,38 @@ export default class extends Controller { } } + // --- 新しく追加したメソッド --- + + // マーカーを更新し、マップの中心を移動するメソッド + updateMarker(lat, lng) { + if (this.currentMarker) { + this.map.removeLayer(this.currentMarker); + } + const latlng = [lat, lng]; + this.currentMarker = L.marker(latlng).addTo(this.map); + this.map.setView(latlng, this.map.getZoom()); // 既存のズームレベルでビューを更新 + } + + // フォームの入力値からマーカーを更新するメソッド + updateMarkerFromInput() { + const lat = parseFloat(this.latitudeInputTarget.value); + const lng = parseFloat(this.longitudeInputTarget.value); + + // 有効な数値であることを確認 + if (!isNaN(lat) && !isNaN(lng)) { + this.updateMarker(lat, lng); + } + } + // === プライベートメソッド(内部処理用) === - /** - * HTML要素から温泉データを取得・パース - * @returns {Array} 温泉データの配列 - */ - _parseOnsensData() { + _parsePhotoSpotsData() { try { - const rawData = this.element.dataset.mapOnsens || "[]"; + const rawData = this.element.dataset.mapPhotoSpots || "[]"; return JSON.parse(rawData); } catch (error) { - console.warn("温泉データのパースに失敗:", error); + console.warn("写真スポットデータのパースに失敗:", error); return []; } } -} +} \ No newline at end of file diff --git a/app/views/admin/photo_spots/_form.html.erb b/app/views/admin/photo_spots/_form.html.erb index 42275c6..7bb9bb9 100644 --- a/app/views/admin/photo_spots/_form.html.erb +++ b/app/views/admin/photo_spots/_form.html.erb @@ -19,6 +19,31 @@ <%= form.text_field :address, class: "block shadow-sm rounded-md border px-3 py-2 w-full border-gray-300 focus:outline-blue-600" %> + <%# 地図エリア(デスクトップ時) %> + <%= form.hidden_field :geo_lat, data: { map_target: 'latitude' } %> + <%= form.hidden_field :geo_lng, data: { map_target: 'longitude' } %> +
+
+ <%= form.label :latitude, '緯度', class: 'block font-semibold mb-1' %> + <%= form.text_field :latitude, + data: { map_target: 'latitudeInput', action: 'input->map#updateMarkerFromInput' }, + class: "block shadow-sm rounded-md border px-3 py-2 w-full border-gray-300 focus:outline-blue-600" %> +
+
+ <%= form.label :longitude, '経度', class: 'block font-semibold mb-1' %> + <%= form.text_field :longitude, + data: { map_target: 'longitudeInput', action: 'input->map#updateMarkerFromInput' }, + class: "block shadow-sm rounded-md border px-3 py-2 w-full border-gray-300 focus:outline-blue-600" %> +
+
+
+
+ +
<%= form.label :detail, '説明', class: 'block font-semibold mb-1' %> <%= form.text_area :detail, rows: 4, class: "block shadow-sm rounded-md border px-3 py-2 w-full border-gray-300 focus:outline-blue-600" %> diff --git a/app/views/photo_spots/_map.html.erb b/app/views/photo_spots/_map.html.erb new file mode 100644 index 0000000..ecaefd3 --- /dev/null +++ b/app/views/photo_spots/_map.html.erb @@ -0,0 +1,9 @@ +<%# 地図エリア(左側・デスクトップ時) %> +
+
+ +
\ No newline at end of file diff --git a/app/views/photo_spots/index.html.erb b/app/views/photo_spots/index.html.erb index ff9c3f3..e8ed65a 100644 --- a/app/views/photo_spots/index.html.erb +++ b/app/views/photo_spots/index.html.erb @@ -7,7 +7,6 @@ <%= render 'search_form' %> <%# メインコンテンツ:地図 + フォトスポット一覧 %>
- <%# 地図エリア(左側・デスクトップ時) %>
From 040d75195c1d4c813d0955b657a67e80c297e64e Mon Sep 17 00:00:00 2001 From: Mr-T18 Date: Thu, 28 Aug 2025 02:10:32 +0000 Subject: [PATCH 2/2] rails Merge branch 'main' into register_map_mark --- .../admin/photo_spots_controller.rb | 5 +- app/javascript/controllers/map_controller.js | 48 ++++++++++++------- app/views/admin/photo_spots/_form.html.erb | 11 ++--- db/schema.rb | 5 ++ 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/app/controllers/admin/photo_spots_controller.rb b/app/controllers/admin/photo_spots_controller.rb index d7539a3..b252b96 100644 --- a/app/controllers/admin/photo_spots_controller.rb +++ b/app/controllers/admin/photo_spots_controller.rb @@ -71,8 +71,7 @@ def set_photo_spot end def photo_spot_params - - params.require(:photo_spot).permit(:name, :address, :detail, :parking_flag, :tags, images: []) - + params.require(:photo_spot).permit(:name, :address, :detail, :parking_flag, :tags, { images: [] }, + :latitude, :longitude, :timestart, :timeend) end end diff --git a/app/javascript/controllers/map_controller.js b/app/javascript/controllers/map_controller.js index 7f312b4..02dacd4 100644 --- a/app/javascript/controllers/map_controller.js +++ b/app/javascript/controllers/map_controller.js @@ -3,7 +3,7 @@ import L from "leaflet" // Connects to data-controller="map" export default class extends Controller { - // 新しいターゲットを追加しました + // ターゲット名を変更しました static targets = ["container", "latitudeInput", "longitudeInput"] static values = { photo_spots: Array } @@ -11,21 +11,22 @@ export default class extends Controller { this.currentMarker = null; this.photo_spots = this._parsePhotoSpotsData(); + // 地図の初期化 this.map = L.map(this.containerTarget).setView([35.468, 133.0483], 11.5); - L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap' }).addTo(this.map); + // 既存のスポットのマーカーを設置 const photo_spotIcon = L.icon({ iconUrl: '/photo_spot.svg', iconSize: [32, 32], iconAnchor: [16, 32], }); - this.photo_spots.forEach(photo_spot => { - L.marker([photo_spot.geo_lat, photo_spot.geo_lng], { icon: photo_spotIcon }) + // プロパティ名を変更しました + L.marker([photo_spot.latitude, photo_spot.longitude], { icon: photo_spotIcon }) .addTo(this.map) .bindPopup(photo_spot.name); }); @@ -45,41 +46,54 @@ export default class extends Controller { }); } - disconnect() { - if (this.map) { - this.map.remove(); - this.map = null; - } - } - - // --- 新しく追加したメソッド --- - - // マーカーを更新し、マップの中心を移動するメソッド + // マーカーを更新し、マップの中心を移動する updateMarker(lat, lng) { if (this.currentMarker) { this.map.removeLayer(this.currentMarker); } const latlng = [lat, lng]; this.currentMarker = L.marker(latlng).addTo(this.map); - this.map.setView(latlng, this.map.getZoom()); // 既存のズームレベルでビューを更新 + this.map.setView(latlng, this.map.getZoom()); } - // フォームの入力値からマーカーを更新するメソッド + // フォームの入力値からマーカーを更新する updateMarkerFromInput() { const lat = parseFloat(this.latitudeInputTarget.value); const lng = parseFloat(this.longitudeInputTarget.value); - // 有効な数値であることを確認 if (!isNaN(lat) && !isNaN(lng)) { this.updateMarker(lat, lng); } } + // 現在地を取得するメソッドを追加 + locate() { + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + (position) => { + const lat = position.coords.latitude; + const lng = position.coords.longitude; + this.updateMarker(lat, lng); + this.latitudeInputTarget.value = lat.toFixed(5); + this.longitudeInputTarget.value = lng.toFixed(5); + }, + (error) => { + console.error("現在地の取得に失敗しました: ", error); + alert("現在地の取得に失敗しました。ブラウザの設定をご確認ください。"); + } + ); + } else { + alert("お使いのブラウザは現在地の取得に対応していません。"); + } + } + // === プライベートメソッド(内部処理用) === _parsePhotoSpotsData() { try { const rawData = this.element.dataset.mapPhotoSpots || "[]"; + // パース後のデータが{id:..., name:..., geo_lat:..., geo_lng:...}のような構造であれば + // 必要に応じてキーをリマップする必要があります return JSON.parse(rawData); } catch (error) { console.warn("写真スポットデータのパースに失敗:", error); diff --git a/app/views/admin/photo_spots/_form.html.erb b/app/views/admin/photo_spots/_form.html.erb index 7bb9bb9..519913e 100644 --- a/app/views/admin/photo_spots/_form.html.erb +++ b/app/views/admin/photo_spots/_form.html.erb @@ -1,4 +1,4 @@ -<%= form_with(model: [:admin, photo_spot || @photo_spot], html: { multipart: true, class: "space-y-6" }) do |form| %> +<%= form_with(model: [:admin, photo_spot || @photo_spot], html: { multipart: true, class: "space-y-6", data: { controller: "map" } }) do |form| %> <% if photo_spot.errors.any? %>

<%= pluralize(photo_spot.errors.count, "件のエラー") %>があります:

@@ -19,9 +19,7 @@ <%= form.text_field :address, class: "block shadow-sm rounded-md border px-3 py-2 w-full border-gray-300 focus:outline-blue-600" %>
- <%# 地図エリア(デスクトップ時) %> - <%= form.hidden_field :geo_lat, data: { map_target: 'latitude' } %> - <%= form.hidden_field :geo_lng, data: { map_target: 'longitude' } %> + <%# 緯度・経度の入力フィールド %>
<%= form.label :latitude, '緯度', class: 'block font-semibold mb-1' %> @@ -36,8 +34,9 @@ class: "block shadow-sm rounded-md border px-3 py-2 w-full border-gray-300 focus:outline-blue-600" %>
+ <%# 地図エリア(デスクトップ時) %>
-
+