Google mapを使って住所検索できるようにした

Google map apiを使って住所を検索して表示できるようにした
## Google map apiの利用
こちらの記事を参考にしました。
[Google Maps API を使ってみた](https://qiita.com/Haruka-Ogawa/items/997401a2edcd20e61037)
["Maps JavaScript API”を利用してGoogle Mapをページに埋め込む方法](https://web-saku.net/googlemap-mapsjavascriptapi/#apiloads-limitation)

- googleアカウントを作成
- Google Cloud Pratformにアクセスし、プロジェクトを作成
- Map JavaScript APIを有効化し、APIキーを取得

###　試してみる
```html:html
<!DOCTYPE html>
<html lang="ja">

<head>
<meta charset="UTF-8">
<title>Sample_GoogleMap</title>

<script src="http://maps.google.com/maps/api/js?key=YOUR_KEY&language=ja"></script>

<style>
html { height: 100% }
body { height: 100% }
#map { height: 100%; width: 100%}
</style>
</head>

<body>
<div id="map"></div>

<script>
var MyLatLng = new google.maps.LatLng(35.6811673, 139.7670516);
var Options = {
 zoom: 15,      //地図の縮尺値
 center: MyLatLng,    //地図の中心座標
 mapTypeId: 'roadmap'   //地図の種類
};
var map = new google.maps.Map(document.getElementById('map'), Options);
</script>

</body>
</html>
```

## API_Keyを環境変数にする
```ruby:Gemfile
gem 'dotenv-rails'
```
```ruby:ターミナル
bundle install
```
``.envファイル``を手動で作成する

```ruby:.env
GoogleMap_API_Key = YOUR_Key
```
```ruby:.gitignore
/.env
```

## showページに合わせて動的に地図を変更する
- Geocording APIを有効化する

### View
```erb:institution_map.html.erb
〜略〜
    <div class="institution_show_address_container">
      <div class="institution_show_address_label show_label">住所</div>
      <%#- 表示元の住所 -%>
      <div class="institution_show_address_text show_text", id="institution_mapping_address"><%= institution.address %></div>
    </div>
〜略〜

<div class="map_container">
  <%# マップを表示する場所 %>
  <div id='institution_map'></div>
</div>

<script>
let map
let geocoder

<%# ページ呼出し時の処理 %>
function initMap(){
  geocoder = new google.maps.Geocoder()
  <%# 表示住所を取得 %>
  let putAddress = document.getElementById('institution_mapping_address').textContent;
  geocoder.geocode( { 'address': putAddress}, function(results, status) {
    if (status == 'OK') {
      <%# 表示場所にマップを表示 %>
      map = new google.maps.Map(document.getElementById('institution_map'), {
      zoom: 17,
      });
      map.setCenter(results[0].geometry.location);
      <%# マップにマーカーを表示 %>
      var marker = new google.maps.Marker({
          map: map,
          position: results[0].geometry.location
      });
    } else {
      alert('該当する結果がありませんでした：' + status);
    }
  });
}

</script>
<%# ページを表示するときにinitMapを動作させる %>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GoogleMap_API_KEY'] %>&callback=initMap" async defer></script>
```
## 住所を検索して地図を表示できるようにする
```erb:search_address.html.erb
<div class="search_map_container">
  <h2>医療機関を検索</h2>
  <%# 検索したい住所や場所を入力するテキストボックス %>
  <input id="search_address" type="textbox" value="GeekSalon">
  <%# 処理を行うボタン %>
  <input id="search_address_button", type="button" value="検索" onclick="codeAddress()">
  <div>
    <%# 住所を表示する %>
    <div id="result_address">住所が表示されます</div>
  </div>
  <%# 地図を表示する %>
  <div id='search_map'></div>
</div>

<script>
let map
let geocoder

<%# 初期化処理 %>
function initMap(){
  geocoder = new google.maps.Geocoder()
  
  <%# 参考にする住所がないので初期設定を行う。 lat:緯度、lng:経度。サンプルは東京駅 %>
  map = new google.maps.Map(document.getElementById('search_map'), {
    center: {lat: 35.6809591, lng: 139.7673068},
    zoom: 15,
  });
  <%# マーカーを配置 %>
  marker = new google.maps.Marker({
    position:  {lat: 35.6809591, lng: 139.7673068},
    map: map
  });
}
<%# ボタンを押した時の処理 %>
function codeAddress(){
  <%# 検索したいアドレスを取得 %>
  let inputAddress = document.getElementById('search_address').value;
  
  geocoder.geocode( { 'address': inputAddress}, function(results, status) {
    if (status == 'OK') {
      <%# マップを表示
      map.setCenter(results[0].geometry.location);
      <%# 検索で得られた住所を取得 %>
      result_address = results[0].formatted_address;
      <%# 取得した住所を表示 %>
      document.getElementById('result_address').textContent = result_address
      <%# マーカーを表示 %>
      var marker = new google.maps.Marker({
          map: map,
          position: results[0].geometry.location
      });
    } else {
      alert('該当する結果がありませんでした：' + status);
    }
  });
}
</script>
<%# 初期設定処理 %>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GoogleMap_API_KEY'] %>&callback=initMap" async defer></script>
```
## 周辺の施設を表示する
```erb:place_map.html.erb
<%# 初期住所 %>
<div class="place_medicalstaff_address"></div>
<%# 地図表示部分 %>
<div class="place_map_container">
  <h2>周辺の医療機関検索</h2>
  <div class="place_map_search_area">
    <%# 検索したい住所を入力 %>
    <input type="text" size="55" id="place_search">
    <%# 処理するボタン %>
    <input type="button" size="55" value="検索" id="place_search_button" onClick="SearchGo()">
  </div>
  <div id="place_map style="width: ３００px; height: ３００px;">"></div>
</div>

<%# 先にapiへアクセスしないと、googleメソッドがエラーになる %>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV['GoogleMap_API_KEY'] %>&libraries=places"></script>


<script>
var myMap;
var service;

<%# 初期処理 %>
function initialize() {
  geocoder = new google.maps.Geocoder()
<%# 初期住所の場所を取得する %>
  let defaultAddress = document.getElementById('place_medicalstaff_address').textContent;
  geocoder.geocode( { 'address': defaultAddress}, function(results, status) {
    if (status == 'OK') {
<%# 取得した住所情報からmapを表示　%>
      let lat = results[0].geometry.location.lat();
      let lng = results[0].geometry.location.lng();
      var initPos = new google.maps.LatLng(lat, lng);
<%# オプション（MapOptionクラス)で地図のプロパティを設定（倍率、マーカー表示位置、地図の種類) %>
      var myOptions = {
          zoom: 15,
          center: initPos,
      };
      myMap = new google.maps.Map(document.getElementById("place_map"), myOptions);
<%# 検索の条件を指定（緯度経度、半径、検索の分類） %>
      var request = {
      location: initPos,
      radius: 1000,             <%# ※１ 表示する半径領域を設定(1 = 1M) %>
      types: ['hospital']       <%# ※２ typesプロパティの施設タイプを設定 %>
      };
<%# placeAPIを使用して周辺情報を取得する %>
      var service = new google.maps.places.PlacesService(myMap);
      service.search(request, Result_Places);
    } else {
      alert('該当する結果がありませんでした：' + status);
    }
  });
}

<%# 検索結果を受け取る %>
function Result_Places(results, status){
<%# Placesが検索に成功したかとマうかをチェック %>
    if(status == google.maps.places.PlacesServiceStatus.OK) {
<%# 検索結果の数だけ反復処理を変数placeに格納 %>
        for (var i = 0; i < results.length; i++) {
            var place = results[i];
<%# 定義したcreateMarker関数で、取得したplaceにマーカーを表示 %>
            createMarker({
              text : place.name,
              position : place.geometry.location
            });
        }
    }
}

<%# 検索フォームから検索したとき %>
function SearchGo() {
  geocoder = new google.maps.Geocoder()
  <%# 検索したい住所を受け取る %>
  let defaultAddress = document.getElementById('place_search').value;
  geocoder.geocode( { 'address': defaultAddress}, function(results, status) {
    if (status == 'OK') {
      let lat = results[0].geometry.location.lat();
      let lng = results[0].geometry.location.lng();
      var initPos = new google.maps.LatLng(lat, lng);
      var myOptions = {
          center : initPos,
          zoom: 15
      };
      myMap = new google.maps.Map(document.getElementById("place_map"), myOptions);
      var request = {
        location: initPos,
        radius: 1000,
        types: ['hospital']
      };
      var service = new google.maps.places.PlacesService(myMap);
      service.search(request, Result_Places);
    } else {
      alert('該当する結果がありませんでした：' + status);
    }
  });
}

<%# 検索フォームで検索した場合の、map検索結果を受け取る %>
function result_search(results, status) {
    var bounds = new google.maps.LatLngBounds();
    for(var i = 0; i < results.length; i++){
        createMarker({
          position : results[i].geometry.location,
          text : results[i].name,
          map : myMap
        });
        bounds.extend(results[i].geometry.location);
    }
<%# Map.fitBounds()で、マーカーの場所を全て表示するように地図のビューポート(位置座標とズーム値)を変更 %>
    myMap.fitBounds(bounds);
}

<%# 該当する位置にマーカーを表示 %>
function createMarker(options) {
<%# マップ情報を保持しているmyMapオブジェクトを指定 %>
    options.map = myMap;
<%# Markcrクラスのオブジェクトmarkerを作成 %>
    var marker = new google.maps.Marker(options);
<%# 各施設の吹き出し(情報ウインドウ)に表示させる処理 %>
    var infoWnd = new google.maps.InfoWindow();
    infoWnd.setContent(options.text);
<%# addListenerメソッドを使ってイベントリスナーを登録 %>
    google.maps.event.addListener(marker, 'click', function(){
        infoWnd.open(myMap, marker);
    });
    return marker;
}

<%# ページ読み込み完了後、Googleマップを表示 %>
google.maps.event.addDomListener(window, 'load', initialize);
</script>
```
## Turbolinks
viewに``script``を記述する場合は、Turbolinksを切る方がいいらしい。

```erb:views/layouts/application.html.erb
<%= link_to "home", root_path, "data-turbolinks": false %>
```

全部切る場合は、``app/javascript/application.js``、``Gemfile``などから、Turbolinksを削除すればOK（非推奨)

## 参考記事
[Google Maps API の使い方・利用方法](https://www.webdesignleaves.com/pr/plugins/googlemap_01.html)
[【Google MAP】名称から場所を検索・特定する](https://qiita.com/yoshi_yast/items/521c1f36306a180f45dd)
[Google Maps APIで住所から緯度経度を求める](https://leben.mobi/blog/google_maps_api_jusho_idokeido/javascript/)
[【Rails6 / Google Map API】初学者向け！Ruby on Railsで簡単にGoogle Map APIの導入する](https://qiita.com/nagase_toya/items/e49977efb686ed05eadb)
[GoogleMapsAPIでマップで施設検索してやんよ!!!](https://tokidoki-web.com/2015/11/googlemapsapi%E3%81%A7%E3%83%9E%E3%83%83%E3%83%97%E3%81%A7%E6%96%BD%E8%A8%AD%E6%A4%9C%E7%B4%A2%E3%81%97%E3%81%A6%E3%82%84%E3%82%93%E3%82%88/)
[Google API 第8回](https://vintage.ne.jp/blog/2015/04/395)
[SYNCER (Google Maps API)](https://lab.syncer.jp/Web/API/Google_Maps/)
[Rails Turbolinksをページ毎に無効化する方法](https://qiita.com/keitah/items/05f02efe4e11ab2473e8)