Googleマップで応用編。

Google Maps JavaScript APIはJavaScriptで書かれてるので、JavaScriptを使えばいろいろ拡張できたりします:D
ひとつの地図にマーカーをたくさん置いたり、1ページにたくさん地図を設置したり、地図と他のコンテンツを連動させたり、更新しやすくする工夫とか、いろいろおさらいしながらまとめてゆければと思います。
まずはマーカー周りのあれやこれやをまとめます!

以下は目次です。クリックすると各項目へ移動します。

Google マップを表示する。

ひとまずザッとおさらい。シンプルな地図を表示します。
head要素内でGoogle Maps JavaScript APIを読み込んだらば、地図を表示するためのJavaScriptを書き込みます。

YOUR_API_KEYのところは、自分で取得したAPI キーに書き換えること!

あと、前に書いたGoogle Maps APIの記事と、ちょっと書き方が違うところが、API キーの後の&callback=initMap
body要素で、onload="initialize();"という風に関数を実行するんじゃなく、URLのcallbackというパラメータを使って実行するようにしてます(Google Developersサイトに合わせて関数の名前も変えました;p
それからdeferを指定してます。こちらについては後述

  1. <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" defer></script>

latlngと、mapOptionという変数を使って書いてるところを全部、変数を使わないで書くと以下のようになります。

Google Maps APIのMap()という関数が地図を作ります。
オプションのzoomは最初の拡大率、centerは最初の中心座標を指定します。

  1. var map = new google.maps.Map(document.getElementById('map_canvas'), {
  2. zoom: 10,
  3. center: new google.maps.LatLng(35.681382, 139.766084)
  4. });

そして、Marker()という関数がマーカーを作ります。
オプションのmapにはマーカーを置く地図の名前、positionはマーカーを置く座標を指定します。

  1. var marker = new google.maps.Marker({
  2. map: map,
  3. position: new google.maps.LatLng(35.681382, 139.766084)
  4. });

これにてひとまず、シンプルな地図を表示できるようになりました;D

Google Maps JavaScript APIでの地図の設置について、詳しくは下記ページを参照のこと。

上下スクロールで地図をズームしないようにする(使わない方がよい)

Webサイトをパソコンで見てる最中、不意にスクロールが止まって地図がズームされちゃうと、ちょっとウザいです:(
デフォルトの状態では、Google Maps APIのscrollwheelプロパティが有効になっていて、上下スクロールで、地図をズームイン・ズームアウトできるようになっているんですけれど、まずはこの機能を無効にしてしまいましょう。
マップオプションのところに、scrollwheelプロパティを追加して、falseを指定すれば、マウスホイールやトラックパッドの上下スクロールでのズーム機能が無効になります。

  1. var mapOptions = {
  2. zoom: 10,
  3. center: latlng,
  4. scrollwheel: false
  5. };

これでもうスクロール中に勝手にズームしたりしません。
全面地図の場合だとちょっと不便かもしれないけれど、長いページの中の地図なら、スクロールでズームされない方が都合が良いですものね;)

と思っていたのも束の間、マウスホイールでのズームイン・ズームアウトは、(ctrl)を押しながらでないとできなくなってました…XD

タッチデバイス(上)と、デスクトップ(下)のメッセージ

スマホなどタッチデバイスでは、地図上でフリック操作をすると「地図を移動させるには指2本で操作します」というメッセージが表示されますけれど、デスクトップなどマウスを使用するデバイスで、地図上でマウスホイールを動かすと「Use ⌘(ctrl) + scroll to zoom the map」というメッセージが表示されるようになってました。いずれ日本語で表示されるようになるのかな…?

scrollwheel: falseを指定すると、このメッセージも表示されなくなってしまうので、今後はこのプロパティは使わない方がよいですね…X)(2017.8.16追記)

目次に戻る

マーカーを常に中央に表示する。

ところで、ブラウザのウィンドウサイズを変えたり、スマホの向きを変えたりして、地図の大きさが変わると、マーカーの位置が地図の中央からズレちゃいますね。地図のサイズが変わっても、ずっと中央に我が社を表示していて欲しいところです。
地図のサイズが変わってもマーカーがちゃんと地図の中央に表示されるようにする方法を解説してゆきます。

リサイズイベントを利用する

JavaScriptではブラウザで何かが起きると「イベントハンドラ」というもので反応します。
クリックしたら「クリックしたね」と言ってclickというMouseEventが発生したり、スマホでタップした時には「タッチしてから離したね」と言ってtouchstarttouchendというTouchEventが発生したりします。
そんな中、ウィンドウのサイズが変わった時にはresizeというUIEventが発生します。

イベントハンドラについて詳しくは下記ページを参照のこと;)

window - Web API インターフェイス - イベントハンドラー | MDN

地図のサイズが変わるのは、ウィンドウサイズを変えたり、スマホの向きが変わった時だとすると、ウィンドウのサイズが変わった時のイベントを利用して、下記みたく指定することができます。

  1. window.onresize = function() {
  2. // ウィンドウをリサイズした時に起こること
  3. }

上記のように書くことで、ウィンドウをリサイズした時に実行する処理を登録することができるんです。つまりfunction() {}の波括弧の中に「マーカーを地図の中央に移動させる」ための処理を書けばよいということですね!

マーカーを中央に移動させるために、Google Maps APIにはsetCenter()というメソッドが用意されています。

ウィンドウをリサイズするたびに、マーカーの位置を地図の中央に移動することができました;)

けど、ウインドウサイズをグイグイ変更するとたまに中央にならないことがあるし、スマホの向きを変えた時はなんだか余計にズレちゃいます…X(

リサイズし終わったら動かす

たまに中央にならない理由は、リサイズ中ずっとmap.setCenter()の処理が発生していているため、処理が追っつかなくなってる状態なんです。スマホで向きを変えた時には、リサイズ中が一瞬すぎて、これもやっぱり処理が追っつかないのですね…。リサイズ中よりも、リサイズした後にちゃんと中央になってくれればいいのだけど…¦(

リサイズした後に1回だけ、map.setCenter()の処理を実行するようにするには、JavaScriptのsetTimeout()clearTimeout()というメソッドを利用して、以下のように書きます。

  1. var timeoutID;
  2. window.onresize = function() {
  3. clearTimeout(timeoutID);
  4. timeoutID = setTimeout(function() {
  5. map.setCenter(latlng);
  6. }, 200);
  7. }

setTimeout()は、指定したミリ秒後に、指定した関数を実行するための、タイマー機能みたいなことを実現するメソッドです。丸括弧の中に書いた関数を、その後カンマ区切りで書いた数値のミリ秒後に実行するようになります。ここではtimeoutIDという変数名のタイマーを用意しました。
そしてclearTimeout()は、そんなsetTimeout()で設定したタイマー機能を止めるためのメソッド。丸括弧の中には、止めたいタイマーの変数名を指定します。

リサイズ中は、「200ミリ秒(0.2秒)後に中央に移動するためのタイマー」を止めては動かして止めては動かしてを繰り返し、リサイズをやめると、タイマーを止めるのが止まって、0.2秒後にmap.setCenter()が実行される、という仕組み。

setTimeout()clearTimeout()について、詳しくは下記ページを参照のこと。

WindowOrWorkerGlobalScope.setTimeout() - Web API インターフェイス | MDN

けどなんだか中央への移動の仕方が、カチッと切り替わる感じで、一瞬、地図がどうなったのかわからなくなる時がありますね…:(map.setCenter()の代わりにmap.panTo()というメソッドを使うと、座標の移動がスムーズになってよいです。

  1. timeoutID = setTimeout(function() {
  2. map.panTo(latlng);
  3. }, 200);

うん、割とスムーズ:D


けれども、地図が画面いっぱいならば、ウィンドウのリサイズイベントを利用しても問題なかったですけれど、「地図に横幅があってレスポンシブ」というケースだとどうでしょう。
下のサンプルを、マーカーの位置を移動してからウィンドウをリサイズしてみると、地図の大きさが変わらなくてもmap.panTo()が実行されてるのがわかります。地図の大きさが変わった時だけ動けばいいんだけどな…。

地図の大きさが変わった時だけ動かす

ウィンドウをリサイズした時に「地図の大きさが変わった」ことが分かるためには、元の地図の大きさを知ってないといけません。「元の地図の大きさと、ウィンドウリサイズ後の地図の大きさを比べて、違ってたらmap.panTo()を実行する」、というふうにしたいです。

というわけでまずは、地図の要素のサイズを取得する必要があります。
JavaScriptで、要素のサイズを調べるには、clientWidthと、clientHeightという要素のプロパティを使って、横幅と高さを個別に調べます。

  1. var mapCanvas = document.getElementById('map_canvas');
  2. var mapWidth = mapCanvas.clientWidth;
  3. var mapHeight = mapCanvas.clientHeight;

getElementById()は、丸括弧の中に指定したid属性を持つ要素を取得するための、JavaScriptのメソッド。これを、変数mapCanvasに入れておきます。
この変数の後にドットで繋げてclientWidthと書くことで、その要素の横幅が取得できます。同じように、要素の高さclientHeightを使って取得して、それぞれ、mapWidthと、mapHeightという変数に入れておきます。

あとは、リサイズした後の処理のところで、この変数の値と、今の地図の大きさを比べて、横幅が違う、もしくは高さが違う時だけmap.panTo()を実行するようにすればよいわけですねー。

違う時と違わない時の処理を仕分けるには、「条件分岐」というJavaScriptにある便利な方法を使います。

条件となるとして、「リサイズ前とリサイズ後で、要素のサイズが違うかどうか」は、下記のように表せます。

  1. (mapWidth !== mapCanvas.clientWidth) || (mapHeight !== mapCanvas.clientHeight)

mapWidthは、リサイズ前に取得した横幅。これを、リサイズ後時点での横幅mapCanvas.clientWidthとで比較しています。比較には演算子という記号を使います。
!==は「左右の値が等しくない」ことを表す演算子、そして||は「もしくは」を表す演算子です。
なので上の記述で「リサイズ前の横幅と今の横幅が等しくない、もしくは、リサイズ前の高さと今の高さが等しくない」ということを表せてるってわけです:)

演算子について詳しくは下記ページを参照のこと。

式と演算子 - JavaScript | MDN

これを、if文を使って、さっきのsetTimeoutを使った処理の中に入れ込めば、要素のサイズが変わった時だけmap.panTo()が実行されるようになるはずです。※ここでは、条件が正しくない時に実行する処理はないのでelseはなくても大丈夫。
そして実行した後には、また今度リサイズした時に、リサイズ前の地図の要素サイズが分かるように、mapWidthmapHeightに、今の地図の要素サイズを入れ直しておきます。

  1. timeoutID = setTimeout(function() {
  2. if (mapWidth !== mapCanvas.clientWidth || mapHeight !== mapCanvas.clientHeight) {
  3. map.panTo(latlng);
  4. }
  5. mapWidth = mapCanvas.clientWidth;
  6. mapHeight = mapCanvas.clientHeight;
  7. }, 200);

地図の大きさが変わった時だけ、動くようになりました;D

目次に戻る

複数のマーカーを置く。

事業所の位置関係を示したい時とか、最寄りの駐車場を案内する時とかには、ひとつの地図の中に複数のマーカーを置いて示したいものです。ひとつの地図に複数のマーカーを置く方法を解説してゆきます。

マーカーをふたつ置く

まずはマーカーを置く座標を用意します。ここではとりあえず、スカイツリーの場所に置くことにします。
座標を調べるには下記サイトが便利ですよ;)

Googleマップの座標とか取得したい。 - Lopan.jp

「スカイツリー」を検索して出た座標を、latlng2という変数に入れておきます。

  1. var latlng2 = new google.maps.LatLng(35.7100627, 139.8107004);

続いて、ひとつめのマーカーmarkerをもうひとつコピペして、変数名をmarker2に書き換えます。
positionプロパティには、さっきのスカイツリーの座標を入れたlatlng2を指定します。
mapプロパティには、最初のマーカーとおなじくmapを指定ですね。

  1. var marker2 = new google.maps.Marker({
  2. position: latlng2,
  3. map: map
  4. });

あーっという間に、地図上にふたつマーカーが表示されました;)

マーカーをたくさん置く

それでは、さらに8箇所、座標を追加して、合計10個のマーカーを置いてみましょう。ここからはコピペじゃ追っつかないので、よりプログラミングぽい方法で進めてゆきます。

さっきはlatlnglatlng2と、変数をマーカーの数分用意しましたけれど、今度は「配列」を使って、latlngというひとつの変数に、座標を10個用意することにします。

配列を使って、座標を10個用意するとこんな感じ。

  1. var latlng = [
  2. new google.maps.LatLng(35.6811673, 139.7670516),
  3. new google.maps.LatLng(35.7100627, 139.8107004),
  4. new google.maps.LatLng(35.6796430, 139.8081122),
  5. ... 略 ...
  6. new google.maps.LatLng(35.6620300, 139.7026202),
  7. new google.maps.LatLng(35.6962920, 139.7681170)
  8. ];

ちょっとnew googleがいっぱいで、ボリューミーなので、もうちょっと簡単な書き方に変えます…。
実は、座標の指定方法はnew google.maps.LatLng()を使う他に、{ lat: 緯度, lng: 経度 }という記法でも指定できるので、そちらの記法で指定することにします。
latはlatitude(緯度)の略で、lngはlongitude(経度)の略。波括弧の中に、CSSのプロパティと値みたいな書き方で、緯度と経度を、カンマ区切りで並べて指定してます。

  1. var latlng = [
  2. { lat: 35.6811673, lng: 139.7670516 },
  3. { lat: 35.7100627, lng: 139.8107004 },
  4. { lat: 35.6796430, lng: 139.8081122 },
  5. ... 略 ...
  6. { lat: 35.6620300, lng: 139.7026202 },
  7. { lat: 35.6962920, lng: 139.7681170 },
  8. ];

うん、ちょっとスッキリ見やすくなりました。
さっきまで、マップオプションのcenterプロパティに指定していた東京駅の座標も、配列に入れてしまったので、centerプロパティの値は下記のように書き換えておきます。
東京駅の座標は1つめなのでlatlng[0]と指定すればよいですね。

  1. var mapOptions = {
  2. zoom: 10,
  3. center: latlng[0],
  4. scrollwheel: false
  5. };

あとはこの座標たちの位置に、マーカーを設置してゆけばよいわけです。

  1. var marker0 = new google.maps.Marker({ position: latlng[0], map: map });
  2. var marker1 = new google.maps.Marker({ position: latlng[1], map: map });
  3. var marker2 = new google.maps.Marker({ position: latlng[2], map: map });
  4. ...略...
  5. var marker8 = new google.maps.Marker({ position: latlng[8], map: map });
  6. var marker9 = new google.maps.Marker({ position: latlng[9], map: map });

上記みたいにひとつずつマーカーを設置してゆくのも良いですけれど、ここでは、「繰り返し処理」という、これまたJavaScriptにある便利な方法を使って、10個のマーカーをいっぺんに設置してみたいと思います。

この、1ずつ増えていくiの値を、for文の{}の中でlatlng[i]というふうに使えば、10個の座標を順番に指定してゆくことができますね;D
地図中に置くマーカーも、ぜんぶ違う変数にしなくちゃいけないわけですけれど、これも、for文の前にvar marker = [];というふうに変数を配列として用意しておいて、同じようにfor文の中でmarker[i]とすればいいです。

  1. var marker = [];
  2. for (var i = 0; i < 10; i++) {
  3. marker[i] = new google.maps.Marker({
  4. position: latlng[i],
  5. map: map
  6. });
  7. }

地図上にたくさんマーカーが表示されました;D

目次に戻る

オリジナルアイコンをたくさん置く。

ico_museum.png

デフォルトのマーカーでなくて、オリジナルのアイコンにしたい時には、マーカーのオプションにiconプロパティを追加して、アイコンに使用する画像を指定すればよいです。
ここでは右図のような画像を用意しました。
imageという変数に画像へのパスを入れて、これをiconプロパティに指定します。

  1. var image = '../img/s/ico_museum.png';
  2. for (var i = 0; i < 10; i++) {
  3. marker[i] = new google.maps.Marker({
  4. position: latlng[i],
  5. map: map,
  6. icon: image
  7. });
  8. }
スマホで見るとアイコンがボケてる

あれ?けれどこの地図をスマホで見てみると、右図みたく、ボケたアイコンになっちゃってます…:ᗡ。スマホは解像度が高いので、それに見合ってない画像はボケて表示されちゃうんです…:(

スマホでも綺麗に表示する

ボケちゃう理由は、スマホ・パソコンの違いというよりも、表示しているデバイスの「解像度(画素ピクセル密度)」の違いが原因です。ピクセルの密度が高いということは、ピクセルがそれだけ細かいということ。
例えば、AppleのRetinaディスプレイは、通常のディスプレイの2倍とか。それなのに、通常ディスプレイと見ための大きさがほぼ変わらないのは、何かしら調整してるからなのです。

ico_museum.png

そんなわけで、スマホなどの高精細なディスプレイでも画像を綺麗に表示させるためには、大きめの画像を用意しておけばよいということです。ここでは、さっきの4倍の大きさのアイコンを用意してみました。
ただ、このまま画像を変更するだけじゃ、地図上でも4倍のアイコンが表示されちゃうだけなので、アイコン画像の指定の仕方を変える必要があります。

変数imageの値を、以下のように修正します。
アイコンとして使いたい画像のURLアイコンの表示サイズ画像の左上隅の座標画像の支点画像の使用サイズを、それぞれプロパティ: 値という形式で、個別に指定します。

  1. var image = {
  2. url: '../img/l/ico_museum.png',
  3. size: new google.maps.Size(26,42),
  4. origin: new google.maps.Point(0,0),
  5. anchor: new google.maps.Point(13,42),
  6. scaledSize: new google.maps.Size(26,42)
  7. };

アイコン画像のオプションとして指定できるプロパティと、デフォルトの値は以下のとおり。


url: 'URL'
アイコン画像のURL。必須。
size: new google.maps.Size(画像の幅, 画像の高さ)
アイコンのサイズ。urlにスプライト画像(1枚の画像内にいろんなアイコンをいくつも並べた画像)を指定している場合は、このサイズでトリミングされる。
デフォルトは画像のサイズ、もしくはscaledSizeに指定したサイズ。
origin: new google.maps.Point(0, 0)
画像の左上隅の座標。スプライト画像を指定している場合には、スプライト画像内の、使いたいアイコン部分の左上隅の座標を指定すれば、そこを起点にsizeに指定したサイズでトリミングされる。
デフォルトは画像の左上隅
anchor: new google.maps.Point(アイコンの幅の半分, アイコンの高さ)
画像の支点。マーカーオプションでpositionプロパティに指定した座標の位置にくる、画像の位置を指定する。
デフォルトはアイコンの下辺の中央
scaledSize: new google.maps.Size(画像の幅, 画像の高さ)
画像の使用サイズ。スプライト画像を指定している場合は、アイコン部分だけのサイズじゃなくて、スプライト画像自体の使用サイズ。
デフォルトは画像の実際のサイズ

指定する値がデフォルトと同じならばハショっても大丈夫。なので、ここでは、sizeoriginanchorがハショれるやつです。というわけで、最終的にこれだけの指定だけでよくなりました。

  1. var image = {
  2. url: '../img/l/ico_museum.png',
  3. scaledSize: new google.maps.Size(26,42)
  4. };

スマホでも綺麗に表示されるようになりましたねー;D

目次に戻る

解像度が高いデバイスだけ大きい画像を使う

今は通常ディスプレイでも高精細ディスプレイでも構わず大きい画像を使っているけれども、デバイスの解像度に合わせて画像の大きさを出し分けられたら、です¦D
前述のwindow.devicePixelRatioを使えば、デバイスのデバイス・ピクセル比が分かるので、これを条件として利用すればイケそうです。

window.devicePixelRatio2以上である」なら「imgフォルダ」の中の「lフォルダ」に入れた大きな画像を使うようにして、そうじゃないなら「imgフォルダ」の中の「sフォルダ」に入れた小さな画像を使うようにします。

  1. if (window.devicePixelRatio >= 2) {
  2. var imgPath = '../img/l/ico_museum.png';
  3. } else {
  4. var imgPath = '../img/s/ico_museum.png';
  5. }
  6. var image = {
  7. url: imgPath,
  8. scaledSize: new google.maps.Size(26,42)
  9. };

イケましたー:D

場所ごとに違うアイコンにする

店舗ごとに取り扱うジャンルが違ったり、事務所と工場を区別したい時なんかには、場所ごとに違うアイコンを置きたいです。サンプルも、今のところぜんぶ美術館を表すアイコンになってますけれど、東京駅は駅のアイコン、スカイツリーはスカイツリーのアイコン、ギャラリーにはギャラリーのアイコンに変えた方がわかりやすいですものね:)

場所ごとにアイコンを変えるということは、アイコンを置く時に画像を出し分けなきゃいけないということです。ということは、繰り返し処理の中で、「ここはこのアイコン、そこはそのアイコン」てな具合に判断できるように、場所ごとにアイコンを指定しておく必要があるということですねー。

そんなわけで、ここでは、前述までのサンプルのlatlngという変数の配列の中に、場所ごとのアイコンも入れ込んじゃうことにします!つまり以下のようになります。

  1. var data = [
  2. { lat: 35.6811673, lng: 139.7670516, ico: 'ico_station.png' },
  3. { lat: 35.7100627, lng: 139.8107004, ico: 'ico_skytree.png' },
  4. { lat: 35.6796430, lng: 139.8081122, ico: 'ico_museum.png' },
  5. ... 略 ...
  6. { lat: 35.6620300, lng: 139.7026202, ico: 'ico_gallery.png' },
  7. { lat: 35.6962920, lng: 139.7681170, ico: 'ico_gallery.png' }
  8. ];

latlngのプロパティに加えて、icoというプロパティを作って、その値にアイコン画像名を指定しました。それから、内容が座標だけじゃなくなったので、変数名をdataに変えました;p

となると、マップオプションのcenterプロパティの値は、latlng[0]のままだとicoも入っちゃうので、修正しないといけないです。新たにcenterという変数を用意して、その中に、{ lat: 緯度, lng: 経度 }を入れる事にします。
東京駅の座標を地図の中央にしたいので、data[0].latと、data[0].lngを使って以下のようにします;)

  1. var center = { lat: data[0].lat, lng: data[0].lng };
  2. var mapOptions = {
  3. zoom: 10,
  4. center: center,
  5. scrollwheel: false
  6. };

あと、マーカーオプションのpositionプロパティの値も同じように修正します。同じくcenterを使えばよいですね。

  1. marker[i] = new google.maps.Marker({
  2. position: center,
  3. map: map,
  4. icon: image
  5. });

そして、アイコンのところはというと、さっきまでは画像名までをimgPathという変数に入れてたところを、画像名の手前までに留めておいて、画像名は繰り返し処理の中で、data[i].icoというふうに入れるようにします。
プロパティの値には、imgPath + data[i].icoと書けば、文字列が繋がって'../img/l/ico_museum.png'となるわけです:)

  1. if (window.devicePixelRatio >= 2) {
  2. var imgPath = '../img/l/';
  3. } else {
  4. var imgPath = '../img/s/';
  5. }
  6. for (var i = 0; i < 10; i++) {
  7. var image = {
  8. url: imgPath + data[i].ico,
  9. scaledSize: new google.maps.Size(26,42)
  10. };
  11. ... 略 ...
  12. }

場所ごとに、指定したアイコンが表示されるようになりましたね:D

目次に戻る

情報ウィンドウを表示する。

マーカーがたくさんあると、どのマーカーがどこなんだか分からなくなるので、クリックしたらフキダシで情報が表示されるようにすれば便利です。マーカーにそれぞれ情報ウィンドウを表示する方法を解説してゆきます。

マーカーごとに違う情報ウィンドウを表示する

とにもかくにも、情報ウィンドウに表示するための情報を用意しないと始まりません。どこに用意するかというと…、これまた、配列の中にまとめちゃいますXD
latlngicoというプロパティに加え、nameaddressというプロパティを用意して、各場所ごとに、名前と住所をじゃんじゃか書き込んでいきます。

  1. var data = [
  2. { lat: 35.6811673, lng: 139.7670516, ico: 'ico_station.png', name: '東京駅', address: '東京都千代田区丸の内1' },
  3. { lat: 35.7100627, lng: 139.8107004, ico: 'ico_skytree.png', name: '東京スカイツリー', address: '東京都墨田区押上1-1-2' },
  4. { lat: 35.6796430, lng: 139.8081122, ico: 'ico_museum.png', name: '東京都現代美術館', address: '東京都江東区三好4-1−1' },
  5. ... 略 ...
  6. { lat: 35.6620300, lng: 139.7026202, ico: 'ico_gallery.png', name: 'DIESEL ART GALLERY', address: '東京都渋谷区渋谷1-23-16 cocoti B1F' },
  7. { lat: 35.6962920, lng: 139.7681170, ico: 'ico_gallery.png', name: 'AWAJI Cafe and Gallery', address: '東京都千代田区神田淡路町2-4-6 エフアンドエフロイヤルビル1F' },
  8. ];

…できました!

情報ウィンドウを作るには、Google Maps APIのInfoWindow()という関数を使います。以下のように書けば、infowindowという変数名で、情報ウィンドウがひとつ作られます。

  1. var infowindow = new google.maps.InfoWindow({
  2. content: '<h1>東京駅</h1><p>東京都千代田区丸の内1</p>',
  3. maxWidth: 200
  4. });

contentプロパティの値には、情報ウィンドウの中に載せる情報を指定します。htmlも使えるので、名前をh1要素に、住所はp要素で括ってみました。あと、長い住所だと、情報ウィンドウがその分、横に広がってしまうといけないので、maxWidthプロパティで最大横幅を200pxとなるようにしています。
これをマーカーと同じように、繰り返し処理の中で、contentプロパティの値を順々に変えながら作っていけばよいわけですねー。

情報ウィンドウのオプションに指定できるプロパティについては下記ページの表を参照のこと。

Google Maps JavaScript API V3 Reference - InfoWindowOptions object specification

情報ウィンドウもmarkerと同じく複数作られることになるので、先にinfowindowという変数を配列として用意しておいて、for文の中ではinfowindow[i]として使います。
名前のところはdata[i].name、住所のところはdata[i].addressとしたhtmlを、一旦contentという変数に入れて、それをcontentプロパティの値として使うようにします。つまり以下のようにします!

  1. var infowindow = [];
  1. for (var i = 0, len = data.length; i < len; i++) {
  2. ... 略 ...
  3. var content = '<h1>' + data[i].name + '</h1><p>' + data[i].address + '</p>';
  4. infowindow[i] = new google.maps.InfoWindow({
  5. content: content,
  6. maxWidth: 200
  7. });
  8. }

けどあれ?何にも表示されない…:ᗡ
これだけだとまだ、情報ウィンドウが用意されただけ:(。マーカーをクリックした時に表示されるように設定しなくちゃいけないんですね…Xo

マーカーがクリックされた、というイベントを見つけるために、Googla MapsAPIのaddListener()イベントハンドラ、というのを使います。マーカーの変数marker[i]と、addListener()をドットで繋いで、丸括弧の中に、見つけたいイベント名と、そのイベントがあった時に実行する処理を書きます。

  1. marker[i].addListener('click', function() {
  2. // マーカーがクリックされた時に実行する処理
  3. });

情報ウィンドウを開くには、Googla MapsAPIのopen()メソッドを使います。情報ウィンドウの変数とopen()をドットで繋げばよいです。丸括弧の中には、情報ウィンドウを開く地図の変数名と、情報ウィンドウに紐づけるマーカーの変数名を指定します。

  1. marker[i].addListener('click', function() {
  2. infowindow[i].open(map, marker[i]);
  3. });

けどあれれ?マーカーをクリックしても何にも表示されない…:ᗡ

実は、クリックされた時に実行する処理を設定した場所がいけなかったみたい。
繰り返し処理の中で、マーカーをmarker[0]からmarker[9]まで作って、そのmarker[0]からmarker[9]に対してクリックイベントを設定したんですけれど、実際にクリックする頃には、[]の中で使ってたi10なんです。for文が終わっているということは、i10になってるんです…X(
なので、どのマーカーをクリックしてもmarker[10]をクリックしてることになっちゃうんですね…:(

ではどうすればよいかというと、クリックされた時に実行する処理の設定を、関数にするという手があります。

ここでは、markerClickという名前の関数を作ることにします。
引数はnとして、for文で使ってたiの値を、ここではnとして流用するようにしてます。

  1. function markerClick(n) {
  2. marker[n].addListener('click', function() {
  3. infowindow[n].open(map, marker[n]);
  4. });
  5. }

関数にすることで、for文の中のi10になったとしても、それとは別のところで、クリックされた時に実行する処理を設定してるから、問題ない、ということなんです。for文で使ってるiとの関連を切って、marker[n]に対して、クリックされた時に実行する処理を設定してる、ってことになるんですねー:o

この関数をfor文の中で実行するためには、下記のように書きます。丸括弧の中にはiを引数として入れます。これが、markerClick()関数の中でnとして使われる事になります。

  1. markerClick(i);

無事、マーカーをクリックで情報ウィンドウが表示されるようになりましたーXD

けどちょっとタイトルが大きすぎるしバランスが悪いですね…。情報ウィンドウは、CSSで見た目を調整することができるんです。
さっそく、head要素内にstyle要素を用意して、CSSを書いていきましょう。CSSで要素を指定しやすいように、情報ウィンドウ内に表示されるhtmlをちょっと修正します。
h1要素とp要素をdiv要素で括って、infoというclass名を付けてみました。

  1. var content = '<div class="info"><h1>' + data[i].name + '</h1><p>' + data[i].address + '</p></div>';

.infoという要素の中のh1pのスタイルを調整します。

  1. .info h1 {
  2. margin: 0 0 .5em;
  3. font-size: 1.4em;
  4. }
  5. .info p {
  6. margin: 0;
  7. font-size: 1em;
  8. }

なんとか、落ち着いた感じになりましたねー:)

目次に戻る

情報ウィンドウはひとつずつ表示する

マーカーをクリックするたびにどんどん情報ウィンドウが表示されて、「×」マークをクリックしないと閉じないなんて、ちょっとウザいです:(。マーカーをクリックしたら、開いてる情報ウィンドウは閉じて、常にひとつずつ表示されるようにできたら、きっと見やすいです。
そのためには、マーカーをクリックした時に、情報ウィンドウが表示されているかどうかが分からないといけません。
そこで、まずはopenedInfowindowという変数を、最初は値を何も入れずに、変数名だけ用意しておきます。そして、情報ウィンドウを開いた時に、その「情報ウィンドウ自体」を、この変数に保持しておくんです。

  1. var openedInfowindow;

マーカーをクリックした時に、その変数に値(=いま開いている情報ウィンドウ)が入っていたら、それを閉じるようにします。
if文の条件にはopenedInfowindowだけ書いておけば、値が何もない時は{}の中は実行せず、何か値が入っていたら実行することになります。

情報ウィンドウを閉じるには、Google Maps APIのclose()メソッドを使います。丸括弧の中は空っぽでOK。

  1. function markerClick(n) {
  2. marker[n].addListener('click', function() {
  3. if (openedInfowindow) {
  4. openedInfowindow.close();
  5. }
  6. openedInfowindow = infowindow[n];
  7. infowindow[n].open(map, marker[n]);
  8. });
  9. }

マーカーをクリックするたびに、前まで開いていたマーカーが閉じられるようになりましたね:D

しかし!下のサンプルを見てみてください。

開くたび閉じてる (確認用) - Google マップで応用編。

マーカーをクリックして情報ウィンドウを閉じるところでアラートを出すようにしてみたら、情報ウィンドウを「×」で閉じてても、マーカーをクリックすると「閉じるよ」と出るし、同じマーカーを2度クリックした時も「閉じるよ」と、close()が実行されてるみたいです。

  1. marker[n].addListener('click', function() {
  2. if (openedInfowindow) {
  3. alert('閉じるよ');
  4. openedInfowindow.close();
  5. }
  6. ... 略 ...

ぜひここは、情報ウィンドウが開いてる時だけ、閉じるようにしたいところです:(

情報ウィンドウが開いてる時だけ閉じるようにする

まず、同じマーカーを2度クリックした時に閉じないように、下記のように条件を作って、openedInfowindowに入っている情報ウィンドウと、いま開こうとしている情報ウィンドウが違う時だけ、閉じたり開いたりするようにすることにします。

  1. marker[n].addListener('click', function() {
  2. if (openedInfowindow !== infowindow[n]) {
  3. if (openedInfowindow) {
  4. alert('閉じるよ');
  5. openedInfowindow.close();
  6. }
  7. openedInfowindow = infowindow[n];
  8. infowindow[n].open(map, marker[n]);
  9. }
  10. });

同じマーカーをクリックしても閉じなくなりました:)

同じマーカーをクリックしても閉じなくなった (確認用) - Google マップで応用編。

あとは、情報ウィンドウを「×」で閉じてるのに、マーカーをクリックすると閉じちゃうのをなんとかしたいです。
情報ウィンドウの「×」をクリックしたら、開いてる情報ウィンドウがなくなるわけなので、つまり、openedInfowindow空っぽに戻さないといけないってことです。
Google Maps APIのリファレンスによると、情報ウィンドウの「×」をクリックした時に、'closeclick'というイベントが発生するとのことなので、これをaddListener()で登録すればできそうです。

Google Maps JavaScript API V3 Reference - InfoWindow class

以下のように、infowindow[i]に対して、closeclickイベントが発生した時に、openedInfowindowの値を空っぽにするための処理を書きます。

  1. markerClick(i);
  2. infowindow[i].addListener('closeclick', function() {
  3. openedInfowindow = '';
  4. });
  5. }

情報ウィンドウが閉じてれば、閉じなくなりました!つまり、違うマーカーをクリックした時だけ、閉じて開くようになりましたね;D

開いてなければ閉じなくなった (確認用) - Google マップで応用編。

アラートをなくしてできあがりーX)

目次に戻る

あとがき。

取り急ぎ、マーカーをたくさん表示したりする方法のまとめでした。

次回は、1ページの中に地図をたくさん表示したり、座標じゃなくて住所や名称で場所を指定するようにしたり、JSONに情報をまとめたりするのを、まとめたいと思います。
あと!地図の見た目をカスタマイズすることができるサイト「The Styled Map Wizard」が、新たに「Google Maps APIs Styling Wizard」として生まれ変わってたので、使い方を紹介できればなと思ってます:)

といったところで今回はお開き、また次回のお楽しみ! To Be Continued...X)

Comment & Pingback

コメントを残す