Produced by FOURIER

Google Maps API の使い方

IchikawaIchikawa calender 2023.2.20

はじめに

みなさま、Google マップ 使われてますでしょうか?2022 年の調査では 99.4%の人が「使ったことがある」と回答しており、地図アプリではダントツトップの利用率となっております。

Google マップが埋め込まれている Web サイトもよくあります。Google マップからコードを抽出するだけなので、とても簡単に実装できますよね。

今回の記事では Google Maps Api を使って以下の機能を実装していきたいと思います。

  • ピンを移動すると、住所と座標(緯度・経度)を算出する
  • 住所を入力すると、ピンを住所の位置に移動し座標(緯度・経度)を算出する

ちなみに、Laravel9 と JavaScript で実装を進めます。

Google Maps Platform に登録

まずは、Google Maps Platform に登録して Maps API Key を取得しましょう。

Google Cloud プロジェクトを設定する  |  Maps JavaScript API   | Google Developers

https://developers.google.com/maps/documentation/javascript/cloud-setup?hl=ja

この API は 1 ヶ月あたり 28,500 回分(200 ドル)の無料枠があり、その範囲内であれば無料で API を使用できます。(2023 年 2 月現在)

※ ローカル環境で開発するだけでしたら API キーの保護はなくても大丈夫ですが、本番環境で使用する際にはドメインや IP アドレスで制限をして不正利用されないにしましょう

Laravel

Laravel の初期設定は済んでおり、http://localhost にアクセスすると初期画面が表示される状態を想定しています。

また、ビルドツールは Laravel9 で標準装備されている Vite を使用します。

ビュー

/resources/views/welcome.blade.php を以下のよう変更します。

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Laravel</title>

    <!-- Fonts -->
    <link href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">

    <!-- Styles -->
    <style>
        body {
            font-family: 'Nunito', sans-serif;
        }
    </style>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>

<body class="antialiased">
    <div id="map" style="height:500px; width:800px;"></div>

    <form>
        <input type="text" name="address" value="静岡県浜松市早出町234-2" id="address">
        <button type="button" id="button">検索</button>
    </form>

    <ul>
        <li>lat: <span id="lat"></span></li>
        <li>lng: <span id="lng"></span></li>
    </ul>

    <script src="{{ asset('/js/map.js') }}"></script>
    <script src="https://maps.googleapis.com/maps/api/js?language=ja&region=JP&key=AIzaSxxxxxxxxxxxxxxxxxxxxxxxx&callback=initMap" async defer></script>
</body>

</html>

ポイント

地図を描画する領域とサイズを指定しま

<div id="map" style="height:500px; width:800px;"></div>

GoogleMaps の API を読み込みをします。

<script src="https://maps.googleapis.com/maps/api/js?language=ja&region=JP&key=AIzaSxxxxxxxxxxxxxxxxxxxxxxxx&callback=initMap" async defer></script>

パラメータはそれぞれ以下のようになっています。

language=ja:言語設定(日本語)

region=JP:地域設定(日本)

key=AIzaSxxx…:API キー設定(取得した Maps API Key)

callback=initMap:API 読み込み後、initMap() というコールバック関数を実行する

コールバック関数で呼ばれる initMap()を読み込みます。@vite(['resources/js/map.js']) の形式でない理由は後述します。

<script src="{{ asset('/js/map.js') }}"></script>

コールバック関数

続いて/public/js/map.js を作成します。コードは以下のよう設定してください。

const inputAddress = document.getElementById("address");
const button = document.getElementById("button");
button.onclick = initMap;

function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    zoom: 15, // 地図の尺度
    mapTypeId: google.maps.MapTypeId.ROADMAP, // マップタイプ(ROADMAPはデフォルトのもの)
  });

  const geocoder = new google.maps.Geocoder(); // Googlgのサーバーと通信するためのインスタンスを生成
  geocoder.geocode(
    {
      address: inputAddress.value, // フォームに入力された値を渡す
      region: "jp",
    },
    function (result, status) {
      // ↑の検索結果に対しての処理
      if (status == google.maps.GeocoderStatus.OK) {
        const location = result[0].geometry.location;
        const marker = new google.maps.Marker({
          position: location, // 検索結果の緯度・経度を設定
          map: map, // マップの描画設定
          title: location.toString(), // アイコンにカーソルが重なった際に表示されるテキスト
          draggable: true, // trueにすることでアイコンを自由に移動できる
        });

        map.setCenter(location); // マップ中央にアイコンが表示される
        document.getElementById("lat").textContent = location.lat();
        document.getElementById("lng").textContent = location.lng();

        google.maps.event.addListener(
          // アイコンが移動した際に発火するイベントを登録
          marker,
          "dragend",
          function (event) {
            const latLng = event.latLng; // 移動したアイコンの座標を取得
            marker.setTitle(latLng.toString());
            document.getElementById("lat").textContent = latLng.lat();
            document.getElementById("lng").textContent = latLng.lng();

            const geocoder = new google.maps.Geocoder();
            geocoder.geocode(
              // 取得した座標で再描画
              { location: latLng },
              function (result, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                  // 住所を取得してフォームに値を入れる
                  let address = "";
                  const addressComponents = result[0].address_components;
                  for (let i = 0; i < addressComponents.length - 2; i++) {
                    address = addressComponents[i].long_name + address;
                  }
                  inputAddress.value = address;
                }
              }
            );
          }
        );
      } else {
        alert("住所を確認してください"); // フォームに入力された住所から、座標が取得できなかった場合のアラート
      }
    }
  );
}

当初は、/resources/js/map.js のようにファイルを配置して、@vite(['resources/js/map.js']) で読み込む形を考えていましたが、message: 'initMap is not a function’ とエラーが発生するので、/public/js/ に直接配置しています。

どうやら vite でビルドすると参照できなくなるようです…少しハマりました。

ポイント

コードを分割して解説すると、逆にわかりづらくなるかと思いコード上に記載しました。

処理の流れは以下のようになっています。

  1. フォームの値から座標を取得して、マップの初期化と描画

    ※リロード or「検索」ボタンを押す のどちらかで処理が動きます

  2. アイコンを移動するとイベントが発火、移動後のアイコンの位置から座標を取得

  3. 取得した座標でマップを再描画

最後に

とてもシンプルに地図周りの機能を実装できました。さすがは Google。さすがは GAFA。

いろいろとオプションは充実しているので、公式ドキュメントをご参照ください。

Google Maps Platform のドキュメント  |  Maps JavaScript API  |  Google Developers

Google Maps Platform のドキュメント

https://developers.google.com/maps/documentation/javascript?hl=ja

この記事がそれなりに反響あるようでしたら、次は Vue.js とかで実装してみようかと思います。

最後まで読んでいただきありがとうございます。では、また次回の記事で!

新しいメンバーを募集しています

Ichikawa

Ichikawa / Engineer

パン屋から転身してエンジニア3年目。主にPHP/Laravelを使っています。最近ではVue.js/Nuxt.jsと人間に興味あり。