 
        はじめに
みなさま、Google マップ 使われてますでしょうか? 2022 年の調査 では 99.4%の人が「使ったことがある」と回答しており、地図アプリではダントツトップの利用率となっております。
Google マップが埋め込まれている Web サイトもよくあります。Google マップからコードを抽出するだけなので、とても簡単に実装できますよね。
今回の記事では Google Maps Api を使って以下の機能を実装していきたいと思います。
- ピンを移動すると、住所と座標(緯度・経度)を算出する
- 住所を入力すると、ピンを住所の位置に移動し座標(緯度・経度)を算出する
ちなみに、Laravel9 と JavaScript で実装を進めます。
Google Maps Platform に登録
まずは、Google Maps Platformに登録してMaps API Key を取得しましょう。
この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®ion=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®ion=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 でビルドすると参照できなくなるようです…少しハマりました。
ポイント
コードを分割して解説すると、逆にわかりづらくなるかと思いコード上に記載しました。
処理の流れは以下のようになっています。
- 
    フォームの値から座標を取得して、マップの初期化と描画
    ※リロード or「検索」ボタンを押す のどちらかで処理が動きます 
- アイコンを移動するとイベントが発火、移動後のアイコンの位置から座標を取得
- 取得した座標でマップを再描画
最後に
とてもシンプルに地図周りの機能を実装できました。さすがはGoogle。さすがはGAFA。
いろいろとオプションは充実しているので、公式ドキュメントをご参照ください。
この記事がそれなりに反響あるようでしたら、次はVue.jsとかで実装してみようかと思います。
最後まで読んでいただきありがとうございます。では、また次回の記事で!
 
     
     
         
         
    