Google マップは、最新のロードマップ、企業や店舗などのビジネスリスト、ルート検索、ストリートビューなど、さまざまな情報を提供してくれます。
Google マップには、MapboxやOpen Street Mapといった有名な代替サービスがあります。しかし、どの競合サービスもGoogle マップが持つビジネスリストの完全性などを理由に、Google マップには追いつけません。グーグルは、検索サービスとのクロスオーバーのおかげで、企業の最新の詳細を含む完全で広範な地図を提供しています。
Google マップには、シンプルなGoogle Static Maps APIから、この記事のテーマでもあるパワフルなMaps JavaScript APIまで、マップと連動して活用するための多様なAPIがあります。
もちろん、このAPIサービスを活用しなくても自分のWebサイトにGoogle マップを表示できます。APIサービスを使わないことで開発が簡単になるのはもちろん、たくさんの便利な機能を使えます。しかし、JavaScript APIを使えば、パフォーマンス向上やカスタマイズといった点でGoogle マップをより完全にコントロールできます。
Google Maps JavaScript APIを活用する
この記事では、Google Maps JavaScript APIを最大限に活用する方法、つまり適切に使う方法を紹介します。
すでにGoogle Maps JavaScript APIに関しては多くのチュートリアルや例があちらこちらに公開されていますが、ほとんどの場合、望ましい結果を得るためのベストな方法にはフォーカスされていません。簡単な説明に終始していて、なぜある特定の手順で作ったのかという理由に関して熟考されたアプローチや説明が欠けています。
この記事の完全なソースコードは、GitHubで公開されています。
ベーシックなmapキャンバスを作る
最初にしなければならないのが、地図アプリケーションを構築するためのシンプルなフロントエンドフレームワークの設定です。
HTMLを作る
次のようなマークアップでHTMLファイルを作ります。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Google Maps API Template</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="map"></div>
<script src="script.js" defer></script>
</body>
</html>
完全な地図アプリケーションを構築するための盤石なプラットフォームができました。
スクリプト内でのdefer属性の使い方に注目してください。指定されたスクリプトをできるだけ早くダウンロードするようにブラウザーに指示するのですが、HTMLの構文解析が終了するまでスクリプトの実行を待つように指示しています。この方法が使えるケースなら、deferの活用は必ず重要になります。なぜなら、ページのレンダリングが終了する前にJavaScriptコードが実行されて、レンダリングが停止してしまうのを阻止してくれるからです。
ページのレンダリングが停止してしまうのは、ユーザーにとって散々な体験となります。鋭い読者は、ここまででGoogle マップのJavaScriptについて語られていない点に気付いているかもしれません。それは意図的に触れなかっただけですので、あとで説明します!
on-readyコールバックの構築
それでは、最初のJavaScriptファイルであるscript.jsを設定します。document.addEventListener('DOMContentLoaded', function () {})のようなコールバックから始めます。
document.addEventListener('DOMContentLoaded', function () {
if (document.querySelectorAll('#map').length > 0)
{
if (document.querySelector('html').lang)
lang = document.querySelector('html').lang;
else
lang = 'en';
var js_file = document.createElement('script');
js_file.type = 'text/javascript';
js_file.src = 'https://maps.googleapis.com/maps/api/js?callback=initMap&signed_in=true&language=' + lang;
document.getElementsByTagName('head')[0].appendChild(js_file);
}
});
#mapの要素数のチェックは、現在のページに特定の要素があるかどうかを確かめるための迅速な方法です。同じ外部JavaScriptファイルがWebサイト全体にわたって使われているケースが多く、ほとんどは特定ページでいくつかのコードが必要になるだけです。具体的には、ユーザーにmapキャンバスを見せたいときのみ、このコードの実行が必要になります。同じコードの使用を必要最小限に抑えれば、コードを必要としないページでの無駄なコードによる肥大化を防げます(パフォーマンスは大事ですからね!)。
mapキャンバスがページにあることが確認できれば、すぐに次の作業に進めると考えてしまうかもしれませんが、その前にもう1つチェックしておくべき点があります。Google Maps JavaScript APIが使われるページの言語が何であれ、デフォルトは英語でロードされます。この問題に対処する最適な手段は、lang属性をベースにしたlang変数の<html>要素内での設定です。設定しておけば、Google Maps JavaScript APIを各ユーザーにとって最適な言語に変えられます。この方法は特に、すべての言語にわたって同じscript.jsファイルを使うような、多言語(i18)Webサイトに役立ちます。
グーグルのJavaScriptファイルを入手する
ここまで来たら、外部のGoogle Maps JavaScriptファイルを読み込めます。Google Maps JavaScriptファイルを、この段階までHTMLの中の普通の<script>タグに入れなかった理由は、mapキャンバスを持っていないページ内での無用なコードの肥大化を防ぎたかったからです。ファイル内に読み込むためには、新しい<script>要素を作り、これをDOMの<head>に注入します。Google APIがコールバックに対処してくれるので、これ以上の特別な作業の必要はありません。
var js_file = document.createElement('script');
js_file.type = 'text/javascript';
js_file.src = 'https://maps.googleapis.com/maps/api/js?callback=initMap&signed_in=true&key=YOUR_API_KEY&language=' + lang;
document.getElementsByTagName('head')[0].appendChild(js_file);
APIに渡されるクエリー文字列パラメーターの中には、説明しておきたい興味深いものがいくつかあります。
1つ目は、地図で使いたい言語をGoogleに指示するlanguageパラメーターとして、lang変数を渡す点です。
2つ目は、signed_inパラメーターをtrueとして返す点です。trueとするのはマップをさらにパーソナライズ化するためです(例:目印をつけた場所をほかの場所よりも目立つようにする)。
次に、keyパラメーターをAPIキーとともに渡します(これはあとで詳しく触れます)。
最後に、これがもっとも重要ですが、callbackパラメーターを使ってコールバック関数を指定します。ファイルのフェッチが成功したあと、どの関数を起動すればよいのかをGoogleに指示できます。
Google Maps JavaScript APIは有効なkeyパラメーターなしでも十分動作しますが、JavaScriptエラーのコンソール警告メッセージが表示されてしまいます(編注:2016年6月22日からAPIキーの取得が必須になりました)。Google’s guideを参照して必ず無料のAPIキーを入手してください。
mapキャンバスの初期化
初期化関数コールの設定が済んだので、コールバック関数のinitMap()を定義します。Google Maps JavaScript APIの読み込みが成功すれば、すぐに起動される関数です。
var map;
function initMap() {
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
}
新たなGoogle マップを作るときには、グローバルスコープで(すべての関数の外で)マップの変数を作るのが最適な方法です。initMap()の外にmapを定義する理由は、あとからのマップとのやりとりがより簡単になるからです。
ここまでで、作業用の空のmap変数ができました。作業を進めてGoogle マップオブジェクトをinitMap関数の中にあるmap変数に割り当てます。これは、まさにGoogle’s Simple Map exampleにある通りです。重要な注意点はcenterとzoomの両方を指定しなければならないことです。そうしないとマップはまったく初期化されません。これにはデフォルトがないのです!
CSSも忘れずに
最後の重要なステップは、土台となる基礎的なmapキャンバスを機能させるために、style.cssファイル内になんらかのCSSを追加する作業です。
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
}
ここで挙げておきたいもっともありがちな失敗は、#canvas要素でheight属性の設定を忘れることです。ここまでで説明したすべてのテクニックをミスなく活用したとしても、height(またはmin-height)のCSS属性が設定されていなかったら、マップ自体がまったく表示されません。<html>や<body>へのスタイルの適用でできるのは、単にキャンバスのフルスクリーン表示だけです。
mapキャンバスにマーカーを加える
ベーシックなmapキャンバスが表示されたあとのもっとも一般的な作業は、マップへのマーカーのプロットです。ほとんどのプログラミングコンテキストで毎回伝えているアドバイスの1つは、できるだけロジックとデータを分離するという点です。今回のケースで言えば、マーカーの詳細を別々のJSONファイルへ格納している点です。
マーカー用のJSONファイルを作る
次のような、markers.jsonと名付けたJSONファイルを作ってください。
[
{
"lat": 53.817680,
"lng": -1.537657
},
{
"lat": 53.790123,
"lng": -1.53243
},
{
"lat": 53.756745,
"lng": -1.5309087
},
{
"lat": 53.6474675,
"lng": -1.49564554
},
{
"lat": 53.69123456,
"lng": -1.6545466
}
]
Google マップのマーカーを、住所情報ではなく緯度と経度を使って保存するいう方法はどんな場合にも通用する良いアイデアです。そうしないと、実行時にGeocoding serviceを使わなければなりません。Geocoding serviceは厳密な使用制限がある独立したAPIです。またマップの初期化プロセスの際、ほかの外部エンティティーを加える必要があり、しかも遅延の増大を伴います。
これでJSONファイルの設定が終わりましたので、マップの初期化にあたって実際に使ってみる必要があります。
マーカーをプロットする
マップが初期化されたあとは、markers.jsonファイルを手に入れるためにinitMap()関数の修正から始めます。このときthe shiny new Fetch APIを使い、コールバックを新たな関数であるplotMarkers()に渡します。ここではレスポンスがコールバック関数に渡される前に、確実にJSONに解析されるようにPromiseを使います。
fetch('markers.json')
.then(function(response){return response.json()})
.then(plotMarkers);
次のようにplotMarkers()を定義します。
var markers;
var bounds;
function plotMarkers(m)
{
markers = [];
bounds = new google.maps.LatLngBounds();
m.forEach(function (marker) {
var position = new google.maps.LatLng(marker.lat, marker.lng);
markers.push(
new google.maps.Marker({
position: position,
map: map,
animation: google.maps.Animation.DROP
})
);
bounds.extend(position);
});
map.fitBounds(bounds);
}
map変数がありますので、plotMarkers()関数以外にいくつかの変数をグローバルスコープで定義します。この関数では、markersとboundsを使って作業します。両方ともあとで役に立ちますので、この2つをグローバルスコープで作っておくと良いでしょう。
plotMarkers関数の内部で最初にしなければならないのは、空の配列へのmarkers変数のセットと、空のGoogle LatLngBoundsオブジェクトへのbounds変数のセットです。LatLngBoundsオブジェクトは、全部のマーカーが表示されるキャンバスのエリアを常に把握しておくために使います。
次に、JSONファイルにあるlatとlngのパラメーターから新たにGoogleポジションオブジェクトを作る匿名のコールバック関数のために、それぞれのマーカーを反復する必要があります。匿名のコールバック関数さえあれば、新たなGoogle マップマーカーオブジェクトを作って、それをmarkers配列に入れられるようになります。最後は、次のように新たなポジションを使って境界を拡張します。
var position = new google.maps.LatLng(this.lat, this.lng);
markers.push(
new google.maps.Marker({
position: position,
map: map,
animation: google.maps.Animation.DROP
})
);
bounds.extend(position);
留意点:新たなGoogle マップマーカーオブジェクトを作るときに必要となるパラメーターは、positionとmapの2つだけです。positionパラメーターは、latとlngパラメーターを含んだオブジェクト、Googleポジションオブジェクト(ここで見せたもの)のどちらにもなれます。mapパラメーターは単にマーカーをプロットするためのGoogle マップオブジェクトです。またanimationパラメーターも使えます。これは必須ではありませんが、実装が簡単ですし、みんな小さなアニメーションが大好きですよね!
mapキャンバスを中央に戻す
すべてのマーカーの反復が終わり、マップに加え、キャンバス境界を作ったので、マーカーがプロットされたmapキャンバスを中央に戻すためにmap.fitBounds()をコールし、centerとzoomパラメーターをマップオブジェクト初期化の際に設定した値で上書きします。これはとても役に立ちます。なぜならcenterやzoomのパラメーターについて心配なくmarkers.jsonファイルを更新できるからです。
最後に
フルスクリーンのGoogle マップのキャンバスを表示して、マップ上にJSONファイルから得たマーカーをプロットできるアプリケーションが完成しました。
もっと深掘りしたい人にとって次のステップとして適当なのは、マーカーがクリックされたときに現れる情報ウィンドウの実装です。
この記事ですでに紹介した機能の追加に興味がある人にとっては、マップにプロットされたアイコンをカスタマイズするのがよい手始めになるでしょう。iconパラメーターとして、1つのURLを新たなマーカーオブジェクトに渡すというシンプルな作業です。
フルソースコードがGitHubで手に入るのを忘れないでください。
(原文:Harnessing the Google Maps JavaScript API the Right Way)
[翻訳:島田理彩]
[編集:Livit]
Image:dennizn / Shutterstock.com