Google Map APIで動的にマップを表示する

# はじめに
Google Mapの組み込み用のiframeをHTMLに埋め込む方法ではなく、`Maps JavaScript API`を使用して動的にサイトに埋め込む方法です。

※使用には、後述の注意事項を確認してください！

### 使用するAPI

- Places API
- Maps Embbed API

# 実装コード
今回はHTMLとJavascriptでのコードをご紹介します。
フォルダ構造は以下のようにします。
```
.
┣ index.html
┗ map.js
```

::: note warn
実際はWordpressで実装したので、データベースを介す仕様ですが、今回は簡潔に説明するためにHTMLとJavascriptのみでご紹介します。
:::


### ①HTMLを用意
まずは必要最小限のHTMLを用意します。

HTMLでは単純に`Maps JavaScript API`の読み込みと入力用の`input`要素、マップ表示用の`div`要素だけを用意しています。
```index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">  
  <title>Generate a map iframe dynamically.</title>
  <!-- Maps JavaScript APIの読み込み -->
  <script>
    (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
      key: "YOUR_API_KEY", // GCPで取得できるAPIキーを設定
    });
  </script>
  <script defer src="map.js"></script>
</head>
<body>
  <input id="place" type="text"> <!-- Places APIの自動補完を使用するためのinput要素を作成 -->
  <div id="map"></div> <!-- 生成したマップのiframeを表示するdiv要素を作成 -->
</body>
</html>
```


### ②Javascriptを用意
Javascript側では、
- 必要なライブラリの読み込み
- 自動補完の設定
- 自動補完のリストから選択された時のイベントリスナーの設定
- 組み込みマップの生成処理

を実装しています。
```map.js
let autocomplete;

async function initMap() {
  // Maps JavaScript APIライブラリのcoreからeventを読み込み
  const { event } = await google.maps.importLibrary("core");
  // Maps JavaScript APIライブラリのplacesからAutompleteを読み込み
  const { Autocomplete } = await google.maps.importLibrary("places");

  // input要素に入力したときに候補リストを表示するように設定
  // 例えば、入力欄で[東京]と入力すると[東京タワー]や[東京ドーム]などが候補のとしてリストに出てくる
  autocomplete = new Autocomplete(document.getElementById("place"));

  // 候補リストから場所を選択したとき(place_changedのトリガー)に動作するようにリスナーを設定
  // これを設定することで場所が変更されることを監視しているような状態になる
  event.addListener(autocomplete, "place_changed", () => {
    // 候補リストから場所を選択すると、getPlace()メソッドで選択した場所の情報が入った
    // オブジェクトが返される
    // そこから、place_idを取得する
    const placeId = autocomplete.getPlace().place_id;

    // Maps Embbed APIのiframeマップを生成する
    const embbedMapNode = document.createElement("iframe");
    embbedMapNode.width = "100%";
    embbedMapNode.height = "200px";
    embbedMapNode.frameborder = "0";
    // place_idを代入したplaceIdをクエリとして以下のように渡すことで、placeIdをもとに
    // 該当のマップ情報が取得できる
    embbedMapNode.src = `https://www.google.com/maps/embed/v1/place?key=YOUR_API_KEY&q=place_id:${placeId}`;
    embbedMapNode.allowfullscreen = true;
    document.getElementById("map").appendChild(embbedMapNode);
  });
}

initMap();
```

`Maps Embbed API`で使用する`src`属性のURLの中に`q`パラメータがあります。
このパラメータに`q=place_id:<取得したplace_id>`としてあげることで、`place_id`をもとにマップを生成してくれます。

# 注意事項
::: note warn
GoogleのAPIを使用するには、APIキーが必要です。また、今回のような実装ではAPIキーはデベロッパーツールなどで確認できてしまうため、しっかりと使用制限を設定するようにしてください。
:::

::: note warn
`Places API`、`Maps Embbed API`は使用量によって課金されるようになっています。こちらも使用制限を書けることができますので、かけられるコストを照らし合わせながら使用してください。
:::

::: note alert
本番環境などで実装する場合は、セキュリティなどをしっかり確認したうえで実装するようにしてください。
:::

# まとめ
今回は、Google Map APIを使って動的にGoogle Mapを生成するようにしてみました。

紹介したのはあくまでも流れを簡単に見えるようにしたもので、実際はWordpressのテーマ内で`place_id`をデータベースに格納して、出力する際にそのデータを埋め込むというような形で実装しています。

これ以外にもたくさんの方法がありますので、ぜひみなさんもカスタマイズしてみてください！