はじめに
みなさま、Google マップ 使われてますでしょうか? 2022 年の調査 では 99.4%の人が「使ったことがある」と回答しており、地図アプリではダントツトップの利用率となっております。
Google マップが埋め込まれている Web サイトもよくあります。Google マップからコードを抽出するだけなので、とても簡単に実装できますよね。
今回の記事では Google Maps Api を使って以下の機能を実装していきたいと思います。
ピンを移動すると、住所と座標(緯度・経度)を算出する
住所を入力すると、ピンを住所の位置に移動し座標(緯度・経度)を算出する
ちなみに、Laravel9 と JavaScript で実装を進めます。
Google Maps Platform に登録
まずは、Google Maps Platformに登録してMaps API Key を取得しましょう。
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®ion=JP&key=AIzaSxxxxxxxxxxxxxxxxxxxxxxxx&callback=initMap" async defer></script>
</body>

</html>
/resources/views/welcome.blade.php
ポイント
地図を描画する領域とサイズを指定します。
<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("住所を確認してください"); // フォームに入力された住所から、座標が取得できなかった場合のアラート
 }
 }
 );
}
/public/js/map.js
当初は、 /resources/js/map.js
のようにファイルを配置して、 @vite(['resources/js/map.js'])
で読み込む形を考えていましたが、 message: 'initMap is not a function’
とエラーが発生するので、 /public/js/
に直接配置しています。
どうやら vite でビルドすると参照できなくなるようです…少しハマりました。
ポイント
コードを分割して解説すると、逆にわかりづらくなるかと思いコード上に記載しました。
処理の流れは以下のようになっています。
フォームの値から座標を取得して、マップの初期化と描画
※リロード or「検索」ボタンを押す のどちらかで処理が動きます
アイコンを移動するとイベントが発火、移動後のアイコンの位置から座標を取得
取得した座標でマップを再描画
最後に
とてもシンプルに地図周りの機能を実装できました。さすがはGoogle。さすがはGAFA。
いろいろとオプションは充実しているので、公式ドキュメントをご参照ください。
https://developers.google.com/maps/documentation/javascript?hl=ja
この記事がそれなりに反響あるようでしたら、次はVue.jsとかで実装してみようかと思います。
最後まで読んでいただきありがとうございます。では、また次回の記事で!