[初心者向け]GoogleMapJavascript APIを用いたマップと検索ボックスの表示方法


googlemapAPIを使ってアプリ上にマップを追加する方法です。

基本的には[公式ドキュメント][1]~~をコピペ~~に沿って記述すれば実現できますが、Javascript初心者の私は解読するのにやたらと時間がかかったので同じ様なことを始める方のために共有しておきます。

[1]:https://developers.google.com/maps/documentation/javascript/examples/places-searchbox?hl=ja#maps_places_searchbox-javascript

#出来ること
･マップの表示
･検索ボックスの表示
･マップ、検索ボックスの装飾
![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634043/3b49630a-8bd0-463c-0390-82d52a9a6eb8.gif)

少し手を加えれば以下のように
装飾することもできます。
![ezgif.com-gif-maker (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634043/8b11f7c2-433f-a7ec-90bd-d4a956f84188.gif)


#手順
1.マップを配置したいhtmlファイルにidを記述
2.javascriptファイルにWebAPIに関する記述をする
3.CSSをいじってマップを装飾する

これだけ。


#手順1:マップを配置したいhtmlファイルにidを記述
以下[公式ドキュメント][1]からの引用です。
ポイントはAPI呼び出し用scriptに libraries=places を記述すること。
これにより検索&検索位置へ画面を切り替えるライブラリを読み込めます。

```html:sample.html
<!DOCTYPE html>
<html>
  <head>
    <title>Places Search Box</title>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    #｢key=YOUR_API｣の部分に取得したAPIキーを記述
    #今回は検索機能を追加するのでPlacesライブラリを使う為｢libraries=places｣を追記
    #｢v=weekly｣はマップ自体の更新頻度を表しています。何も記述しなければ四半期に一度の更新となり、この記述だと毎週更新で最新状態を保てます。なので別に記述しなくても大丈夫です。
    <script
      src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initAutocomplete&libraries=places&v=weekly"
      defer
    ></script>
    <link rel="stylesheet" type="text/css" href="./style.css" />
    <script src="./index.js"></script>
  </head>
  <body>
  #ここで指定したidにjavascriptでマップを表示させます。
    <input
      id="pac-input"
      class="controls"
      type="text"
      placeholder="Search Box"
    />
    <div id="map"></div>
  </body>
</html>
```

この状態でブラウザを開くとただの検索ボックスだけが存在すると思います。
次へ行きましょう。


#手順2:javascriptファイルにWebAPIに関する記述をする
以下も[公式ドキュメント][1]からの引用ですが、記述の出どころがどこなのか一つ一つ探していたら時間がかかりました。。

初心者向けに簡単にjsの記述について超簡単に説明しておくと
const ~~ はそこで｢~~｣という定数を定義しています。
function ~~ は関数です。
google.maps はマップそのものを指定するオブジェクトです。


```javascript:index.js
function initAutocomplete() {
  //マップの初期設定です。
  const map = new google.maps.Map(document.getElementById("map"), {
    center: { lat: -33.8688, lng: 151.2195 },
    zoom: 13,
    mapTypeId: "roadmap",
  });
  const input = document.getElementById("pac-input");
  const searchBox = new google.maps.places.SearchBox(input);
 ////"SearchBoxクラス"はPlacesライブラリのメソッド。引数はinput(ドキュメント上ではinputFieldとある)。
 ////[https://developers.google.com/maps/documentation/javascript/reference/places-widget#SearchBox]
  
  map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
  ////"ControlPosition"クラスはコントローラーの位置を定める。
  ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/ControlPosition/
  ////https://developers.google.com/maps/documentation/javascript/examples/control-positioning
  
  map.addListener("bounds_changed", () => {
    searchBox.setBounds(map.getBounds());
  });
  ////"bound_changed"イベントは(見えてる範囲の地図･ビューポートに変化があったときに発火)
  ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Map/bounds_changed/ 
  ////"getBounds"メソッドはビューポートの境界を取得。Mapクラスのメソッド。
  ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Map/getBounds/
  
  let markers = [];
  searchBox.addListener("places_changed", () => {
  ////"place_chaged"イベントはAutoCompleteクラスのイベント.
  ////https://developers.google.com/maps/documentation/javascript/reference/places-widget#Autocomplete.place_changed
  
    const places = searchBox.getPlaces();
    ////"getPlaces"メソッドはクエリ(検索キーワード)を配列(PlaceResult)で返す。
    ////https://developers.google.com/maps/documentation/javascript/reference/places-widget#Autocomplete.place_changed

    if (places.length == 0) {
      return;
    }
    // Clear out the old markers.
    markers.forEach((marker) => {
      //"forEach"メソッドは引数にある関数へ、Mapオブジェクトのキー/値を順に代入･関数の実行をする。
        //Mapオブジェクト:
          //https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Map
      marker.setMap(null);
      ////setMapメソッドはMarker(Polyline,Circleなど)クラスのメソッド。Markerを指定した位置に配置する。引数nullにすると地図から取り除く。
    });
    markers = [];
    // For each place, get the icon, name and location.
    const bounds = new google.maps.LatLngBounds();
    ////"LatLngBounds"クラスは境界を作るインスンタンスを作成。引数は左下、右上の座標。
    ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/LatLngBounds/#:~:text=LatLngBounds%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AF%E5%A2%83%E7%95%8C(Bounding,%E4%BD%9C%E3%82%8B%E3%81%93%E3%81%A8%E3%82%82%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82
    places.forEach((place) => {
      if (!place.geometry) {
        ////"geometry"はplaceライブラリのメソッド。
        
        console.log("Returned place contains no geometry");
        return;
      }
      const icon = {
        url: place.icon,
        ////"icon"はアイコンを表すオブジェクト。マーカーをオリジナル画像にしたいときなど。
        ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Icon/
        size: new google.maps.Size(71, 71),
        origin: new google.maps.Point(0, 0),
        anchor: new google.maps.Point(17, 34),
        ////"Point"クラスはマーカーのラベルなどの位置を決めるインスタンスメソッド。
        ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Point/
        
        scaledSize: new google.maps.Size(25, 25),
      };
      // Create a marker for each place.
      markers.push(
        new google.maps.Marker({
          map,
          icon,
          title: place.name,
          position: place.geometry.location,
        })
      );

      if (place.geometry.viewport) {
        ////viewport"メソッド
        // Only geocodes have viewport.
        bounds.union(place.geometry.viewport);
        ////"union"メソッドはLatLngBoundsクラスのメソッド。自身の境界に指定した境界を取り込んで合成する。
        ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/LatLngBounds/union/
      } else {
        bounds.extend(place.geometry.location);
        ////"extend"メソッドはLatLngBoundsクラスのメソッド。自身の境界に新しく位置座標を追加する。
        ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/LatLngBounds/extend/
      }
    });
    map.fitBounds(bounds);
    ////"fitBounds"メソッドはmapクラスのメソッド。指定した境界を見えやすい位置にビューポートを変更する。
    ////https://lab.syncer.jp/Web/API/Google_Maps/JavaScript/Map/fitBounds/#:~:text=Map.fitBounds()%E3%81%AFMap,%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%A6%E3%81%8F%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82

  });
}
```

```css:style.css
#map {
  height: 100%;
}

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

#description {
  font-family: Roboto;
  font-size: 15px;
  font-weight: 300;
}

#infowindow-content .title {
  font-weight: bold;
}

#infowindow-content {
  display: none;
}

#map #infowindow-content {
  display: inline;
}

.pac-card {
  margin: 10px 10px 0 0;
  border-radius: 2px 0 0 2px;
  box-sizing: border-box;
  -moz-box-sizing: border-box;
  outline: none;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  background-color: #fff;
  font-family: Roboto;
}

#pac-container {
  padding-bottom: 12px;
  margin-right: 12px;
}

.pac-controls {
  display: inline-block;
  padding: 5px 11px;
}

.pac-controls label {
  font-family: Roboto;
  font-size: 13px;
  font-weight: 300;
}

#pac-input {
  background-color: #fff;
  font-family: Roboto;
  font-size: 15px;
  font-weight: 300;
  margin-left: 12px;
  padding: 0 11px 0 13px;
  text-overflow: ellipsis;
  width: 400px;
}

#pac-input:focus {
  border-color: #4d90fe;
}

#title {
  color: #fff;
  background-color: #4d90fe;
  font-size: 25px;
  font-weight: 500;
  padding: 6px 12px;
}

#target {
  width: 345px;
}

```

これでマップと検索ボックスが表示されたと思います。

cssは自由にいじって自分好みにしてみましょう。

尚、同名の定数や関数を別に記述していると上手く動かないので
エラーの際にはチェックしてみてください。

#手順3:CSSをいじってマップを装飾する
手順2のままだと検索ボックスが無粋な位置にいるので、ボックスの装飾を行います。


jsファイルの

```
  map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
```

で検索ボックスの位置をTOP_LEFTに配置してしまっています。

javascriptはcss読み込み後に読み込ませるので、
cssの記述を上書きする形で指定されてしまいます。

この一行を削除して、後は好きなようにCSSでinputタグのidやクラスに対して装飾しましょう。
###snazzyでマップのstyleを遊ぶ
この[snazzy][4]というサイトでは自分の好きなように、直感的編集が可能です。
ここで選んだor編集したstyleはJSONデータをコピーできるので、そのままJSに貼り付ければ思いのままstyleを編集できます。

snazzyで遊んでるだけでも楽しいので一度見てみてくださいな。
[4]:https://snazzymaps.com/editor/customize/21

因みに記事冒頭で貼り付けた紫色のマップstyleは以下になります。

```javascript:input.js
const map = new google.maps.Map(document.getElementById("map"), opts);
var opts = {
    zoom: 15,
    center: { lat: -33.8688, lng: 151.2195 },
    styles: [
      //全てのラベルを非表示
      {
        featureType: 'all',
        elementType: 'labels',
        stylers: [
          {visibility: 'off'},
        ],
      },
      {
        featureType: 'transit',
        elementType: 'labels',
        stylers: [
          {visibility: 'on'},
        ],
      },
      //poi=観光スポットや施設など」のアイコンのみ再表示
      {
        featureType: 'poi',
        elementType: 'labels.icon',
        stylers: [
          {visibility: 'inherit'},
        ],
      },
      //地図全体の色味をカスタマイズ
      //基本色を赤に統一 + 彩度を落とす
      {
        featureType: 'all',
        elementType: 'all',
        stylers: [
          {hue: '#5f0285'},
          {saturation : -50},
        ],
      }
    ]
```

以上です。
上手く行かないときは公式ドキュメントに沿って記述してみてください。