Railsで地図 投稿 地名検索 一覧表示 GoogleMapsAPI(JavaScript) gemなし



# 作る機能
###### ・投稿する際に緯度と経度を保存する
###### ・すべての投稿内容を一つの地図上で一覧表示させる
###### ・マーカーをクリックすると投稿の内容が出る
###### ・地図上で詳細ページまでのリンクを表示させる

# 完成デモ
新規投稿画面
![map_new.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/ad1a2d74-f464-6322-903d-ebc5b9bae0a4.gif)


一覧ページ
![スクリーンショット 2020-12-03 040439.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/7f3746b4-00db-316d-1345-ae000f5f0184.jpeg)




# それでは作っていきましょう！

まずは先人の知恵を借ります。
以下の記事を参考に最後まで作り上げてください！

【Rails6 / Google Map API】初学者向け！Ruby on Railsで簡単にGoogle Map APIの導入する
https://qiita.com/nagaseToya/items/e49977efb686ed05eadb

※補足
自分が実装した際は課金設定のとこで少々手間取りましたが、
左のメニューバーから設定できるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/29363a08-0ea8-0505-218e-3aac6a1dcdb2.png)

以上が、実装できましたら
機能を追加していきます！

※YOURAPIKEYには自分自身のAPIキーを代入してください
※GitHubなどへアップロードする際は.envに記載し他人から確認できないようにしましょう。

# 投稿する際に緯度と経度を保存する

###### カラムの追加
※この記事ではPostモデルがある前提で、postsというテーブルに対してカラムの追加をしています。もし他のモデル名やテーブル名で既に作成している場合は、以降、適宜、ご自身の環境に適したモデル名とテーブル名に変換して実装してください。
```
$ rails generate migration AddDetailsToPosts lat:float lng:float
$ rails db:migrate
```

###### 投稿フォームを追加
元記事から少々、書き換えているので注意して以下の文をコピペしてください。

```erb:posts/new.html.erb
<%= form_with(model: @post, local: true) do |f| %>
  <div class="actions">
    <%= f.label :body,"内容" %>
    <%= f.text_field :body %>
    <%= f.label :lat,"緯度" %>
    <%= f.text_field :lat,:value =>"緯度", id: :lat %>
    <%= f.label :lng,"経度" %>
    <%= f.text_field :lng,:value =>"経度", id: :lng %>
    <%= f.submit %>
  </div>
<% end %>

<h2>Map</h2>

<input id="address" type="textbox" value="GeekSalon">
<input type="button" value="検索" onclick="codeAddress()">
<p>マーカーをドラック＆ドロップで位置の調整ができます。<p>
<div id='map'></div>

<style>
#map {
  height: 600px;
  width: 600px;
}
</style>

<script>
//初期マップの設定
let map
let marker
function initMap(){
  geocoder = new google.maps.Geocoder()

  map = new google.maps.Map(document.getElementById('map'), {
    center:  {lat: 35.6803997, lng:139.7690174},  //東京
    zoom: 15,
    
  });
}

//検索後のマップ作成
let geocoder
let aft
function codeAddress(){
  let inputAddress = document.getElementById('address').value;
  geocoder.geocode( { 'address': inputAddress}, function(results, status) {
    if (status == 'OK') {
        //マーカーが複数できないようにする
        if (aft == true){
            marker.setMap(null);
        }

        //新しくマーカーを作成する
        map.setCenter(results[0].geometry.location);
            marker = new google.maps.Marker({
            map: map,
            position: results[0].geometry.location,
            draggable: true	// ドラッグ可能にする
        });

        //二度目以降か判断
        aft = true

        //検索した時に緯度経度を入力する
        document.getElementById('lat').value = results[0].geometry.location.lat();
        document.getElementById('lng').value = results[0].geometry.location.lng();

        // マーカーのドロップ（ドラッグ終了）時のイベント
        google.maps.event.addListener( marker, 'dragend', function(ev){
            // イベントの引数evの、プロパティ.latLngが緯度経度
            document.getElementById('lat').value = ev.latLng.lat();
            document.getElementById('lng').value = ev.latLng.lng();
        });
    } else {
      alert('該当する結果がありませんでした：' + status);
    }
  });   
}

</script>
<script src="https://maps.googleapis.com/maps/api/js?key=YOURAPIKEY&callback=initMap" async defer></script>
```

ストロングパラメーターの設定も忘れずに！

```ruby:controllers/posts_controller.rb
def post_params
      params.require(:post).permit(:body,:lat,:lng)
end
```

緯度と経度のtext_fieldにidを追加しのマーカーのドラック後に
valueをJavaScriptで書き換えるコードを追加しました。

場所や地名で検索し緯度と経度をtext_fieldに入力し、保存できるようになりました。
本当に保存できたか確認してみましょう。

```
$ rails c
$ Post.last
```
無事ターミナルにデータが表示されていればオッケーです！
もしここで、latとlngにデータが入っていない(nilなど)場合は、ここまでの実装に不備がありますので、もう一度最初からこの記事をよく読んで実装してみてください。

また、<font color="red"><b>緯度と経度のデータを持たない他のレコードデータがある場合、完成時に地図が表示されない</b></font>のでここでテーブルを綺麗にしておきましょう！

```
$ Post.delete_all
$ exit
```


# すべての投稿内容を一つの地図上で一覧表示させる、マーカーをクリックすると投稿の内容が出る、地図上で詳細ページまでのリンクを表示させる

以上を一気に作っていきます

```erb:posts/index.html.erb
<div id='map'></div>

<style>
#map {
  height: 600px;
  width: 100%;
}
</style>

<!-- js部分 -->
<script>
    function initMap() {

      //初期表示位置
      let latlng = new google.maps.LatLng(38.60, 139.5);
      //デザイン
      let styles = [
           {
            stylers: [
             { "saturation": 0 },
             { "lightness": 0 }
            ]
           }
          ];

      let map = new google.maps.Map(document.getElementById('map'), {
          zoom: 5.5,
          styles: styles,
          center: latlng
      });
      let transitLayer = new google.maps.TransitLayer();
      transitLayer.setMap(map);

//複数マーカー ここから
      <% @posts.each do |post|%>
        ( function() { 
          let markerLatLng = { lat: <%= post.lat %>, lng: <%= post.lng %> }; // 緯度経度のデータ作成
          let marker = new google.maps.Marker({ 
            position: markerLatLng, 
            map: map 
          });
//マーカーをクリックしたとき、詳細情報を表示
          let infowindow = new google.maps.InfoWindow({
            position: markerLatLng,
            content: "<a href='<%= post_url(post.id) %>' target='_blank'><%= post.body %></a>"
          }); //ここで詳細ページへのリンクを表示させる
          marker.addListener('click', function() {
            infowindow.open(map, marker);
          });

       })();
      <% end %>
      //複数マーカー ここまで  
  }
</script>

<script src="https://maps.googleapis.com/maps/api/js?key=YOURAPIKEY&callback=initMap" async defer></script>
```

以上、お疲れ様でした！

「飲食店などの場所を投稿したい」ってよくありますよね。
ぜひこの記事が役に立ったら、幸いです！

# おまけ①練習問題
詳細ページではどのように実装すれば、地図が表示されるでしょうか？

上記を参考にして少し考えてみてください！

## 答え

緯度と経度を取得して地図に反映させればオッケーですね！

```erb:posts/show.html.erb
<div id='map'></div>

<style>
#map {
  height: 600px;
  width: 100%;
}
</style>

<!-- js部分 -->
<script>
//初期マップの設定
let map
let marker
function initMap(){
  geocoder = new google.maps.Geocoder()

  map = new google.maps.Map(document.getElementById('map'), {
    center:  {lat: <%= @post.lat %>, lng: <%= @post.lng %>},
    zoom: 15,
  });

   marker = new google.maps.Marker({
    position:  {lat: <%= @post.lat %>, lng: <%= @post.lng %>},
    map: map
  });
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=YOURAPIKEYI&callback=initMap" async defer></script>
```


# おまけ②turbolinksによって引き起こされるエラー(Rails6)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324819/a6c608a1-d830-39f6-8388-6f2815c7b7a4.png)


リンクで移動した場合、詳細ページにも関わらず、一覧の地図が表示されてしまう場合や地図が表示されない場合があります。
リロードすれば、正常に表示されることもあるのですが。。。。
以下の記述を削除しサーバ―を再起動すればリンクで移動した場合にも、正常に表示されるはずです。

```javascript:app/javascript/packs/application.js
require("turbolinks").start()
```

```ruby:views/layouts/application.html.erb
'data-turbolinks-track': 'reload'
```